Panda3D
qtessInputEntry.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file qtessInputEntry.cxx
10  * @author drose
11  * @date 2003-10-13
12  */
13 
14 #include "qtessInputEntry.h"
15 #include "qtessSurface.h"
16 #include "qtessGlobals.h"
17 #include "config_egg_qtess.h"
18 #include "indent.h"
19 #include "string_utils.h"
20 
21 #include <ctype.h>
22 #include <algorithm>
23 
24 using std::string;
25 
26 /**
27  *
28  */
29 QtessInputEntry::
30 QtessInputEntry(const string &name) {
31  _type = T_undefined;
32  _num_patches = 0.0;
33  _auto_place = QtessGlobals::_auto_place;
34  _auto_distribute = QtessGlobals::_auto_distribute;
35  _curvature_ratio = QtessGlobals::_curvature_ratio;
36  if (!name.empty()) {
37  add_node_name(name);
38  }
39 }
40 
41 /**
42  *
43  */
44 void QtessInputEntry::
45 operator = (const QtessInputEntry &copy) {
46  _node_names = copy._node_names;
47  _type = copy._type;
48  _num_tris = copy._num_tris;
49  _num_u = copy._num_u;
50  _num_v = copy._num_v;
51  _per_isoparam = copy._per_isoparam;
52  _iso_u = copy._iso_u;
53  _iso_v = copy._iso_v;
54  _surfaces = copy._surfaces;
55  _num_patches = copy._num_patches;
56  _auto_place = copy._auto_place;
57  _auto_distribute = copy._auto_distribute;
58  _curvature_ratio = copy._curvature_ratio;
59  _importance = copy._importance;
60  _constrain_u = copy._constrain_u;
61  _constrain_v = copy._constrain_v;
62 }
63 
64 /**
65  * An STL function object to determine if two doubles are very nearly equal.
66  * Used in set_uv(), below.
67  */
68 class DoublesAlmostEqual {
69 public:
70  int operator ()(double a, double b) const {
71  return fabs(a - b) < 0.00001;
72  }
73 };
74 
75 /**
76  * An STL function object to determine if a double is vert nearly equal the
77  * supplied value . Used in set_uv(), below.
78  */
79 class DoubleAlmostMatches {
80 public:
81  DoubleAlmostMatches(double v) : _v(v) {}
82  int operator ()(double a) const {
83  return fabs(a - _v) < 0.00001;
84  }
85  double _v;
86 };
87 
88 
89 /**
90  * Sets specific tesselation. The tesselation will be u by v quads, with the
91  * addition of any isoparams described in the list of params.
92  */
93 void QtessInputEntry::
94 set_uv(int u, int v, const string params[], int num_params) {
95  _num_u = u;
96  _num_v = v;
97 
98  // First, fill up the arrays with the defaults.
99  int i;
100  for (i = 0; i <= _num_u; i++) {
101  _iso_u.push_back(i);
102  }
103  for (i = 0; i <= _num_v; i++) {
104  _iso_v.push_back(i);
105  }
106 
107  // Then get out all the additional entries.
108  for (i = 0; i < num_params; i++) {
109  const string &param = params[i];
110 
111  if (param[0] == '!' && param.size() > 2) {
112  double value;
113  if (!string_to_double(param.substr(2), value)) {
114  qtess_cat.warning()
115  << "Ignoring invalid parameter: " << param << "\n";
116  } else {
117  switch (tolower(param[1])) {
118  case 'u':
119  _auto_place = false;
120  _iso_u.erase(remove_if(_iso_u.begin(), _iso_u.end(),
121  DoubleAlmostMatches(value)),
122  _iso_u.end());
123  break;
124 
125  case 'v':
126  _auto_place = false;
127  _iso_v.erase(remove_if(_iso_v.begin(), _iso_v.end(),
128  DoubleAlmostMatches(value)),
129  _iso_v.end());
130  break;
131 
132  default:
133  qtess_cat.warning()
134  << "Ignoring invalid parameter: " << params[i] << "\n";
135  }
136  }
137  } else {
138  double value;
139  if (!string_to_double(param.substr(1), value)) {
140  qtess_cat.warning()
141  << "Ignoring invalid parameter: " << param << "\n";
142  } else {
143  switch (tolower(param[0])) {
144  case 'u':
145  _auto_place = false;
146  _iso_u.push_back(value);
147  break;
148 
149  case 'v':
150  _auto_place = false;
151  _iso_v.push_back(value);
152  break;
153 
154  default:
155  qtess_cat.warning()
156  << "Ignoring invalid parameter: " << params[i] << "\n";
157  }
158  }
159  }
160  }
161 
162  // Now sort them into ascending order and remove duplicates.
163  sort(_iso_u.begin(), _iso_u.end());
164  sort(_iso_v.begin(), _iso_v.end());
165  _iso_u.erase(unique(_iso_u.begin(), _iso_u.end(), DoublesAlmostEqual()), _iso_u.end());
166  _iso_v.erase(unique(_iso_v.begin(), _iso_v.end(), DoublesAlmostEqual()), _iso_v.end());
167 
168  _type = T_uv;
169 }
170 
171 
172 /**
173  * May be called a number of times before set_uv() to add specific additional
174  * isoparams to the tesselation.
175  */
177 add_extra_u_isoparam(double u) {
178  _iso_u.push_back(u);
179 }
180 
181 /**
182  * May be called a number of times before set_uv() to add specific additional
183  * isoparams to the tesselation.
184  */
186 add_extra_v_isoparam(double v) {
187  _iso_v.push_back(v);
188 }
189 
190 /**
191  * Tests the surface to see if it matches any of the regular expressions that
192  * define this node entry. If so, adds it to the set of matched surfaces and
193  * returns the type of the matching entry. If no match is found, returns
194  * T_undefined.
195  */
196 QtessInputEntry::Type QtessInputEntry::
197 match(QtessSurface *surface) {
198  const string &name = surface->get_name();
199 
200  NodeNames::const_iterator nni;
201  for (nni = _node_names.begin();
202  nni != _node_names.end();
203  ++nni) {
204  const GlobPattern &pattern = (*nni);
205  if (pattern.matches(name)) {
206  // We have a winner!
207  switch (_type) {
208  case T_importance:
209  // A type of "Importance" is a special case. This entry doesn't
210  // specify any kind of tesselation on the surface, and in fact doesn't
211  // preclude the surface from matching anything later. It just
212  // specifies the relative importance of the surface to all the other
213  // surfaces.
214  if (qtess_cat.is_debug()) {
215  qtess_cat.debug()
216  << "Assigning importance of " << _importance*100.0
217  << "% to " << name << "\n";
218  }
219  surface->set_importance(_importance);
220  return T_undefined;
221 
222  case T_match_uu:
223  case T_match_uv:
224  // Similarly for type "matchUU". This indicates that all the surfaces
225  // that match this one must all share the U-tesselation with whichever
226  // surface first matched against the first node name.
227  if (nni == _node_names.begin() && _constrain_u==nullptr) {
228  // This is the lucky surface that dominates!
229  _constrain_u = surface;
230  } else {
231  if (_type == T_match_uu) {
232  surface->set_match_u(&_constrain_u, true);
233  } else {
234  surface->set_match_v(&_constrain_u, false);
235  }
236  }
237  return T_undefined;
238 
239  case T_match_vv:
240  case T_match_vu:
241  // Ditto for "matchVV".
242  if (nni == _node_names.begin() && _constrain_v==nullptr) {
243  // This is the lucky surface that dominates!
244  _constrain_v = surface;
245  } else {
246  if (_type == T_match_vv) {
247  surface->set_match_v(&_constrain_v, true);
248  } else {
249  surface->set_match_u(&_constrain_v, false);
250  }
251  }
252  return T_undefined;
253 
254  case T_min_u:
255  // And for min U and V.
256  if (qtess_cat.is_debug()) {
257  qtess_cat.debug()
258  << "Assigning minimum of " << _num_u << " in U to "
259  << name << "\n";
260  }
261  surface->set_min_u(_num_u);
262  return T_undefined;
263 
264  case T_min_v:
265  if (qtess_cat.is_debug()) {
266  qtess_cat.debug()
267  << "Assigning minimum of " << _num_v << " in V to "
268  << name << "\n";
269  }
270  surface->set_min_v(_num_v);
271  return T_undefined;
272 
273  default:
274  _surfaces.push_back(surface);
275  if (_auto_distribute) {
276  _num_patches += surface->get_score(_curvature_ratio);
277  } else {
278  _num_patches += surface->count_patches();
279  }
280  return _type;
281  }
282  }
283  }
284 
285  return T_undefined;
286 }
287 
288 /**
289  * Determines the tesselation u,v amounts of each attached surface, and stores
290  * this information in the surface pointer. Returns the total number of tris
291  * that will be produced.
292  */
294 count_tris(double tri_factor, int attempts) {
295  int total_tris = 0;
296  bool aim_for_tris = false;
297 
298  if (_type == T_num_tris && _num_patches > 0.0) {
299  // If we wanted to aim for a particular number of triangles for the group,
300  // choose a per-isoparam setting that will approximately achieve this.
301  if (_auto_distribute) {
302  set_per_score(sqrt(0.5 * (double)_num_tris / _num_patches / tri_factor));
303  } else {
304  set_per_isoparam(sqrt(0.5 * (double)_num_tris / _num_patches / tri_factor));
305  }
306  aim_for_tris = true;
307  }
308 
309  Surfaces::iterator si;
310  for (si = _surfaces.begin(); si != _surfaces.end(); ++si) {
311  QtessSurface *surface = (*si);
312 
313  switch (_type) {
314  case T_undefined:
315  case T_omit:
316  surface->omit();
317  break;
318 
319  case T_uv:
320  if (!_iso_u.empty() && !_iso_v.empty() && !_auto_place) {
321  surface->tesselate_specific(_iso_u, _iso_v);
322  } else {
323  surface->tesselate_uv(_num_u, _num_v, _auto_place, _curvature_ratio);
324  }
325  break;
326 
327  case T_per_isoparam:
328  surface->tesselate_per_isoparam(_per_isoparam, _auto_place, _curvature_ratio);
329  break;
330 
331  case T_per_score:
332  surface->tesselate_per_score(_per_isoparam, _auto_place, _curvature_ratio);
333  break;
334 
335  default:
336  break;
337  }
338 
339  total_tris += surface->count_tris();
340  }
341 
342  if (aim_for_tris && attempts < 10 &&
343  (double)total_tris / (double)_num_tris > 1.1) {
344  // We'd like to get within 10% of the requested number of triangles, if
345  // possible. Keep trying until we do, or until we just need to give up.
346  set_num_tris(_num_tris);
347  return count_tris(tri_factor * total_tris / _num_tris, attempts + 1);
348  }
349 
350  return total_tris;
351 }
352 
353 
354 /**
355  * This function is used to identify the extra isoparams in the list added by
356  * user control.
357  */
359 output_extra(std::ostream &out, const pvector<double> &iso, char axis) {
361  int expect = 0;
362  for (di = iso.begin(); di != iso.end(); ++di) {
363  while ((*di) > (double)expect) {
364  // Didn't find one we were expecting. Omit it.
365  out << " !" << axis << expect;
366  }
367  if ((*di)==(double)expect) {
368  // Here's one we were expecting; ignore it.
369  expect++;
370  } else {
371  // Here's a new one. Write it.
372  out << " " << axis << *di;
373  }
374  }
375 }
376 
377 /**
378  *
379  */
380 void QtessInputEntry::
381 output(std::ostream &out) const {
382  NodeNames::const_iterator nni;
383  for (nni = _node_names.begin();
384  nni != _node_names.end();
385  ++nni) {
386  out << (*nni) << " ";
387  }
388  out << ": ";
389 
390  bool show_auto = false;
391 
392  switch (_type) {
393  case T_undefined:
394  break;
395 
396  case T_omit:
397  out << "omit";
398  break;
399 
400  case T_num_tris:
401  out << _num_tris;
402  show_auto = true;
403  break;
404 
405  case T_uv:
406  out << _num_u << " " << _num_v;
407  output_extra(out, _iso_u, 'u');
408  output_extra(out, _iso_v, 'v');
409  show_auto = true;
410  break;
411 
412  case T_per_isoparam:
413  case T_per_score:
414  out << "i" << _per_isoparam;
415  show_auto = true;
416  break;
417 
418  case T_importance:
419  out << _importance * 100.0 << "%";
420  break;
421 
422  case T_match_uu:
423  out << "matchuu";
424  break;
425 
426  case T_match_vv:
427  out << "matchvv";
428  break;
429 
430  case T_match_uv:
431  out << "matchuv";
432  break;
433 
434  case T_match_vu:
435  out << "matchvu";
436  break;
437 
438  case T_min_u:
439  out << "minu " << _num_u;
440  break;
441 
442  case T_min_v:
443  out << "minv " << _num_v;
444  break;
445 
446  default:
447  out << "Invalid!";
448  }
449 
450  if (show_auto) {
451  out << " " << (_auto_place?"":"!") << "ap"
452  << " " << (_auto_distribute?"":"!") << "ad";
453  if (_auto_place || _auto_distribute) {
454  out << " ar" << _curvature_ratio;
455  }
456  }
457 }
458 
459 /**
460  *
461  */
462 void QtessInputEntry::
463 write(std::ostream &out, int indent_level) const {
464  indent(out, indent_level) << (*this) << "\n";
465 }
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
Stores one entry in the qtess input file.
Type match(QtessSurface *surface)
Tests the surface to see if it matches any of the regular expressions that define this node entry.
void add_extra_u_isoparam(double u)
May be called a number of times before set_uv() to add specific additional isoparams to the tesselati...
void add_extra_v_isoparam(double u)
May be called a number of times before set_uv() to add specific additional isoparams to the tesselati...
static void output_extra(std::ostream &out, const pvector< double > &iso, char axis)
This function is used to identify the extra isoparams in the list added by user control.
int count_tris(double tri_factor=1.0, int attempts=0)
Determines the tesselation u,v amounts of each attached surface, and stores this information in the s...
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:32
int count_tris() const
Returns the number of triangles that will be generated by the current tesselation parameters.
Definition: qtessSurface.I:104
double get_score(double ratio)
Computes the curvature/stretch score for the surface, if it has not been already computed,...
void set_min_u(int min_u)
Specifies the absolute minimum number of segments allowed in the U direction.
Definition: qtessSurface.I:75
void tesselate_per_isoparam(double pi, bool autoplace, double ratio)
Sets the surface up to tesselate itself to a uniform amount per isoparam.
void omit()
Sets up the surface to omit itself from the output.
void set_match_u(QtessSurface **match_u, bool match_u_to_u)
Indicates the surface to which this surface must match in its U direction.
Definition: qtessSurface.I:50
double count_patches() const
Returns the number of patches the NURBS contains.
Definition: qtessSurface.I:95
void set_min_v(int min_v)
Specifies the absolute minimum number of segments allowed in the V direction.
Definition: qtessSurface.I:84
void set_match_v(QtessSurface **match_v, bool match_v_to_v)
Indicates the surface to which this surface must match in its V direction.
Definition: qtessSurface.I:65
void tesselate_uv(int u, int v, bool autoplace, double ratio)
Sets the surface up to tesselate itself uniformly at u x v, or if autoplace is true,...
void tesselate_per_score(double pi, bool autoplace, double ratio)
Sets the surface up to tesselate itself according to its computed curvature score in both dimensions.
void set_importance(double importance2)
Sets the importance of the surface, as a ratio in proportion to the square of its size.
Definition: qtessSurface.I:35
void tesselate_specific(const pvector< double > &u_list, const pvector< double > &v_list)
Sets the surface up to tesselate itself at specific isoparams only.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.