Panda3D

geom.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 geom.cxx
10 * @author drose
11 * @date 2005-03-06
12 */
13
14 #include "geom.h"
15 #include "geomPoints.h"
16 #include "geomVertexReader.h"
17 #include "geomVertexRewriter.h"
18 #include "graphicsStateGuardianBase.h"
19 #include "preparedGraphicsObjects.h"
20 #include "pStatTimer.h"
21 #include "bamReader.h"
22 #include "bamWriter.h"
23 #include "boundingSphere.h"
24 #include "boundingBox.h"
25 #include "lightMutexHolder.h"
26 #include "config_mathutil.h"
27
28 using std::max;
29 using std::min;
30
31 UpdateSeq Geom::_next_modified;
32 PStatCollector Geom::_draw_primitive_setup_pcollector("Draw:Primitive:Setup");
33
34 TypeHandle Geom::_type_handle;
35 TypeHandle Geom::CDataCache::_type_handle;
36 TypeHandle Geom::CacheEntry::_type_handle;
37 TypeHandle Geom::CData::_type_handle;
38 TypeHandle GeomPipelineReader::_type_handle;
39
40 /**
41 * Required to implement CopyOnWriteObject.
42 */
43 PT(CopyOnWriteObject) Geom::
44 make_cow_copy() {
45 return make_copy();
46 }
47
48 /**
49 *
50 */
51 Geom::
52 Geom(const GeomVertexData *data) : _cycler(CData((GeomVertexData *)data)) {
53 }
54
55 /**
56 * Use make_copy() to duplicate a Geom.
57 */
58 Geom::
59 Geom(const Geom &copy) :
60 CopyOnWriteObject(copy),
61 _cycler(copy._cycler)
62 {
63 }
64
65 /**
66 * The copy assignment operator is not pipeline-safe. This will completely
67 * obliterate all stages of the pipeline, so don't do it for a Geom that is
68 * actively being used for rendering.
69 */
70 void Geom::
71 operator = (const Geom &copy) {
72 CopyOnWriteObject::operator = (copy);
73
74 clear_cache();
75
76 _cycler = copy._cycler;
77
78 OPEN_ITERATE_ALL_STAGES(_cycler) {
79 CDStageWriter cdata(_cycler, pipeline_stage);
80 mark_internal_bounds_stale(cdata);
81 }
82 CLOSE_ITERATE_ALL_STAGES(_cycler);
83 }
84
85 /**
86 *
87 */
88 Geom::
89 ~Geom() {
90 clear_cache();
91 release_all();
92 }
93
94 /**
95 * Returns a newly-allocated Geom that is a shallow copy of this one. It will
96 * be a different Geom pointer, but its internal data may or may not be shared
97 * with that of the original Geom.
98 */
99 Geom *Geom::
100 make_copy() const {
101 return new Geom(*this);
102 }
103
104 /**
105 * Returns the minimum (i.e. most dynamic) usage_hint among all of the
106 * individual GeomPrimitives that have been added to the geom.
107 * @deprecated This is no longer very useful.
108 */
109 Geom::UsageHint Geom::
110 get_usage_hint() const {
111 CDReader cdata(_cycler);
112 GeomEnums::UsageHint hint = UH_unspecified;
113 Primitives::const_iterator pi;
114 for (pi = cdata->_primitives.begin();
115 pi != cdata->_primitives.end();
116 ++pi) {
117 hint = min(hint, (*pi).get_read_pointer()->get_usage_hint());
118 }
119 return hint;
120 }
121
122 /**
123 * Changes the UsageHint hint for all of the primitives on this Geom to the
124 * same value. See get_usage_hint().
125 *
126 * Don't call this in a downstream thread unless you don't mind it blowing
127 * away other changes you might have recently made in an upstream thread.
128 */
129 void Geom::
130 set_usage_hint(Geom::UsageHint usage_hint) {
131 Thread *current_thread = Thread::get_current_thread();
132 CDWriter cdata(_cycler, true, current_thread);
133
134 Primitives::iterator pi;
135 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
136 PT(GeomPrimitive) prim = (*pi).get_write_pointer();
137 prim->set_usage_hint(usage_hint);
138 }
139
140 clear_cache_stage(current_thread);
141 cdata->_modified = Geom::get_next_modified();
142 }
143
144 /**
145 * Returns a modifiable pointer to the GeomVertexData, so that application
146 * code may directly maniuplate the geom's underlying data.
147 *
148 * Don't call this in a downstream thread unless you don't mind it blowing
149 * away other changes you might have recently made in an upstream thread.
150 */
151 PT(GeomVertexData) Geom::
152 modify_vertex_data() {
153 Thread *current_thread = Thread::get_current_thread();
154 // Perform copy-on-write: if the reference count on the vertex data is
155 // greater than 1, assume some other Geom has the same pointer, so make a
156 // copy of it first.
157 CDWriter cdata(_cycler, true, current_thread);
158 clear_cache_stage(current_thread);
159 mark_internal_bounds_stale(cdata);
160 return cdata->_data.get_write_pointer();
161 }
162
163 /**
164 * Replaces the Geom's underlying vertex data table with a completely new
165 * table.
166 *
167 * Don't call this in a downstream thread unless you don't mind it blowing
168 * away other changes you might have recently made in an upstream thread.
169 */
170 void Geom::
171 set_vertex_data(const GeomVertexData *data) {
172 Thread *current_thread = Thread::get_current_thread();
173 nassertv(check_will_be_valid(data));
174 CDWriter cdata(_cycler, true, current_thread);
175 cdata->_data = (GeomVertexData *)data;
176 clear_cache_stage(current_thread);
177 mark_internal_bounds_stale(cdata);
178 reset_geom_rendering(cdata);
179 }
180
181 /**
182 * Replaces a Geom's vertex table with a new table, and simultaneously adds
183 * the indicated offset to all vertex references within the Geom's primitives.
184 * This is intended to be used to combine multiple GeomVertexDatas from
185 * different Geoms into a single big buffer, with each Geom referencing a
186 * subset of the vertices in the buffer.
187 *
188 * Don't call this in a downstream thread unless you don't mind it blowing
189 * away other changes you might have recently made in an upstream thread.
190 */
191 void Geom::
192 offset_vertices(const GeomVertexData *data, int offset) {
193 Thread *current_thread = Thread::get_current_thread();
194 CDWriter cdata(_cycler, true, current_thread);
195 cdata->_data = (GeomVertexData *)data;
196
197 #ifndef NDEBUG
198 GeomVertexDataPipelineReader data_reader(data, current_thread);
199 data_reader.check_array_readers();
200
201 bool all_is_valid = true;
202 #endif
203 Primitives::iterator pi;
204 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
205 PT(GeomPrimitive) prim = (*pi).get_write_pointer();
206 prim->offset_vertices(offset);
207
208 #ifndef NDEBUG
209 if (!prim->check_valid(&data_reader)) {
210 gobj_cat.warning()
211 << *prim << " is invalid for " << *data << ":\n";
212 prim->write(gobj_cat.warning(false), 4);
213
214 all_is_valid = false;
215 }
216 #endif
217 }
218
219 cdata->_modified = Geom::get_next_modified();
220 clear_cache_stage(current_thread);
221 nassertv(all_is_valid);
222 }
223
224 /**
225 * Converts the geom from indexed to nonindexed by duplicating vertices as
226 * necessary. If composite_only is true, then only composite primitives such
227 * as trifans and tristrips are converted. Returns the number of
228 * GeomPrimitive objects converted.
229 *
230 * Don't call this in a downstream thread unless you don't mind it blowing
231 * away other changes you might have recently made in an upstream thread.
232 */
233 int Geom::
234 make_nonindexed(bool composite_only) {
235 Thread *current_thread = Thread::get_current_thread();
236 int num_changed = 0;
237
238 CDWriter cdata(_cycler, true, current_thread);
239 CPT(GeomVertexData) orig_data = cdata->_data.get_read_pointer(current_thread);
240 PT(GeomVertexData) new_data = new GeomVertexData(*orig_data);
241 new_data->clear_rows();
242
243 #ifndef NDEBUG
244 bool all_is_valid = true;
245 #endif
246 Primitives::iterator pi;
247 Primitives new_prims;
248 new_prims.reserve(cdata->_primitives.size());
249 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
250 PT(GeomPrimitive) primitive = (*pi).get_read_pointer(current_thread)->make_copy();
251 new_prims.push_back(primitive.p());
252
253 // GeomPoints are considered "composite" for the purposes of making
254 // nonindexed, since there's no particular advantage to having indexed
255 // points (as opposed to, say, indexed triangles or indexed lines).
256 if (primitive->is_indexed() &&
257 (primitive->is_composite() ||
258 primitive->is_exact_type(GeomPoints::get_class_type()) ||
259 !composite_only)) {
260 primitive->make_nonindexed(new_data, orig_data);
261 ++num_changed;
262 } else {
263 // If it's a simple primitive, pack it anyway, so it can share the same
264 // GeomVertexData.
265 primitive->pack_vertices(new_data, orig_data);
266 }
267
268 #ifndef NDEBUG
269 if (!primitive->check_valid(new_data)) {
270 all_is_valid = false;
271 }
272 #endif
273 }
274
275 nassertr(all_is_valid, 0);
276
277 if (num_changed != 0) {
278 // If any at all were changed, then keep the result (otherwise, discard
279 // it, since we might have de-optimized the indexed geometry a bit).
280 cdata->_data = new_data;
281 cdata->_primitives.swap(new_prims);
282 cdata->_modified = Geom::get_next_modified();
283 clear_cache_stage(current_thread);
284 }
285
286 return num_changed;
287 }
288
289 /**
290 * Returns a GeomVertexData that represents the results of computing the
291 * vertex animation on the CPU for this Geom's vertex data.
292 *
293 * If there is no CPU-defined vertex animation on this object, this just
294 * returns the original object.
295 *
296 * If there is vertex animation, but the VertexTransform values have not
297 * changed since last time, this may return the same pointer it returned
298 * previously. Even if the VertexTransform values have changed, it may still
299 * return the same pointer, but with its contents modified (this is preferred,
300 * since it allows the graphics backend to update vertex buffers optimally).
301 *
302 * If force is false, this method may return immediately with stale data, if
303 * the vertex data is not completely resident. If force is true, this method
304 * will never return stale data, but may block until the data is available.
305 */
306 CPT(GeomVertexData) Geom::
307 get_animated_vertex_data(bool force, Thread *current_thread) const {
308 return get_vertex_data()->animate_vertices(force, current_thread);
309 }
310
311 /**
312 * Replaces the ith GeomPrimitive object stored within the Geom with the new
313 * object.
314 *
315 * Don't call this in a downstream thread unless you don't mind it blowing
316 * away other changes you might have recently made in an upstream thread.
317 */
318 void Geom::
319 set_primitive(size_t i, const GeomPrimitive *primitive) {
320 Thread *current_thread = Thread::get_current_thread();
321 CDWriter cdata(_cycler, true, current_thread);
322 nassertv(i < cdata->_primitives.size());
323 nassertv(primitive->check_valid(cdata->_data.get_read_pointer(current_thread)));
324
325 // All primitives within a particular Geom must have the same fundamental
326 // primitive type (triangles, points, or lines).
327 nassertv(cdata->_primitive_type == PT_none ||
328 cdata->_primitive_type == primitive->get_primitive_type());
329
330 // They also should have a compatible shade model.
331 CPT(GeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
332 nassertv_always(compat != nullptr);
333
334 cdata->_primitives[i] = (GeomPrimitive *)compat.p();
335 PrimitiveType new_primitive_type = compat->get_primitive_type();
336 if (new_primitive_type != cdata->_primitive_type) {
337 cdata->_primitive_type = new_primitive_type;
338 }
339 ShadeModel new_shade_model = compat->get_shade_model();
340 if (new_shade_model != cdata->_shade_model &&
341 new_shade_model != SM_uniform) {
342 cdata->_shade_model = new_shade_model;
343 }
344
345 reset_geom_rendering(cdata);
346 cdata->_modified = Geom::get_next_modified();
347 clear_cache_stage(current_thread);
348 mark_internal_bounds_stale(cdata);
349 }
350
351 /**
352 * Inserts a new GeomPrimitive structure to the Geom object. This specifies a
353 * particular subset of vertices that are used to define geometric primitives
354 * of the indicated type.
355 *
356 * Don't call this in a downstream thread unless you don't mind it blowing
357 * away other changes you might have recently made in an upstream thread.
358 */
359 void Geom::
360 insert_primitive(size_t i, const GeomPrimitive *primitive) {
361 Thread *current_thread = Thread::get_current_thread();
362 CDWriter cdata(_cycler, true, current_thread);
363
364 nassertv(primitive->check_valid(cdata->_data.get_read_pointer(current_thread)));
365
366 // All primitives within a particular Geom must have the same fundamental
367 // primitive type (triangles, points, or lines).
368 nassertv(cdata->_primitive_type == PT_none ||
369 cdata->_primitive_type == primitive->get_primitive_type());
370
371 // They also should have a compatible shade model.
372 CPT(GeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
373 nassertv_always(compat != nullptr);
374
375 if (i >= cdata->_primitives.size()) {
376 cdata->_primitives.push_back((GeomPrimitive *)compat.p());
377 } else {
378 cdata->_primitives.insert(cdata->_primitives.begin() + i, (GeomPrimitive *)compat.p());
379 }
380 PrimitiveType new_primitive_type = compat->get_primitive_type();
381 if (new_primitive_type != cdata->_primitive_type) {
382 cdata->_primitive_type = new_primitive_type;
383 }
384 ShadeModel new_shade_model = compat->get_shade_model();
385 if (new_shade_model != cdata->_shade_model &&
386 new_shade_model != SM_uniform) {
387 cdata->_shade_model = new_shade_model;
388 }
389
390 reset_geom_rendering(cdata);
391 cdata->_modified = Geom::get_next_modified();
392 clear_cache_stage(current_thread);
393 mark_internal_bounds_stale(cdata);
394 }
395
396 /**
397 * Removes the ith primitive from the list.
398 *
399 * Don't call this in a downstream thread unless you don't mind it blowing
400 * away other changes you might have recently made in an upstream thread.
401 */
402 void Geom::
403 remove_primitive(size_t i) {
404 Thread *current_thread = Thread::get_current_thread();
405 CDWriter cdata(_cycler, true, current_thread);
406 nassertv(i < cdata->_primitives.size());
407 cdata->_primitives.erase(cdata->_primitives.begin() + i);
408 if (cdata->_primitives.empty()) {
409 cdata->_primitive_type = PT_none;
410 cdata->_shade_model = SM_uniform;
411 }
412 reset_geom_rendering(cdata);
413 cdata->_modified = Geom::get_next_modified();
414 clear_cache_stage(current_thread);
415 mark_internal_bounds_stale(cdata);
416 }
417
418 /**
419 * Removes all the primitives from the Geom object (but keeps the same table
420 * of vertices). You may then re-add primitives one at a time via calls to
421 * add_primitive().
422 *
423 * Don't call this in a downstream thread unless you don't mind it blowing
424 * away other changes you might have recently made in an upstream thread.
425 */
426 void Geom::
427 clear_primitives() {
428 Thread *current_thread = Thread::get_current_thread();
429 CDWriter cdata(_cycler, true, current_thread);
430 cdata->_primitives.clear();
431 cdata->_primitive_type = PT_none;
432 cdata->_shade_model = SM_uniform;
433 reset_geom_rendering(cdata);
434 clear_cache_stage(current_thread);
435 mark_internal_bounds_stale(cdata);
436 }
437
438 /**
439 * Decomposes all of the primitives within this Geom, leaving the results in
440 * place. See GeomPrimitive::decompose().
441 *
442 * Don't call this in a downstream thread unless you don't mind it blowing
443 * away other changes you might have recently made in an upstream thread.
444 */
445 void Geom::
446 decompose_in_place() {
447 Thread *current_thread = Thread::get_current_thread();
448 CDWriter cdata(_cycler, true, current_thread);
449
450 #ifndef NDEBUG
451 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
452 data_reader.check_array_readers();
453
454 bool all_is_valid = true;
455 #endif
456 Primitives::iterator pi;
457 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
458 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->decompose();
459 (*pi) = (GeomPrimitive *)new_prim.p();
460
461 #ifndef NDEBUG
462 if (!new_prim->check_valid(&data_reader)) {
463 all_is_valid = false;
464 }
465 #endif
466 }
467
468 cdata->_modified = Geom::get_next_modified();
469 reset_geom_rendering(cdata);
470 clear_cache_stage(current_thread);
471
472 nassertv(all_is_valid);
473 }
474
475 /**
476 * Doublesides all of the primitives within this Geom, leaving the results in
477 * place. See GeomPrimitive::doubleside().
478 *
479 * Don't call this in a downstream thread unless you don't mind it blowing
480 * away other changes you might have recently made in an upstream thread.
481 */
482 void Geom::
483 doubleside_in_place() {
484 Thread *current_thread = Thread::get_current_thread();
485 CDWriter cdata(_cycler, true, current_thread);
486
487 #ifndef NDEBUG
488 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
489 data_reader.check_array_readers();
490
491 bool all_is_valid = true;
492 #endif
493 Primitives::iterator pi;
494 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
495 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->doubleside();
496 (*pi) = (GeomPrimitive *)new_prim.p();
497
498 #ifndef NDEBUG
499 if (!new_prim->check_valid(&data_reader)) {
500 all_is_valid = false;
501 }
502 #endif
503 }
504
505 cdata->_modified = Geom::get_next_modified();
506 reset_geom_rendering(cdata);
507 clear_cache_stage(current_thread);
508
509 nassertv(all_is_valid);
510 }
511
512 /**
513 * Reverses all of the primitives within this Geom, leaving the results in
514 * place. See GeomPrimitive::reverse().
515 *
516 * Don't call this in a downstream thread unless you don't mind it blowing
517 * away other changes you might have recently made in an upstream thread.
518 */
519 void Geom::
520 reverse_in_place() {
521 Thread *current_thread = Thread::get_current_thread();
522 CDWriter cdata(_cycler, true, current_thread);
523
524 #ifndef NDEBUG
525 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
526 data_reader.check_array_readers();
527
528 bool all_is_valid = true;
529 #endif
530 Primitives::iterator pi;
531 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
532 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->reverse();
533 (*pi) = (GeomPrimitive *)new_prim.p();
534
535 #ifndef NDEBUG
536 if (!new_prim->check_valid(&data_reader)) {
537 all_is_valid = false;
538 }
539 #endif
540 }
541
542 cdata->_modified = Geom::get_next_modified();
543 reset_geom_rendering(cdata);
544 clear_cache_stage(current_thread);
545
546 nassertv(all_is_valid);
547 }
548
549 /**
550 * Rotates all of the primitives within this Geom, leaving the results in
551 * place. See GeomPrimitive::rotate().
552 *
553 * Don't call this in a downstream thread unless you don't mind it blowing
554 * away other changes you might have recently made in an upstream thread.
555 */
556 void Geom::
557 rotate_in_place() {
558 Thread *current_thread = Thread::get_current_thread();
559 CDWriter cdata(_cycler, true, current_thread);
560
561 #ifndef NDEBUG
562 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
563 data_reader.check_array_readers();
564
565 bool all_is_valid = true;
566 #endif
567 Primitives::iterator pi;
568 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
569 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->rotate();
570 (*pi) = (GeomPrimitive *)new_prim.p();
571
572 #ifndef NDEBUG
573 if (!new_prim->check_valid(&data_reader)) {
574 all_is_valid = false;
575 }
576 #endif
577 }
578
579 switch (cdata->_shade_model) {
580 case SM_flat_first_vertex:
581 cdata->_shade_model = SM_flat_last_vertex;
582 break;
583
584 case SM_flat_last_vertex:
585 cdata->_shade_model = SM_flat_first_vertex;
586 break;
587
588 default:
589 break;
590 }
591
592 cdata->_modified = Geom::get_next_modified();
593 clear_cache_stage(current_thread);
594
595 nassertv(all_is_valid);
596 }
597
598 /**
599 * Unifies all of the primitives contained within this Geom into a single (or
600 * as few as possible, within the constraints of max_indices) primitive
601 * objects. This may require decomposing the primitives if, for instance, the
602 * Geom contains both triangle strips and triangle fans.
603 *
604 * max_indices represents the maximum number of indices that will be put in
605 * any one GeomPrimitive. If preserve_order is true, then the primitives will
606 * not be reordered during the operation, even if this results in a suboptimal
607 * result.
608 *
609 * Don't call this in a downstream thread unless you don't mind it blowing
610 * away other changes you might have recently made in an upstream thread.
611 */
612 void Geom::
613 unify_in_place(int max_indices, bool preserve_order) {
614 if (gobj_cat.is_debug()) {
615 gobj_cat.debug()
616 << "unify_in_place(" << max_indices << ", " << preserve_order
617 << "): " << *this << "\n";
618 }
619
620 Thread *current_thread = Thread::get_current_thread();
621 if (get_num_primitives() <= 1) {
622 // If we don't have more than one primitive to start with, no need to do
623 // anything.
624 return;
625 }
626
627 CDWriter cdata(_cycler, true, current_thread);
628
629 typedef pmap<TypeHandle, PT(GeomPrimitive) > NewPrims;
630
631 NewPrims new_prims;
632
633 bool keep_different_types = preserve_triangle_strips && !preserve_order;
634
635 Primitives::const_iterator pi;
636 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
637 CPT(GeomPrimitive) primitive = (*pi).get_read_pointer(current_thread);
638 NewPrims::iterator npi = new_prims.find(primitive->get_type());
639 if (npi == new_prims.end()) {
640 // This is the first primitive of this type.
641 if (!keep_different_types && !new_prims.empty()) {
642 // Actually, since we aren't trying to keep the different types of
643 // primitives, we should try to combine this type and the other type
644 // by decomposing them both (into triangles, segments, or whatever).
645
646 // First, decompose the incoming one.
647 primitive = primitive->decompose();
648 npi = new_prims.find(primitive->get_type());
649 if (npi == new_prims.end()) {
650 // That didn't help, so decompose the one already in the table.
651 nassertv(new_prims.size() == 1);
652 npi = new_prims.begin();
653 CPT(GeomPrimitive) np = (*npi).second->decompose();
654 new_prims.clear();
655 new_prims.insert(NewPrims::value_type(np->get_type(), np->make_copy()));
656 npi = new_prims.find(primitive->get_type());
657 }
658 }
659 }
660
661 if (npi == new_prims.end()) {
662 // This is the first primitive of this type. Just store it.
663 new_prims.insert(NewPrims::value_type(primitive->get_type(), primitive->make_copy()));
664
665 } else {
666 // We have already encountered another primitive of this type. Combine
667 // them.
668 combine_primitives((*npi).second, std::move(primitive), current_thread);
669 }
670 }
671
672 // Now, we have one or more primitives, but only one of each type.
673 #ifndef NDEBUG
674 if (!keep_different_types && new_prims.size() > 1) {
675 // This shouldn't be possible, because we decompose as we go, in the loop
676 // above. (We have to decompose as we go to preserve the ordering of the
677 // primitives.)
678 nassertv(false);
679 }
680
681 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
682 data_reader.check_array_readers();
683 #endif
684
685 // Finally, iterate through the remaining primitives, and copy them to the
686 // output list.
687 cdata->_primitives.clear();
688 NewPrims::iterator npi;
689 for (npi = new_prims.begin(); npi != new_prims.end(); ++npi) {
690 GeomPrimitive *prim = (*npi).second;
691
692 nassertv(prim->check_valid(&data_reader));
693
694 // Each new primitive, naturally, inherits the Geom's overall shade model.
695 prim->set_shade_model(cdata->_shade_model);
696
697 // Should we split it up again to satisfy max_indices?
698 if (prim->get_num_vertices() > max_indices) {
699 // Copy prim into smaller prims, no one of which has more than
700 // max_indices vertices.
701 GeomPrimitivePipelineReader reader(prim, current_thread);
702
703 // Copy prim into smaller prims, no one of which has more than
704 // max_indices vertices.
705 int i = 0;
706 int num_primitives = reader.get_num_primitives();
707 int num_vertices_per_primitive = prim->get_num_vertices_per_primitive();
708 int num_unused_vertices_per_primitive = prim->get_num_unused_vertices_per_primitive();
709 if (num_vertices_per_primitive != 0) {
710 // This is a simple primitive type like a triangle, where all the
711 // primitives share the same number of vertices.
712 int total_vertices_per_primitive = num_vertices_per_primitive + num_unused_vertices_per_primitive;
713 int max_primitives = max_indices / total_vertices_per_primitive;
714 const unsigned char *ptr = reader.get_read_pointer(true);
715 size_t stride = reader.get_index_stride();
716
717 while (i < num_primitives) {
718 PT(GeomPrimitive) smaller = prim->make_copy();
719 smaller->clear_vertices();
720
721 // Since the number of vertices is consistent, we can calculate how
722 // many primitives will fit, and copy them all in one go.
723 int copy_primitives = min((num_primitives - i), max_primitives);
724 int num_vertices = copy_primitives * total_vertices_per_primitive;
725 nassertv(num_vertices > 0);
726 {
727 smaller->set_index_type(reader.get_index_type());
728 GeomVertexArrayDataHandle writer(smaller->modify_vertices(), current_thread);
729 writer.unclean_set_num_rows(num_vertices);
730 memcpy(writer.get_write_pointer(), ptr, stride * (size_t)(num_vertices - num_unused_vertices_per_primitive));
731 }
732
733 cdata->_primitives.push_back(smaller.p());
734
735 ptr += stride * (size_t)num_vertices;
736 i += copy_primitives;
737 }
738 } else {
739 // This is a complex primitive type like a triangle strip.
740 CPTA_int ends = reader.get_ends();
741 int start = 0;
742 int end = ends[0];
743
744 while (i < num_primitives) {
745 PT(GeomPrimitive) smaller = prim->make_copy();
746 smaller->clear_vertices();
747
748 while (smaller->get_num_vertices() + (end - start) < max_indices) {
749 for (int n = start; n < end; ++n) {
750 smaller->add_vertex(reader.get_vertex(n));
751 }
752 smaller->close_primitive();
753
754 ++i;
755 if (i >= num_primitives) {
756 break;
757 }
758
759 start = end + num_unused_vertices_per_primitive;
760 end = ends[i];
761 }
762
763 cdata->_primitives.push_back(smaller.p());
764 }
765 }
766 } else {
767 // The prim has few enough vertices; keep it.
768 cdata->_primitives.push_back(prim);
769 }
770 }
771
772 cdata->_modified = Geom::get_next_modified();
773 clear_cache_stage(current_thread);
774 reset_geom_rendering(cdata);
775 }
776
777 /**
778 * Replaces the GeomPrimitives within this Geom with corresponding GeomLines,
779 * representing a wireframe of the primitives. See
780 * GeomPrimitive::make_lines().
781 *
782 * Don't call this in a downstream thread unless you don't mind it blowing
783 * away other changes you might have recently made in an upstream thread.
784 */
785 void Geom::
786 make_lines_in_place() {
787 Thread *current_thread = Thread::get_current_thread();
788 CDWriter cdata(_cycler, true, current_thread);
789
790 #ifndef NDEBUG
791 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
792 data_reader.check_array_readers();
793
794 bool all_is_valid = true;
795 #endif
796 Primitives::iterator pi;
797 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
798 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_lines();
799 (*pi) = (GeomPrimitive *)new_prim.p();
800
801 #ifndef NDEBUG
802 if (!new_prim->check_valid(&data_reader)) {
803 all_is_valid = false;
804 }
805 #endif
806 }
807
808 cdata->_modified = Geom::get_next_modified();
809 reset_geom_rendering(cdata);
810 clear_cache_stage(current_thread);
811
812 nassertv(all_is_valid);
813 }
814
815 /**
816 * Replaces the GeomPrimitives within this Geom with corresponding GeomPoints.
817 * See GeomPrimitive::make_points().
818 *
819 * Don't call this in a downstream thread unless you don't mind it blowing
820 * away other changes you might have recently made in an upstream thread.
821 */
822 void Geom::
823 make_points_in_place() {
824 Thread *current_thread = Thread::get_current_thread();
825 CDWriter cdata(_cycler, true, current_thread);
826
827 #ifndef NDEBUG
828 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
829 data_reader.check_array_readers();
830
831 bool all_is_valid = true;
832 #endif
833 Primitives::iterator pi;
834 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
835 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_points();
836 (*pi) = (GeomPrimitive *)new_prim.p();
837
838 #ifndef NDEBUG
839 if (!new_prim->check_valid(&data_reader)) {
840 all_is_valid = false;
841 }
842 #endif
843 }
844
845 cdata->_modified = Geom::get_next_modified();
846 reset_geom_rendering(cdata);
847 clear_cache_stage(current_thread);
848
849 nassertv(all_is_valid);
850 }
851
852 /**
853 * Replaces the GeomPrimitives within this Geom with corresponding
854 * GeomPatches. See GeomPrimitive::make_patches().
855 *
856 * Don't call this in a downstream thread unless you don't mind it blowing
857 * away other changes you might have recently made in an upstream thread.
858 */
859 void Geom::
860 make_patches_in_place() {
861 Thread *current_thread = Thread::get_current_thread();
862 CDWriter cdata(_cycler, true, current_thread);
863
864 #ifndef NDEBUG
865 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
866 data_reader.check_array_readers();
867
868 bool all_is_valid = true;
869 #endif
870 Primitives::iterator pi;
871 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
872 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_patches();
873 (*pi) = (GeomPrimitive *)new_prim.p();
874
875 #ifndef NDEBUG
876 if (!new_prim->check_valid(&data_reader)) {
877 all_is_valid = false;
878 }
879 #endif
880 }
881
882 cdata->_modified = Geom::get_next_modified();
883 reset_geom_rendering(cdata);
884 clear_cache_stage(current_thread);
885
886 nassertv(all_is_valid);
887 }
888
889 /**
890 * Replaces the GeomPrimitives within this Geom with corresponding versions
891 * with adjacency information. See GeomPrimitive::make_adjacency().
892 *
893 * Don't call this in a downstream thread unless you don't mind it blowing
894 * away other changes you might have recently made in an upstream thread.
895 *
896 * @since 1.10.0
897 */
898 void Geom::
899 make_adjacency_in_place() {
900 Thread *current_thread = Thread::get_current_thread();
901 CDWriter cdata(_cycler, true, current_thread);
902
903 #ifndef NDEBUG
904 GeomVertexDataPipelineReader data_reader(cdata->_data.get_read_pointer(current_thread), current_thread);
905 data_reader.check_array_readers();
906
907 bool all_is_valid = true;
908 #endif
909 Primitives::iterator pi;
910 for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
911 CPT(GeomPrimitive) new_prim = (*pi).get_read_pointer(current_thread)->make_adjacency();
912 if (new_prim != nullptr) {
913 (*pi) = (GeomPrimitive *)new_prim.p();
914
915 #ifndef NDEBUG
916 if (!new_prim->check_valid(&data_reader)) {
917 all_is_valid = false;
918 }
919 #endif
920 }
921 }
922
923 cdata->_modified = Geom::get_next_modified();
924 reset_geom_rendering(cdata);
925 clear_cache_stage(current_thread);
926
927 nassertv(all_is_valid);
928 }
929
930 /**
931 * Copies the primitives from the indicated Geom into this one. This does
932 * require that both Geoms contain the same fundamental type primitives, both
933 * have a compatible shade model, and both use the same GeomVertexData. Both
934 * Geoms must also be the same specific class type (i.e. if one is a
935 * GeomTextGlyph, they both must be.)
936 *
937 * Returns true if the copy is successful, or false otherwise (because the
938 * Geoms were mismatched).
939 */
940 bool Geom::
941 copy_primitives_from(const Geom *other) {
942 if (get_primitive_type() != PT_none &&
943 other->get_primitive_type() != get_primitive_type()) {
944 return false;
945 }
946 if (get_vertex_data() != other->get_vertex_data()) {
947 return false;
948 }
949 if (get_type() != other->get_type()) {
950 return false;
951 }
952
953 ShadeModel this_shade_model = get_shade_model();
954 ShadeModel other_shade_model = other->get_shade_model();
955 if (this_shade_model != SM_uniform && other_shade_model != SM_uniform &&
956 this_shade_model != other_shade_model) {
957 if ((this_shade_model == SM_flat_first_vertex && other_shade_model == SM_flat_last_vertex) ||
958 (this_shade_model == SM_flat_last_vertex && other_shade_model == SM_flat_first_vertex)) {
959 // This is acceptable; we can rotate the primitives to match.
960
961 } else {
962 // Otherwise, we have incompatible shade models.
963 return false;
964 }
965 }
966
967 int num_primitives = other->get_num_primitives();
968 for (int i = 0; i < num_primitives; i++) {
969 add_primitive(other->get_primitive(i));
970 }
971
972 return true;
973 }
974
975 /**
976 * Returns the number of bytes consumed by the geom and its primitives (but
977 * not including its vertex table).
978 */
979 int Geom::
980 get_num_bytes() const {
981 CDReader cdata(_cycler);
982
983 int num_bytes = sizeof(Geom);
984 Primitives::const_iterator pi;
985 for (pi = cdata->_primitives.begin();
986 pi != cdata->_primitives.end();
987 ++pi) {
988 num_bytes += (*pi).get_read_pointer()->get_num_bytes();
989 }
990
991 return num_bytes;
992 }
993
994 /**
995 * Returns true if all the primitive arrays are currently resident in memory.
996 * If this returns false, the data will be brought back into memory shortly;
997 * try again later.
998 *
999 * This does not also test the Geom's associated GeomVertexData. That must be
1000 * tested separately.
1001 */
1002 bool Geom::
1003 request_resident() const {
1004 Thread *current_thread = Thread::get_current_thread();
1005
1006 CDReader cdata(_cycler, current_thread);
1007
1008 bool resident = true;
1009
1010 Primitives::const_iterator pi;
1011 for (pi = cdata->_primitives.begin();
1012 pi != cdata->_primitives.end();
1013 ++pi) {
1014 if (!(*pi).get_read_pointer(current_thread)->request_resident()) {
1015 resident = false;
1016 }
1017 }
1018
1019 return resident;
1020 }
1021
1022 /**
1023 * Applies the indicated transform to all of the vertices in the Geom. If the
1024 * Geom happens to share a vertex table with another Geom, this operation will
1025 * duplicate the vertex table instead of breaking the other Geom; however, if
1026 * multiple Geoms with shared tables are transformed by the same matrix, they
1027 * will no longer share tables after the operation. Consider using the
1028 * GeomTransformer if you will be applying the same transform to multiple
1029 * Geoms.
1030 */
1031 void Geom::
1032 transform_vertices(const LMatrix4 &mat) {
1033 PT(GeomVertexData) new_data = modify_vertex_data();
1034 CPT(GeomVertexFormat) format = new_data->get_format();
1035
1036 size_t ci;
1037 for (ci = 0; ci < format->get_num_points(); ci++) {
1038 GeomVertexRewriter data(new_data, format->get_point(ci));
1039
1040 while (!data.is_at_end()) {
1041 const LPoint3 &point = data.get_data3();
1042 data.set_data3(point * mat);
1043 }
1044 }
1045 for (ci = 0; ci < format->get_num_vectors(); ci++) {
1046 GeomVertexRewriter data(new_data, format->get_vector(ci));
1047
1048 while (!data.is_at_end()) {
1049 const LVector3 &vector = data.get_data3();
1050 data.set_data3(normalize(vector * mat));
1051 }
1052 }
1053 }
1054
1055 /**
1056 * Verifies that the all of the primitives within the geom reference vertices
1057 * that actually exist within the geom's GeomVertexData. Returns true if the
1058 * geom appears to be valid, false otherwise.
1059 */
1060 bool Geom::
1061 check_valid() const {
1062 Thread *current_thread = Thread::get_current_thread();
1063 GeomPipelineReader geom_reader(this, current_thread);
1064 CPT(GeomVertexData) vertex_data = geom_reader.get_vertex_data();
1065 GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
1066 data_reader.check_array_readers();
1067 return geom_reader.check_valid(&data_reader);
1068 }
1069
1070 /**
1071 * Verifies that the all of the primitives within the geom reference vertices
1072 * that actually exist within the indicated GeomVertexData. Returns true if
1073 * the geom appears to be valid, false otherwise.
1074 */
1075 bool Geom::
1076 check_valid(const GeomVertexData *vertex_data) const {
1077 Thread *current_thread = Thread::get_current_thread();
1078 GeomPipelineReader geom_reader(this, current_thread);
1079 GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
1080 data_reader.check_array_readers();
1081 return geom_reader.check_valid(&data_reader);
1082 }
1083
1084 /**
1085 * Returns the bounding volume for the Geom.
1086 */
1087 CPT(BoundingVolume) Geom::
1088 get_bounds(Thread *current_thread) const {
1089 CDLockedReader cdata(_cycler, current_thread);
1090 if (cdata->_user_bounds != nullptr) {
1091 return cdata->_user_bounds;
1092 }
1093
1094 if (cdata->_internal_bounds_stale) {
1095 CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
1096 compute_internal_bounds(cdataw, current_thread);
1097 return cdataw->_internal_bounds;
1098 }
1099 return cdata->_internal_bounds;
1100 }
1101
1102 /**
1103 * Returns the number of vertices rendered by all primitives within the Geom.
1104 */
1105 int Geom::
1106 get_nested_vertices(Thread *current_thread) const {
1107 CDLockedReader cdata(_cycler, current_thread);
1108 if (cdata->_internal_bounds_stale) {
1109 CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
1110 compute_internal_bounds(cdataw, current_thread);
1111 return cdataw->_nested_vertices;
1112 }
1113 return cdata->_nested_vertices;
1114 }
1115
1116 /**
1117 *
1118 */
1119 void Geom::
1120 output(std::ostream &out) const {
1121 CDReader cdata(_cycler);
1122
1123 // Get a list of the primitive types contained within this object.
1124 int num_faces = 0;
1125 pset<TypeHandle> types;
1126 Primitives::const_iterator pi;
1127 for (pi = cdata->_primitives.begin();
1128 pi != cdata->_primitives.end();
1129 ++pi) {
1130 CPT(GeomPrimitive) prim = (*pi).get_read_pointer();
1131 num_faces += prim->get_num_faces();
1132 types.insert(prim->get_type());
1133 }
1134
1135 out << get_type() << " [";
1136 pset<TypeHandle>::iterator ti;
1137 for (ti = types.begin(); ti != types.end(); ++ti) {
1138 out << " " << (*ti);
1139 }
1140 out << " ], " << num_faces << " faces";
1141 }
1142
1143 /**
1144 *
1145 */
1146 void Geom::
1147 write(std::ostream &out, int indent_level) const {
1148 CDReader cdata(_cycler);
1149
1150 // Get a list of the primitive types contained within this object.
1151 Primitives::const_iterator pi;
1152 for (pi = cdata->_primitives.begin();
1153 pi != cdata->_primitives.end();
1154 ++pi) {
1155 (*pi).get_read_pointer()->write(out, indent_level);
1156 }
1157 }
1158
1159 /**
1160 * Removes all of the previously-cached results of munge_geom().
1161 *
1162 * This blows away the entire cache, upstream and downstream the pipeline.
1163 * Use clear_cache_stage() instead if you only want to blow away the cache at
1164 * the current stage and upstream.
1165 */
1166 void Geom::
1167 clear_cache() {
1168 LightMutexHolder holder(_cache_lock);
1169 for (Cache::iterator ci = _cache.begin();
1170 ci != _cache.end();
1171 ++ci) {
1172 CacheEntry *entry = (*ci).second;
1173 entry->erase();
1174 }
1175 _cache.clear();
1176 }
1177
1178 /**
1179 * Removes all of the previously-cached results of munge_geom(), at the
1180 * current pipeline stage and upstream. Does not affect the downstream cache.
1181 *
1182 * Don't call this in a downstream thread unless you don't mind it blowing
1183 * away other changes you might have recently made in an upstream thread.
1184 */
1185 void Geom::
1186 clear_cache_stage(Thread *current_thread) {
1187 LightMutexHolder holder(_cache_lock);
1188 for (Cache::iterator ci = _cache.begin();
1189 ci != _cache.end();
1190 ++ci) {
1191 CacheEntry *entry = (*ci).second;
1192 CDCacheWriter cdata(entry->_cycler, current_thread);
1193 cdata->set_result(nullptr, nullptr);
1194 }
1195 }
1196
1197 /**
1198 * Indicates that the geom should be enqueued to be prepared in the indicated
1199 * prepared_objects at the beginning of the next frame. This will ensure the
1200 * geom is already loaded into geom memory if it is expected to be rendered
1201 * soon.
1202 *
1203 * Use this function instead of prepare_now() to preload geoms from a user
1204 * interface standpoint.
1205 */
1206 void Geom::
1207 prepare(PreparedGraphicsObjects *prepared_objects) {
1208 prepared_objects->enqueue_geom(this);
1209 }
1210
1211 /**
1212 * Returns true if the geom has already been prepared or enqueued for
1213 * preparation on the indicated GSG, false otherwise.
1214 */
1215 bool Geom::
1216 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
1217 Contexts::const_iterator ci;
1218 ci = _contexts.find(prepared_objects);
1219 if (ci != _contexts.end()) {
1220 return true;
1221 }
1222 return prepared_objects->is_geom_queued(this);
1223 }
1224
1225 /**
1226 * Frees the geom context only on the indicated object, if it exists there.
1227 * Returns true if it was released, false if it had not been prepared.
1228 */
1229 bool Geom::
1230 release(PreparedGraphicsObjects *prepared_objects) {
1231 Contexts::iterator ci;
1232 ci = _contexts.find(prepared_objects);
1233 if (ci != _contexts.end()) {
1234 GeomContext *gc = (*ci).second;
1235 prepared_objects->release_geom(gc);
1236 return true;
1237 }
1238
1239 // Maybe it wasn't prepared yet, but it's about to be.
1240 return prepared_objects->dequeue_geom(this);
1241 }
1242
1243 /**
1244 * Frees the context allocated on all objects for which the geom has been
1245 * declared. Returns the number of contexts which have been freed.
1246 */
1247 int Geom::
1248 release_all() {
1249 // We have to traverse a copy of the _contexts list, because the
1250 // PreparedGraphicsObjects object will call clear_prepared() in response to
1251 // each release_geom(), and we don't want to be modifying the _contexts list
1252 // while we're traversing it.
1253 Contexts temp = _contexts;
1254 int num_freed = (int)_contexts.size();
1255
1256 Contexts::const_iterator ci;
1257 for (ci = temp.begin(); ci != temp.end(); ++ci) {
1258 PreparedGraphicsObjects *prepared_objects = (*ci).first;
1259 GeomContext *gc = (*ci).second;
1260 prepared_objects->release_geom(gc);
1261 }
1262
1263 // Now that we've called release_geom() on every known context, the
1264 // _contexts list should have completely emptied itself.
1265 nassertr(_contexts.empty(), num_freed);
1266
1267 return num_freed;
1268 }
1269
1270 /**
1271 * Creates a context for the geom on the particular GSG, if it does not
1272 * already exist. Returns the new (or old) GeomContext. This assumes that
1273 * the GraphicsStateGuardian is the currently active rendering context and
1274 * that it is ready to accept new geoms. If this is not necessarily the case,
1275 * you should use prepare() instead.
1276 *
1277 * Normally, this is not called directly except by the GraphicsStateGuardian;
1278 * a geom does not need to be explicitly prepared by the user before it may be
1279 * rendered.
1280 */
1281 GeomContext *Geom::
1282 prepare_now(PreparedGraphicsObjects *prepared_objects,
1283 GraphicsStateGuardianBase *gsg) {
1284 Contexts::const_iterator ci;
1285 ci = _contexts.find(prepared_objects);
1286 if (ci != _contexts.end()) {
1287 return (*ci).second;
1288 }
1289
1290 GeomContext *gc = prepared_objects->prepare_geom_now(this, gsg);
1291 if (gc != nullptr) {
1292 _contexts[prepared_objects] = gc;
1293 }
1294 return gc;
1295 }
1296
1297 /**
1298 * Actually draws the Geom with the indicated GSG, using the indicated vertex
1299 * data (which might have been pre-munged to support the GSG's needs).
1300 *
1301 * Returns true if all of the primitives were drawn normally, false if there
1302 * was a problem (for instance, some of the data was nonresident). If force
1303 * is passed true, it will wait for the data to become resident if necessary.
1304 */
1305 bool Geom::
1306 draw(GraphicsStateGuardianBase *gsg, const GeomVertexData *vertex_data,
1307 bool force, Thread *current_thread) const {
1308 GeomPipelineReader geom_reader(this, current_thread);
1309 GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
1310 data_reader.check_array_readers();
1311
1312 return geom_reader.draw(gsg, &data_reader, force);
1313 }
1314
1315 /**
1316 * Returns a monotonically increasing sequence. Each time this is called, a
1317 * new sequence number is returned, higher than the previous value.
1318 *
1319 * This is used to ensure that GeomVertexArrayData::get_modified() and
1320 * GeomPrimitive::get_modified() update from the same space, so that
1321 * Geom::get_modified() returns a meaningful value.
1322 */
1323 UpdateSeq Geom::
1324 get_next_modified() {
1325 ++_next_modified;
1326 return _next_modified;
1327 }
1328
1329 /**
1330 * Recomputes the dynamic bounding volume for this Geom. This includes all of
1331 * the vertices.
1332 */
1333 void Geom::
1334 compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
1335 int num_vertices = 0;
1336
1337 // Get the vertex data, after animation.
1338 CPT(GeomVertexData) vertex_data = get_animated_vertex_data(true, current_thread);
1339
1340 // Now actually compute the bounding volume. We do this by using
1341 // calc_tight_bounds to determine our box first.
1342 LPoint3 pmin, pmax;
1343 PN_stdfloat sq_center_dist = 0.0f;
1344 bool found_any = false;
1345 do_calc_tight_bounds(pmin, pmax, sq_center_dist, found_any,
1346 vertex_data, false, LMatrix4::ident_mat(),
1347 InternalName::get_vertex(),
1348 cdata, current_thread);
1349
1350 BoundingVolume::BoundsType btype = cdata->_bounds_type;
1351 if (btype == BoundingVolume::BT_default) {
1352 btype = bounds_type;
1353 }
1354
1355 if (found_any) {
1356 nassertv(!pmin.is_nan());
1357 nassertv(!pmax.is_nan());
1358
1359 // Then we put the bounding volume around both of those points.
1360 PN_stdfloat avg_box_area;
1361 switch (btype) {
1362 case BoundingVolume::BT_best:
1363 case BoundingVolume::BT_fastest:
1364 case BoundingVolume::BT_default:
1365 {
1366 // When considering a box, calculate (roughly) the average area of the
1367 // sides. We will use this to determine whether a sphere or box is a
1368 // better fit.
1369 PN_stdfloat min_extent = min(pmax[0] - pmin[0],
1370 min(pmax[1] - pmin[1],
1371 pmax[2] - pmin[2]));
1372 PN_stdfloat max_extent = max(pmax[0] - pmin[0],
1373 max(pmax[1] - pmin[1],
1374 pmax[2] - pmin[2]));
1375 avg_box_area = ((min_extent * min_extent) + (max_extent * max_extent)) / 2;
1376 }
1377 // Fall through
1378 case BoundingVolume::BT_sphere:
1379 {
1380 // Determine the best radius for a bounding sphere.
1381 LPoint3 aabb_center = (pmin + pmax) * 0.5f;
1382 PN_stdfloat best_sq_radius = (pmax - aabb_center).length_squared();
1383
1384 if (btype != BoundingVolume::BT_fastest && best_sq_radius > 0.0f &&
1385 aabb_center.length_squared() / best_sq_radius >= (0.2f * 0.2f)) {
1386 // Hmm, this is an off-center model. Maybe we can do a better job
1387 // by calculating the bounding sphere from the AABB center.
1388
1389 PN_stdfloat better_sq_radius;
1390 bool found_any = false;
1391 do_calc_sphere_radius(aabb_center, better_sq_radius, found_any,
1392 vertex_data, cdata, current_thread);
1393
1394 if (found_any && better_sq_radius > 0.0f &&
1395 better_sq_radius <= best_sq_radius) {
1396 // Great. This is as good a sphere as we're going to get.
1397 if (btype == BoundingVolume::BT_best &&
1398 avg_box_area < better_sq_radius * MathNumbers::pi) {
1399 // But the box is better, anyway. Use that instead.
1400 cdata->_internal_bounds = new BoundingBox(pmin, pmax);
1401 break;
1402 }
1403 cdata->_internal_bounds =
1404 new BoundingSphere(aabb_center, csqrt(better_sq_radius));
1405 break;
1406 }
1407 }
1408
1409 if (btype != BoundingVolume::BT_sphere &&
1410 avg_box_area < sq_center_dist * MathNumbers::pi) {
1411 // A box is probably a tighter fit.
1412 cdata->_internal_bounds = new BoundingBox(pmin, pmax);
1413 break;
1414
1415 } else if (sq_center_dist >= 0.0f && sq_center_dist <= best_sq_radius) {
1416 // No, but a sphere centered on the origin is apparently still
1417 // better than a sphere around the bounding box.
1418 cdata->_internal_bounds =
1419 new BoundingSphere(LPoint3::origin(), csqrt(sq_center_dist));
1420 break;
1421
1422 } else if (btype == BoundingVolume::BT_sphere) {
1423 // This is the worst sphere we can make, which is why we will only
1424 // do it when the user specifically requests a sphere.
1425 cdata->_internal_bounds =
1426 new BoundingSphere(aabb_center,
1427 (best_sq_radius > 0.0f) ? csqrt(best_sq_radius) : 0.0f);
1428 break;
1429 }
1430 }
1431 // Fall through.
1432
1433 case BoundingVolume::BT_box:
1434 cdata->_internal_bounds = new BoundingBox(pmin, pmax);
1435 }
1436
1437 Primitives::const_iterator pi;
1438 for (pi = cdata->_primitives.begin();
1439 pi != cdata->_primitives.end();
1440 ++pi) {
1441 CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread);
1442 num_vertices += prim->get_num_vertices();
1443 }
1444
1445 } else {
1446 // No points; empty bounding volume.
1447 if (btype == BoundingVolume::BT_sphere) {
1448 cdata->_internal_bounds = new BoundingSphere;
1449 } else {
1450 cdata->_internal_bounds = new BoundingBox;
1451 }
1452 }
1453
1454 cdata->_nested_vertices = num_vertices;
1455 cdata->_internal_bounds_stale = false;
1456 }
1457
1458 /**
1459 * The private implementation of calc_tight_bounds().
1460 */
1461 void Geom::
1462 do_calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
1463 PN_stdfloat &sq_center_dist, bool &found_any,
1464 const GeomVertexData *vertex_data,
1465 bool got_mat, const LMatrix4 &mat,
1466 const InternalName *column_name,
1467 const CData *cdata, Thread *current_thread) const {
1468 Primitives::const_iterator pi;
1469 for (pi = cdata->_primitives.begin();
1470 pi != cdata->_primitives.end();
1471 ++pi) {
1472 CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread);
1473 prim->calc_tight_bounds(min_point, max_point, sq_center_dist,
1474 found_any, vertex_data, got_mat, mat,
1475 column_name, current_thread);
1476 }
1477 }
1478
1479 /**
1480 *
1481 */
1482 void Geom::
1483 do_calc_sphere_radius(const LPoint3 &center, PN_stdfloat &sq_radius,
1484 bool &found_any, const GeomVertexData *vertex_data,
1485 const CData *cdata, Thread *current_thread) const {
1486 Primitives::const_iterator pi;
1487 for (pi = cdata->_primitives.begin();
1488 pi != cdata->_primitives.end();
1489 ++pi) {
1490 CPT(GeomPrimitive) prim = (*pi).get_read_pointer(current_thread);
1491 prim->calc_sphere_radius(center, sq_radius, found_any,
1492 vertex_data, current_thread);
1493 }
1494 }
1495
1496 /**
1497 * Removes the indicated PreparedGraphicsObjects table from the Geom's table,
1498 * without actually releasing the geom. This is intended to be called only
1499 * from PreparedGraphicsObjects::release_geom(); it should never be called by
1500 * user code.
1501 */
1502 void Geom::
1503 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
1504 Contexts::iterator ci;
1505 ci = _contexts.find(prepared_objects);
1506 if (ci != _contexts.end()) {
1507 _contexts.erase(ci);
1508 } else {
1509 // If this assertion fails, clear_prepared() was given a prepared_objects
1510 // that the geom didn't know about.
1511 nassert_raise("unknown PreparedGraphicsObjects");
1512 }
1513 }
1514
1515 /**
1516 * Verifies that the all of the primitives within the geom reference vertices
1517 * that actually exist within the indicated GeomVertexData (presumably in
1518 * preparation for assigning the geom to use this data). Returns true if the
1519 * data appears to be valid, false otherwise.
1520 */
1521 bool Geom::
1522 check_will_be_valid(const GeomVertexData *vertex_data) const {
1523 Thread *current_thread = Thread::get_current_thread();
1524
1525 CDReader cdata(_cycler, current_thread);
1526
1527 GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
1528 data_reader.check_array_readers();
1529
1530 Primitives::const_iterator pi;
1531 for (pi = cdata->_primitives.begin();
1532 pi != cdata->_primitives.end();
1533 ++pi) {
1534 GeomPrimitivePipelineReader reader((*pi).get_read_pointer(), current_thread);
1535 reader.check_minmax();
1536 if (!reader.check_valid(&data_reader)) {
1537 return false;
1538 }
1539 }
1540
1541 return true;
1542 }
1543
1544 /**
1545 * Rederives the _geom_rendering member.
1546 */
1547 void Geom::
1548 reset_geom_rendering(Geom::CData *cdata) {
1549 cdata->_geom_rendering = 0;
1550 Primitives::const_iterator pi;
1551 for (pi = cdata->_primitives.begin();
1552 pi != cdata->_primitives.end();
1553 ++pi) {
1554 cdata->_geom_rendering |= (*pi).get_read_pointer()->get_geom_rendering();
1555 }
1556
1557 if ((cdata->_geom_rendering & GR_point) != 0) {
1558 CPT(GeomVertexData) data = cdata->_data.get_read_pointer();
1559 if (data->has_column(InternalName::get_size())) {
1560 cdata->_geom_rendering |= GR_per_point_size;
1561 }
1562 if (data->has_column(InternalName::get_aspect_ratio())) {
1563 cdata->_geom_rendering |= GR_point_aspect_ratio;
1564 }
1565 if (data->has_column(InternalName::get_rotate())) {
1566 cdata->_geom_rendering |= GR_point_rotate;
1567 }
1568 }
1569
1570 switch (get_shade_model()) {
1571 case SM_flat_first_vertex:
1572 cdata->_geom_rendering |= GR_flat_first_vertex;
1573 break;
1574
1575 case SM_flat_last_vertex:
1576 cdata->_geom_rendering |= GR_flat_last_vertex;
1577 break;
1578
1579 default:
1580 break;
1581 }
1582 }
1583
1584 /**
1585 * Combines two primitives of the same type into a single primitive. a_prim
1586 * is modified to append the vertices from b_prim, which is unmodified.
1587 */
1588 void Geom::
1589 combine_primitives(GeomPrimitive *a_prim, CPT(GeomPrimitive) b_prim,
1590 Thread *current_thread) {
1591 nassertv(a_prim != b_prim);
1592 nassertv(a_prim->get_type() == b_prim->get_type());
1593
1594 if (a_prim->get_index_type() != b_prim->get_index_type()) {
1595 GeomPrimitive::NumericType index_type = max(a_prim->get_index_type(), b_prim->get_index_type());
1596 a_prim->set_index_type(index_type);
1597 if (b_prim->get_index_type() != index_type) {
1598 PT(GeomPrimitive) b_prim_copy = b_prim->make_copy();
1599 b_prim_copy->set_index_type(index_type);
1600 b_prim = b_prim_copy;
1601 }
1602 }
1603
1604 if (!b_prim->is_indexed()) {
1605 PT(GeomPrimitive) b_prim_copy = b_prim->make_copy();
1606 b_prim_copy->make_indexed();
1607 b_prim = b_prim_copy;
1608 }
1609
1610 PT(GeomVertexArrayData) a_vertices = a_prim->modify_vertices();
1611 CPT(GeomVertexArrayData) b_vertices = b_prim->get_vertices();
1612
1613 if (a_prim->requires_unused_vertices()) {
1614 GeomVertexReader index(b_vertices, 0);
1615 int b_vertex = index.get_data1i();
1616 a_prim->append_unused_vertices(a_vertices, b_vertex);
1617 }
1618
1619 PT(GeomVertexArrayDataHandle) a_handle =
1620 new GeomVertexArrayDataHandle(std::move(a_vertices), current_thread);
1621 CPT(GeomVertexArrayDataHandle) b_handle =
1622 new GeomVertexArrayDataHandle(std::move(b_vertices), current_thread);
1623
1624 size_t orig_a_vertices = a_handle->get_num_rows();
1625
1626 a_handle->copy_subdata_from(a_handle->get_data_size_bytes(), 0,
1627 b_handle, 0, b_handle->get_data_size_bytes());
1628 a_prim->clear_minmax();
1629 if (a_prim->is_composite()) {
1630 // Also copy the ends array.
1631 PTA_int a_ends = a_prim->modify_ends();
1632 CPTA_int b_ends = b_prim->get_ends();
1633 for (size_t i = 0; i < b_ends.size(); ++i) {
1634 a_ends.push_back(b_ends[i] + orig_a_vertices);
1635 }
1636 }
1637 }
1638
1639 /**
1640 * Tells the BamReader how to create objects of type Geom.
1641 */
1642 void Geom::
1643 register_with_read_factory() {
1644 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1645 }
1646
1647 /**
1648 * Writes the contents of this object to the datagram for shipping out to a
1649 * Bam file.
1650 */
1651 void Geom::
1652 write_datagram(BamWriter *manager, Datagram &dg) {
1653 TypedWritable::write_datagram(manager, dg);
1654
1655 manager->write_cdata(dg, _cycler);
1656 }
1657
1658 /**
1659 * This function is called by the BamReader's factory when a new object of
1660 * type Geom is encountered in the Bam file. It should create the Geom and
1661 * extract its information from the file.
1662 */
1663 TypedWritable *Geom::
1664 make_from_bam(const FactoryParams &params) {
1665 Geom *object = new Geom(nullptr);
1666 DatagramIterator scan;
1667 BamReader *manager;
1668
1669 parse_params(params, scan, manager);
1670 object->fillin(scan, manager);
1671 manager->register_finalize(object);
1672
1673 return object;
1674 }
1675
1676 /**
1677 * Called by the BamReader to perform any final actions needed for setting up
1678 * the object after all objects have been read and all pointers have been
1679 * completed.
1680 */
1681 void Geom::
1682 finalize(BamReader *manager) {
1683 CDWriter cdata(_cycler, true);
1684
1685 // Make sure our GeomVertexData is finalized first. This may result in the
1686 // data getting finalized multiple times, but it doesn't mind that.
1687 if (!cdata->_data.is_null()) {
1688 // We shouldn't call get_write_pointer(), which might replicate the
1689 // GeomVertexData unnecessarily.
1690 cdata->_data.get_unsafe_pointer()->finalize(manager);
1691 }
1692
1693 reset_geom_rendering(cdata);
1694 }
1695
1696 /**
1697 * This internal function is called by make_from_bam to read in all of the
1698 * relevant data from the BamFile for the new Geom.
1699 */
1700 void Geom::
1701 fillin(DatagramIterator &scan, BamReader *manager) {
1702 TypedWritable::fillin(scan, manager);
1703
1704 manager->read_cdata(scan, _cycler);
1705 }
1706
1707 /**
1708 *
1709 */
1710 Geom::CDataCache::
1711 ~CDataCache() {
1712 set_result(nullptr, nullptr);
1713 }
1714
1715 /**
1716 *
1717 */
1718 CycleData *Geom::CDataCache::
1719 make_copy() const {
1720 return new CDataCache(*this);
1721 }
1722
1723 /**
1724 * Called when the entry is evicted from the cache, this should clean up the
1725 * owning object appropriately.
1726 */
1727 void Geom::CacheEntry::
1728 evict_callback() {
1729 LightMutexHolder holder(_source->_cache_lock);
1730 Cache::iterator ci = _source->_cache.find(&_key);
1731 nassertv(ci != _source->_cache.end());
1732 nassertv((*ci).second == this);
1733 _source->_cache.erase(ci);
1734 }
1735
1736 /**
1737 *
1738 */
1739 void Geom::CacheEntry::
1740 output(std::ostream &out) const {
1741 out << "geom " << (void *)_source << ", "
1742 << (const void *)_key._modifier;
1743 }
1744
1745
1746 /**
1747 *
1748 */
1749 CycleData *Geom::CData::
1750 make_copy() const {
1751 return new CData(*this);
1752 }
1753
1754 /**
1755 * Writes the contents of this object to the datagram for shipping out to a
1756 * Bam file.
1757 */
1758 void Geom::CData::
1759 write_datagram(BamWriter *manager, Datagram &dg) const {
1760 manager->write_pointer(dg, _data.get_read_pointer());
1761
1762 dg.add_uint16(_primitives.size());
1763 Primitives::const_iterator pi;
1764 for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
1765 manager->write_pointer(dg, (*pi).get_read_pointer());
1766 }
1767
1768 dg.add_uint8(_primitive_type);
1769 dg.add_uint8(_shade_model);
1770
1771 // Actually, we shouldn't bother writing out _geom_rendering; we'll just
1772 // throw it away anyway.
1773 dg.add_uint16(_geom_rendering);
1774
1775 dg.add_uint8(_bounds_type);
1776 }
1777
1778 /**
1779 * Receives an array of pointers, one for each time manager->read_pointer()
1780 * was called in fillin(). Returns the number of pointers processed.
1781 */
1782 int Geom::CData::
1783 complete_pointers(TypedWritable **p_list, BamReader *manager) {
1784 int pi = CycleData::complete_pointers(p_list, manager);
1785
1786 _data = DCAST(GeomVertexData, p_list[pi++]);
1787
1788 Primitives::iterator pri;
1789 for (pri = _primitives.begin(); pri != _primitives.end(); ++pri) {
1790 (*pri) = DCAST(GeomPrimitive, p_list[pi++]);
1791 }
1792
1793 return pi;
1794 }
1795
1796 /**
1797 * This internal function is called by make_from_bam to read in all of the
1798 * relevant data from the BamFile for the new Geom.
1799 */
1800 void Geom::CData::
1801 fillin(DatagramIterator &scan, BamReader *manager) {
1802 manager->read_pointer(scan);
1803
1804 int num_primitives = scan.get_uint16();
1805 _primitives.reserve(num_primitives);
1806 for (int i = 0; i < num_primitives; ++i) {
1807 manager->read_pointer(scan);
1808 _primitives.push_back(nullptr);
1809 }
1810
1811 _primitive_type = (PrimitiveType)scan.get_uint8();
1812 _shade_model = (ShadeModel)scan.get_uint8();
1813
1814 // To be removed: we no longer read _geom_rendering from the bam file;
1815 // instead, we rederive it in finalize().
1816 scan.get_uint16();
1817
1818 _modified = Geom::get_next_modified();
1819
1820 _bounds_type = BoundingVolume::BT_default;
1821 if (manager->get_file_minor_ver() >= 19) {
1822 _bounds_type = (BoundingVolume::BoundsType)scan.get_uint8();
1823 }
1824 }
1825
1826 /**
1827 *
1828 */
1829 bool GeomPipelineReader::
1830 check_valid(const GeomVertexDataPipelineReader *data_reader) const {
1831 Geom::Primitives::const_iterator pi;
1832 for (pi = _cdata->_primitives.begin();
1833 pi != _cdata->_primitives.end();
1834 ++pi) {
1835 GeomPrimitivePipelineReader reader((*pi).get_read_pointer(_current_thread), _current_thread);
1836 reader.check_minmax();
1837 if (!reader.check_valid(data_reader)) {
1838 return false;
1839 }
1840 }
1841
1842 return true;
1843 }
1844
1845 /**
1846 * The implementation of Geom::draw().
1847 */
1848 bool GeomPipelineReader::
1849 draw(GraphicsStateGuardianBase *gsg,
1850 const GeomVertexDataPipelineReader *data_reader, bool force) const {
1851 bool all_ok;
1852 {
1853 PStatTimer timer(Geom::_draw_primitive_setup_pcollector);
1854 all_ok = gsg->begin_draw_primitives(this, data_reader, force);
1855 }
1856 if (all_ok) {
1857 Geom::Primitives::const_iterator pi;
1858 for (pi = _cdata->_primitives.begin();
1859 pi != _cdata->_primitives.end();
1860 ++pi) {
1861 GeomPrimitivePipelineReader reader((*pi).get_read_pointer(_current_thread), _current_thread);
1862 if (reader.get_num_vertices() != 0) {
1863 reader.check_minmax();
1864 nassertr(reader.check_valid(data_reader), false);
1865 if (!reader.draw(gsg, force)) {
1866 all_ok = false;
1867 }
1868 }
1869 }
1870 gsg->end_draw_primitives();
1871 }
1872
1873 return all_ok;
1874 }
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
bamWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
boundingBox.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
boundingSphere.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
BamReader::register_finalize
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
BamReader::read_pointer
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
BamReader::get_file_minor_ver
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
BamReader::read_cdata
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
BamWriter::write_cdata
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
BamWriter::write_pointer
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
BoundingBox
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:29
BoundingSphere
This defines a bounding sphere, consisting of a center and a radius.
Definition: boundingSphere.h:25
BoundingVolume
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Definition: boundingVolume.h:41
ConstPointerToArray
Similar to PointerToArray, except that its contents may not be modified.
Definition: pointerToArray.h:252
CopyOnWriteObject
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
Definition: copyOnWriteObject.h:41
CycleDataReader
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
Definition: cycleDataReader.h:35
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
CycleData
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
CycleData::complete_pointers
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: cycleData.cxx:48
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
DatagramIterator::get_uint8
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: datagramIterator.I:72
DatagramIterator::get_uint16
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
Definition: datagramIterator.I:145
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Datagram::add_uint8
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
Datagram::add_uint16
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
GeomContext
This is a special class object that holds all the information returned by a particular GSG to indicat...
Definition: geomContext.h:34
GeomPipelineReader
Encapsulates the data from a Geom, pre-fetched for one stage of the pipeline.
Definition: geom.h:405
GeomPipelineReader::draw
bool draw(GraphicsStateGuardianBase *gsg, const GeomVertexDataPipelineReader *data_reader, bool force) const
The implementation of Geom::draw().
Definition: geom.cxx:1849
GeomPrimitivePipelineReader
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
Definition: geomPrimitive.h:352
GeomPrimitivePipelineReader::check_minmax
void check_minmax() const
Ensures that the primitive's minmax cache has been computed.
Definition: geomPrimitive.cxx:2267
GeomPrimitivePipelineReader::get_vertex
int get_vertex(int i) const
Returns the ith vertex index in the table.
Definition: geomPrimitive.cxx:2319
GeomPrimitive
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
GeomPrimitive::clear_vertices
void clear_vertices()
Removes all of the vertices and primitives from the object, so they can be re-added.
Definition: geomPrimitive.cxx:405
GeomPrimitive::get_ends
CPTA_int get_ends() const
Returns a const pointer to the primitive ends array so application code can read it directly.
Definition: geomPrimitive.I:314
GeomPrimitive::make_indexed
void make_indexed()
Converts the primitive from nonindexed form to indexed form.
Definition: geomPrimitive.cxx:692
GeomPrimitive::get_num_vertices
get_num_vertices
Returns the number of indices used by all the primitives in this object.
Definition: geomPrimitive.h:99
GeomPrimitive::check_valid
bool check_valid(const GeomVertexData *vertex_data) const
Verifies that the primitive only references vertices that actually exist within the indicated GeomVer...
Definition: geomPrimitive.I:224
GeomPrimitive::is_composite
bool is_composite() const
Returns true if the primitive is a composite primitive such as a tristrip or trifan,...
Definition: geomPrimitive.I:74
GeomPrimitive::set_shade_model
void set_shade_model(ShadeModel shade_model)
Changes the ShadeModel hint for this primitive.
Definition: geomPrimitive.I:36
GeomPrimitive::get_index_type
get_index_type
Returns the numeric type of the index column.
Definition: geomPrimitive.h:85
GeomPrimitive::get_num_unused_vertices_per_primitive
get_num_unused_vertices_per_primitive
Returns the number of vertices that are added between primitives that aren't, strictly speaking,...
Definition: geomPrimitive.h:196
GeomPrimitive::set_index_type
void set_index_type(NumericType index_type)
Changes the numeric type of the index column.
Definition: geomPrimitive.cxx:142
GeomPrimitive::get_num_vertices_per_primitive
get_num_vertices_per_primitive
If the primitive type is a simple type in which all primitives have the same number of vertices,...
Definition: geomPrimitive.h:194
GeomPrimitive::modify_ends
PTA_int modify_ends()
Returns a modifiable pointer to the primitive ends array, so application code can directly fiddle wit...
Definition: geomPrimitive.cxx:1310
GeomPrimitive::clear_minmax
void clear_minmax()
Undoes a previous call to set_minmax(), and allows the minimum and maximum values to be recomputed no...
Definition: geomPrimitive.cxx:1386
GeomPrimitive::is_indexed
bool is_indexed() const
Returns true if the primitive is indexed, false otherwise.
Definition: geomPrimitive.I:86
GeomVertexArrayDataHandle
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
Definition: geomVertexArrayData.h:250
GeomVertexArrayDataHandle::get_write_pointer
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
Definition: geomVertexArrayData.cxx:609
GeomVertexArrayData
This is the data for one array of a GeomVertexData structure.
Definition: geomVertexArrayData.h:58
GeomVertexDataPipelineReader
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Definition: geomVertexData.h:442
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
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
GeomVertexRewriter
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
Definition: geomVertexRewriter.h:33
Geom::CacheEntry
Definition: geom.h:269
Geom::CacheEntry::evict_callback
virtual void evict_callback()
Called when the entry is evicted from the cache, this should clean up the owning object appropriately...
Definition: geom.cxx:1728
Geom
A container for geometry primitives.
Definition: geom.h:54
Geom::decompose_in_place
void decompose_in_place()
Decomposes all of the primitives within this Geom, leaving the results in place.
Definition: geom.cxx:446
Geom::request_resident
bool request_resident() const
Returns true if all the primitive arrays are currently resident in memory.
Definition: geom.cxx:1003
Geom::prepare
void prepare(PreparedGraphicsObjects *prepared_objects)
Indicates that the geom should be enqueued to be prepared in the indicated prepared_objects at the be...
Definition: geom.cxx:1207
Geom::insert_primitive
insert_primitive
Inserts a new GeomPrimitive structure to the Geom object.
Definition: geom.h:101
Geom::release_all
int release_all()
Frees the context allocated on all objects for which the geom has been declared.
Definition: geom.cxx:1248
Geom::reverse_in_place
void reverse_in_place()
Reverses all of the primitives within this Geom, leaving the results in place.
Definition: geom.cxx:520
Geom::draw
bool draw(GraphicsStateGuardianBase *gsg, const GeomVertexData *vertex_data, bool force, Thread *current_thread) const
Actually draws the Geom with the indicated GSG, using the indicated vertex data (which might have bee...
Definition: geom.cxx:1306
Geom::offset_vertices
void offset_vertices(const GeomVertexData *data, int offset)
Replaces a Geom's vertex table with a new table, and simultaneously adds the indicated offset to all ...
Definition: geom.cxx:192
Geom::make_patches_in_place
void make_patches_in_place()
Replaces the GeomPrimitives within this Geom with corresponding GeomPatches.
Definition: geom.cxx:860
Geom::copy_primitives_from
virtual bool copy_primitives_from(const Geom *other)
Copies the primitives from the indicated Geom into this one.
Definition: geom.cxx:941
Geom::clear_primitives
void clear_primitives()
Removes all the primitives from the Geom object (but keeps the same table of vertices).
Definition: geom.cxx:427
Geom::release
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the geom context only on the indicated object, if it exists there.
Definition: geom.cxx:1230
Geom::make_nonindexed
int make_nonindexed(bool composite_only)
Converts the geom from indexed to nonindexed by duplicating vertices as necessary.
Definition: geom.cxx:234
Geom::doubleside_in_place
void doubleside_in_place()
Doublesides all of the primitives within this Geom, leaving the results in place.
Definition: geom.cxx:483
Geom::check_valid
bool check_valid() const
Verifies that the all of the primitives within the geom reference vertices that actually exist within...
Definition: geom.cxx:1061
Geom::remove_primitive
remove_primitive
Removes the ith primitive from the list.
Definition: geom.h:101
Geom::add_primitive
void add_primitive(const GeomPrimitive *primitive)
Inserts a new GeomPrimitive structure to the Geom object.
Definition: geom.I:116
Geom::clear_cache_stage
void clear_cache_stage(Thread *current_thread)
Removes all of the previously-cached results of munge_geom(), at the current pipeline stage and upstr...
Definition: geom.cxx:1186
Geom::prepare_now
GeomContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the geom on the particular GSG, if it does not already exist.
Definition: geom.cxx:1282
Geom::operator=
void operator=(const Geom &copy)
The copy assignment operator is not pipeline-safe.
Definition: geom.cxx:71
Geom::make_points_in_place
void make_points_in_place()
Replaces the GeomPrimitives within this Geom with corresponding GeomPoints.
Definition: geom.cxx:823
Geom::rotate_in_place
void rotate_in_place()
Rotates all of the primitives within this Geom, leaving the results in place.
Definition: geom.cxx:557
Geom::make_lines_in_place
void make_lines_in_place()
Replaces the GeomPrimitives within this Geom with corresponding GeomLines, representing a wireframe o...
Definition: geom.cxx:786
Geom::unify_in_place
void unify_in_place(int max_indices, bool preserve_order)
Unifies all of the primitives contained within this Geom into a single (or as few as possible,...
Definition: geom.cxx:613
Geom::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: geom.cxx:1652
Geom::finalize
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
Definition: geom.cxx:1682
Geom::get_shade_model
get_shade_model
Returns the shade model common to all of the individual GeomPrimitives that have been added to the ge...
Definition: geom.h:75
Geom::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type Geom.
Definition: geom.cxx:1643
Geom::get_next_modified
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition: geom.cxx:1324
Geom::is_prepared
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the geom has already been prepared or enqueued for preparation on the indicated GSG,...
Definition: geom.cxx:1216
Geom::make_adjacency_in_place
void make_adjacency_in_place()
Replaces the GeomPrimitives within this Geom with corresponding versions with adjacency information.
Definition: geom.cxx:899
Geom::get_num_bytes
get_num_bytes
Returns the number of bytes consumed by the geom and its primitives (but not including its vertex tab...
Definition: geom.h:127
Geom::transform_vertices
void transform_vertices(const LMatrix4 &mat)
Applies the indicated transform to all of the vertices in the Geom.
Definition: geom.cxx:1032
Geom::make_copy
virtual Geom * make_copy() const
Returns a newly-allocated Geom that is a shallow copy of this one.
Definition: geom.cxx:100
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
Geom::set_usage_hint
void set_usage_hint(UsageHint usage_hint)
Changes the UsageHint hint for all of the primitives on this Geom to the same value.
Definition: geom.cxx:130
Geom::get_primitive_type
get_primitive_type
Returns the fundamental primitive type that is common to all GeomPrimitives added within the Geom.
Definition: geom.h:74
Geom::get_usage_hint
UsageHint get_usage_hint() const
Returns the minimum (i.e.
Definition: geom.cxx:110
GraphicsStateGuardianBase
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Definition: graphicsStateGuardianBase.h:110
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
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
PreparedGraphicsObjects
A table of objects that are saved within the graphics context for reference by handle later.
Definition: preparedGraphicsObjects.h:58
PreparedGraphicsObjects::release_geom
void release_geom(GeomContext *gc)
Indicates that a geom context, created by a previous call to prepare_geom(), is no longer needed.
Definition: preparedGraphicsObjects.cxx:604
PreparedGraphicsObjects::is_geom_queued
bool is_geom_queued(const Geom *geom) const
Returns true if the geom has been queued on this GSG, false otherwise.
Definition: preparedGraphicsObjects.cxx:557
PreparedGraphicsObjects::prepare_geom_now
GeomContext * prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg)
Immediately creates a new GeomContext for the indicated geom and returns it.
Definition: preparedGraphicsObjects.cxx:681
PreparedGraphicsObjects::dequeue_geom
bool dequeue_geom(Geom *geom)
Removes a geom from the queued list of geoms to be prepared.
Definition: preparedGraphicsObjects.cxx:574
PreparedGraphicsObjects::enqueue_geom
void enqueue_geom(Geom *geom)
Indicates that a geom would like to be put on the list to be prepared when the GSG is next ready to d...
Definition: preparedGraphicsObjects.cxx:547
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
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
TypedWritable::fillin
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
Definition: typedWritable.cxx:103
TypedWritable::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: typedWritable.cxx:54
UpdateSeq
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
pvector< COWPT(GeomPrimitive) >
config_mathutil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomPoints.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
geomVertexRewriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CPT
CPT(GeomVertexData) Geom
Returns a GeomVertexData that represents the results of computing the vertex animation on the CPU for...
Definition: geom.cxx:306
PT
PT(CopyOnWriteObject) Geom
Required to implement CopyOnWriteObject.
Definition: geom.cxx:43
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
graphicsStateGuardianBase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
lightMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
preparedGraphicsObjects.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.