Panda3D
 All Classes Functions Variables Enumerations
geomTransformer.cxx
1 // Filename: geomTransformer.cxx
2 // Created by: drose (14Mar02)
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 "geomTransformer.h"
16 #include "sceneGraphReducer.h"
17 #include "geomNode.h"
18 #include "geom.h"
19 #include "geomVertexRewriter.h"
20 #include "renderState.h"
21 #include "transformTable.h"
22 #include "transformBlendTable.h"
23 #include "sliderTable.h"
24 #include "pStatCollector.h"
25 #include "pStatTimer.h"
26 #include "vector_int.h"
27 #include "userVertexTransform.h"
28 #include "geomMunger.h"
29 #include "texture.h"
30 #include "texturePeeker.h"
31 #include "textureAttrib.h"
32 #include "colorAttrib.h"
33 #include "config_pgraph.h"
34 
35 PStatCollector GeomTransformer::_apply_vertex_collector("*:Flatten:apply:vertex");
36 PStatCollector GeomTransformer::_apply_texcoord_collector("*:Flatten:apply:texcoord");
37 PStatCollector GeomTransformer::_apply_set_color_collector("*:Flatten:apply:set color");
38 PStatCollector GeomTransformer::_apply_scale_color_collector("*:Flatten:apply:scale color");
39 PStatCollector GeomTransformer::_apply_texture_color_collector("*:Flatten:apply:texture color");
40 PStatCollector GeomTransformer::_apply_set_format_collector("*:Flatten:apply:set format");
41 
42 TypeHandle GeomTransformer::NewCollectedData::_type_handle;
43 
44 ////////////////////////////////////////////////////////////////////
45 // Function: GeomTransformer::Constructor
46 // Access: Public
47 // Description:
48 ////////////////////////////////////////////////////////////////////
49 GeomTransformer::
50 GeomTransformer() :
51  // The default value here comes from the Config file.
52  _max_collect_vertices(max_collect_vertices)
53 {
54 }
55 
56 ////////////////////////////////////////////////////////////////////
57 // Function: GeomTransformer::Copy Constructor
58 // Access: Public
59 // Description:
60 ////////////////////////////////////////////////////////////////////
61 GeomTransformer::
62 GeomTransformer(const GeomTransformer &copy) :
63  _max_collect_vertices(copy._max_collect_vertices)
64 {
65 }
66 
67 ////////////////////////////////////////////////////////////////////
68 // Function: GeomTransformer::Destructor
69 // Access: Public
70 // Description:
71 ////////////////////////////////////////////////////////////////////
72 GeomTransformer::
73 ~GeomTransformer() {
74  finish_collect(false);
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: GeomTransformer::register_vertices
79 // Access: Public
80 // Description: Records the association of the Geom with its
81 // GeomVertexData, for the purpose of later removing
82 // unused vertices.
83 ////////////////////////////////////////////////////////////////////
85 register_vertices(Geom *geom, bool might_have_unused) {
86  VertexDataAssoc &assoc = _vdata_assoc[geom->get_vertex_data()];
87  assoc._geoms.push_back(geom);
88  if (might_have_unused) {
89  assoc._might_have_unused = true;
90  }
91 }
92 
93 ////////////////////////////////////////////////////////////////////
94 // Function: GeomTransformer::register_vertices
95 // Access: Public
96 // Description: Records the association of the Geom with its
97 // GeomVertexData, for the purpose of later removing
98 // unused vertices.
99 ////////////////////////////////////////////////////////////////////
101 register_vertices(GeomNode *node, bool might_have_unused) {
102  Thread *current_thread = Thread::get_current_thread();
103  OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
104  GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
105  GeomNode::GeomList::iterator gi;
106  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
107  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
108  GeomNode::GeomEntry &entry = (*gi);
109  PT(Geom) geom = entry._geom.get_write_pointer();
110  register_vertices(geom, might_have_unused);
111  }
112  }
113  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
114 }
115 
116 ////////////////////////////////////////////////////////////////////
117 // Function: GeomTransformer::transform_vertices
118 // Access: Public
119 // Description: Transforms the vertices and the normals in the
120 // indicated Geom by the indicated matrix. Returns true
121 // if the Geom was changed, false otherwise.
122 ////////////////////////////////////////////////////////////////////
124 transform_vertices(Geom *geom, const LMatrix4 &mat) {
125  PStatTimer timer(_apply_vertex_collector);
126 
127  nassertr(geom != (Geom *)NULL, false);
128  SourceVertices sv;
129  sv._mat = mat;
130  sv._vertex_data = geom->get_vertex_data();
131 
132  NewVertexData &new_data = _vertices[sv];
133  if (new_data._vdata.is_null()) {
134  // We have not yet converted these vertices. Do so now.
135  PT(GeomVertexData) new_vdata = new GeomVertexData(*sv._vertex_data);
136  new_vdata->transform_vertices(mat);
137  new_data._vdata = new_vdata;
138  }
139 
140  geom->set_vertex_data(new_data._vdata);
141  if (sv._vertex_data->get_ref_count() > 1) {
142  _vdata_assoc[new_data._vdata]._might_have_unused = true;
143  _vdata_assoc[sv._vertex_data]._might_have_unused = true;
144  }
145 
146  return true;
147 }
148 
149 
150 ////////////////////////////////////////////////////////////////////
151 // Function: GeomTransformer::transform_vertices
152 // Access: Public
153 // Description: Transforms the vertices and the normals in all of the
154 // Geoms within the indicated GeomNode by the indicated
155 // matrix. Does not destructively change Geoms;
156 // instead, a copy will be made of each Geom to be
157 // changed, in case multiple GeomNodes reference the
158 // same Geom. Returns true if the GeomNode was changed,
159 // false otherwise.
160 ////////////////////////////////////////////////////////////////////
163  bool any_changed = false;
164 
165  Thread *current_thread = Thread::get_current_thread();
166  OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
167  GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
168  GeomNode::GeomList::iterator gi;
169  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
170  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
171  GeomNode::GeomEntry &entry = (*gi);
172  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
173  if (transform_vertices(new_geom, mat)) {
174  entry._geom = new_geom;
175  any_changed = true;
176  }
177  }
178  }
179  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
180 
181  if (any_changed) {
183  }
184 
185  return any_changed;
186 }
187 
188 
189 ////////////////////////////////////////////////////////////////////
190 // Function: GeomTransformer::transform_texcoords
191 // Access: Public
192 // Description: Transforms the texture coordinates in the indicated
193 // Geom by the indicated matrix. Returns true if the
194 // Geom was changed, false otherwise.
195 ////////////////////////////////////////////////////////////////////
197 transform_texcoords(Geom *geom, const InternalName *from_name,
198  InternalName *to_name, const LMatrix4 &mat) {
199  PStatTimer timer(_apply_texcoord_collector);
200 
201  nassertr(geom != (Geom *)NULL, false);
202 
203  SourceTexCoords st;
204  st._mat = mat;
205  st._from = from_name;
206  st._to = to_name;
207  st._vertex_data = geom->get_vertex_data();
208 
209  NewVertexData &new_data = _texcoords[st];
210  if (new_data._vdata.is_null()) {
211  if (!st._vertex_data->has_column(from_name)) {
212  // No from_name column; no change.
213  return false;
214  }
215 
216  PT(GeomVertexData) new_vdata;
217 
218  // We have not yet converted these texcoords. Do so now.
219  if (st._vertex_data->has_column(to_name)) {
220  new_vdata = new GeomVertexData(*st._vertex_data);
221  } else {
222  const GeomVertexColumn *old_column =
223  st._vertex_data->get_format()->get_column(from_name);
224  new_vdata = st._vertex_data->replace_column
225  (to_name, old_column->get_num_components(),
226  old_column->get_numeric_type(),
227  old_column->get_contents());
228  }
229 
230  CPT(GeomVertexFormat) format = new_vdata->get_format();
231 
232  GeomVertexWriter tdata(new_vdata, to_name);
233  GeomVertexReader fdata(new_vdata, from_name);
234 
235  while (!fdata.is_at_end()) {
236  const LPoint4 &coord = fdata.get_data4();
237  tdata.set_data4(coord * mat);
238  }
239  new_data._vdata = new_vdata;
240  }
241 
242  geom->set_vertex_data(new_data._vdata);
243  if (st._vertex_data->get_ref_count() > 1) {
244  _vdata_assoc[new_data._vdata]._might_have_unused = true;
245  _vdata_assoc[st._vertex_data]._might_have_unused = true;
246  }
247 
248  return true;
249 }
250 
251 
252 ////////////////////////////////////////////////////////////////////
253 // Function: GeomTransformer::transform_texcoords
254 // Access: Public
255 // Description: Transforms the texture coordinates in all of the
256 // Geoms within the indicated GeomNode by the indicated
257 // matrix. Does not destructively change Geoms;
258 // instead, a copy will be made of each Geom to be
259 // changed, in case multiple GeomNodes reference the
260 // same Geom. Returns true if the GeomNode was changed,
261 // false otherwise.
262 ////////////////////////////////////////////////////////////////////
264 transform_texcoords(GeomNode *node, const InternalName *from_name,
265  InternalName *to_name, const LMatrix4 &mat) {
266  bool any_changed = false;
267 
268  GeomNode::CDWriter cdata(node->_cycler);
269  GeomNode::GeomList::iterator gi;
270  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
271  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
272  GeomNode::GeomEntry &entry = (*gi);
273  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
274  if (transform_texcoords(new_geom, from_name, to_name, mat)) {
275  entry._geom = new_geom;
276  any_changed = true;
277  }
278  }
279 
280  return any_changed;
281 }
282 
283 
284 ////////////////////////////////////////////////////////////////////
285 // Function: GeomTransformer::set_color
286 // Access: Public
287 // Description: Overrides the color indicated within the Geom with
288 // the given replacement color. Returns true if the
289 // Geom was changed, false otherwise.
290 ////////////////////////////////////////////////////////////////////
292 set_color(Geom *geom, const LColor &color) {
293  PStatTimer timer(_apply_set_color_collector);
294 
295  SourceColors sc;
296  sc._color = color;
297  sc._vertex_data = geom->get_vertex_data();
298 
299  NewVertexData &new_data = _fcolors[sc];
300  if (new_data._vdata.is_null()) {
301  // We have not yet converted these colors. Do so now.
302  if (sc._vertex_data->has_column(InternalName::get_color())) {
303  new_data._vdata = sc._vertex_data->set_color(color);
304  } else {
305  new_data._vdata = sc._vertex_data->set_color
306  (color, 1, Geom::NT_packed_dabc, Geom::C_color);
307  }
308  }
309 
310  geom->set_vertex_data(new_data._vdata);
311  if (sc._vertex_data->get_ref_count() > 1) {
312  _vdata_assoc[new_data._vdata]._might_have_unused = true;
313  _vdata_assoc[sc._vertex_data]._might_have_unused = true;
314  }
315 
316  return true;
317 }
318 
319 
320 ////////////////////////////////////////////////////////////////////
321 // Function: GeomTransformer::set_color
322 // Access: Public
323 // Description: Overrides the color indicated within the GeomNode
324 // with the given replacement color. Returns true if
325 // any Geom in the GeomNode was changed, false
326 // otherwise.
327 ////////////////////////////////////////////////////////////////////
329 set_color(GeomNode *node, const LColor &color) {
330  bool any_changed = false;
331 
332  GeomNode::CDWriter cdata(node->_cycler);
333  GeomNode::GeomList::iterator gi;
334  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
335  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
336  GeomNode::GeomEntry &entry = (*gi);
337  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
338  if (set_color(new_geom, color)) {
339  entry._geom = new_geom;
340  any_changed = true;
341  }
342  }
343 
344  return any_changed;
345 }
346 
347 ////////////////////////////////////////////////////////////////////
348 // Function: GeomTransformer::transform_colors
349 // Access: Public
350 // Description: Transforms the colors in the indicated Geom by the
351 // indicated scale. Returns true if the Geom was
352 // changed, false otherwise.
353 ////////////////////////////////////////////////////////////////////
355 transform_colors(Geom *geom, const LVecBase4 &scale) {
356  PStatTimer timer(_apply_scale_color_collector);
357 
358  nassertr(geom != (Geom *)NULL, false);
359 
360  SourceColors sc;
361  sc._color = scale;
362  sc._vertex_data = geom->get_vertex_data();
363 
364  NewVertexData &new_data = _tcolors[sc];
365  if (new_data._vdata.is_null()) {
366  // We have not yet converted these colors. Do so now.
367  if (sc._vertex_data->has_column(InternalName::get_color())) {
368  new_data._vdata = sc._vertex_data->scale_color(scale);
369  } else {
370  new_data._vdata = sc._vertex_data->set_color
371  (scale, 1, Geom::NT_packed_dabc, Geom::C_color);
372  }
373  }
374 
375  geom->set_vertex_data(new_data._vdata);
376  if (sc._vertex_data->get_ref_count() > 1) {
377  _vdata_assoc[new_data._vdata]._might_have_unused = true;
378  _vdata_assoc[sc._vertex_data]._might_have_unused = true;
379  }
380 
381  return true;
382 }
383 
384 
385 ////////////////////////////////////////////////////////////////////
386 // Function: GeomTransformer::transform_colors
387 // Access: Public
388 // Description: Transforms the colors in all of the Geoms within the
389 // indicated GeomNode by the indicated scale. Does
390 // not destructively change Geoms; instead, a copy will
391 // be made of each Geom to be changed, in case multiple
392 // GeomNodes reference the same Geom. Returns true if
393 // the GeomNode was changed, false otherwise.
394 ////////////////////////////////////////////////////////////////////
396 transform_colors(GeomNode *node, const LVecBase4 &scale) {
397  bool any_changed = false;
398 
399  GeomNode::CDWriter cdata(node->_cycler);
400  GeomNode::GeomList::iterator gi;
401  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
402  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
403  GeomNode::GeomEntry &entry = (*gi);
404  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
405  if (transform_colors(new_geom, scale)) {
406  entry._geom = new_geom;
407  any_changed = true;
408  }
409  }
410 
411  return any_changed;
412 }
413 
414 
415 ////////////////////////////////////////////////////////////////////
416 // Function: GeomTransformer::apply_texture_colors
417 // Access: Public
418 // Description: Removes textures from Geoms by applying the texture
419 // colors to the vertices.
420 //
421 // See apply_texure_colors(GeomNode *, RenderState *).
422 ////////////////////////////////////////////////////////////////////
425  const TexMatrixAttrib *tma, const LColor &base_color,
426  bool keep_vertex_color) {
427  PStatTimer timer(_apply_texture_color_collector);
428 
429  nassertr(geom != (Geom *)NULL, false);
430 
431  PT(TexturePeeker) peeker = tex->peek();
432  if (peeker == (TexturePeeker *)NULL) {
433  return false;
434  }
435 
436  if (peeker->get_x_size() == 1 &&
437  peeker->get_y_size() == 1 &&
438  peeker->get_z_size() == 1) {
439  // If it's just a one-pixel texture (e.g. a simple ram image),
440  // don't bother scanning the UV's. Just extract the color and
441  // apply it.
442  LColor color;
443  peeker->lookup(color, 0.0f, 0.0f);
444  color.set(color[0] * base_color[0],
445  color[1] * base_color[1],
446  color[2] * base_color[2],
447  color[3] * base_color[3]);
448  if (keep_vertex_color) {
449  return transform_colors(geom, color);
450  } else {
451  return set_color(geom, color);
452  }
453  }
454 
455  bool got_mat = false;
457  if (tma != (TexMatrixAttrib *)NULL && tma->has_stage(ts)) {
458  mat = tma->get_mat(ts);
459  got_mat = !mat.almost_equal(LMatrix4::ident_mat());
460  }
461 
462  // This version of the code just applied one overall flat color to
463  // the entire mesh. Turned out not to be good enough. Instead,
464  // we'll look up each vertex in the texture map and apply the
465  // nearest color to the vertex.
466  /*
467  // Scan the UV's to get the used range. This is particularly
468  // necessary for palettized textures.
469 
470  LPoint3 min_point, max_point;
471  bool found_any = false;
472  geom->calc_tight_bounds(min_point, max_point, found_any,
473  geom->get_vertex_data(),
474  got_mat, mat,
475  ts->get_texcoord_name(),
476  Thread::get_current_thread());
477  if (found_any) {
478  // Now use that UV range to determine the overall color of the
479  // geom's texture.
480  LColor color;
481  peeker->filter_rect(color,
482  min_point[0], min_point[1], min_point[2],
483  max_point[0], max_point[1], max_point[2]);
484  color.set(color[0] * base_color[0],
485  color[1] * base_color[1],
486  color[2] * base_color[2],
487  color[3] * base_color[3]);
488  if (keep_vertex_color) {
489  return transform_colors(geom, color);
490  } else {
491  return set_color(geom, color);
492  }
493  }
494 
495  return false;
496  */
497 
498  SourceTextureColors stc;
499  stc._ts = ts;
500  stc._tex = tex;
501  stc._tma = tma;
502  stc._base_color = base_color;
503  stc._keep_vertex_color = keep_vertex_color;
504  stc._vertex_data = geom->get_vertex_data();
505 
506  NewVertexData &new_data = _tex_colors[stc];
507  if (new_data._vdata.is_null()) {
508  // We have not yet applied these texture colors. Do so now.
509 
510  PT(GeomVertexData) vdata;
511 
512  // Make sure the vdata has a color column.
513  if (stc._vertex_data->has_column(InternalName::get_color())) {
514  vdata = new GeomVertexData(*stc._vertex_data);
515  } else {
516  // Create a color column where there wasn't one before.
517  vdata = new GeomVertexData(*stc._vertex_data->set_color
518  (LColor(1.0f, 1.0f, 1.0f, 1.0f), 1, Geom::NT_packed_dabc, Geom::C_color));
519  keep_vertex_color = false;
520  }
521 
522  // Check whether it has 2-d or 3-d texture coordinates.
523  bool tex3d = false;
524  const GeomVertexColumn *column = vdata->get_format()->get_column(ts->get_texcoord_name());
525  if (column == (GeomVertexColumn *)NULL) {
526  return false;
527  }
528  if (column->get_num_components() >= 3) {
529  tex3d = true;
530  }
531 
532  // Now walk through the vertices and apply each color from the
533  // texture as we go.
534  if (keep_vertex_color) {
535  // We want to modulate the existing vertex color.
536  GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
537  GeomVertexRewriter gcolor(vdata, InternalName::get_color());
538 
539  if (got_mat || tex3d) {
540  while (!gtexcoord.is_at_end()) {
541  LTexCoord3 p = gtexcoord.get_data3();
542  LColor c = gcolor.get_data4();
543  p = p * mat;
544  LColor color;
545  peeker->lookup(color, p[0], p[1], p[2]);
546  color.set(color[0] * base_color[0] * c[0],
547  color[1] * base_color[1] * c[1],
548  color[2] * base_color[2] * c[2],
549  color[3] * base_color[3] * c[3]);
550  gcolor.set_data4(color);
551  }
552  } else {
553  while (!gtexcoord.is_at_end()) {
554  LTexCoord p = gtexcoord.get_data2();
555  LColor c = gcolor.get_data4();
556  LColor color;
557  peeker->lookup(color, p[0], p[1]);
558  color.set(color[0] * base_color[0] * c[0],
559  color[1] * base_color[1] * c[1],
560  color[2] * base_color[2] * c[2],
561  color[3] * base_color[3] * c[3]);
562  gcolor.set_data4(color);
563  }
564  }
565  } else {
566  // We want to replace any existing vertex color.
567  GeomVertexReader gtexcoord(vdata, ts->get_texcoord_name());
568  GeomVertexWriter gcolor(vdata, InternalName::get_color());
569 
570  if (got_mat || tex3d) {
571  while (!gtexcoord.is_at_end()) {
572  LTexCoord3 p = gtexcoord.get_data3();
573  p = p * mat;
574  LColor color;
575  peeker->lookup(color, p[0], p[1], p[2]);
576  color.set(color[0] * base_color[0],
577  color[1] * base_color[1],
578  color[2] * base_color[2],
579  color[3] * base_color[3]);
580  gcolor.set_data4(color);
581  }
582  } else {
583  while (!gtexcoord.is_at_end()) {
584  LTexCoord p = gtexcoord.get_data2();
585  LColor color;
586  peeker->lookup(color, p[0], p[1]);
587  color.set(color[0] * base_color[0],
588  color[1] * base_color[1],
589  color[2] * base_color[2],
590  color[3] * base_color[3]);
591  gcolor.set_data4(color);
592  }
593  }
594  }
595 
596  new_data._vdata = vdata;
597  }
598 
599  geom->set_vertex_data(new_data._vdata);
600  if (stc._vertex_data->get_ref_count() > 1) {
601  _vdata_assoc[new_data._vdata]._might_have_unused = true;
602  _vdata_assoc[stc._vertex_data]._might_have_unused = true;
603  }
604 
605  return true;
606 }
607 
608 ////////////////////////////////////////////////////////////////////
609 // Function: GeomTransformer::apply_texture_colors
610 // Access: Public
611 // Description: Removes textures from Geoms by applying the texture
612 // colors to the vertices. This is primarily useful to
613 // simplify a low-LOD model.
614 //
615 // Only the bottommost texture is used (if there is more
616 // than one), and it is applied as if it were
617 // M_modulate, and WM_repeat, regardless of its actual
618 // settings. If the texture has a simple_ram_image,
619 // this may be used if the main image isn't resident.
620 //
621 // After this call, there will be no texturing specified
622 // on the GeomNode level. Of course, there might still
623 // be texturing inherited from above.
624 ////////////////////////////////////////////////////////////////////
627  bool any_changed = false;
628 
629  GeomNode::CDWriter cdata(node->_cycler);
630  GeomNode::GeomList::iterator gi;
631  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
632  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
633  GeomNode::GeomEntry &entry = (*gi);
634  CPT(RenderState) geom_state = state->compose(entry._state);
635 
636  const TextureAttrib *ta = DCAST(TextureAttrib, geom_state->get_attrib(TextureAttrib::get_class_slot()));
637  if (ta != (TextureAttrib *)NULL) {
638  CPT(TextureAttrib) ta2 = ta->filter_to_max(1);
639  if (ta2->get_num_on_stages() > 0) {
640  TextureStage *ts = ta2->get_on_stage(0);
641  Texture *tex = ta2->get_on_texture(ts);
642  const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, geom_state->get_attrib(TexMatrixAttrib::get_class_slot()));
643 
644  const ColorAttrib *ca = DCAST(ColorAttrib, geom_state->get_attrib(ColorAttrib::get_class_slot()));
645  LColor base_color(1.0f, 1.0f, 1.0f, 1.0f);
646  bool keep_vertex_color = true;
647  if (ca != (ColorAttrib *)NULL && ca->get_color_type() == ColorAttrib::T_flat) {
648  base_color = ca->get_color();
649  keep_vertex_color = false;
650  }
651 
652  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
653  if (apply_texture_colors(new_geom, ts, tex, tma, base_color, keep_vertex_color)) {
654  entry._geom = new_geom;
655  any_changed = true;
656 
657  if (new_geom->get_vertex_data()->has_column(InternalName::get_color())) {
658  // Ensure we have a ColorAttrib::make_vertex() attrib.
659  CPT(RenderState) color_state = entry._state->set_attrib(ColorAttrib::make_vertex());
660  if (entry._state != color_state) {
661  entry._state = color_state;
662  any_changed = true;
663  }
664  }
665  }
666 
667  // Also remove any texture references from the GeomState.
668  CPT(RenderState) no_tex_state = entry._state->remove_attrib(TextureAttrib::get_class_slot());
669  if (entry._state != no_tex_state) {
670  entry._state = no_tex_state;
671  any_changed = true;
672  }
673  }
674  }
675  }
676 
677  return any_changed;
678 }
679 
680 ////////////////////////////////////////////////////////////////////
681 // Function: GeomTransformer::apply_state
682 // Access: Public
683 // Description: Applies the indicated render state to all the of
684 // Geoms. Returns true if the GeomNode was changed,
685 // false otherwise.
686 ////////////////////////////////////////////////////////////////////
688 apply_state(GeomNode *node, const RenderState *state) {
689  bool any_changed = false;
690 
691  GeomNode::CDWriter cdata(node->_cycler);
692  GeomNode::GeomList::iterator gi;
693  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
694  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
695  GeomNode::GeomEntry &entry = (*gi);
696  CPT(RenderState) new_state = state->compose(entry._state);
697  if (entry._state != new_state) {
698  entry._state = new_state;
699  any_changed = true;
700  }
701  }
702 
703  return any_changed;
704 }
705 
706 ////////////////////////////////////////////////////////////////////
707 // Function: GeomTransformer::set_format
708 // Access: Public
709 // Description: Changes the GeomVertexData of the indicated Geom to
710 // use the specified format.
711 ////////////////////////////////////////////////////////////////////
713 set_format(Geom *geom, const GeomVertexFormat *new_format) {
714  PStatTimer timer(_apply_set_format_collector);
715 
716  nassertr(geom != (Geom *)NULL, false);
717 
718  SourceFormat sf;
719  sf._format = new_format;
720  sf._vertex_data = geom->get_vertex_data();
721 
722  NewVertexData &new_data = _format[sf];
723  if (new_data._vdata.is_null()) {
724  if (sf._vertex_data->get_format() == new_format) {
725  // No change.
726  return false;
727  }
728 
729  // We have not yet converted this vertex data. Do so now.
730  PT(GeomVertexData) new_vdata = new GeomVertexData(*sf._vertex_data);
731  new_vdata->set_format(new_format);
732  new_data._vdata = new_vdata;
733  }
734 
735  geom->set_vertex_data(new_data._vdata);
736  if (sf._vertex_data->get_ref_count() > 1) {
737  _vdata_assoc[new_data._vdata]._might_have_unused = true;
738  _vdata_assoc[sf._vertex_data]._might_have_unused = true;
739  }
740 
741  return true;
742 }
743 
744 ////////////////////////////////////////////////////////////////////
745 // Function: GeomTransformer::remove_column
746 // Access: Public
747 // Description: Removes the named column from the vertex data in the
748 // Geom. Returns true if the Geom was changed, false
749 // otherwise.
750 ////////////////////////////////////////////////////////////////////
752 remove_column(Geom *geom, const InternalName *column) {
753  CPT(GeomVertexFormat) format = geom->get_vertex_data()->get_format();
754  if (!format->has_column(column)) {
755  return false;
756  }
757 
758  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*format);
759  new_format->remove_column(column);
760  new_format->pack_columns();
761  format = GeomVertexFormat::register_format(new_format);
762 
763  return set_format(geom, format);
764 }
765 
766 
767 ////////////////////////////////////////////////////////////////////
768 // Function: GeomTransformer::remove_column
769 // Access: Public
770 // Description: Removes the named column from the vertex datas within
771 // the GeomNode. Returns true if the GeomNode was
772 // changed, false otherwise.
773 ////////////////////////////////////////////////////////////////////
775 remove_column(GeomNode *node, const InternalName *column) {
776  bool any_changed = false;
777 
778  GeomNode::CDWriter cdata(node->_cycler);
779  GeomNode::GeomList::iterator gi;
780  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
781  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
782  GeomNode::GeomEntry &entry = (*gi);
783  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
784  if (remove_column(new_geom, column)) {
785  entry._geom = new_geom;
786  any_changed = true;
787  }
788  }
789 
790  return any_changed;
791 }
792 
793 ////////////////////////////////////////////////////////////////////
794 // Function: GeomTransformer::make_compatible_state
795 // Access: Public
796 // Description: Checks if the different geoms in the GeomNode have
797 // different RenderStates. If so, tries to make the
798 // RenderStates the same. It does this by
799 // canonicalizing the ColorAttribs, and in the future,
800 // possibly other attribs.
801 ////////////////////////////////////////////////////////////////////
804  if (node->get_num_geoms() < 2) {
805  return false;
806  }
807 
808  GeomNode::CDWriter cdata(node->_cycler);
809  GeomNode::GeomList::iterator gi;
810  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
811 
812  // For each geom, calculate a canonicalized RenderState, and
813  // classify all the geoms according to that. By "canonicalize"
814  // here, we simply mean removing the ColorAttrib.
815 
816  typedef pmap <CPT(RenderState), pvector<int> > StateTable;
817  StateTable state_table;
818 
819  for (int i = 0; i < (int)geoms->size(); i++) {
820  GeomNode::GeomEntry &entry = (*geoms)[i];
821  CPT(RenderState) canon = entry._state->remove_attrib(ColorAttrib::get_class_slot());
822  state_table[canon].push_back(i);
823  }
824 
825  // For each group of geoms, check for mismatch.
826 
827  bool any_changed = false;
828  StateTable::iterator si;
829  for (si = state_table.begin(); si != state_table.end(); si++) {
830 
831  // If the geoms in the group already have the same RenderStates,
832  // then nothing needs to be done to this group.
833 
834  const pvector<int> &indices = (*si).second;
835  bool mismatch = false;
836  for (int i = 1; i < (int)indices.size(); i++) {
837  if ((*geoms)[indices[i]]._state != (*geoms)[indices[0]]._state) {
838  mismatch = true;
839  break;
840  }
841  }
842  if (!mismatch) {
843  continue;
844  }
845 
846  // The geoms do not have the same RenderState, but they could,
847  // since their canonicalized states are the same. Canonicalize
848  // them, by applying the colors to the vertices.
849 
850  const RenderState *canon_state = (*si).first;
851  for (int i = 0; i < (int)indices.size(); i++) {
852  GeomNode::GeomEntry &entry = (*geoms)[indices[i]];
853  const RenderAttrib *ra = entry._state->get_attrib_def(ColorAttrib::get_class_slot());
854  const ColorAttrib *ca = DCAST(ColorAttrib, ra);
855  if (ca->get_color_type() == ColorAttrib::T_vertex) {
856  // All we need to do is ensure that the geom has a color column.
857  if (!entry._geom.get_read_pointer()->get_vertex_data()->has_column(InternalName::get_color())) {
858  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
859  if (set_color(new_geom, LColor(1,1,1,1))) {
860  entry._geom = new_geom;
861  }
862  }
863  } else {
864  // A flat color (or "off", which is white). Set the vertices
865  // to the indicated flat color.
866  LColor c = ca->get_color();
867  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
868  if (set_color(new_geom, c)) {
869  entry._geom = new_geom;
870  }
871  }
872  entry._state = canon_state->add_attrib(ColorAttrib::make_vertex());
873  any_changed = true;
874  }
875  }
876 
877  return any_changed;
878 }
879 
880 ////////////////////////////////////////////////////////////////////
881 // Function: GeomTransformer::reverse_normals
882 // Access: Public
883 // Description: Reverses the lighting normals on the vertex data, if
884 // any. Returns true if the Geom was changed, false
885 // otherwise.
886 ////////////////////////////////////////////////////////////////////
889  nassertr(geom != (Geom *)NULL, false);
890  CPT(GeomVertexData) orig_data = geom->get_vertex_data();
891  NewVertexData &new_data = _reversed_normals[orig_data];
892  if (new_data._vdata.is_null()) {
893  new_data._vdata = orig_data->reverse_normals();
894  }
895 
896  if (new_data._vdata == orig_data) {
897  // No change.
898  return false;
899  }
900 
901  geom->set_vertex_data(new_data._vdata);
902  if (orig_data->get_ref_count() > 1) {
903  _vdata_assoc[new_data._vdata]._might_have_unused = true;
904  _vdata_assoc[orig_data]._might_have_unused = true;
905  }
906 
907  return true;
908 }
909 
910 ////////////////////////////////////////////////////////////////////
911 // Function: GeomTransformer::doubleside
912 // Access: Public
913 // Description: Duplicates triangles in this GeomNode so that each
914 // triangle is back-to-back with another triangle facing
915 // in the opposite direction. If the geometry has
916 // vertex normals, this will also duplicate and reverse
917 // the normals, so that lighting will work correctly
918 // from both sides. Note that calling this when the
919 // geometry is already doublesided (with back-to-back
920 // polygons) will result in multiple redundant coplanar
921 // polygons.
922 //
923 // Also see CullFaceAttrib, which can enable rendering
924 // of both sides of a triangle without having to
925 // duplicate it (but which doesn't necessarily work in
926 // the presence of lighting).
927 //
928 // Returns true if any Geoms are modified, false
929 // otherwise.
930 ////////////////////////////////////////////////////////////////////
933  int num_geoms = node->get_num_geoms();
934  for (int i = 0; i < num_geoms; ++i) {
935  CPT(Geom) orig_geom = node->get_geom(i);
936  bool has_normals = (orig_geom->get_vertex_data()->has_column(InternalName::get_normal()));
937  if (has_normals) {
938  // If the geometry has normals, we have to duplicate it to
939  // reverse the normals on the duplicate copy.
940  PT(Geom) new_geom = orig_geom->reverse();
941  reverse_normals(new_geom);
942  node->add_geom(new_geom, node->get_geom_state(i));
943 
944  } else {
945  // If there are no normals, we can just doubleside it in
946  // place. This is preferable because we can share vertices.
947  orig_geom.clear();
948  node->modify_geom(i)->doubleside_in_place();
949  }
950  }
951 
952  return (num_geoms != 0);
953 }
954 
955 
956 ////////////////////////////////////////////////////////////////////
957 // Function: GeomTransformer::reverse
958 // Access: Public
959 // Description: Reverses the winding order of triangles in this
960 // GeomNode so that each triangle is facing in the
961 // opposite direction. If the geometry has vertex
962 // normals, this will also reverse the normals, so that
963 // lighting will work correctly.
964 //
965 // Also see CullFaceAttrib, which can effectively change
966 // the facing of a triangle having to modify its
967 // vertices (but which doesn't necessarily work in the
968 // presence of lighting).
969 //
970 // Returns true if any Geoms are modified, false
971 // otherwise.
972 ////////////////////////////////////////////////////////////////////
975  int num_geoms = node->get_num_geoms();
976  for (int i = 0; i < num_geoms; ++i) {
977  PT(Geom) geom = node->modify_geom(i);
978  geom->reverse_in_place();
979  reverse_normals(geom);
980  }
981 
982  return (num_geoms != 0);
983 }
984 
985 ////////////////////////////////////////////////////////////////////
986 // Function: GeomTransformer::finish_apply
987 // Access: Public
988 // Description: Should be called after performing any
989 // operations--particularly
990 // PandaNode::apply_attribs_to_vertices()--that might
991 // result in new GeomVertexData objects being duplicated
992 // and modified. This walks through those newly
993 // duplicated objects and ensures that redundant unused
994 // vertices have not been created, removing them if they
995 // have.
996 ////////////////////////////////////////////////////////////////////
999  VertexDataAssocMap::iterator vi;
1000  for (vi = _vdata_assoc.begin(); vi != _vdata_assoc.end(); ++vi) {
1001  const GeomVertexData *vdata = (*vi).first;
1002  VertexDataAssoc &assoc = (*vi).second;
1003  if (assoc._might_have_unused) {
1004  assoc.remove_unused_vertices(vdata);
1005  }
1006  }
1007  _vdata_assoc.clear();
1008 
1009  _texcoords.clear();
1010  _fcolors.clear();
1011  _tcolors.clear();
1012  _format.clear();
1013  _reversed_normals.clear();
1014 }
1015 
1016 ////////////////////////////////////////////////////////////////////
1017 // Function: GeomTransformer::collect_vertex_data
1018 // Access: Public
1019 // Description: Collects together GeomVertexDatas from different
1020 // geoms into one big (or several big) GeomVertexDatas.
1021 // Returns the number of unique GeomVertexDatas created.
1022 //
1023 // If format_only is true, this only makes
1024 // GeomVertexFormats compatible; it does not otherwise
1025 // combine vertices.
1026 //
1027 // You should follow this up with a call to
1028 // finish_collect(), but you probably don't want to call
1029 // this method directly anyway. Call
1030 // SceneGraphReducer::collect_vertex_data() instead.
1031 ////////////////////////////////////////////////////////////////////
1033 collect_vertex_data(Geom *geom, int collect_bits, bool format_only) {
1034  CPT(GeomVertexData) vdata = geom->get_vertex_data();
1035  if (vdata->get_num_rows() > _max_collect_vertices) {
1036  // Don't even bother.
1037  return 0;
1038  }
1039 
1040  CPT(GeomVertexFormat) format = vdata->get_format();
1041 
1042  NewCollectedKey key;
1043  if ((collect_bits & SceneGraphReducer::CVD_name) != 0) {
1044  key._name = vdata->get_name();
1045  }
1046  if ((collect_bits & SceneGraphReducer::CVD_format) != 0) {
1047  key._format = format;
1048  }
1049  if ((collect_bits & SceneGraphReducer::CVD_usage_hint) != 0) {
1050  key._usage_hint = vdata->get_usage_hint();
1051  } else {
1052  key._usage_hint = Geom::UH_unspecified;
1053  }
1054  if ((collect_bits & SceneGraphReducer::CVD_animation_type) != 0) {
1055  key._animation_type = format->get_animation().get_animation_type();
1056  } else {
1057  key._animation_type = Geom::AT_none;
1058  }
1059 
1060  AlreadyCollectedMap::const_iterator ai;
1061  ai = _already_collected_map.find(vdata);
1062  if (ai != _already_collected_map.end()) {
1063  // We've previously collected this vertex data; reuse it.
1064  const AlreadyCollectedData &acd = (*ai).second;
1065  SourceGeom source_geom;
1066  source_geom._geom = geom;
1067  source_geom._vertex_offset = acd._vertex_offset;
1068  acd._ncd->_source_geoms.push_back(source_geom);
1069  return 0;
1070  }
1071 
1072  // We haven't collected this vertex data yet; associate it with a
1073  // new data.
1074  NewCollectedMap::iterator ni = _new_collected_map.find(key);
1075  NewCollectedData *ncd;
1076  if (ni != _new_collected_map.end()) {
1077  ncd = (*ni).second;
1078 
1079  } else {
1080  // We haven't encountered a compatible GeomVertexData before.
1081  // Create a new entry.
1082  ncd = new NewCollectedData(vdata);
1083  _new_collected_list.push_back(ncd);
1084  _new_collected_map[key] = ncd;
1085  }
1086 
1087  if (ncd->_new_format != format) {
1088  ncd->_new_format = format->get_union_format(ncd->_new_format);
1089  }
1090 
1091  int this_num_vertices = vdata->get_num_rows();
1092 
1093  if (!format_only &&
1094  ncd->_num_vertices + this_num_vertices > _max_collect_vertices) {
1095  // Whoa, hold the phone! Too many vertices going into this one
1096  // GeomVertexData object; we'd better start over.
1097  ncd = new NewCollectedData(vdata);
1098  _new_collected_list.push_back(ncd);
1099  _new_collected_map[key] = ncd;
1100  }
1101 
1102  int vertex_offset = ncd->_num_vertices;
1103 
1104  AlreadyCollectedData &acd = _already_collected_map[vdata];
1105  acd._ncd = ncd;
1106  acd._vertex_offset = vertex_offset;
1107 
1108  SourceGeom source_geom;
1109  source_geom._geom = geom;
1110  source_geom._vertex_offset = vertex_offset;
1111  ncd->_source_geoms.push_back(source_geom);
1112 
1113  SourceData source_data;
1114  source_data._vdata = vdata;
1115  source_data._num_vertices = this_num_vertices;
1116 
1117  ncd->_source_datas.push_back(source_data);
1118  ncd->_num_vertices += this_num_vertices;
1119 
1120  return 0;
1121 }
1122 
1123 
1124 ////////////////////////////////////////////////////////////////////
1125 // Function: GeomTransformer::collect_vertex_data
1126 // Access: Public
1127 // Description: Collects together individual GeomVertexData
1128 // structures that share the same format into one big
1129 // GeomVertexData structure. This is intended to
1130 // minimize context switches on the graphics card.
1131 //
1132 // If format_only is true, this only makes
1133 // GeomVertexFormats compatible; it does not otherwise
1134 // combine vertices.
1135 //
1136 // You should follow this up with a call to
1137 // finish_collect(), but you probably don't want to call
1138 // this method directly anyway. Call
1139 // SceneGraphReducer::collect_vertex_data() instead.
1140 ////////////////////////////////////////////////////////////////////
1142 collect_vertex_data(GeomNode *node, int collect_bits, bool format_only) {
1143  int num_adjusted = 0;
1144  GeomTransformer *dynamic = NULL;
1145 
1146  GeomNode::CDWriter cdata(node->_cycler);
1147  GeomNode::GeomList::iterator gi;
1148  PT(GeomNode::GeomList) geoms = cdata->modify_geoms();
1149  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
1150  GeomNode::GeomEntry &entry = (*gi);
1151  PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
1152  entry._geom = new_geom;
1153 
1154  if ((collect_bits & SceneGraphReducer::CVD_avoid_dynamic) != 0 &&
1155  new_geom->get_vertex_data()->get_usage_hint() < Geom::UH_static) {
1156  // This one has some dynamic properties. Collect it
1157  // independently of the outside world.
1158  if (dynamic == (GeomTransformer *)NULL) {
1159  dynamic = new GeomTransformer(*this);
1160  }
1161  num_adjusted += dynamic->collect_vertex_data(new_geom, collect_bits, format_only);
1162 
1163  } else {
1164  num_adjusted += collect_vertex_data(new_geom, collect_bits, format_only);
1165  }
1166  }
1167 
1168  if (dynamic != (GeomTransformer *)NULL) {
1169  num_adjusted += dynamic->finish_collect(format_only);
1170  delete dynamic;
1171  }
1172 
1173  return num_adjusted;
1174 }
1175 
1176 ////////////////////////////////////////////////////////////////////
1177 // Function: GeomTransformer::finish_collect
1178 // Access: Public
1179 // Description: This should be called after a call to
1180 // collect_vertex_data() to finalize the changes and
1181 // apply them to the vertices in the graph. If this is
1182 // not called, it will be called automatically by the
1183 // GeomTransformer destructor.
1184 //
1185 // If format_only is true, this returns the number of
1186 // GeomVertexDatas modified to use a new format. If
1187 // false, it returns the number of GeomVertexDatas
1188 // created.
1189 ////////////////////////////////////////////////////////////////////
1191 finish_collect(bool format_only) {
1192  int num_adjusted = 0;
1193 
1194  NewCollectedList::iterator nci;
1195  for (nci = _new_collected_list.begin();
1196  nci != _new_collected_list.end();
1197  ++nci) {
1198  NewCollectedData *ncd = (*nci);
1199  if (format_only) {
1200  num_adjusted += ncd->apply_format_only_changes();
1201  } else {
1202  num_adjusted += ncd->apply_collect_changes();
1203  }
1204  delete ncd;
1205  }
1206 
1207  _new_collected_list.clear();
1208  _new_collected_map.clear();
1209  _already_collected_map.clear();
1210 
1211  return num_adjusted;
1212 }
1213 
1214 ////////////////////////////////////////////////////////////////////
1215 // Function: GeomTransformer::premunge_geom
1216 // Access: Public
1217 // Description: Uses the indicated munger to premunge the given Geom
1218 // to optimize it for eventual rendering. See
1219 // SceneGraphReducer::premunge().
1220 ////////////////////////////////////////////////////////////////////
1221 PT(Geom) GeomTransformer::
1222 premunge_geom(const Geom *geom, GeomMunger *munger) {
1223  // This method had been originally provided to cache the result for
1224  // a particular geom/munger and vdata/munger combination, similar to
1225  // the way other GeomTransformer methods work. On reflection, this
1226  // additional caching is not necessary, since the GeomVertexFormat
1227  // does its own caching, and there's no danger of that cache filling
1228  // up during the span of one frame.
1229 
1230  CPT(GeomVertexData) vdata = geom->get_vertex_data();
1231  vdata = munger->premunge_data(vdata);
1232  CPT(Geom) pgeom = geom;
1233  munger->premunge_geom(pgeom, vdata);
1234 
1235  PT(Geom) geom_copy = pgeom->make_copy();
1236  geom_copy->set_vertex_data(vdata);
1237 
1238  return geom_copy;
1239 }
1240 
1241 ////////////////////////////////////////////////////////////////////
1242 // Function: GeomTransformer::NewCollectedData::Constructor
1243 // Access: Public
1244 // Description:
1245 ////////////////////////////////////////////////////////////////////
1246 GeomTransformer::NewCollectedData::
1247 NewCollectedData(const GeomVertexData *source_data) {
1248  _new_format = source_data->get_format();
1249  _vdata_name = source_data->get_name();
1250  _usage_hint = source_data->get_usage_hint();
1251  _num_vertices = 0;
1252 }
1253 
1254 ////////////////////////////////////////////////////////////////////
1255 // Function: GeomTransformer::NewCollectedData::apply_format_only_changes
1256 // Access: Public
1257 // Description: Actually adjusts the GeomVertexDatas found in a
1258 // collect_vertex_data() format-only call to have the
1259 // same vertex format. Returns the number of vdatas
1260 // modified.
1261 ////////////////////////////////////////////////////////////////////
1262 int GeomTransformer::NewCollectedData::
1263 apply_format_only_changes() {
1264  int num_modified = 0;
1265 
1266  // We probably don't need to use a map, since
1267  // GeomVertexData::convert_to() already caches its result, but we do
1268  // it anyway just in case there's danger of overflowing the cache.
1269  // What the heck, it's easy to do.
1271  VDataMap vdata_map;
1272 
1273  SourceGeoms::iterator sgi;
1274  for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
1275  SourceGeom &sg = (*sgi);
1276  CPT(GeomVertexData) orig_data = sg._geom->get_vertex_data();
1277 
1278  if (orig_data->get_format() != _new_format) {
1279  VDataMap::iterator mi = vdata_map.find(orig_data);
1280  if (mi != vdata_map.end()) {
1281  // Already modified this vdata.
1282  sg._geom->set_vertex_data((*mi).second);
1283 
1284  } else {
1285  // Modify this vdata to the new format.
1286  CPT(GeomVertexData) new_data = orig_data->convert_to(_new_format);
1287  vdata_map[orig_data] = new_data;
1288  ++num_modified;
1289 
1290  sg._geom->set_vertex_data(new_data);
1291  }
1292  }
1293  }
1294 
1295  return num_modified;
1296 }
1297 
1298 ////////////////////////////////////////////////////////////////////
1299 // Function: GeomTransformer::NewCollectedData::apply_collect_changes
1300 // Access: Public
1301 // Description: Actually combines all of the vertex datas found in a
1302 // previous call to collect_vertex_data().
1303 ////////////////////////////////////////////////////////////////////
1304 int GeomTransformer::NewCollectedData::
1305 apply_collect_changes() {
1306  if (_num_vertices == 0) {
1307  return 0;
1308  }
1309 
1310  _new_data =
1311  new GeomVertexData(_vdata_name, _new_format, _usage_hint);
1312 
1313  _new_data->unclean_set_num_rows(_num_vertices);
1314 
1315  // Copy each source data into the new GeomVertexData, one at a time.
1316  int vertex_offset = 0;
1317  SourceDatas::iterator sdi;
1318  for (sdi = _source_datas.begin(); sdi != _source_datas.end(); ++sdi) {
1319  SourceData &sd = (*sdi);
1320  CPT(GeomVertexData) vdata = sd._vdata;
1321 
1322  if (_new_format != vdata->get_format()) {
1323  // Convert (non-destructively) the current Geom's vertex
1324  // data to the new format, so we can just blindly append the
1325  // vertices to _new_data, within append_vdata().
1326  vdata = vdata->convert_to(_new_format);
1327  }
1328 
1329  append_vdata(vdata, vertex_offset);
1330  vertex_offset += sd._num_vertices;
1331  }
1332 
1333  nassertr(vertex_offset == _num_vertices, 0);
1334 
1335  if (_new_btable != (TransformBlendTable *)NULL) {
1336  _new_btable->set_rows(_new_btable_rows);
1337  _new_data->set_transform_blend_table(_new_btable);
1338  }
1339 
1340  update_geoms();
1341 
1342  _new_data.clear();
1343  _new_btable.clear();
1344  _new_btable_rows.clear();
1345 
1346  return 1;
1347 }
1348 
1349 ////////////////////////////////////////////////////////////////////
1350 // Function: GeomTransformer::NewCollectedData::append_vdata
1351 // Access: Public
1352 // Description: Appends the vertices from the indicated source
1353 // GeomVertexData to the end of the working data.
1354 ////////////////////////////////////////////////////////////////////
1355 void GeomTransformer::NewCollectedData::
1356 append_vdata(const GeomVertexData *vdata, int vertex_offset) {
1357  for (int i = 0; i < vdata->get_num_arrays(); ++i) {
1358  PT(GeomVertexArrayData) new_array = _new_data->modify_array(i);
1359  CPT(GeomVertexArrayData) old_array = vdata->get_array(i);
1360  int stride = _new_format->get_array(i)->get_stride();
1361  int start_byte = vertex_offset * stride;
1362  int copy_bytes = old_array->get_data_size_bytes();
1363  nassertv(start_byte + copy_bytes <= new_array->get_data_size_bytes());
1364 
1365  new_array->modify_handle()->copy_subdata_from
1366  (start_byte, copy_bytes,
1367  old_array->get_handle(), 0, copy_bytes);
1368  }
1369 
1370  // Also, copy the animation data (if any). This means combining
1371  // transform and/or slider tables, and might therefore mean
1372  // remapping transform indices in the vertices. Each of these has a
1373  // slightly different way to handle the remapping, because they have
1374  // slightly different kinds of data.
1375 
1376  if (vdata->get_transform_table() != (TransformTable *)NULL ||
1377  _new_data->get_transform_table() != (TransformTable *)NULL) {
1378  // The TransformTable.
1379  CPT(TransformTable) old_table;
1380  if (vdata->get_transform_table() != (TransformTable *)NULL) {
1381  old_table = vdata->get_transform_table();
1382  } else {
1383  PT(TransformTable) temp_table = new TransformTable;
1384  // There's an implicit identity transform for all nodes.
1385  PT(VertexTransform) identity_transform = new UserVertexTransform("identity");
1386  temp_table->add_transform(identity_transform);
1387  old_table = TransformTable::register_table(temp_table);
1388  }
1389 
1390  // First, build a mapping of the transforms we already have in the
1391  // current table. We must do this because the TransformTable
1392  // doesn't automatically unquify index numbers for us (it doesn't
1393  // store an index).
1394  typedef pmap<const VertexTransform *, int> AddedTransforms;
1395  AddedTransforms added_transforms;
1396 
1397  int num_old_transforms = old_table->get_num_transforms();
1398  for (int i = 0; i < num_old_transforms; i++) {
1399  added_transforms[old_table->get_transform(i)] = i;
1400  }
1401 
1402  // Now create a new table. We have to create a new table instead
1403  // of modifying the existing one, since a registered
1404  // TransformTable cannot be modified.
1405  PT(TransformTable) new_table;
1406  if (_new_data->get_transform_table() != (TransformTable *)NULL) {
1407  new_table = new TransformTable(*_new_data->get_transform_table());
1408  } else {
1409  new_table = new TransformTable;
1410  }
1411 
1412  // Now walk through the old table and copy over its transforms.
1413  // We will build up an IndexMap of old index numbers to new index
1414  // numbers while we go, which we can use to modify the vertices.
1415  IndexMap transform_map;
1416 
1417  int num_transforms = old_table->get_num_transforms();
1418  transform_map.reserve(num_transforms);
1419  for (int ti = 0; ti < num_transforms; ++ti) {
1420  const VertexTransform *transform = old_table->get_transform(ti);
1421  AddedTransforms::iterator ai = added_transforms.find(transform);
1422  if (ai != added_transforms.end()) {
1423  // Already got this one in the table.
1424  transform_map.push_back((*ai).second);
1425  } else {
1426  // This is a new one.
1427  int tj = new_table->add_transform(transform);
1428  transform_map.push_back(tj);
1429  added_transforms[transform] = tj;
1430  }
1431  }
1432  _new_data->set_transform_table(TransformTable::register_table(new_table));
1433 
1434  // And now modify the vertices to update the indices to their new
1435  // values in the new table. This requires a nested loop, since
1436  // each column of transform_index might define multiple index
1437  // values.
1438  GeomVertexRewriter index(_new_data, InternalName::get_transform_index());
1439  if (index.has_column()) {
1440  int num_values = index.get_column()->get_num_values();
1441  int num_rows = vdata->get_num_rows();
1442 
1443  index.set_row_unsafe(vertex_offset);
1444  for (int ci = 0; ci < num_rows; ++ci) {
1445  LVecBase4i indices = index.get_data4i();
1446  for (int i = 0; i < num_values; i++) {
1447  nassertv(indices[i] >= 0 && indices[i] < (int)transform_map.size());
1448  indices[i] = transform_map[indices[i]];
1449  }
1450  index.set_data4i(indices);
1451  }
1452  }
1453  }
1454 
1455  if (vdata->get_transform_blend_table() != (TransformBlendTable *)NULL) {
1456  // The TransformBlendTable. This one is the easiest, because we
1457  // can modify it directly, and it will uniquify blend objects for
1458  // us.
1459 
1460  // We have few special optimizations to handle the
1461  // TransformBlendTable, since it's a very common case and
1462  // therefore worth spending a bit of effort to optimize deeply.
1463 
1464  CPT(TransformBlendTable) old_btable = vdata->get_transform_blend_table();
1465 
1466  if (_new_btable == (TransformBlendTable *)NULL) {
1467  _new_btable = new TransformBlendTable;
1468  _new_btable->add_blend(TransformBlend());
1469  }
1470 
1471  SparseArray new_rows = old_btable->get_rows();
1472  new_rows <<= vertex_offset;
1473  _new_btable_rows |= new_rows;
1474 
1475  // We still need to build up the IndexMap.
1476  IndexMap blend_map;
1477 
1478  int num_blends = old_btable->get_num_blends();
1479  blend_map.reserve(num_blends);
1480  for (int bi = 0; bi < num_blends; ++bi) {
1481  int bj = _new_btable->add_blend(old_btable->get_blend(bi));
1482  blend_map.push_back(bj);
1483  }
1484 
1485  // Modify the indices. This is simpler than the transform_index,
1486  // above, because each column of transform_blend may only define
1487  // one index value.
1488  GeomVertexRewriter index(_new_data, InternalName::get_transform_blend());
1489  if (index.has_column()) {
1490  int num_rows = vdata->get_num_rows();
1491  index.set_row_unsafe(vertex_offset);
1492 
1493  for (int ci = 0; ci < num_rows; ++ci) {
1494  int orig_index = index.get_data1i();
1495  nassertv(orig_index >= 0 && orig_index < (int)blend_map.size());
1496  int new_index = blend_map[orig_index];
1497  index.set_data1i(new_index);
1498  }
1499  }
1500  }
1501 
1502  if (vdata->get_slider_table() != (SliderTable *)NULL) {
1503  // The SliderTable. This one requires making a copy, like the
1504  // TransformTable (since it can't be modified once registered
1505  // either), but at least it uniquifies sliders added to it. Also,
1506  // it doesn't require indexing into it, so we don't have to build
1507  // an IndexMap to modify the vertices with.
1508  const SliderTable *old_sliders = vdata->get_slider_table();
1509  PT(SliderTable) new_sliders;
1510  if (_new_data->get_slider_table() != (SliderTable *)NULL) {
1511  new_sliders = new SliderTable(*_new_data->get_slider_table());
1512  } else {
1513  new_sliders = new SliderTable;
1514  }
1515  int num_sliders = old_sliders->get_num_sliders();
1516  for (int si = 0; si < num_sliders; ++si) {
1517  SparseArray new_rows = old_sliders->get_slider_rows(si);
1518  new_rows <<= vertex_offset;
1519  new_sliders->add_slider(old_sliders->get_slider(si), new_rows);
1520  }
1521  _new_data->set_slider_table(SliderTable::register_table(new_sliders));
1522  }
1523 }
1524 
1525 ////////////////////////////////////////////////////////////////////
1526 // Function: GeomTransformer::NewCollectedData::update_geoms
1527 // Access: Public
1528 // Description: Updates all of the source Geoms to reference the new
1529 // vertex data.
1530 ////////////////////////////////////////////////////////////////////
1531 void GeomTransformer::NewCollectedData::
1532 update_geoms() {
1533  SourceGeoms::iterator sgi;
1534  for (sgi = _source_geoms.begin(); sgi != _source_geoms.end(); ++sgi) {
1535  SourceGeom &sg = (*sgi);
1536  sg._geom->offset_vertices(_new_data, sg._vertex_offset);
1537  }
1538 }
1539 
1540 ////////////////////////////////////////////////////////////////////
1541 // Function: GeomTransformer::VertexDataAssoc::remove_unused_vertices
1542 // Access: Public
1543 // Description:
1544 ////////////////////////////////////////////////////////////////////
1545 void GeomTransformer::VertexDataAssoc::
1546 remove_unused_vertices(const GeomVertexData *vdata) {
1547  if (_geoms.empty()) {
1548  // Trivial case.
1549  return;
1550  }
1551 
1552  PT(Thread) current_thread = Thread::get_current_thread();
1553 
1554  BitArray referenced_vertices;
1555  bool any_referenced = false;
1556  GeomList::iterator gi;
1557  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
1558  Geom *geom = (*gi);
1559  if (geom->get_vertex_data() != vdata) {
1560  continue;
1561  }
1562 
1563  any_referenced = true;
1564  int num_primitives = geom->get_num_primitives();
1565  for (int i = 0; i < num_primitives; ++i) {
1566  CPT(GeomPrimitive) prim = geom->get_primitive(i);
1567 
1568  GeomPrimitivePipelineReader reader(prim, current_thread);
1569  int num_vertices = reader.get_num_vertices();
1570  for (int vi = 0; vi < num_vertices; ++vi) {
1571  referenced_vertices.set_bit(reader.get_vertex(vi));
1572  }
1573  }
1574  }
1575 
1576  if (!any_referenced) {
1577  return;
1578  }
1579 
1580  int num_vertices = vdata->get_num_rows();
1581  int new_num_vertices = referenced_vertices.get_num_on_bits();
1582  if (num_vertices <= new_num_vertices) {
1583  // All vertices are used.
1584  nassertv(num_vertices == new_num_vertices);
1585  return;
1586  }
1587 
1588  // Remap the vertices.
1589  int *remap_array = (int *)alloca(sizeof(int) * num_vertices);
1590  int new_index = 0;
1591  int index;
1592  int next_index = 0;
1593  for (index = 0; index < num_vertices; ++index) {
1594  if (referenced_vertices.get_bit(index)) {
1595  while (next_index <= index) {
1596  remap_array[next_index] = new_index;
1597  ++next_index;
1598  }
1599  ++new_index;
1600  }
1601  }
1602  while (next_index < num_vertices) {
1603  remap_array[next_index] = new_num_vertices - 1;
1604  ++next_index;
1605  }
1606 
1607  // Now recopy the actual vertex data, one array at a time.
1608  PT(GeomVertexData) new_vdata = new GeomVertexData(*vdata);
1609  new_vdata->unclean_set_num_rows(new_num_vertices);
1610 
1611  int num_arrays = vdata->get_num_arrays();
1612  nassertv(num_arrays == new_vdata->get_num_arrays());
1613 
1614  GeomVertexDataPipelineReader reader(vdata, current_thread);
1615  reader.check_array_readers();
1616  GeomVertexDataPipelineWriter writer(new_vdata, true, current_thread);
1617  writer.check_array_writers();
1618 
1619  for (int a = 0; a < num_arrays; ++a) {
1620  const GeomVertexArrayDataHandle *array_reader = reader.get_array_reader(a);
1621  GeomVertexArrayDataHandle *array_writer = writer.get_array_writer(a);
1622 
1623  int stride = array_reader->get_array_format()->get_stride();
1624  nassertv(stride == array_writer->get_array_format()->get_stride());
1625 
1626  int new_index = 0;
1627  int index;
1628  for (index = 0; index < num_vertices; ++index) {
1629  if (referenced_vertices.get_bit(index)) {
1630  array_writer->copy_subdata_from(new_index * stride, stride,
1631  array_reader,
1632  index * stride, stride);
1633  ++new_index;
1634  }
1635  }
1636  }
1637 
1638  // Update the subranges in the TransformBlendTable, if any.
1639  PT(TransformBlendTable) tbtable = new_vdata->modify_transform_blend_table();
1640  if (!tbtable.is_null()) {
1641  const SparseArray &rows = tbtable->get_rows();
1642  SparseArray new_rows;
1643  int num_subranges = rows.get_num_subranges();
1644  for (int si = 0; si < num_subranges; ++si) {
1645  int from = rows.get_subrange_begin(si);
1646  int to = rows.get_subrange_end(si);
1647  nassertv(from >= 0 && from < num_vertices && to > from && to <= num_vertices);
1648  int new_from = remap_array[from];
1649  int new_to = remap_array[to - 1] + 1;
1650  nassertv(new_from >= 0 && new_from < new_num_vertices && new_to >= new_from && new_to <= new_num_vertices);
1651  new_rows.set_range(new_from, new_to - new_from);
1652  }
1653  tbtable->set_rows(new_rows);
1654  }
1655 
1656  // Finally, reindex the Geoms.
1657  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
1658  Geom *geom = (*gi);
1659  if (geom->get_vertex_data() != vdata) {
1660  continue;
1661  }
1662 
1663  int num_primitives = geom->get_num_primitives();
1664  for (int i = 0; i < num_primitives; ++i) {
1665  PT(GeomPrimitive) prim = geom->modify_primitive(i);
1666  prim->make_indexed();
1667  PT(GeomVertexArrayData) vertices = prim->modify_vertices();
1668  GeomVertexRewriter rewriter(vertices, 0, current_thread);
1669 
1670  while (!rewriter.is_at_end()) {
1671  index = rewriter.get_data1i();
1672  nassertv(index >= 0 && index < num_vertices);
1673  new_index = remap_array[index];
1674  nassertv(new_index >= 0 && new_index < new_num_vertices);
1675  rewriter.set_data1i(new_index);
1676  }
1677  }
1678 
1679  geom->set_vertex_data(new_vdata);
1680  }
1681 }
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitArray.I:207
bool reverse(GeomNode *node)
Reverses the winding order of triangles in this GeomNode so that each triangle is facing in the oppos...
This class records a set of integers, where each integer is either present or not present in the set...
Definition: sparseArray.h:49
static const LMatrix4f & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:903
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
bool set_color(Geom *geom, const LColor &color)
Overrides the color indicated within the Geom with the given replacement color.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
bool transform_vertices(Geom *geom, const LMatrix4 &mat)
Transforms the vertices and the normals in the indicated Geom by the indicated matrix.
void register_vertices(Geom *geom, bool might_have_unused)
Records the association of the Geom with its GeomVertexData, for the purpose of later removing unused...
const SparseArray & get_slider_rows(int n) const
Returns the set of rows (vertices) governed by the nth slider in the table.
Definition: sliderTable.I:88
bool transform_texcoords(Geom *geom, const InternalName *from_name, InternalName *to_name, const LMatrix4 &mat)
Transforms the texture coordinates in the indicated Geom by the indicated matrix. ...
bool reverse_normals(Geom *geom)
Reverses the lighting normals on the vertex data, if any.
const TransformTable * get_transform_table() const
Returns a const pointer to the TransformTable assigned to this data.
UsageHint get_usage_hint() const
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:57
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:63
int collect_vertex_data(Geom *geom, int collect_bits, bool format_only)
Collects together GeomVertexDatas from different geoms into one big (or several big) GeomVertexDatas...
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
void set_data1i(int data)
Sets the write row to a particular 1-component value, and advances the write row. ...
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
Type get_color_type() const
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.I:46
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise...
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
const SliderTable * get_slider_table() const
Returns a const pointer to the SliderTable assigned to this data.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:3162
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
This defines how a single column is interleaved within a vertex array stored within a Geom...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
const string & get_name() const
Returns the name passed to the constructor, if any.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:42
int get_subrange_end(int n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:545
int add_transform(const VertexTransform *transform)
Adds a new transform to the table and returns the index number of the new transform.
InternalName * get_texcoord_name() const
See set_texcoord_name.
Definition: textureStage.I:148
A lightweight class that represents a single element that may be timed and/or counted via stats...
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:42
int get_num_sliders() const
Returns the number of sliders in the table.
Definition: sliderTable.I:66
void mark_internal_bounds_stale(Thread *current_thread=Thread::get_current_thread())
Should be called by a derived class to mark the internal bounding volume stale, so that compute_inter...
Definition: pandaNode.cxx:2467
int finish_collect(bool format_only)
This should be called after a call to collect_vertex_data() to finalize the changes and apply them to...
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
const LColor & get_color() const
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
Definition: colorAttrib.I:58
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
int add_slider(const VertexSlider *slider, const SparseArray &rows)
Adds a new slider to the table, and returns the index number of the new slider.
bool apply_texture_colors(Geom *geom, TextureStage *ts, Texture *tex, const TexMatrixAttrib *tma, const LColor &base_color, bool keep_vertex_color)
Removes textures from Geoms by applying the texture colors to the vertices.
const GeomVertexFormat * get_format() const
Returns a pointer to the GeomVertexFormat structure that defines this data.
const VertexTransform * get_transform(int n) const
Returns the nth transform in the table.
void set_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row. ...
int get_num_arrays() const
Returns the number of individual arrays stored within the data.
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
bool remove_column(Geom *geom, const InternalName *column)
Removes the named column from the vertex data in the Geom.
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
const RenderState * get_geom_state(int n) const
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.I:106
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
const LMatrix4 & get_mat() const
Returns the transformation matrix associated with the default texture stage.
bool has_stage(TextureStage *stage) const
Returns true if there is a transform associated with the indicated stage, or false otherwise (in whic...
This defines a single entry in a TransformBlendTable.
bool apply_state(GeomNode *node, const RenderState *state)
Applies the indicated render state to all the of Geoms.
An instance of this object is returned by Texture::peek().
Definition: texturePeeker.h:30
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void set_bit(int index)
Sets the nth bit on.
Definition: bitArray.I:225
Applies a transform matrix to UV&#39;s before they are rendered.
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition: sparseArray.I:305
int get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:518
void finish_apply()
Should be called after performing any operations–particularly PandaNode::apply_attribs_to_vertices()–...
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
void set_vertex_data(const GeomVertexData *data)
Replaces the Geom&#39;s underlying vertex data table with a completely new table.
Definition: geom.cxx:183
This is a four-component point in space.
Definition: lpoint4.h:91
A thread; that is, a lightweight process.
Definition: thread.h:51
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
This is a specialization on VertexTransform that allows the user to specify any arbitrary transform m...
void copy_subdata_from(size_t to_start, size_t to_size, const GeomVertexArrayDataHandle *other, size_t from_start, size_t from_size)
Copies a portion of the data array from the other object into a portion of the data array of this obj...
int get_num_transforms() const
Returns the number of transforms in the table.
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
bool doubleside(GeomNode *node)
Duplicates triangles in this GeomNode so that each triangle is back-to-back with another triangle fac...
This is similar to RefCountObj, but it implements a CopyOnWriteObject inheritance instead of a Refere...
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
This structure collects together the different combinations of transforms and blend amounts used by a...
static int size()
Returns 4: the number of components of a LVecBase4.
Definition: lvecBase4.h:3615
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:30
bool almost_equal(const LMatrix4f &other, float threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
Definition: lmatrix.cxx:879
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
bool set_format(Geom *geom, const GeomVertexFormat *new_format)
Changes the GeomVertexData of the indicated Geom to use the specified format.
int get_subrange_begin(int n) const
Returns the first numeric element in the nth subrange.
Definition: sparseArray.I:531
This is a two-component point in space.
Definition: lpoint2.h:92
const VertexSlider * get_slider(int n) const
Returns the nth slider in the table.
Definition: sliderTable.I:76
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the array.
Definition: bitArray.cxx:314
int add_blend(const TransformBlend &blend)
Adds a new blend to the table, and returns its index number.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
bool make_compatible_state(GeomNode *node)
Checks if the different geoms in the GeomNode have different RenderStates.
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:38
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:642
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter, combined together into one convenient package.
This is the data for one array of a GeomVertexData structure.
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
Definition: geomNode.cxx:96
int get_num_primitives() const
Returns the number of GeomPrimitive objects stored within the Geom, each of which represents a number...
Definition: geom.I:110
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
bool transform_colors(Geom *geom, const LVecBase4 &scale)
Transforms the colors in the indicated Geom by the indicated scale.
int get_num_rows() const
Returns the number of rows stored within all the arrays.