Panda3D
Loading...
Searching...
No Matches
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
24using std::string;
25
26/**
27 *
28 */
29QtessInputEntry::
30QtessInputEntry(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 */
44void QtessInputEntry::
45operator = (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 */
68class DoublesAlmostEqual {
69public:
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 */
79class DoubleAlmostMatches {
80public:
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 */
93void QtessInputEntry::
94set_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 */
177add_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 */
186add_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 */
196QtessInputEntry::Type QtessInputEntry::
197match(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 */
294count_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 */
359output_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 */
380void QtessInputEntry::
381output(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 */
462void QtessInputEntry::
463write(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.
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...
int count_tris() const
Returns the number of triangles that will be generated by the current tesselation parameters.
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.
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.
double count_patches() const
Returns the number of patches the NURBS contains.
void set_min_v(int min_v)
Specifies the absolute minimum number of segments allowed in the V direction.
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.
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.
void tesselate_specific(const pvector< double > &u_list, const pvector< double > &v_list)
Sets the surface up to tesselate itself at specific isoparams only.
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
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.