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