Panda3D
qtessSurface.cxx
1 // Filename: qtessSurface.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 "qtessSurface.h"
16 #include "qtessGlobals.h"
17 #include "qtessInputEntry.h"
18 #include "config_egg_qtess.h"
19 #include "eggPolygon.h"
20 #include "eggVertexPool.h"
21 #include "eggVertex.h"
22 #include "eggComment.h"
23 #include "egg_parametrics.h"
24 #include "pset.h"
25 #include "pmap.h"
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: QtessSurface::Constructor
29 // Access: Public
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 QtessSurface::
33 QtessSurface(EggNurbsSurface *egg_surface) :
34  _egg_surface(egg_surface)
35 {
36  _nurbs = make_nurbs_surface(_egg_surface, LMatrix4d::ident_mat());
37  _has_vertex_color = _egg_surface->has_vertex_color();
38 
39  // The first four slots are reserved for vertex color.
40  _next_d = 4;
41 
42  _importance = 1.0;
43  _importance2 = 1.0;
44  _match_u = _match_v = NULL;
45  _tess_u = _tess_v = 0;
46  _got_scores = false;
47 
48  // If the surface is closed in either dimension, the mininum
49  // tesselation in that dimension is by default 3, so we don't
50  // ribbonize the surface. Otherwise the minimum is 1.
51  _min_u = _min_v = 1;
52  if (egg_surface->is_closed_u()) {
53  _min_u = 3;
54  }
55  if (egg_surface->is_closed_v()) {
56  _min_v = 3;
57  }
58 
59  if (_nurbs == (NurbsSurfaceEvaluator *)NULL) {
60  _num_u = _num_v = 0;
61 
62  } else {
63  record_vertex_extras();
64 
65  _nurbs->normalize_u_knots();
66  _nurbs->normalize_v_knots();
67  _nurbs_result = _nurbs->evaluate();
68 
69  _num_u = _nurbs->get_num_u_segments();
70  _num_v = _nurbs->get_num_v_segments();
71 
72  if (QtessGlobals::_respect_egg) {
73  if (egg_surface->get_u_subdiv() != 0) {
74  _num_u = egg_surface->get_u_subdiv();
75  }
76  if (egg_surface->get_v_subdiv() != 0) {
77  _num_v = egg_surface->get_v_subdiv();
78  }
79  }
80  }
81 }
82 
83 ////////////////////////////////////////////////////////////////////
84 // Function: get_score
85 // Access: Public
86 // Description: Computes the curvature/stretch score for the surface,
87 // if it has not been already computed, and returns the
88 // net surface score. This is used both for
89 // automatically distributing isoparams among the
90 // surfaces by curvature, as well as for automatically
91 // placing the isoparams within each surface by
92 // curvature.
93 ////////////////////////////////////////////////////////////////////
94 double QtessSurface::
95 get_score(double ratio) {
96  if (_nurbs == (NurbsSurfaceEvaluator *)NULL) {
97  return 0.0;
98  }
99 
100  if (!_got_scores) {
101  _u_placer.get_scores(_nurbs->get_num_u_segments() * 100,
102  _nurbs->get_num_v_segments() * 2,
103  ratio, _nurbs_result, true);
104  _v_placer.get_scores(_nurbs->get_num_v_segments() * 100,
105  _nurbs->get_num_u_segments() * 2,
106  ratio, _nurbs_result, false);
107  _got_scores = true;
108  }
109 
110  return _u_placer.get_total_score() * _v_placer.get_total_score() * _importance2;
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: QtessSurface::tesselate
115 // Access: Public
116 // Description: Applies the appropriate tesselation to the surface,
117 // and replaces its node in the tree with an EggGroup
118 // containing both the new vertex pool and all of the
119 // polygons.
120 ////////////////////////////////////////////////////////////////////
121 int QtessSurface::
123  apply_match();
124  int tris = 0;
125 
126  PT(EggGroup) group = do_uniform_tesselate(tris);
127  PT(EggNode) new_node = group.p();
128  if (new_node == (EggNode *)NULL) {
129  new_node = new EggComment(_egg_surface->get_name(),
130  "Omitted NURBS surface.");
131  tris = 0;
132  }
133  EggGroupNode *parent = _egg_surface->get_parent();
134  nassertr(parent != (EggGroupNode *)NULL, 0);
135  parent->remove_child(_egg_surface);
136  parent->add_child(new_node);
137 
138  return tris;
139 }
140 
141 ////////////////////////////////////////////////////////////////////
142 // Function: QtessSurface::write_qtess_parameter
143 // Access: Public
144 // Description: Writes a line to the given output file telling qtess
145 // how this surface should be tesselated uniformly.
146 // Returns the number of tris.
147 ////////////////////////////////////////////////////////////////////
148 int QtessSurface::
149 write_qtess_parameter(ostream &out) {
150  apply_match();
151 
152  if (_tess_u == 0 || _tess_v == 0) {
153  out << get_name() << " : omit\n";
154 
155  } else if (_iso_u.empty() || _iso_v.empty()) {
156  out << get_name() << " : " << _tess_u << " " << _tess_v << "\n";
157 
158  } else {
159  out << get_name() << " : " << _iso_u.back() << " " << _iso_v.back();
160  QtessInputEntry::output_extra(out, _iso_u, 'u');
161  QtessInputEntry::output_extra(out, _iso_v, 'v');
162  out << "\n";
163  }
164 
165  return count_tris();
166 }
167 
168 
169 ////////////////////////////////////////////////////////////////////
170 // Function: QtessSurface::omit
171 // Access: Public
172 // Description: Sets up the surface to omit itself from the output.
173 ////////////////////////////////////////////////////////////////////
174 void QtessSurface::
175 omit() {
176  _tess_u = 0;
177  _tess_v = 0;
178 }
179 
180 ////////////////////////////////////////////////////////////////////
181 // Function: QtessSurface::tesselate_uv
182 // Access: Public
183 // Description: Sets the surface up to tesselate itself uniformly at
184 // u x v, or if autoplace is true, automatically with u
185 // x v quads.
186 ////////////////////////////////////////////////////////////////////
187 void QtessSurface::
188 tesselate_uv(int u, int v, bool autoplace, double ratio) {
189  _tess_u = u;
190  _tess_v = v;
191  _iso_u.clear();
192  _iso_v.clear();
193  if (autoplace) {
194  tesselate_auto(_tess_u, _tess_v, ratio);
195  }
196 }
197 
198 ////////////////////////////////////////////////////////////////////
199 // Function: QtessSurface::tesselate_specific
200 // Access: Public
201 // Description: Sets the surface up to tesselate itself at specific
202 // isoparams only.
203 ////////////////////////////////////////////////////////////////////
204 void QtessSurface::
206  const pvector<double> &v_list) {
207  _iso_u = u_list;
208  _iso_v = v_list;
209  _tess_u = (int)_iso_u.size() - 1;
210  _tess_v = (int)_iso_v.size() - 1;
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: QtessSurface::tesselate_per_isoparam
215 // Access: Public
216 // Description: Sets the surface up to tesselate itself to a uniform
217 // amount per isoparam.
218 ////////////////////////////////////////////////////////////////////
219 void QtessSurface::
220 tesselate_per_isoparam(double pi, bool autoplace, double ratio) {
221  if (_num_u == 0 || _num_v == 0) {
222  omit();
223 
224  } else {
225  _tess_u = max(_min_u, (int)floor(_num_u * _importance * pi + 0.5));
226  _tess_v = max(_min_v, (int)floor(_num_v * _importance * pi + 0.5));
227  _iso_u.clear();
228  _iso_v.clear();
229  if (autoplace) {
230  tesselate_auto(_tess_u, _tess_v, ratio);
231  }
232  }
233 }
234 
235 
236 ////////////////////////////////////////////////////////////////////
237 // Function: QtessSurface::TesselatePerScore
238 // Access: Public
239 // Description: Sets the surface up to tesselate itself according to
240 // its computed curvature score in both dimensions.
241 ////////////////////////////////////////////////////////////////////
242 void QtessSurface::
243 tesselate_per_score(double pi, bool autoplace, double ratio) {
244  if (get_score(ratio) <= 0.0) {
245  omit();
246 
247  } else {
248  _tess_u = max(_min_u, (int)floor(_u_placer.get_total_score() * _importance * pi + 0.5));
249  _tess_v = max(_min_v, (int)floor(_v_placer.get_total_score() * _importance * pi + 0.5));
250  _iso_u.clear();
251  _iso_v.clear();
252  if (autoplace) {
253  tesselate_auto(_tess_u, _tess_v, ratio);
254  }
255  }
256 }
257 
258 ////////////////////////////////////////////////////////////////////
259 // Function: QtessSurface::tesselate_auto
260 // Access: Public
261 // Description: Sets the surface up to tesselate itself by
262 // automatically determining the best place to put the
263 // indicated u x v isoparams.
264 ////////////////////////////////////////////////////////////////////
265 void QtessSurface::
266 tesselate_auto(int u, int v, double ratio) {
267  if (get_score(ratio) <= 0.0) {
268  omit();
269 
270  } else {
271  _u_placer.place(u, _iso_u);
272  _v_placer.place(v, _iso_v);
273  _tess_u = (int)_iso_u.size() - 1;
274  _tess_v = (int)_iso_v.size() - 1;
275  }
276 }
277 
278 ////////////////////////////////////////////////////////////////////
279 // Function: QtessSurface::record_vertex_extras
280 // Access: Private
281 // Description: Records the joint membership and morph offsets of
282 // each control vertex in the extra-dimensional space of
283 // the NURBS, so that we can extract this data out again
284 // later to apply to the polygon vertices.
285 ////////////////////////////////////////////////////////////////////
286 void QtessSurface::
287 record_vertex_extras() {
288  int num_u_vertices = _egg_surface->get_num_u_cvs();
289  int num_v_vertices = _egg_surface->get_num_v_cvs();
290 
291  for (int ui = 0; ui < num_u_vertices; ui++) {
292  for (int vi = 0; vi < num_v_vertices; vi++) {
293  int i = _egg_surface->get_vertex_index(ui, vi);
294  EggVertex *egg_vertex = _egg_surface->get_vertex(i);
295 
296  // The joint membership.
297  EggVertex::GroupRef::const_iterator gi;
298  for (gi = egg_vertex->gref_begin(); gi != egg_vertex->gref_end(); ++gi) {
299  EggGroup *joint = (*gi);
300  int d = get_joint_membership_index(joint);
301  double membership = joint->get_vertex_membership(egg_vertex);
302  _nurbs->set_extended_vertex(ui, vi, d, membership);
303  }
304 
305  // The xyz morphs.
306  EggMorphVertexList::const_iterator dxi;
307  for (dxi = egg_vertex->_dxyzs.begin();
308  dxi != egg_vertex->_dxyzs.end();
309  ++dxi) {
310  const string &morph_name = (*dxi).get_name();
311  LVector3 delta = LCAST(PN_stdfloat, (*dxi).get_offset());
312  int d = get_dxyz_index(morph_name);
313  _nurbs->set_extended_vertices(ui, vi, d, delta.get_data(), 3);
314  }
315 
316  // The rgba morphs.
317  EggMorphColorList::const_iterator dri;
318  for (dri = egg_vertex->_drgbas.begin();
319  dri != egg_vertex->_drgbas.end();
320  ++dri) {
321  const string &morph_name = (*dri).get_name();
322  const LVector4 &delta = (*dri).get_offset();
323  int d = get_drgba_index(morph_name);
324  _nurbs->set_extended_vertices(ui, vi, d, delta.get_data(), 4);
325  }
326  }
327  }
328 }
329 
330 ////////////////////////////////////////////////////////////////////
331 // Function: QtessSurface::apply_match
332 // Access: Private
333 // Description: If the surface was set up to copy its tesselation in
334 // either axis from another surface, makes this copy
335 // now.
336 ////////////////////////////////////////////////////////////////////
337 void QtessSurface::
338 apply_match() {
339  if (_match_u != NULL) {
340  QtessSurface *m = *_match_u;
341  if (m == NULL) {
342  qtess_cat.warning()
343  << "No surface to match " << get_name() << " to in U.\n";
344  } else {
345  if (qtess_cat.is_debug()) {
346  qtess_cat.debug()
347  << "Matching " << get_name() << " in U to " << m->get_name()
348  << " in " << (_match_u_to_u?'U':'V') << ".\n";
349  }
350  if (_match_u_to_u) {
351  _tess_u = m->_tess_u;
352  _iso_u = m->_iso_u;
353  } else {
354  _tess_u = m->_tess_v;
355  _iso_u = m->_iso_v;
356  }
357  }
358  }
359 
360  if (_match_v != NULL) {
361  QtessSurface *m = *_match_v;
362  if (m == NULL) {
363  qtess_cat.warning()
364  << "No surface to match " << get_name() << " in V.\n";
365  } else {
366  if (qtess_cat.is_debug()) {
367  qtess_cat.debug()
368  << "Matching " << get_name() << " in V to " << m->get_name()
369  << " in " << (_match_v_to_v?'V':'U') << ".\n";
370  }
371  if (_match_v_to_v) {
372  _tess_v = m->_tess_v;
373  _iso_v = m->_iso_v;
374  } else {
375  _tess_v = m->_tess_u;
376  _iso_v = m->_iso_u;
377  }
378  }
379  }
380 }
381 
382 ////////////////////////////////////////////////////////////////////
383 // Function: do_uniform_tesselate
384 // Access: Private
385 // Description: Subdivide the surface uniformly according to the
386 // parameters specified by an earlier call to omit(),
387 // teseselate_uv(), or tesselate_per_isoparam().
388 ////////////////////////////////////////////////////////////////////
389 PT(EggGroup) QtessSurface::
390 do_uniform_tesselate(int &tris) const {
391  tris = 0;
392 
393  if (_tess_u == 0 || _tess_v == 0) {
394  // No tesselation!
395  if (qtess_cat.is_debug()) {
396  qtess_cat.debug()
397  << get_name() << " : omit\n";
398  }
399  return NULL;
400  }
401 
402  PT(EggGroup) group = new EggGroup(_egg_surface->get_name());
403 
404  // _tess_u and _tess_v are the number of patches to create. Convert
405  // that to the number of vertices.
406 
407  int num_u = _tess_u + 1;
408  int num_v = _tess_v + 1;
409 
410  if (qtess_cat.is_debug()) {
411  qtess_cat.debug() << get_name() << " : " << tris << "\n";
412  }
413 
414  assert(_iso_u.empty() || (int)_iso_u.size() == num_u);
415  assert(_iso_v.empty() || (int)_iso_v.size() == num_v);
416 
417  // Now how many vertices is that total, and how many vertices per
418  // strip?
419  int num_verts = num_u * num_v;
420 
421  // Create a vertex pool.
422  PT(EggVertexPool) vpool = new EggVertexPool(_egg_surface->get_name());
423  group->add_child(vpool);
424 
425  // Create all the vertices.
426  int ui, vi;
427  double u, v;
428 
429  typedef pvector<EggVertex *> VertexList;
430  VertexList new_verts;
431  new_verts.reserve(num_verts);
432 
433  // Also collect the vertices into this set to group them by spatial
434  // position only. This is relevant for calculating normals.
435  typedef pset<EggVertex *> NVertexGroup;
436  typedef pmap<LVertexd, NVertexGroup> NVertexCollection;
437  NVertexCollection n_collection;
438 
439  for (vi = 0; vi < num_v; vi++) {
440  if (_iso_v.empty()) {
441  v = (double)vi / (double)(num_v-1);
442  } else {
443  v = _iso_v[vi] / _iso_v.back();
444  }
445  for (ui = 0; ui < num_u; ui++) {
446  if (_iso_u.empty()) {
447  u = (double)ui / (double)(num_u-1);
448  } else {
449  u = _iso_u[ui] / _iso_u.back();
450  }
451 
452  PT(EggVertex) egg_vertex = evaluate_vertex(u, v);
453  vpool->add_vertex(egg_vertex);
454  new_verts.push_back(egg_vertex);
455  n_collection[egg_vertex->get_pos3()].insert(egg_vertex);
456  }
457  }
458  nassertr((int)new_verts.size() == num_verts, NULL);
459 
460  // Now create a bunch of quads.
461  for (vi = 1; vi < num_v; vi++) {
462  for (ui = 1; ui < num_u; ui++) {
463  PT(EggPolygon) poly = new EggPolygon;
464  poly->add_vertex(new_verts[vi*num_u + (ui-1)]);
465  poly->add_vertex(new_verts[(vi-1)*num_u + (ui-1)]);
466  poly->add_vertex(new_verts[(vi-1)*num_u + ui]);
467  poly->add_vertex(new_verts[vi*num_u + ui]);
468 
469  poly->copy_attributes(*_egg_surface);
470 
471  // We compute a polygon normal just so we can verify the
472  // calculated vertex normals. It's also helpful for identifying
473  // degenerate polygons.
474  if (poly->recompute_polygon_normal()) {
475  tris += 2;
476  group->add_child(poly);
477  }
478  }
479  }
480 
481  // Now check all the vertex normals by comparing them to the polygon
482  // normals. Some might have not been computed at all; others might
483  // be facing in the wrong direction.
484 
485  // Now go back through and normalize the computed normals.
486  NVertexCollection::const_iterator nci;
487  for (nci = n_collection.begin(); nci != n_collection.end(); ++nci) {
488  const NVertexGroup &group = (*nci).second;
489 
490  // Calculate the normal these vertices should have based on the
491  // polygons that share it.
492  LNormald normal = LNormald::zero();
493  int num_polys = 0;
494  NVertexGroup::const_iterator ngi;
495  for (ngi = group.begin(); ngi != group.end(); ++ngi) {
496  EggVertex *egg_vertex = (*ngi);
497  EggVertex::PrimitiveRef::const_iterator pri;
498  for (pri = egg_vertex->pref_begin();
499  pri != egg_vertex->pref_end();
500  ++pri) {
501  EggPrimitive *egg_primitive = (*pri);
502  nassertr(egg_primitive->has_normal(), NULL);
503  normal += egg_primitive->get_normal();
504  num_polys++;
505  }
506  }
507 
508  if (num_polys > 0) {
509  normal /= (double)num_polys;
510 
511  // Now compare this normal with what the NURBS representation
512  // calculated. It should be facing in at least vaguely the same
513  // direction.
514  for (ngi = group.begin(); ngi != group.end(); ++ngi) {
515  EggVertex *egg_vertex = (*ngi);
516  if (egg_vertex->has_normal()) {
517  if (normal.dot(egg_vertex->get_normal()) < 0.0) {
518  // This one is backwards.
519  egg_vertex->set_normal(-egg_vertex->get_normal());
520  }
521  } else {
522  // This vertex doesn't have a normal; it gets the computed
523  // normal.
524  egg_vertex->set_normal(normal);
525  }
526  }
527  }
528  }
529 
530  return group;
531 }
532 
533 ////////////////////////////////////////////////////////////////////
534 // Function: evaluate_vertex
535 // Access: Private
536 // Description: Evaluates the surface at the given u, v position and
537 // sets the vertex to the appropriate values. Also sets
538 // the joint membership of the vertex.
539 ////////////////////////////////////////////////////////////////////
540 PT(EggVertex) QtessSurface::
541 evaluate_vertex(double u, double v) const {
542  PT(EggVertex) egg_vertex = new EggVertex;
543 
544  LVertex point;
545  LNormal normal;
546  _nurbs_result->eval_point(u, v, point);
547  _nurbs_result->eval_normal(u, v, normal);
548 
549  // If the normal is too short, don't consider it--it's probably
550  // inaccurate due to numerical limitations. We'll recompute it
551  // later based on the polygon normals.
552  PN_stdfloat length = normal.length();
553  if (length > 0.0001f) {
554  normal /= length;
555  egg_vertex->set_normal(LCAST(double, normal));
556  }
557 
558  egg_vertex->set_pos(LCAST(double, point));
559  egg_vertex->set_uv(LVecBase2d(u, v));
560 
561  // The color is stored, by convention, in slots 0-4 of the surface.
562  if (_has_vertex_color) {
563  LColor rgba;
564  _nurbs_result->eval_extended_points(u, v, 0, &rgba[0], 4);
565  egg_vertex->set_color(rgba);
566  }
567 
568  // Also fill in the joint membership.
569  JointTable::const_iterator jti;
570  for (jti = _joint_table.begin(); jti != _joint_table.end(); ++jti) {
571  EggGroup *joint = (*jti).first;
572  int d = (*jti).second;
573 
574  double membership = _nurbs_result->eval_extended_point(u, v, d);
575  if (membership > 0.0) {
576  joint->ref_vertex(egg_vertex, membership);
577  }
578  }
579 
580  // And the morphs.
581  MorphTable::const_iterator mti;
582  for (mti = _dxyz_table.begin(); mti != _dxyz_table.end(); ++mti) {
583  const string &morph_name = (*mti).first;
584  int d = (*mti).second;
585 
586  LVector3 delta;
587  _nurbs_result->eval_extended_points(u, v, d, &delta[0], 3);
588  if (!delta.almost_equal(LVector3::zero())) {
589  egg_vertex->_dxyzs.insert(EggMorphVertex(morph_name, LCAST(double, delta)));
590  }
591  }
592 
593  for (mti = _drgba_table.begin(); mti != _drgba_table.end(); ++mti) {
594  const string &morph_name = (*mti).first;
595  int d = (*mti).second;
596 
597  LVector4 delta;
598  _nurbs_result->eval_extended_points(u, v, d, &delta[0], 4);
599  if (!delta.almost_equal(LVector4::zero())) {
600  egg_vertex->_drgbas.insert(EggMorphColor(morph_name, delta));
601  }
602  }
603 
604  return egg_vertex;
605 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
A reference to an EggNurbsSurface in the egg file, and its parameters as set by the user input file a...
Definition: qtessSurface.h:34
GroupRef::const_iterator gref_end() const
Returns an iterator that can, in conjunction with gref_begin(), be used to traverse the entire set of...
Definition: eggVertex.cxx:805
const float * get_data() const
Returns the address of the first of the four data elements in the vector.
Definition: lvecBase4.h:746
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
int count_tris() const
Returns the number of triangles that will be generated by the current tesselation parameters...
Definition: qtessSurface.I:132
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:1257
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
A comment that appears in an egg file within a <Comment> entry.
Definition: eggComment.h:27
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:270
int get_v_subdiv() const
Returns the requested number of subdivisions in the U direction, or 0 if no particular subdivisions h...
Definition: eggSurface.I:104
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
GroupRef::const_iterator gref_begin() const
Returns an iterator that can, in conjunction with gref_end(), be used to traverse the entire set of g...
Definition: eggVertex.cxx:789
This class is an abstraction for evaluating NURBS surfaces.
A single <Dxyz> or <Duv> or some such entry.
Definition: eggMorph.h:33
static const LVector3d & zero()
Returns a zero-length vector.
Definition: lvector3.h:930
bool is_closed_v() const
Returns true if the surface appears to be closed in the V direction.
bool almost_equal(const LVecBase3f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:1280
static const LVector4f & zero()
Returns a zero-length vector.
Definition: lvector4.h:232
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:36
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
void tesselate_auto(int u, int v, double ratio)
Sets the surface up to tesselate itself by automatically determining the best place to put the indica...
bool almost_equal(const LVecBase4f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase4.h:1339
PrimitiveRef::const_iterator pref_end() const
Returns an iterator that can, in conjunction with pref_begin(), be used to traverse the entire set of...
Definition: eggVertex.cxx:916
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
Definition: eggGroup.cxx:756
double get_score(double ratio)
Computes the curvature/stretch score for the surface, if it has not been already computed, and returns the net surface score.
int write_qtess_parameter(ostream &out)
Writes a line to the given output file telling qtess how this surface should be tesselated uniformly...
This is a four-component vector distance.
Definition: lvector4.h:91
void omit()
Sets up the surface to omit itself from the output.
A single polygon.
Definition: eggPolygon.h:26
void tesselate_specific(const pvector< double > &u_list, const pvector< double > &v_list)
Sets the surface up to tesselate itself at specific isoparams only.
bool is_closed_u() const
Returns true if the surface appears to be closed in the U direction.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:760
float length() const
Returns the length of the vector, by the Pythagorean theorem.
Definition: lvecBase3.h:766
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
int tesselate()
Applies the appropriate tesselation to the surface, and replaces its node in the tree with an EggGrou...
A parametric NURBS surface.
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...
void ref_vertex(EggVertex *vert, double membership=1.0)
Adds the vertex to the set of those referenced by the group, at the indicated membership level...
Definition: eggGroup.cxx:678
This is our own Panda specialization on the default STL set.
Definition: pset.h:52
PrimitiveRef::const_iterator pref_begin() const
Returns an iterator that can, in conjunction with pref_end(), be used to traverse the entire set of p...
Definition: eggVertex.cxx:900
const float * get_data() const
Returns the address of the first of the three data elements in the vector.
Definition: lvecBase3.h:637
A collection of vertices.
Definition: eggVertexPool.h:46
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive&#39;s list of vertices, and returns it...
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, automatically with u x v quads.
static void output_extra(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 get_u_subdiv() const
Returns the requested number of subdivisions in the U direction, or 0 if no particular subdivisions h...
Definition: eggSurface.I:77