Panda3D
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) {
182  node->mark_internal_bounds_stale();
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 }
const RenderState * get_geom_state(int n) const
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.I:106
bool reverse(GeomNode *node)
Reverses the winding order of triangles in this GeomNode so that each triangle is facing in the oppos...
const SliderTable * get_slider_table() const
Returns a const pointer to the SliderTable assigned to this data.
This class records a set of integers, where each integer is either present or not present in the set...
Definition: sparseArray.h:49
bool is_at_end() const
Returns true if the reader or writer is currently at the end of the list of vertices, false otherwise.
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...
const TransformTable * get_transform_table() const
Returns a const pointer to the TransformTable assigned to this data.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
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
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
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
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
int get_num_arrays() const
Returns the number of individual arrays stored within the data.
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...
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 get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitArray.I:207
bool reverse_normals(Geom *geom)
Reverses the lighting normals on the vertex data, if any.
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
void set_data4i(int a, int b, int c, int d)
Sets the write row to a particular 4-component value, and advances the write row. ...
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
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
void reverse_in_place()
Reverses all of the primitives within this Geom, leaving the results in place.
Definition: geom.cxx:537
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
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
Type get_color_type() const
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.I:46
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:3194
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
int get_subrange_end(int n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:545
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
A lightweight class that represents a single element that may be timed and/or counted via stats...
int get_num_transforms() const
Returns the number of transforms in the table.
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:42
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
int get_subrange_begin(int n) const
Returns the first numeric element in the nth subrange.
Definition: sparseArray.I:531
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...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
const VertexSlider * get_slider(int n) const
Returns the nth slider in the table.
Definition: sliderTable.I:76
const GeomVertexFormat * get_format() const
Returns a pointer to the GeomVertexFormat structure that defines this data.
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the array.
Definition: bitArray.cxx:314
UsageHint get_usage_hint() const
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
int get_num_sliders() const
Returns the number of sliders in the table.
Definition: sliderTable.I:66
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 get_num_values() const
Returns the number of numeric values of the column: the number of distinct numeric values that go int...
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.
int get_vertex(int i) const
Returns the ith vertex index 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. ...
InternalName * get_texcoord_name() const
See set_texcoord_name.
Definition: textureStage.I:148
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
int get_num_rows() const
Returns the number of rows stored within all the arrays.
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...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
int get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:518
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
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
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
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...
bool doubleside(GeomNode *node)
Duplicates triangles in this GeomNode so that each triangle is back-to-back with another triangle fac...
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise...
This is similar to RefCountObj, but it implements a CopyOnWriteObject inheritance instead of a Refere...
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:3648
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:30
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.
This is a two-component point in space.
Definition: lpoint2.h:92
int add_blend(const TransformBlend &blend)
Adds a new blend to the table, and returns its index number.
const GeomVertexColumn * get_column() const
Returns the description of the data type that the rewriter is working on.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
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
const LVecBase4i & get_data4i()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
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
const string & get_name() const
Returns the name passed to the constructor, if any.
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.
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.