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