Panda3D
qtessInputFile.cxx
1 // Filename: qtessInputFile.cxx
2 // Created by: drose (13Oct03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "qtessInputFile.h"
16 #include "config_egg_qtess.h"
17 #include "string_utils.h"
18 
19 ////////////////////////////////////////////////////////////////////
20 // Function: QtessInputFile::Constructor
21 // Access: Public
22 // Description:
23 ////////////////////////////////////////////////////////////////////
24 QtessInputFile::
25 QtessInputFile() {
26 }
27 
28 ////////////////////////////////////////////////////////////////////
29 // Function: QtessInputFile::read
30 // Access: Public
31 // Description: reads the input file.
32 ////////////////////////////////////////////////////////////////////
34 read(const Filename &filename) {
35  _filename = Filename::text_filename(filename);
36  _entries.clear();
37 
38  ifstream input;
39  if (!_filename.open_read(input)) {
40  qtess_cat.error()
41  << "Unable to open input file " << _filename << "\n";
42  return false;
43  }
44 
45  string complete_line;
46 
47  int line_number = 0;
48  string line;
49  while (getline(input, line)) {
50  line_number++;
51 
52  // Eliminate comments. We have to scan the line repeatedly until
53  // we find the first hash mark that's preceded by whitespace.
54  size_t comment = line.find('#');
55  while (comment != string::npos) {
56  if (comment == 0 || isspace(line[comment - 1])) {
57  line = line.substr(0, comment);
58  comment = string::npos;
59 
60  } else {
61  comment = line.find('#', comment + 1);
62  }
63  }
64 
65  // Check for a trailing backslash: continuation character.
66  line = trim_right(line);
67  if (!line.empty() && line[line.size() - 1] == '\\') {
68  // We have a continuation character; go back and read some more.
69  complete_line += line.substr(0, line.size() - 1);
70 
71  } else {
72  // It's a complete line. Begin parsing.
73  line = trim(complete_line + line);
74  complete_line = string();
75 
76  if (!line.empty()) {
77  QtessInputEntry entry;
78 
79  // Scan for the first colon followed by whitespace.
80  size_t colon = line.find(": ");
81  if (colon == string::npos) {
82  qtess_cat.error()
83  << _filename << ": line " << line_number
84  << " has no colon followed by whitespace.\n";
85  return false;
86  }
87  if (colon == 0) {
88  qtess_cat.error()
89  << _filename << ": line " << line_number
90  << " has no nodes.\n";
91  return false;
92  }
93 
94  // Split the line into two groups of words at the colon: names
95  // before the colon, and params following it.
96  vector_string names, params;
97  extract_words(line.substr(0, colon), names);
98  extract_words(line.substr(colon + 1), params);
99 
100  vector_string::const_iterator ni;
101  for (ni = names.begin(); ni != names.end(); ++ni) {
102  entry.add_node_name(*ni);
103  }
104 
105  // Scan for things like ap, ad, ar, and pull them out of the
106  // stream.
107  vector_string::iterator ci, cnext;
108  ci = params.begin();
109  while (ci != params.end()) {
110  cnext = ci;
111  ++cnext;
112 
113  string param = *ci;
114  bool invert = false;
115  if (param[0] == '!' && param.size() > 1) {
116  invert = true;
117  param = param.substr(1);
118  }
119  if (tolower(param[0]) == 'a' && param.size() > 1) {
120  switch (tolower(param[1])) {
121  case 'p':
122  entry._auto_place = !invert;
123  break;
124 
125  case 'd':
126  entry._auto_distribute = !invert;
127  break;
128 
129  case 'r':
130  if (!string_to_double(param.substr(2), entry._curvature_ratio)) {
131  qtess_cat.error()
132  << _filename << ": line " << line_number
133  << " - invalid field " << param << "\n";
134  return false;
135  }
136  break;
137 
138  default:
139  qtess_cat.error()
140  << _filename << ": invalid parameters at line "
141  << line_number << ".\n";
142  return false;
143  }
144  params.erase(ci);
145  } else {
146  ci = cnext;
147  }
148  }
149 
150  if (!params.empty()) {
151  bool okflag = true;
152  if (cmp_nocase(params[0], "omit")==0) {
153  entry.set_omit();
154 
155  } else if (cmp_nocase(params[0], "matchuu")==0) {
156  entry.set_match_uu();
157  if (params.size() > 1 && cmp_nocase(params[1], "matchvv")==0) {
158  entry.set_match_vv();
159  }
160 
161  } else if (cmp_nocase(params[0], "matchvv")==0) {
162  entry.set_match_vv();
163  if (params.size() > 1 && cmp_nocase(params[1], "matchuu")==0) {
164  entry.set_match_uu();
165  }
166 
167  } else if (cmp_nocase(params[0], "matchuv")==0) {
168  entry.set_match_uv();
169  if (params.size() > 1 && cmp_nocase(params[1], "matchvu")==0) {
170  entry.set_match_vu();
171  }
172 
173  } else if (cmp_nocase(params[0], "matchvu")==0) {
174  entry.set_match_vu();
175  if (params.size() > 1 && cmp_nocase(params[1], "matchuv")==0) {
176  entry.set_match_uv();
177  }
178 
179  } else if (cmp_nocase(params[0], "minu")==0) {
180  // minu #: minimum tesselation in U.
181  if (params.size() < 2) {
182  okflag = false;
183  } else {
184  int value = 0;
185  okflag = string_to_int(params[1], value);
186  entry.set_min_u(value);
187  }
188 
189  } else if (cmp_nocase(params[0], "minv")==0) {
190  // minu #: minimum tesselation in V.
191  if (params.size() < 2) {
192  okflag = false;
193  } else {
194  int value = 0;
195  okflag = string_to_int(params[1], value);
196  entry.set_min_v(value);
197  }
198 
199  } else if (tolower(params[0][0]) == 'i') {
200  // "i#": per-isoparam tesselation.
201  int value = 0;
202  okflag = string_to_int(params[0].substr(1), value);
203  entry.set_per_isoparam(value);
204 
205  } else if (params[0][params[0].length() - 1] == '%') {
206  double value = 0.0;
207  okflag = string_to_double(params[0].substr(0, params[0].length() - 1), value);
208  entry.set_importance(value / 100.0);
209 
210  } else if (params.size() == 1) {
211  // One numeric parameter: the number of triangles.
212  int value = 0;
213  okflag = string_to_int(params[0], value);
214  entry.set_num_tris(value);
215 
216  } else if (params.size() >= 2) {
217  // Two or more numeric parameters: the number of u by v quads,
218  // followed by an optional list of specific isoparams.
219  int u = 0, v = 0;
220  okflag = string_to_int(params[0], u) && string_to_int(params[1], v);
221  entry.set_uv(u, v, &params[2], params.size() - 2);
222 
223  } else {
224  okflag = false;
225  }
226 
227  if (!okflag) {
228  qtess_cat.error()
229  << _filename << ": invalid parameters at line "
230  << line_number << ".\n";
231  return false;
232  }
233  }
234  _entries.push_back(entry);
235  }
236  }
237  }
238 
239  if (qtess_cat.is_info()) {
240  qtess_cat.info()
241  << "read qtess parameter file " << _filename << ".\n";
242  if (qtess_cat.is_debug()) {
243  write(qtess_cat.debug(false));
244  }
245  }
246 
247  add_default_entry();
248 
249  return true;
250 }
251 
252 ////////////////////////////////////////////////////////////////////
253 // Function: QtessInputFile::get_default_entry
254 // Access: Public
255 // Description: Returns a reference to the last entry on the list,
256 // which is the "default" entry that will match any
257 // surface that does not get explicitly named in the
258 // input file.
259 ////////////////////////////////////////////////////////////////////
262  if (_entries.empty()) {
263  // No entries; create one.
264  add_default_entry();
265  }
266  return _entries.back();
267 }
268 
269 
270 ////////////////////////////////////////////////////////////////////
271 // Function: QtessInputFile::match
272 // Access: Public
273 // Description: Attempts to find a match for the given surface in the
274 // user input entries. Searches in the order in which
275 // the entries were defined, and chooses the first
276 // match.
277 //
278 // When a match is found, the surface is added to the
279 // entry's set of matched surfaces. Returns the type of
280 // the matching node if a match is found, or T_undefined
281 // otherwise.
282 ////////////////////////////////////////////////////////////////////
283 QtessInputEntry::Type QtessInputFile::
284 match(QtessSurface *surface) {
285  QtessInputEntry::Type type;
286 
287  if (_entries.empty()) {
288  // No entries; create one.
289  add_default_entry();
290  }
291 
292  Entries::iterator ei;
293  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
294  type = (*ei).match(surface);
295  if (type != QtessInputEntry::T_undefined) {
296  return type;
297  }
298  }
299  return QtessInputEntry::T_undefined;
300 }
301 
302 ////////////////////////////////////////////////////////////////////
303 // Function: QtessInputFile::count_tris
304 // Access: Public
305 // Description: Determines the tesselation u,v amounts of each
306 // attached surface, and stores this information in the
307 // surface pointer. Returns the total number of tris
308 // that will be produced.
309 ////////////////////////////////////////////////////////////////////
312  int total_tris = 0;
313 
314  Entries::iterator ei;
315  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
316  total_tris += (*ei).count_tris();
317  }
318  return total_tris;
319 }
320 
321 ////////////////////////////////////////////////////////////////////
322 // Function: QtessInputFile::write
323 // Access: Public
324 // Description:
325 ////////////////////////////////////////////////////////////////////
326 void QtessInputFile::
327 write(ostream &out, int indent_level) const {
328  Entries::const_iterator ei;
329  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
330  (*ei).write(out, indent_level);
331  }
332 }
333 
334 ////////////////////////////////////////////////////////////////////
335 // Function: QtessInputFile::add_default_entry
336 // Access: Private
337 // Description: Adds one more entry to the end of the list, to catch
338 // all of the surfaces that didn't get explicitly named.
339 ////////////////////////////////////////////////////////////////////
340 void QtessInputFile::
341 add_default_entry() {
342  QtessInputEntry entry("*");
343  entry.set_omit();
344  _entries.push_back(entry);
345 }
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:34
QtessInputEntry::Type match(QtessSurface *surface)
Attempts to find a match for the given surface in the user input entries.
int count_tris()
Determines the tesselation u,v amounts of each attached surface, and stores this information in the s...
bool open_read(ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:2003
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
QtessInputEntry & get_default_entry()
Returns a reference to the last entry on the list, which is the "default" entry that will match any s...
Stores one entry in the qtess input file.
bool read(const Filename &filename)
reads the input file.