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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_texture(EggTexture *texture)
Replaces the current list of textures with the indicated texture.
Definition: eggPrimitive.I:116
set_bface_flag
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.h:116
void copy_attributes(const EggAttributes &other)
Copies the rendering attributes from the indicated primitive.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single polygon.
Definition: eggPolygon.h:24
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
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 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...