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  */
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  */
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 }
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:32
double string_to_double(const string &str, string &tail)
A string-interface wrapper around the C library strtol().
int count_tris() const
Returns the number of triangles that will be generated by the current tesselation parameters.
Definition: qtessSurface.I:104
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
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...
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
Definition: globPattern.I:122
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
void set_min_v(int min_v)
Specifies the absolute minimum number of segments allowed in the V direction.
Definition: qtessSurface.I:84
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
Stores one entry in the qtess input file.
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...
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.
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 omit()
Sets up the surface to omit itself from the output.
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.
void tesselate_per_isoparam(double pi, bool autoplace, double ratio)
Sets the surface up to tesselate itself to a uniform amount per isoparam.
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.
double count_patches() const
Returns the number of patches the NURBS contains.
Definition: qtessSurface.I:95
Type match(QtessSurface *surface)
Tests the surface to see if it matches any of the regular expressions that define this node entry.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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,...
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
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...