Panda3D
indexedFaceSet.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 indexedFaceSet.cxx
10  * @author drose
11  * @date 1999-06-24
12  */
13 
14 #include "indexedFaceSet.h"
15 #include "vrmlAppearance.h"
16 #include "vrmlNodeType.h"
17 #include "vrmlNode.h"
18 #include "pnotify.h"
19 
20 #include "eggGroup.h"
21 #include "eggVertex.h"
22 #include "eggVertexPool.h"
23 #include "eggPolygon.h"
24 
25 using std::cerr;
26 
27 /**
28  *
29  */
30 IndexedFaceSet::
31 IndexedFaceSet(const VrmlNode *geometry, const VRMLAppearance &appearance) :
32  _geometry(geometry), _appearance(appearance)
33 {
34  get_coord_values();
35  get_polys();
36  get_colors();
37  _has_normals = get_normals();
38  if (!_per_vertex_normals.empty()) {
39  assign_per_vertex_normals();
40  }
41  get_uvs();
42  if (!_per_vertex_uvs.empty()) {
43  assign_per_vertex_uvs();
44  }
45 }
46 
47 
48 /**
49  *
50  */
51 void IndexedFaceSet::
52 convert_to_egg(EggGroup *group, const LMatrix4d &net_transform) {
53  EggVertexPool *vpool = new EggVertexPool(group->get_name());
54  group->add_child(vpool);
55 
56  make_polys(vpool, group, net_transform);
57  if (!_has_normals && _appearance._has_material) {
58  compute_normals(group);
59  }
60 }
61 
62 
63 /**
64  *
65  */
66 void IndexedFaceSet::
67 get_coord_values() {
68  const VrmlNode *coord = _geometry->get_value("coord")._sfnode._p;
69 
70  if (coord != nullptr) {
71  const MFArray *point = coord->get_value("point")._mf;
72  MFArray::const_iterator ci;
73  for (ci = point->begin(); ci != point->end(); ++ci) {
74  const double *p = (*ci)._sfvec;
75  _coord_values.push_back(LVertexd(p[0], p[1], p[2]));
76  }
77  }
78 }
79 
80 
81 /**
82  *
83  */
84 void IndexedFaceSet::
85 get_polys() {
86  const MFArray *coordIndex = _geometry->get_value("coordIndex")._mf;
87  VrmlPolygon poly;
88 
89  MFArray::const_iterator ci;
90  for (ci = coordIndex->begin(); ci != coordIndex->end(); ++ci) {
91  if ((*ci)._sfint32 < 0) {
92  _polys.push_back(poly);
93  poly._verts.clear();
94  } else {
95  const LVertexd &p = _coord_values[(*ci)._sfint32];
96  VrmlVertex vert;
97  vert._index = (*ci)._sfint32;
98  vert._pos = p;
99  poly._verts.push_back(vert);
100  }
101  }
102 }
103 
104 /**
105  * Builds up a vector of LColor pointers corresponding to the VRML color node.
106  */
107 void IndexedFaceSet::
108 get_vrml_colors(const VrmlNode *color_node, double transparency,
109  pvector<UnalignedLVecBase4> &color_list) {
110  const MFArray *color = color_node->get_value("color")._mf;
111  MFArray::const_iterator ci;
112  for (ci = color->begin(); ci != color->end(); ++ci) {
113  const double *p = (*ci)._sfvec;
114  LColor color(p[0], p[1], p[2], 1.0 - transparency);
115  color_list.push_back(color);
116  }
117 }
118 
119 /**
120  * Builds up a vector of double array pointers corresponding to the VRML
121  * normal node.
122  */
123 void IndexedFaceSet::
124 get_vrml_normals(const VrmlNode *normal_node,
125  pvector<LNormald> &normal_list) {
126  const MFArray *point = normal_node->get_value("vector")._mf;
127  MFArray::const_iterator ci;
128  for (ci = point->begin(); ci != point->end(); ++ci) {
129  const double *p = (*ci)._sfvec;
130  LNormald normal(p[0], p[1], p[2]);
131  normal_list.push_back(normal);
132  }
133 }
134 
135 /**
136  * Builds up a vector of double array pointers corresponding to the VRML
137  * texCoord node.
138  */
139 void IndexedFaceSet::
140 get_vrml_uvs(const VrmlNode *texCoord_node,
141  pvector<LTexCoordd> &uv_list) {
142  const MFArray *point = texCoord_node->get_value("point")._mf;
143  MFArray::const_iterator ci;
144  for (ci = point->begin(); ci != point->end(); ++ci) {
145  const double *p = (*ci)._sfvec;
146  LTexCoordd uv(p[0], p[1]);
147  uv_list.push_back(uv);
148  }
149 }
150 
151 
152 /**
153  *
154  */
155 bool IndexedFaceSet::
156 get_colors() {
157  const VrmlNode *color = _geometry->get_value("color")._sfnode._p;
158  if (color != nullptr) {
159  // Vertex or face colors.
160  pvector<UnalignedLVecBase4> color_list;
161  get_vrml_colors(color, _appearance._transparency, color_list);
162 
163  bool colorPerVertex = _geometry->get_value("colorPerVertex")._sfbool;
164  MFArray *colorIndex = _geometry->get_value("colorIndex")._mf;
165  if (colorPerVertex) {
166  MFArray::const_iterator ci;
167  size_t pi = 0;
168  size_t pv = 0;
169  for (ci = colorIndex->begin(); ci != colorIndex->end(); ++ci) {
170  if ((*ci)._sfint32 < 0) {
171  // End of poly.
172  if (pv != _polys[pi]._verts.size()) {
173  cerr << "Color indices don't match up!\n";
174  return false;
175  }
176  pi++;
177  pv = 0;
178  } else {
179  if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
180  cerr << "Color indices don't match up!\n";
181  return false;
182  }
183  _polys[pi]._verts[pv]._attrib.set_color(color_list[(*ci)._sfint32]);
184  pv++;
185  }
186  }
187  if (pi != _polys.size()) {
188  cerr << "Not enough color indices!\n";
189  return false;
190  }
191  } else {
192  if (!colorIndex->empty()) {
193  MFArray::const_iterator ci;
194  size_t pi = 0;
195  if (colorIndex->size() != _polys.size()) {
196  cerr << "Wrong number of color indices!\n";
197  return false;
198  }
199  for (ci = colorIndex->begin(); ci != colorIndex->end(); ++ci) {
200  if ((*ci)._sfint32 < 0 || (*ci)._sfint32 >= (int)color_list.size()) {
201  cerr << "Invalid color index!\n";
202  return false;
203  }
204  _polys[pi]._attrib.set_color(color_list[(*ci)._sfint32]);
205  pi++;
206  }
207  } else {
208  if (color_list.size() != _polys.size()) {
209  cerr << "Wrong number of colors!\n";
210  return false;
211  }
212  for (size_t pi = 0; pi < color_list.size(); pi++) {
213  _polys[pi]._attrib.set_color(color_list[pi]);
214  }
215  }
216  }
217  return true;
218  }
219  return false;
220 }
221 
222 /**
223  *
224  */
225 bool IndexedFaceSet::
226 get_normals() {
227  const VrmlNode *normal = _geometry->get_value("normal")._sfnode._p;
228  if (normal != nullptr) {
229  // Vertex or face normals.
230  pvector<LNormald> normal_list;
231  get_vrml_normals(normal, normal_list);
232 
233  bool normalPerVertex = _geometry->get_value("normalPerVertex")._sfbool;
234  MFArray *normalIndex = _geometry->get_value("normalIndex")._mf;
235  MFArray::const_iterator ci;
236 
237  if (normalPerVertex &&
238  normal_list.size() == _polys.size() &&
239  normalIndex->empty()) {
240  // Here's an interesting formZ bug. We end up with a VRML file that
241  // claims to have normals per vertex, yet there is no normal index list,
242  // and there are exactly enough normals in the list to indicate one
243  // normal per face. Silly formZ.
244  normalPerVertex = false;
245  }
246 
247  if (normalPerVertex) {
248 
249  if (normalIndex->empty()) {
250  // If we have *no* normal index array, but we do have per-vertex
251  // normals, assume the VRML writer meant to imply a one-to-one
252  // mapping. This works around a broken formZ VRML file writer.
253  for (size_t i = 0; i < normal_list.size(); i++) {
254  VrmlFieldValue fv;
255  fv._sfint32 = i;
256  (*normalIndex).push_back(fv);
257  }
258  }
259 
260  // It's possible that this .wrl file indexes normals directly into the
261  // vertex array, instead of into the polygon list. Check for this
262  // possibility. This can only happen if the number of normal indices
263  // exactly matches the number of vertices, and none of the indices is
264  // -1.
265  bool linear_list = (normalIndex->size() == _coord_values.size());
266  for (ci = normalIndex->begin();
267  ci != normalIndex->end() && linear_list;
268  ++ci) {
269  linear_list = ((*ci)._sfint32 >= 0);
270  }
271 
272  if (linear_list) {
273  // Ok, we do have such a list. This .wrl file seems to store its
274  // texture coordinates one per vertex, instead of one per polygon
275  // vertex.
276  _per_vertex_normals.reserve(_coord_values.size());
277 
278  for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
279  size_t vi = (*ci)._sfint32;
280  nassertr(vi >= 0, false);
281  if (vi >= normal_list.size()) {
282  cerr << "Invalid normal index: " << vi << "\n";
283  return false;
284  }
285  _per_vertex_normals.push_back(normal_list[vi]);
286  }
287  nassertr(_per_vertex_normals.size() == _coord_values.size(), false);
288 
289  } else {
290  // This is a "correct" .wrl file that stores its texture coordinates
291  // one per polygon vertex. This allows a shared vertex to contain two
292  // different normal values in differing polygons (meaning it's not
293  // actually shared).
294 
295  MFArray::const_iterator ci;
296  size_t pi = 0;
297  size_t pv = 0;
298  for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
299  if ((*ci)._sfint32 < 0) {
300  // End of poly.
301  if (pv != _polys[pi]._verts.size()) {
302  cerr << "Normal indices don't match up!\n";
303  return false;
304  }
305  pi++;
306  pv = 0;
307  } else {
308  if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
309  cerr << "Normal indices don't match up!\n";
310  return false;
311  }
312  const LNormald &d = normal_list[(*ci)._sfint32];
313  _polys[pi]._verts[pv]._attrib.set_normal(d);
314  pv++;
315  }
316  }
317  if (pi != _polys.size()) {
318  cerr << "Not enough normal indices!\n";
319  return false;
320  }
321  }
322  } else {
323  if (!normalIndex->empty()) {
324  size_t pi = 0;
325  if (normalIndex->size() != _polys.size()) {
326  cerr << "Wrong number of normal indices!\n";
327  return false;
328  }
329  for (ci = normalIndex->begin(); ci != normalIndex->end(); ++ci) {
330  if ((*ci)._sfint32 < 0 || (*ci)._sfint32 >= (int)normal_list.size()) {
331  cerr << "Invalid normal index!\n";
332  return false;
333  }
334  const LNormald &d = normal_list[(*ci)._sfint32];
335  _polys[pi]._attrib.set_normal(d);
336  pi++;
337  }
338  } else {
339  if (normal_list.size() != _polys.size()) {
340  cerr << "Wrong number of normals!\n";
341  return false;
342  }
343  for (size_t pi = 0; pi < normal_list.size(); pi++) {
344  const LNormald &d = normal_list[pi];
345  _polys[pi]._attrib.set_normal(d);
346  }
347  }
348  }
349  return true;
350  }
351  return false;
352 }
353 
354 /**
355  * Once the array of _per_vertex_normals has been filled (by a broken .wrl
356  * file that indexes the normal's directly into the vertex array instead of
357  * per polygon vertex), go back through the polygons and assign the normals by
358  * index number.
359  */
360 void IndexedFaceSet::
361 assign_per_vertex_normals() {
362  for (size_t pi = 0; pi < _polys.size(); pi++) {
363  for (size_t pv = 0; pv < _polys[pi]._verts.size(); pv++) {
364  VrmlVertex &vv = _polys[pi]._verts[pv];
365  if (vv._index >= 0 && vv._index < (int)_per_vertex_normals.size()) {
366  const LNormald &d = _per_vertex_normals[vv._index];
367  vv._attrib.set_normal(d);
368  }
369  }
370  }
371 }
372 
373 
374 /**
375  *
376  */
377 bool IndexedFaceSet::
378 get_uvs() {
379  const VrmlNode *texCoord = _geometry->get_value("texCoord")._sfnode._p;
380  if (texCoord != nullptr) {
381  // Vertex or face texCoords.
382  pvector<LTexCoordd> uv_list;
383  get_vrml_uvs(texCoord, uv_list);
384 
385  MFArray *texCoordIndex = _geometry->get_value("texCoordIndex")._mf;
386  MFArray::const_iterator ci;
387 
388  if (texCoordIndex->empty()) {
389  // If we have *no* texture coordinate index array, but we do have
390  // texture coordinates, assume the VRML writer meant to imply a one-to-
391  // one mapping. This works around a broken formZ VRML file writer.
392  for (size_t i = 0; i < uv_list.size(); i++) {
393  VrmlFieldValue fv;
394  fv._sfint32 = i;
395  (*texCoordIndex).push_back(fv);
396  }
397  }
398 
399  // It's possible that this .wrl file indexes texture coordinates directly
400  // into the vertex array, instead of into the polygon list. Check for
401  // this possibility. This can only happen if the number of texture
402  // coordinate indices exactly matches the number of vertices, and none of
403  // the indices is -1.
404  bool linear_list = (texCoordIndex->size() == _coord_values.size());
405  for (ci = texCoordIndex->begin();
406  ci != texCoordIndex->end() && linear_list;
407  ++ci) {
408  linear_list = ((*ci)._sfint32 >= 0);
409  }
410 
411  if (linear_list) {
412  // Ok, we do have such a list. This .wrl file seems to store its
413  // texture coordinates one per vertex, instead of one per polygon
414  // vertex.
415  _per_vertex_uvs.reserve(_coord_values.size());
416 
417  for (ci = texCoordIndex->begin(); ci != texCoordIndex->end(); ++ci) {
418  size_t vi = (*ci)._sfint32;
419  nassertr(vi >= 0, false);
420  if (vi >= uv_list.size()) {
421  cerr << "Invalid texCoord index: " << vi << "\n";
422  return false;
423  }
424  _per_vertex_uvs.push_back(uv_list[vi]);
425  }
426  nassertr(_per_vertex_uvs.size() == _coord_values.size(), false);
427 
428  } else {
429  // This is a "correct" .wrl file that stores its texture coordinates one
430  // per polygon vertex. This allows a shared vertex to contain two
431  // different texture coordinate values in differing polygons (meaning
432  // it's not actually shared).
433 
434  size_t pi = 0;
435  size_t pv = 0;
436  for (ci = texCoordIndex->begin(); ci != texCoordIndex->end(); ++ci) {
437  if ((*ci)._sfint32 < 0) {
438  // End of poly.
439  if (pv != _polys[pi]._verts.size()) {
440  cerr << "texCoord indices don't match up!\n";
441  return false;
442  }
443  pi++;
444  pv = 0;
445  } else {
446  if (pi >= _polys.size() || pv >= _polys[pi]._verts.size()) {
447  cerr << "texCoord indices don't match up!\n";
448  return false;
449  }
450  _polys[pi]._verts[pv]._attrib.set_uv(uv_list[(*ci)._sfint32]);
451  pv++;
452  }
453  }
454  if (pi != _polys.size()) {
455  cerr << "Not enough texCoord indices!\n";
456  return false;
457  }
458  }
459  return true;
460  }
461  return false;
462 }
463 
464 /**
465  * Once the array of _per_vertex_uvs has been filled (by a broken .wrl file
466  * that indexes the uv's directly into the vertex array instead of per polygon
467  * vertex), go back through the polygons and assign the UV's by index number.
468  */
469 void IndexedFaceSet::
470 assign_per_vertex_uvs() {
471  for (size_t pi = 0; pi < _polys.size(); pi++) {
472  for (size_t pv = 0; pv < _polys[pi]._verts.size(); pv++) {
473  VrmlVertex &vv = _polys[pi]._verts[pv];
474  if (vv._index >= 0 && vv._index < (int)_per_vertex_uvs.size()) {
475  const LTexCoordd &d = _per_vertex_uvs[vv._index];
476  vv._attrib.set_uv(d);
477  }
478  }
479  }
480 }
481 
482 
483 /**
484  *
485  */
486 void IndexedFaceSet::
487 make_polys(EggVertexPool *vpool, EggGroup *group,
488  const LMatrix4d &net_transform) {
489  bool ccw = _geometry->get_value("ccw")._sfbool;
490  bool solid = _geometry->get_value("solid")._sfbool;
491 
492  for (size_t pi = 0; pi < _polys.size(); pi++) {
493  EggPolygon *poly = new EggPolygon;
494  group->add_child(poly);
495  poly->copy_attributes(_polys[pi]._attrib);
496 
497  if (!poly->has_color() && _appearance._has_material) {
498  poly->set_color(_appearance._color);
499  }
500 
501  if (_appearance._tex != nullptr) {
502  poly->set_texture(_appearance._tex);
503  }
504 
505  if (!solid) {
506  poly->set_bface_flag(true);
507  }
508 
509  if (ccw) {
510  // The vertices are counterclockwise, same as Egg.
511  for (int pv = 0; pv < (int)_polys[pi]._verts.size(); pv++) {
512  EggVertex vert(_polys[pi]._verts[pv]._attrib);
513  LVertexd pos =
514  _polys[pi]._verts[pv]._pos * net_transform;
515  vert.set_pos(pos);
516 
517  poly->add_vertex(vpool->create_unique_vertex(vert));
518  }
519  } else {
520  // The vertices are clockwise, so add 'em in reverse order.
521  for (int pv = (int)_polys[pi]._verts.size() - 1; pv >= 0; pv--) {
522  EggVertex vert(_polys[pi]._verts[pv]._attrib);
523  LVertexd pos =
524  _polys[pi]._verts[pv]._pos * net_transform;
525  vert.set_pos(pos);
526 
527  poly->add_vertex(vpool->create_unique_vertex(vert));
528  }
529  }
530  }
531 }
532 
533 
534 /**
535  *
536  */
537 void IndexedFaceSet::
538 compute_normals(EggGroup *group) {
539  const VrmlNode *normal = _geometry->get_value("normal")._sfnode._p;
540  if (normal == nullptr) {
541  // Compute normals.
542  double creaseAngle = _geometry->get_value("creaseAngle")._sffloat;
543  if (creaseAngle == 0.0) {
544  group->recompute_polygon_normals();
545  } else {
546  group->recompute_vertex_normals(rad_2_deg(creaseAngle));
547  }
548  }
549 }
vrmlNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggVertexPool.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pvector< VrmlFieldValue >
VRMLAppearance
Definition: vrmlAppearance.h:23
EggPrimitive::set_bface_flag
set_bface_flag
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.h:116
VrmlNode
Definition: vrmlNode.h:23
pnotify.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggGroupNode::recompute_polygon_normals
void recompute_polygon_normals(CoordinateSystem cs=CS_default)
Recomputes all the polygon normals for polygon geometry at this group node and below so that they acc...
Definition: eggGroupNode.cxx:541
EggVertex
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
EggPolygon
A single polygon.
Definition: eggPolygon.h:24
eggVertex.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggPrimitive::set_texture
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
Definition: eggPrimitive.I:116
indexedFaceSet.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VrmlFieldValue
Definition: vrmlNodeType.h:32
EggPrimitive::add_vertex
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
Definition: eggPrimitive.cxx:654
EggPrimitive::copy_attributes
void copy_attributes(const EggAttributes &other)
Copies the rendering attributes from the indicated primitive.
Definition: eggPrimitive.cxx:265
vrmlAppearance.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggGroupNode::recompute_vertex_normals
void recompute_vertex_normals(double threshold, CoordinateSystem cs=CS_default)
Recomputes all the vertex normals for polygon geometry at this group node and below so that they accu...
Definition: eggGroupNode.cxx:475
EggVertexPool::create_unique_vertex
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
Definition: eggVertexPool.cxx:472
EggVertexPool
A collection of vertices.
Definition: eggVertexPool.h:41
EggGroup
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
EggGroupNode::add_child
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
Definition: eggGroupNode.cxx:243
eggGroup.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggPolygon.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.