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 */
74 void GeomTransformer::
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 */
87 void GeomTransformer::
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 */
107 bool GeomTransformer::
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 */
141 bool GeomTransformer::
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 */
173 bool GeomTransformer::
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 */
236 bool GeomTransformer::
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 */
261 bool GeomTransformer::
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 */
295 bool GeomTransformer::
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 */
318 bool GeomTransformer::
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 */
356 bool GeomTransformer::
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 */
381 bool GeomTransformer::
382 apply_texture_colors(Geom *geom, TextureStage *ts, Texture *tex,
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 */
576 bool GeomTransformer::
577 apply_texture_colors(GeomNode *node, const RenderState *state) {
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 */
635 bool GeomTransformer::
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 */
658 bool GeomTransformer::
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 */
694 bool GeomTransformer::
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 */
714 bool GeomTransformer::
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 */
738 bool GeomTransformer::
739 make_compatible_state(GeomNode *node) {
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 */
819 bool GeomTransformer::
820 reverse_normals(Geom *geom) {
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 */
856 bool GeomTransformer::
857 doubleside(GeomNode *node) {
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 */
893 bool GeomTransformer::
894 reverse(GeomNode *node) {
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 */
912 void GeomTransformer::
913 finish_apply() {
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 */
943 int GeomTransformer::
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 */
1046 int GeomTransformer::
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 */
1090 int GeomTransformer::
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 a 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 }
BitArray
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:40
BitArray::get_num_on_bits
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the array.
Definition: bitArray.cxx:296
BitArray::get_bit
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition: bitArray.I:99
ColorAttrib
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:27
ColorAttrib::get_color
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
ColorAttrib::get_color_type
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.h:46
CopyOnWriteObj
This is similar to RefCountObj, but it implements a CopyOnWriteObject inheritance instead of a Refere...
Definition: copyOnWriteObject.h:102
CycleDataStageWriter
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
Definition: cycleDataStageWriter.h:31
CycleDataWriter
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Definition: cycleDataWriter.h:34
GeomMunger
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:50
GeomNode::GeomEntry
Definition: geomNode.h:113
GeomNode
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
GeomNode::add_geom
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:586
GeomNode::get_num_geoms
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
GeomNode::get_geom_state
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
GeomPrimitivePipelineReader
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
Definition: geomPrimitive.h:352
GeomPrimitive
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
GeomTransformer
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
Definition: geomTransformer.h:42
GeomTransformer::finish_apply
void finish_apply()
Should be called after performing any operations–particularly PandaNode::apply_attribs_to_vertices()–...
Definition: geomTransformer.cxx:913
GeomTransformer::make_compatible_state
bool make_compatible_state(GeomNode *node)
Checks if the different geoms in the GeomNode have different RenderStates.
Definition: geomTransformer.cxx:739
GeomTransformer::set_format
bool set_format(Geom *geom, const GeomVertexFormat *new_format)
Changes the GeomVertexData of the indicated Geom to use the specified format.
Definition: geomTransformer.cxx:659
GeomTransformer::apply_texture_colors
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.
Definition: geomTransformer.cxx:382
GeomTransformer::doubleside
bool doubleside(GeomNode *node)
Duplicates triangles in this GeomNode so that each triangle is back-to-back with another triangle fac...
Definition: geomTransformer.cxx:857
GeomTransformer::reverse_normals
bool reverse_normals(Geom *geom)
Reverses the lighting normals on the vertex data, if any.
Definition: geomTransformer.cxx:820
GeomTransformer::transform_colors
bool transform_colors(Geom *geom, const LVecBase4 &scale)
Transforms the colors in the indicated Geom by the indicated scale.
Definition: geomTransformer.cxx:319
GeomTransformer::transform_vertices
bool transform_vertices(Geom *geom, const LMatrix4 &mat)
Transforms the vertices and the normals in the indicated Geom by the indicated matrix.
Definition: geomTransformer.cxx:108
GeomTransformer::set_color
bool set_color(Geom *geom, const LColor &color)
Overrides the color indicated within the Geom with the given replacement color.
Definition: geomTransformer.cxx:262
GeomTransformer::remove_column
bool remove_column(Geom *geom, const InternalName *column)
Removes the named column from the vertex data in the Geom.
Definition: geomTransformer.cxx:695
GeomTransformer::collect_vertex_data
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.
Definition: geomTransformer.cxx:944
GeomTransformer::apply_state
bool apply_state(GeomNode *node, const RenderState *state)
Applies the indicated render state to all the of Geoms.
Definition: geomTransformer.cxx:636
GeomTransformer::transform_texcoords
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.
Definition: geomTransformer.cxx:174
GeomTransformer::finish_collect
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...
Definition: geomTransformer.cxx:1091
GeomTransformer::register_vertices
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...
Definition: geomTransformer.cxx:75
GeomTransformer::reverse
bool reverse(GeomNode *node)
Reverses the winding order of triangles in this GeomNode so that each triangle is facing in the oppos...
Definition: geomTransformer.cxx:894
GeomVertexArrayDataHandle
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
Definition: geomVertexArrayData.h:250
GeomVertexArrayDataHandle::copy_subdata_from
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...
Definition: geomVertexArrayData.cxx:759
GeomVertexArrayData
This is the data for one array of a GeomVertexData structure.
Definition: geomVertexArrayData.h:58
GeomVertexColumn
This defines how a single column is interleaved within a vertex array stored within a Geom.
Definition: geomVertexColumn.h:37
GeomVertexColumn::get_numeric_type
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
Definition: geomVertexColumn.I:116
GeomVertexColumn::get_contents
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
Definition: geomVertexColumn.I:124
GeomVertexColumn::get_num_components
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
Definition: geomVertexColumn.I:87
GeomVertexDataPipelineReader
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Definition: geomVertexData.h:442
GeomVertexDataPipelineWriter
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Definition: geomVertexData.h:508
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
GeomVertexData::get_name
get_name
Returns the name passed to the constructor, if any.
Definition: geomVertexData.h:89
GeomVertexData::get_transform_table
get_transform_table
Returns a const pointer to the TransformTable assigned to this data.
Definition: geomVertexData.h:120
GeomVertexData::get_num_rows
int get_num_rows() const
Returns the number of rows stored within all the arrays.
Definition: geomVertexData.I:62
GeomVertexData::get_num_arrays
get_num_arrays
Returns the number of individual arrays stored within the data.
Definition: geomVertexData.h:111
GeomVertexData::get_slider_table
get_slider_table
Returns a const pointer to the SliderTable assigned to this data.
Definition: geomVertexData.h:130
GeomVertexData::get_format
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
Definition: geomVertexData.h:98
GeomVertexData::get_usage_hint
get_usage_hint
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
Definition: geomVertexData.h:93
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.h:55
GeomVertexReader
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
Definition: geomVertexReader.h:47
GeomVertexReader::is_at_end
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
Definition: geomVertexReader.I:362
GeomVertexReader::get_data4
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
Definition: geomVertexReader.I:590
GeomVertexReader::get_data2
const LVecBase2 & get_data2()
Returns the data associated with the read row, expressed as a 2-component value, and advances the rea...
Definition: geomVertexReader.I:564
GeomVertexReader::get_data3
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
Definition: geomVertexReader.I:577
GeomVertexRewriter
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
Definition: geomVertexRewriter.h:33
GeomVertexWriter
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
Definition: geomVertexWriter.h:55
GeomVertexWriter::set_data4
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.
Definition: geomVertexWriter.I:670
Geom
A container for geometry primitives.
Definition: geom.h:54
Geom::set_vertex_data
void set_vertex_data(const GeomVertexData *data)
Replaces the Geom's underlying vertex data table with a completely new table.
Definition: geom.cxx:171
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
RenderAttrib
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
SliderTable
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:37
SliderTable::get_slider
get_slider
Returns the nth slider in the table.
Definition: sliderTable.h:49
SliderTable::get_num_sliders
get_num_sliders
Returns the number of sliders in the table.
Definition: sliderTable.h:49
SliderTable::get_slider_rows
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
SparseArray
This class records a set of integers, where each integer is either present or not present in the set.
Definition: sparseArray.h:43
SparseArray::get_subrange_begin
int get_subrange_begin(size_t n) const
Returns the first numeric element in the nth subrange.
Definition: sparseArray.I:404
SparseArray::set_range
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition: sparseArray.I:218
SparseArray::get_num_subranges
size_t get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:394
SparseArray::get_subrange_end
int get_subrange_end(size_t n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:415
TexMatrixAttrib
Applies a transform matrix to UV's before they are rendered.
Definition: texMatrixAttrib.h:30
TextureAttrib
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:31
TexturePeeker
An instance of this object is returned by Texture::peek().
Definition: texturePeeker.h:27
TextureStage
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
TextureStage::get_texcoord_name
get_texcoord_name
See set_texcoord_name.
Definition: textureStage.h:194
Texture
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
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
TransformBlendTable
This structure collects together the different combinations of transforms and blend amounts used by a...
Definition: transformBlendTable.h:45
TransformBlendTable::add_blend
size_t add_blend(const TransformBlend &blend)
Adds a new blend to the table, and returns its index number.
Definition: transformBlendTable.cxx:87
TransformBlend
This defines a single entry in a TransformBlendTable.
Definition: transformBlend.h:32
TransformTable
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
Definition: transformTable.h:38
TransformTable::get_num_transforms
get_num_transforms
Returns the number of transforms in the table.
Definition: transformTable.h:50
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
UserVertexTransform
This is a specialization on VertexTransform that allows the user to specify any arbitrary transform m...
Definition: userVertexTransform.h:31
VertexTransform
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
Definition: vertexTransform.h:35
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
pvector< int >
colorAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_pgraph.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomMunger.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT
PT(Geom) GeomTransformer
Uses the indicated munger to premunge the given Geom to optimize it for eventual rendering.
Definition: geomTransformer.cxx:1118
geomTransformer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexRewriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatCollector.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
renderState.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sceneGraphReducer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
sliderTable.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
textureAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
texturePeeker.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
texture.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
transformBlendTable.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
transformTable.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
userVertexTransform.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
vector_int.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.