Panda3D
Loading...
Searching...
No Matches
geomPrimitive.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 geomPrimitive.cxx
10 * @author drose
11 * @date 2005-03-06
12 */
13
14#include "geomPrimitive.h"
15#include "geom.h"
16#include "geomPatches.h"
17#include "geomVertexData.h"
19#include "geomVertexColumn.h"
20#include "geomVertexReader.h"
21#include "geomVertexWriter.h"
22#include "geomVertexRewriter.h"
23#include "geomPoints.h"
24#include "geomLines.h"
25#include "geomTriangles.h"
27#include "internalName.h"
28#include "bamReader.h"
29#include "bamWriter.h"
30#include "ioPtaDatagramInt.h"
31#include "indent.h"
32#include "pStatTimer.h"
33
34using std::max;
35using std::min;
36
37TypeHandle GeomPrimitive::_type_handle;
38TypeHandle GeomPrimitive::CData::_type_handle;
39TypeHandle GeomPrimitivePipelineReader::_type_handle;
40
41PStatCollector GeomPrimitive::_decompose_pcollector("*:Munge:Decompose");
42PStatCollector GeomPrimitive::_doubleside_pcollector("*:Munge:Doubleside");
43PStatCollector GeomPrimitive::_reverse_pcollector("*:Munge:Reverse");
44PStatCollector GeomPrimitive::_rotate_pcollector("*:Munge:Rotate");
45
46/**
47 * Constructs an invalid object. Only used when reading from bam.
48 */
49GeomPrimitive::
50GeomPrimitive() {
51}
52
53/**
54 * Required to implement CopyOnWriteObject.
55 */
56PT(CopyOnWriteObject) GeomPrimitive::
57make_cow_copy() {
58 return make_copy();
59}
60
61/**
62 *
63 */
64GeomPrimitive::
65GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
66 CDWriter cdata(_cycler, true);
67 cdata->_usage_hint = usage_hint;
68}
69
70/**
71 *
72 */
73GeomPrimitive::
74GeomPrimitive(const GeomPrimitive &copy) :
76 _cycler(copy._cycler)
77{
78}
79
80/**
81 * The copy assignment operator is not pipeline-safe. This will completely
82 * obliterate all stages of the pipeline, so don't do it for a GeomPrimitive
83 * that is actively being used for rendering.
84 */
86operator = (const GeomPrimitive &copy) {
87 CopyOnWriteObject::operator = (copy);
88 _cycler = copy._cycler;
89}
90
91/**
92 *
93 */
94GeomPrimitive::
95~GeomPrimitive() {
97}
98
99/**
100 * Returns the set of GeomRendering bits that represent the rendering
101 * properties required to properly render this primitive.
102 */
104get_geom_rendering() const {
105 if (is_indexed()) {
106 return GR_indexed_other;
107 } else {
108 return 0;
109 }
110}
111
112/**
113 * Changes the UsageHint hint for this primitive. See get_usage_hint().
114 *
115 * Don't call this in a downstream thread unless you don't mind it blowing
116 * away other changes you might have recently made in an upstream thread.
117 */
119set_usage_hint(GeomPrimitive::UsageHint usage_hint) {
120 CDWriter cdata(_cycler, true);
121 cdata->_usage_hint = usage_hint;
122
123 if (!cdata->_vertices.is_null()) {
124 cdata->_modified = Geom::get_next_modified();
125 cdata->_usage_hint = usage_hint;
126 }
127}
128
129/**
130 * Changes the numeric type of the index column. Normally, this should be
131 * either NT_uint16 or NT_uint32.
132 *
133 * The index type must be large enough to include all of the index values in
134 * the primitive. It may be automatically elevated, if necessary, to a larger
135 * index type, by a subsequent call to add_index() that names an index value
136 * that does not fit in the index type you specify.
137 *
138 * Don't call this in a downstream thread unless you don't mind it blowing
139 * away other changes you might have recently made in an upstream thread.
140 */
142set_index_type(GeomPrimitive::NumericType index_type) {
143 nassertv(get_max_vertex() <= get_highest_index_value(index_type));
144
145 CDWriter cdata(_cycler, true);
146 if (cdata->_index_type != index_type) {
147 do_set_index_type(cdata, index_type);
148 }
149}
150
151/**
152 * Adds the indicated vertex to the list of vertex indices used by the
153 * graphics primitive type. To define a primitive, you must call add_vertex()
154 * for each vertex of the new primitive, and then call close_primitive() after
155 * you have specified the last vertex of each primitive.
156 *
157 * Don't call this in a downstream thread unless you don't mind it blowing
158 * away other changes you might have recently made in an upstream thread.
159 */
161add_vertex(int vertex) {
162 CDWriter cdata(_cycler, true);
163
164 if (gobj_cat.is_spam()) {
165 gobj_cat.spam()
166 << this << ".add_vertex(" << vertex << ")\n";
167 }
168
169 consider_elevate_index_type(cdata, vertex);
170
171 if (requires_unused_vertices()) {
172 int num_primitives = get_num_primitives();
173 if (num_primitives > 0 &&
174 get_num_vertices() == get_primitive_end(num_primitives - 1)) {
175 // If we are beginning a new primitive, give the derived class a chance to
176 // insert some degenerate vertices.
177 if (cdata->_vertices.is_null()) {
178 do_make_indexed(cdata);
179 }
180 append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex);
181 }
182 }
183
184 if (cdata->_vertices.is_null()) {
185 // The nonindexed case. We can keep the primitive nonindexed only if the
186 // vertex number happens to be the next available vertex.
187 nassertv(cdata->_num_vertices != -1);
188 if (cdata->_num_vertices == 0) {
189 cdata->_first_vertex = vertex;
190 cdata->_num_vertices = 1;
191 cdata->_modified = Geom::get_next_modified();
192 cdata->_got_minmax = false;
193 return;
194
195 } else if (vertex == cdata->_first_vertex + cdata->_num_vertices) {
196 ++cdata->_num_vertices;
197 cdata->_modified = Geom::get_next_modified();
198 cdata->_got_minmax = false;
199 return;
200 }
201
202 // Otherwise, we need to suddenly become an indexed primitive.
203 do_make_indexed(cdata);
204 }
205
206 {
207 GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(),
209 int num_rows = handle.get_num_rows();
210 handle.set_num_rows(num_rows + 1);
211
212 unsigned char *ptr = handle.get_write_pointer();
213 switch (cdata->_index_type) {
214 case GeomEnums::NT_uint8:
215 ((uint8_t *)ptr)[num_rows] = vertex;
216 break;
217 case GeomEnums::NT_uint16:
218 ((uint16_t *)ptr)[num_rows] = vertex;
219 break;
220 case GeomEnums::NT_uint32:
221 ((uint32_t *)ptr)[num_rows] = vertex;
222 break;
223 default:
224 nassert_raise("unsupported index type");
225 break;
226 }
227 }
228
229 cdata->_modified = Geom::get_next_modified();
230 cdata->_got_minmax = false;
231}
232
233/**
234 * Adds a consecutive sequence of vertices, beginning at start, to the
235 * primitive.
236 *
237 * Don't call this in a downstream thread unless you don't mind it blowing
238 * away other changes you might have recently made in an upstream thread.
239 */
241add_consecutive_vertices(int start, int num_vertices) {
242 if (num_vertices == 0) {
243 return;
244 }
245 int end = (start + num_vertices) - 1;
246
247 CDWriter cdata(_cycler, true);
248
249 consider_elevate_index_type(cdata, end);
250
251 int num_primitives = get_num_primitives();
252 if (num_primitives > 0 &&
253 get_num_vertices() == get_primitive_end(num_primitives - 1)) {
254 // If we are beginning a new primitive, give the derived class a chance to
255 // insert some degenerate vertices.
256 if (cdata->_vertices.is_null()) {
257 do_make_indexed(cdata);
258 }
259 append_unused_vertices(cdata->_vertices.get_write_pointer(), start);
260 }
261
262 if (cdata->_vertices.is_null()) {
263 // The nonindexed case. We can keep the primitive nonindexed only if the
264 // vertex number happens to be the next available vertex.
265 nassertv(cdata->_num_vertices != -1);
266 if (cdata->_num_vertices == 0) {
267 cdata->_first_vertex = start;
268 cdata->_num_vertices = num_vertices;
269 cdata->_modified = Geom::get_next_modified();
270 cdata->_got_minmax = false;
271 return;
272
273 } else if (start == cdata->_first_vertex + cdata->_num_vertices) {
274 cdata->_num_vertices += num_vertices;
275 cdata->_modified = Geom::get_next_modified();
276 cdata->_got_minmax = false;
277 return;
278 }
279
280 // Otherwise, we need to suddenly become an indexed primitive.
281 do_make_indexed(cdata);
282 }
283
284 PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer();
285 int old_num_rows = array_obj->get_num_rows();
286 array_obj->set_num_rows(old_num_rows + num_vertices);
287
288 GeomVertexWriter index(array_obj, 0);
289 index.set_row_unsafe(old_num_rows);
290
291 for (int v = start; v <= end; ++v) {
292 index.set_data1i(v);
293 }
294
295 cdata->_modified = Geom::get_next_modified();
296 cdata->_got_minmax = false;
297}
298
299/**
300 * Adds the next n vertices in sequence, beginning from the last vertex added
301 * to the primitive + 1.
302 *
303 * This is most useful when you are building up a primitive and a
304 * GeomVertexData at the same time, and you just want the primitive to
305 * reference the first n vertices from the data, then the next n, and so on.
306 */
308add_next_vertices(int num_vertices) {
309 if (get_num_vertices() == 0) {
310 add_consecutive_vertices(0, num_vertices);
311 } else {
312 add_consecutive_vertices(get_vertex(get_num_vertices() - 1) + 1, num_vertices);
313 }
314}
315
316/**
317 * This ensures that enough memory space for n vertices is allocated, so that
318 * you may increase the number of vertices to n without causing a new memory
319 * allocation. This is a performance optimization only; it is especially
320 * useful when you know ahead of time that you will be adding n vertices to
321 * the primitive.
322 *
323 * Note that the total you specify here should also include implicit vertices
324 * which may be added at each close_primitive() call, according to
325 * get_num_unused_vertices_per_primitive().
326 *
327 * Note also that making this call will implicitly make the primitive indexed
328 * if it is not already, which could result in a performance *penalty*. If
329 * you would prefer not to lose the nonindexed nature of your existing
330 * GeomPrimitives, check is_indexed() before making this call.
331 */
333reserve_num_vertices(int num_vertices) {
334 if (gobj_cat.is_debug()) {
335 gobj_cat.debug()
336 << this << ".reserve_num_vertices(" << num_vertices << ")\n";
337 }
338
339 CDWriter cdata(_cycler, true);
340 consider_elevate_index_type(cdata, num_vertices);
341 do_make_indexed(cdata);
342 PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer();
343 array_obj->reserve_num_rows(num_vertices);
344}
345
346/**
347 * Indicates that the previous n calls to add_vertex(), since the last call to
348 * close_primitive(), have fully defined a new primitive. Returns true if
349 * successful, false otherwise.
350 *
351 * Don't call this in a downstream thread unless you don't mind it blowing
352 * away other changes you might have recently made in an upstream thread.
353 */
356 int num_vertices_per_primitive = get_num_vertices_per_primitive();
357
358 CDWriter cdata(_cycler, true);
359 if (num_vertices_per_primitive == 0) {
360 // This is a complex primitive type like a triangle strip: each primitive
361 // uses a different number of vertices.
362#ifndef NDEBUG
363 int num_added;
364 if (cdata->_ends.empty()) {
365 num_added = get_num_vertices();
366 } else {
367 num_added = get_num_vertices() - cdata->_ends.back();
369 }
370 nassertr(num_added >= get_min_num_vertices_per_primitive(), false);
371#endif
372 if (cdata->_ends.get_ref_count() > 1) {
373 PTA_int new_ends;
374 new_ends.v() = cdata->_ends.v();
375 cdata->_ends = new_ends;
376 }
377 cdata->_ends.push_back(get_num_vertices());
378
379 } else {
380#ifndef NDEBUG
381 // This is a simple primitive type like a triangle: each primitive uses
382 // the same number of vertices. Assert that we added the correct number
383 // of vertices.
384 int num_vertices_per_primitive = get_num_vertices_per_primitive();
385 int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
386
387 int num_vertices = get_num_vertices();
388 nassertr((num_vertices + num_unused_vertices_per_primitive) % (num_vertices_per_primitive + num_unused_vertices_per_primitive) == 0, false)
389#endif
390 }
391
392 cdata->_modified = Geom::get_next_modified();
393
394 return true;
395}
396
397/**
398 * Removes all of the vertices and primitives from the object, so they can be
399 * re-added.
400 *
401 * Don't call this in a downstream thread unless you don't mind it blowing
402 * away other changes you might have recently made in an upstream thread.
403 */
406 CDWriter cdata(_cycler, true);
407 cdata->_first_vertex = 0;
408 cdata->_num_vertices = 0;
409
410 // Since we might have automatically elevated the index type by adding
411 // vertices, we should automatically lower it again when we call
412 // clear_vertices().
413 cdata->_index_type = NT_uint16;
414
415 cdata->_vertices.clear();
416 cdata->_ends.clear();
417 cdata->_mins.clear();
418 cdata->_maxs.clear();
419 cdata->_modified = Geom::get_next_modified();
420 cdata->_got_minmax = false;
421}
422
423/**
424 * Adds the indicated offset to all vertices used by the primitive.
425 *
426 * Don't call this in a downstream thread unless you don't mind it blowing
427 * away other changes you might have recently made in an upstream thread.
428 */
430offset_vertices(int offset) {
431 if (offset == 0) {
432 return;
433 }
434
435 if (is_indexed()) {
436 CDWriter cdata(_cycler, true);
437
438 if (!cdata->_got_minmax) {
439 recompute_minmax(cdata);
440 nassertv(cdata->_got_minmax);
441 }
442
443 consider_elevate_index_type(cdata, cdata->_max_vertex + offset);
444
445 {
446 GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(),
448
449 size_t num_rows = (size_t)handle.get_num_rows();
450 unsigned char *ptr = handle.get_write_pointer();
451 switch (cdata->_index_type) {
452 case GeomEnums::NT_uint8:
453 for (size_t i = 0; i < num_rows; ++i) {
454 uint8_t &v = ((uint8_t *)ptr)[i];
455 if (v != 0xff) {
456 v += offset;
457 }
458 }
459 break;
460
461 case GeomEnums::NT_uint16:
462 for (size_t i = 0; i < num_rows; ++i) {
463 uint16_t &v = ((uint16_t *)ptr)[i];
464 if (v != 0xffff) {
465 v += offset;
466 }
467 }
468 break;
469
470 case GeomEnums::NT_uint32:
471 for (size_t i = 0; i < num_rows; ++i) {
472 uint32_t &v = ((uint32_t *)ptr)[i];
473 if (v != 0xffffffff) {
474 v += offset;
475 }
476 }
477 break;
478
479 default:
480 nassert_raise("unsupported index type");
481 break;
482 }
483 }
484
485 cdata->_modified = Geom::get_next_modified();
486 cdata->_got_minmax = false;
487 }
488 else {
489 CDWriter cdata(_cycler, true);
490
491 cdata->_first_vertex += offset;
492 cdata->_modified = Geom::get_next_modified();
493 cdata->_got_minmax = false;
494
495 consider_elevate_index_type(cdata,
496 cdata->_first_vertex + cdata->_num_vertices - 1);
497 }
498}
499
500/**
501 * Adds the indicated offset to the indicated segment of vertices used by the
502 * primitive. Unlike the other version of offset_vertices, this makes the
503 * geometry indexed if it isn't already.
504 *
505 * Note that end_row indicates one past the last row that should be offset.
506 * In other words, the number of vertices touched is (end_row - begin_row).
507 *
508 * Don't call this in a downstream thread unless you don't mind it blowing
509 * away other changes you might have recently made in an upstream thread.
510 */
512offset_vertices(int offset, int begin_row, int end_row) {
513 if (offset == 0 || end_row <= begin_row) {
514 return;
515 }
516
517 nassertv(begin_row >= 0 && end_row >= 0);
518 nassertv(end_row <= get_num_vertices());
519
520 if (!is_indexed() && (begin_row > 0 || end_row < get_num_vertices())) {
521 // Make it indexed unless the whole array was specified.
522 make_indexed();
523 }
524
525 if (is_indexed()) {
526 CDWriter cdata(_cycler, true);
527
528 int strip_cut_index = get_strip_cut_index(cdata->_index_type);
529
530 // Calculate the maximum vertex over our range.
531 int max_vertex = 0;
532 {
533 GeomVertexReader index_r(cdata->_vertices.get_read_pointer(), 0);
534 index_r.set_row_unsafe(begin_row);
535 for (int j = begin_row; j < end_row; ++j) {
536 int vertex = index_r.get_data1i();
537 if (vertex != strip_cut_index) {
538 max_vertex = max(max_vertex, vertex);
539 }
540 }
541 }
542
543 consider_elevate_index_type(cdata, max_vertex + offset);
544
545 {
546 GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(),
548
549 unsigned char *ptr = handle.get_write_pointer();
550 switch (cdata->_index_type) {
551 case GeomEnums::NT_uint8:
552 for (int i = begin_row; i < end_row; ++i) {
553 uint8_t &v = ((uint8_t *)ptr)[i];
554 if (v != 0xff) {
555 v += offset;
556 }
557 }
558 break;
559
560 case GeomEnums::NT_uint16:
561 for (int i = begin_row; i < end_row; ++i) {
562 uint16_t &v = ((uint16_t *)ptr)[i];
563 if (v != 0xffff) {
564 v += offset;
565 }
566 }
567 break;
568
569 case GeomEnums::NT_uint32:
570 for (int i = begin_row; i < end_row; ++i) {
571 uint32_t &v = ((uint32_t *)ptr)[i];
572 if (v != 0xffffffff) {
573 v += offset;
574 }
575 }
576 break;
577
578 default:
579 nassert_raise("unsupported index type");
580 break;
581 }
582 }
583
584 cdata->_modified = Geom::get_next_modified();
585 cdata->_got_minmax = false;
586 }
587 else {
588 // The supplied values cover all vertices, so we don't need to make it
589 // indexed.
590 CDWriter cdata(_cycler, true);
591
592 cdata->_first_vertex += offset;
593 cdata->_modified = Geom::get_next_modified();
594 cdata->_got_minmax = false;
595
596 consider_elevate_index_type(cdata,
597 cdata->_first_vertex + cdata->_num_vertices - 1);
598 }
599}
600
601/**
602 * Converts the primitive from indexed to nonindexed by duplicating vertices
603 * as necessary into the indicated dest GeomVertexData. Note: does not
604 * support primitives with strip cut indices.
605 */
607make_nonindexed(GeomVertexData *dest, const GeomVertexData *source) {
608 Thread *current_thread = Thread::get_current_thread();
609
610 int num_vertices, dest_start;
611 {
612 GeomPrimitivePipelineReader reader(this, current_thread);
613 num_vertices = reader.get_num_vertices();
614 int strip_cut_index = reader.get_strip_cut_index();
615
616 GeomVertexDataPipelineWriter data_writer(dest, false, current_thread);
617 data_writer.check_array_writers();
618 dest_start = data_writer.get_num_rows();
619 data_writer.set_num_rows(dest_start + num_vertices);
620
621 GeomVertexDataPipelineReader data_reader(source, current_thread);
622 data_reader.check_array_readers();
623
624 for (int i = 0; i < num_vertices; ++i) {
625 int v = reader.get_vertex(i);
626 nassertd(v != strip_cut_index) continue;
627 data_writer.copy_row_from(dest_start + i, data_reader, v);
628 }
629 }
630
631 set_nonindexed_vertices(dest_start, num_vertices);
632}
633
634/**
635 * Packs the vertices used by the primitive from the indicated source array
636 * onto the end of the indicated destination array.
637 */
639pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
640 Thread *current_thread = Thread::get_current_thread();
641 if (!is_indexed()) {
642 // If the primitive is nonindexed, packing is the same as converting
643 // (again) to nonindexed.
644 make_nonindexed(dest, source);
645
646 } else {
647 // The indexed case: build up a new index as we go.
648 CPT(GeomVertexArrayData) orig_vertices = get_vertices();
649 PT(GeomVertexArrayData) new_vertices = make_index_data();
650 GeomVertexWriter index(new_vertices, 0);
651 typedef pmap<int, int> CopiedIndices;
652 CopiedIndices copied_indices;
653
654 int num_vertices = get_num_vertices();
655 int dest_start = dest->get_num_rows();
656 int strip_cut_index = get_strip_cut_index();
657
658 for (int i = 0; i < num_vertices; ++i) {
659 int v = get_vertex(i);
660 if (v == strip_cut_index) {
661 continue;
662 }
663
664 // Try to add the relation { v : size() }. If that succeeds, great; if
665 // it doesn't, look up whatever we previously added for v.
666 std::pair<CopiedIndices::iterator, bool> result =
667 copied_indices.insert(CopiedIndices::value_type(v, (int)copied_indices.size()));
668 int v2 = (*result.first).second + dest_start;
669 index.add_data1i(v2);
670
671 if (result.second) {
672 // This is the first time we've seen vertex v.
673 dest->copy_row_from(v2, source, v, current_thread);
674 }
675 }
676
677 set_vertices(new_vertices);
678 }
679}
680
681/**
682 * Converts the primitive from nonindexed form to indexed form. This will
683 * simply create an index table that is numbered consecutively from
684 * get_first_vertex(); it does not automatically collapse together identical
685 * vertices that may have been split apart by a previous call to
686 * make_nonindexed().
687 *
688 * Don't call this in a downstream thread unless you don't mind it blowing
689 * away other changes you might have recently made in an upstream thread.
690 */
692make_indexed() {
693 CDWriter cdata(_cycler, true);
694 do_make_indexed(cdata);
695}
696
697/**
698 * Returns the element within the _vertices list at which the nth primitive
699 * starts.
700 *
701 * If i is one more than the highest valid primitive vertex, the return value
702 * will be one more than the last valid vertex. Thus, it is generally true
703 * that the vertices used by a particular primitive i are the set
704 * get_primitive_start(n) <= vi < get_primitive_start(n + 1) (although this
705 * range also includes the unused vertices between primitives).
706 */
708get_primitive_start(int n) const {
709 int num_vertices_per_primitive = get_num_vertices_per_primitive();
710 int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
711
712 if (num_vertices_per_primitive == 0) {
713 // This is a complex primitive type like a triangle strip: each primitive
714 // uses a different number of vertices.
715 CDReader cdata(_cycler);
716 nassertr(n >= 0 && n <= (int)cdata->_ends.size(), -1);
717 if (n == 0) {
718 return 0;
719 } else {
720 return cdata->_ends[n - 1] + num_unused_vertices_per_primitive;
721 }
722
723 } else {
724 // This is a simple primitive type like a triangle: each primitive uses
725 // the same number of vertices.
726 return n * (num_vertices_per_primitive + num_unused_vertices_per_primitive);
727 }
728}
729
730/**
731 * Returns the element within the _vertices list at which the nth primitive
732 * ends. This is one past the last valid element for the nth primitive.
733 */
735get_primitive_end(int n) const {
736 int num_vertices_per_primitive = get_num_vertices_per_primitive();
737
738 if (num_vertices_per_primitive == 0) {
739 // This is a complex primitive type like a triangle strip: each primitive
740 // uses a different number of vertices.
741 CDReader cdata(_cycler);
742 nassertr(n >= 0 && n < (int)cdata->_ends.size(), -1);
743 return cdata->_ends[n];
744
745 } else {
746 // This is a simple primitive type like a triangle: each primitive uses
747 // the same number of vertices.
748 int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
749 return n * (num_vertices_per_primitive + num_unused_vertices_per_primitive) + num_vertices_per_primitive;
750 }
751}
752
753/**
754 * Returns the number of vertices used by the nth primitive. This is the same
755 * thing as get_primitive_end(n) - get_primitive_start(n).
756 */
758get_primitive_num_vertices(int n) const {
759 int num_vertices_per_primitive = get_num_vertices_per_primitive();
760
761 if (num_vertices_per_primitive == 0) {
762 // This is a complex primitive type like a triangle strip: each primitive
763 // uses a different number of vertices.
764 CDReader cdata(_cycler);
765 nassertr(n >= 0 && n < (int)cdata->_ends.size(), 0);
766 if (n == 0) {
767 return cdata->_ends[0];
768 } else {
769 int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
770 return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive;
771 }
772
773 } else {
774 // This is a simple primitive type like a triangle: each primitive uses
775 // the same number of vertices.
776 return num_vertices_per_primitive;
777 }
778}
779
780/**
781 * Returns the number of vertices used by all of the primitives. This is the
782 * same as summing get_primitive_num_vertices(n) for n in
783 * get_num_primitives(). It is like get_num_vertices except that it excludes
784 * all of the degenerate vertices and strip-cut indices.
785 */
787get_num_used_vertices() const {
788 int num_primitives = get_num_primitives();
789
790 if (num_primitives > 0) {
791 return get_num_vertices() - ((num_primitives - 1) *
793 } else {
794 return 0;
795 }
796}
797
798/**
799 * Returns the minimum vertex index number used by the nth primitive in this
800 * object.
801 */
803get_primitive_min_vertex(int n) const {
804 if (is_indexed()) {
805 CPT(GeomVertexArrayData) mins = get_mins();
806 nassertr(n >= 0 && n < mins->get_num_rows(), -1);
807
808 GeomVertexReader index(mins, 0);
809 index.set_row_unsafe(n);
810 return index.get_data1i();
811 } else {
812 return get_primitive_start(n);
813 }
814}
815
816/**
817 * Returns the maximum vertex index number used by the nth primitive in this
818 * object.
819 */
821get_primitive_max_vertex(int n) const {
822 if (is_indexed()) {
823 CPT(GeomVertexArrayData) maxs = get_maxs();
824 nassertr(n >= 0 && n < maxs->get_num_rows(), -1);
825
826 GeomVertexReader index(maxs, 0);
827 index.set_row_unsafe(n);
828 return index.get_data1i();
829 } else {
830 return get_primitive_end(n) - 1;
831 }
832}
833
834/**
835 * Decomposes a complex primitive type into a simpler primitive type, for
836 * instance triangle strips to triangles, and returns a pointer to the new
837 * primitive definition. If the decomposition cannot be performed, this might
838 * return the original object.
839 *
840 * This method is useful for application code that wants to iterate through
841 * the set of triangles on the primitive without having to write handlers for
842 * each possible kind of primitive type.
843 */
844CPT(GeomPrimitive) GeomPrimitive::
845decompose() const {
846 if (gobj_cat.is_debug()) {
847 gobj_cat.debug()
848 << "Decomposing " << get_type() << ": " << (void *)this << "\n";
849 }
850
851 PStatTimer timer(_decompose_pcollector);
852 return decompose_impl();
853}
854
855/**
856 * Returns a new primitive with the shade_model reversed (if it is flat
857 * shaded), if possible. If the primitive type cannot be rotated, returns the
858 * original primitive, unrotated.
859 *
860 * If the current shade_model indicates flat_vertex_last, this should bring
861 * the last vertex to the first position; if it indicates flat_vertex_first,
862 * this should bring the first vertex to the last position.
863 */
864CPT(GeomPrimitive) GeomPrimitive::
865rotate() const {
866 if (gobj_cat.is_debug()) {
867 gobj_cat.debug()
868 << "Rotating " << get_type() << ": " << (void *)this << "\n";
869 }
870
871 PStatTimer timer(_rotate_pcollector);
872 CPT(GeomVertexArrayData) rotated_vertices = rotate_impl();
873
874 if (rotated_vertices == nullptr) {
875 // This primitive type can't be rotated.
876 return this;
877 }
878
879 PT(GeomPrimitive) new_prim = make_copy();
880 new_prim->set_vertices(rotated_vertices);
881
882 switch (get_shade_model()) {
883 case SM_flat_first_vertex:
884 new_prim->set_shade_model(SM_flat_last_vertex);
885 break;
886
887 case SM_flat_last_vertex:
888 new_prim->set_shade_model(SM_flat_first_vertex);
889 break;
890
891 default:
892 break;
893 }
894
895 return new_prim;
896}
897
898/**
899 * Duplicates triangles in the primitive so that each triangle is back-to-back
900 * with another triangle facing in the opposite direction. Note that this
901 * doesn't affect vertex normals, so this operation alone won't work in the
902 * presence of lighting (but see SceneGraphReducer::doubleside()).
903 *
904 * Also see CullFaceAttrib, which can enable rendering of both sides of a
905 * triangle without having to duplicate it (but which doesn't necessarily work
906 * in the presence of lighting).
907 */
908CPT(GeomPrimitive) GeomPrimitive::
909doubleside() const {
910 if (gobj_cat.is_debug()) {
911 gobj_cat.debug()
912 << "Doublesiding " << get_type() << ": " << (void *)this << "\n";
913 }
914
915 PStatTimer timer(_doubleside_pcollector);
916 return doubleside_impl();
917}
918
919/**
920 * Reverses the winding order in the primitive so that each triangle is facing
921 * in the opposite direction it was originally. Note that this doesn't affect
922 * vertex normals, so this operation alone won't work in the presence of
923 * lighting (but see SceneGraphReducer::reverse()).
924 *
925 * Also see CullFaceAttrib, which can change the visible direction of a
926 * triangle without having to duplicate it (but which doesn't necessarily work
927 * in the presence of lighting).
928 */
929CPT(GeomPrimitive) GeomPrimitive::
930reverse() const {
931 if (gobj_cat.is_debug()) {
932 gobj_cat.debug()
933 << "Reversing " << get_type() << ": " << (void *)this << "\n";
934 }
935
936 PStatTimer timer(_reverse_pcollector);
937 return reverse_impl();
938}
939
940/**
941 * Returns a new primitive that is compatible with the indicated shade model,
942 * if possible, or NULL if this is not possible.
943 *
944 * In most cases, this will return either NULL or the original primitive. In
945 * the case of a SM_flat_first_vertex vs. a SM_flat_last_vertex (or vice-
946 * versa), however, it will return a rotated primitive.
947 */
948CPT(GeomPrimitive) GeomPrimitive::
949match_shade_model(GeomPrimitive::ShadeModel shade_model) const {
950 ShadeModel this_shade_model = get_shade_model();
951 if (this_shade_model == shade_model) {
952 // Trivially compatible.
953 return this;
954 }
955
956 if (this_shade_model == SM_uniform || shade_model == SM_uniform) {
957 // SM_uniform is compatible with anything.
958 return this;
959 }
960
961 if ((this_shade_model == SM_flat_first_vertex && shade_model == SM_flat_last_vertex) ||
962 (this_shade_model == SM_flat_last_vertex && shade_model == SM_flat_first_vertex)) {
963 // Needs to be rotated.
964 CPT(GeomPrimitive) rotated = rotate();
965 if (rotated.p() == this) {
966 // Oops, can't be rotated, sorry.
967 return nullptr;
968 }
969 return rotated;
970 }
971
972 // Not compatible, sorry.
973 return nullptr;
974}
975
976/**
977 * Returns a new GeomPoints primitive that represents each of the vertices in
978 * the original primitive, rendered exactly once. If the original primitive
979 * is already a GeomPoints primitive, returns the original primitive
980 * unchanged.
981 */
982CPT(GeomPrimitive) GeomPrimitive::
983make_points() const {
984 if (is_exact_type(GeomPoints::get_class_type())) {
985 return this;
986 }
987
988 // First, get a list of all of the vertices referenced by the original
989 // primitive.
990 BitArray bits;
991 {
993 reader.get_referenced_vertices(bits);
994 }
995
996 // Now construct a new index array with just those bits.
997 PT(GeomVertexArrayData) new_vertices = make_index_data();
998 new_vertices->unclean_set_num_rows(bits.get_num_on_bits());
999
1000 GeomVertexWriter new_index(new_vertices, 0);
1001 int p = bits.get_lowest_on_bit();
1002 while (p != -1) {
1003 while (bits.get_bit(p)) {
1004 new_index.set_data1i(p);
1005 ++p;
1006 }
1007 int q = bits.get_next_higher_different_bit(p);
1008 if (q == p) {
1009 break;
1010 }
1011 p = q;
1012 }
1013
1014 PT(GeomPrimitive) points = new GeomPoints(UH_dynamic);
1015 points->set_vertices(new_vertices);
1016
1017 return points;
1018}
1019
1020/**
1021 * Returns a new GeomLines primitive that represents each of the edges in the
1022 * original primitive rendered as a line. If the original primitive is
1023 * already a GeomLines primitive, returns the original primitive unchanged.
1024 */
1025CPT(GeomPrimitive) GeomPrimitive::
1026make_lines() const {
1027 if (is_exact_type(GeomLines::get_class_type())) {
1028 return this;
1029 }
1030
1031 PrimitiveType prim_type = get_primitive_type();
1032 if (prim_type == PT_lines) {
1033 // It's a line strip, just decompose it.
1034 return decompose();
1035
1036 } else if (prim_type != PT_polygons && prim_type != PT_patches) {
1037 // Don't know how to represent this in wireframe.
1038 return this;
1039 }
1040
1041 if (prim_type == PT_polygons && !is_exact_type(GeomTriangles::get_class_type())) {
1042 // Decompose tristrips. We could probably make this more efficient by
1043 // making a specific implementation of make_lines for GeomTristrips.
1044 return decompose()->make_lines();
1045 }
1046
1047 // Iterate through the primitives.
1048 int num_primitives = get_num_primitives();
1049 int verts_per_prim = get_num_vertices_per_primitive();
1050
1051 PT(GeomVertexArrayData) new_vertices = make_index_data();
1052 new_vertices->unclean_set_num_rows(num_primitives * verts_per_prim * 2);
1053
1054 GeomVertexWriter new_index(new_vertices, 0);
1055
1056 for (int i = 0; i < num_primitives; ++i) {
1057 int begin = get_primitive_start(i);
1058 int end = get_primitive_end(i);
1059 if (begin == end) {
1060 continue;
1061 }
1062 for (int vi = begin; vi < end - 1; vi++) {
1063 new_index.set_data1i(get_vertex(vi));
1064 new_index.set_data1i(get_vertex(vi + 1));
1065 }
1066 new_index.set_data1i(get_vertex(end - 1));
1067 new_index.set_data1i(get_vertex(begin));
1068 }
1069
1070 PT(GeomPrimitive) lines = new GeomLines(UH_dynamic);
1071 lines->set_vertices(new_vertices);
1072
1073 return lines;
1074}
1075
1076/**
1077 * Decomposes a complex primitive type into a simpler primitive type, for
1078 * instance triangle strips to triangles, puts these in a new GeomPatches
1079 * object and returns a pointer to the new primitive definition. If the
1080 * decomposition cannot be performed, this might return the original object.
1081 *
1082 * This method is useful for application code that wants to use tesselation
1083 * shaders on arbitrary geometry.
1084 */
1085CPT(GeomPrimitive) GeomPrimitive::
1086make_patches() const {
1087 if (is_exact_type(GeomPatches::get_class_type())) {
1088 return this;
1089 }
1090
1091 CPT(GeomPrimitive) prim = decompose_impl();
1092 int num_vertices_per_patch = prim->get_num_vertices_per_primitive();
1093
1094 PT(GeomPrimitive) patches = new GeomPatches(num_vertices_per_patch, get_usage_hint());
1095
1096 if (prim->is_indexed()) {
1097 patches->set_vertices(prim->get_vertices());
1098 } else {
1099 patches->set_nonindexed_vertices(prim->get_first_vertex(),
1100 prim->get_num_vertices());
1101 }
1102
1103 return patches;
1104}
1105
1106/**
1107 * Adds adjacency information to this primitive. May return null if this type
1108 * of geometry does not support adjacency information.
1109 *
1110 * @since 1.10.0
1111 */
1112CPT(GeomPrimitive) GeomPrimitive::
1113make_adjacency() const {
1114 return nullptr;
1115}
1116
1117/**
1118 * Returns the number of bytes consumed by the primitive and its index
1119 * table(s).
1120 */
1121int GeomPrimitive::
1122get_num_bytes() const {
1123 CDReader cdata(_cycler);
1124 int num_bytes = cdata->_ends.size() * sizeof(int) + sizeof(GeomPrimitive);
1125 if (!cdata->_vertices.is_null()) {
1126 num_bytes += cdata->_vertices.get_read_pointer()->get_data_size_bytes();
1127 }
1128
1129 return num_bytes;
1130}
1131
1132/**
1133 * Returns true if the primitive data is currently resident in memory. If
1134 * this returns false, the primitive data will be brought back into memory
1135 * shortly; try again later.
1136 */
1137bool GeomPrimitive::
1138request_resident(Thread *current_thread) const {
1139 CDReader cdata(_cycler, current_thread);
1140
1141 bool resident = true;
1142
1143 if (!cdata->_vertices.is_null() &&
1144 !cdata->_vertices.get_read_pointer(current_thread)->request_resident(current_thread)) {
1145 resident = false;
1146 }
1147
1148 if (is_composite() && cdata->_got_minmax) {
1149 if (!cdata->_mins.is_null() &&
1150 !cdata->_mins.get_read_pointer(current_thread)->request_resident(current_thread)) {
1151 resident = false;
1152 }
1153 if (!cdata->_maxs.is_null() &&
1154 !cdata->_maxs.get_read_pointer(current_thread)->request_resident(current_thread)) {
1155 resident = false;
1156 }
1157 }
1158
1159 return resident;
1160}
1161
1162/**
1163 *
1164 */
1165void GeomPrimitive::
1166output(std::ostream &out) const {
1167 out << get_type() << ", " << get_num_primitives()
1168 << ", " << get_num_vertices();
1169}
1170
1171/**
1172 *
1173 */
1174void GeomPrimitive::
1175write(std::ostream &out, int indent_level) const {
1176 indent(out, indent_level)
1177 << get_type();
1178 if (is_indexed()) {
1179 out << " (indexed)";
1180 } else {
1181 out << " (nonindexed)";
1182 }
1183 out << ":\n";
1184 int num_primitives = get_num_primitives();
1185 int num_vertices = get_num_vertices();
1186 int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
1187 for (int i = 0; i < num_primitives; ++i) {
1188 indent(out, indent_level + 2)
1189 << "[";
1190 int begin = get_primitive_start(i);
1191 int end = get_primitive_end(i);
1192 for (int vi = begin; vi < end; vi++) {
1193 out << " " << get_vertex(vi);
1194 }
1195 out << " ]";
1196 if (end < num_vertices) {
1197 for (int ui = 0; ui < num_unused_vertices_per_primitive; ++ui) {
1198 if (end + ui < num_vertices) {
1199 out << " " << get_vertex(end + ui);
1200 } else {
1201 out << " ?";
1202 }
1203 }
1204 }
1205 out << "\n";
1206 }
1207}
1208
1209/**
1210 * Returns a modifiable pointer to the vertex index list, so application code
1211 * can directly fiddle with this data. Use with caution, since there are no
1212 * checks that the data will be left in a stable state.
1213 *
1214 * If this is called on a nonindexed primitive, it will implicitly be
1215 * converted to an indexed primitive.
1216 *
1217 * If num_vertices is not -1, it specifies an artificial limit to the number
1218 * of vertices in the array. Otherwise, all of the vertices in the array will
1219 * be used.
1220 *
1221 * Don't call this in a downstream thread unless you don't mind it blowing
1222 * away other changes you might have recently made in an upstream thread.
1223 *
1224 * This method is intended for low-level usage only. There are higher-level
1225 * methods for more common usage. We recommend you do not use this method
1226 * directly. If you do, be sure you know what you are doing!
1227 */
1228PT(GeomVertexArrayData) GeomPrimitive::
1229modify_vertices(int num_vertices) {
1230 CDWriter cdata(_cycler, true);
1231 PT(GeomVertexArrayData) vertices = do_modify_vertices(cdata);
1232 cdata->_num_vertices = num_vertices;
1233 return vertices;
1234}
1235
1236/**
1237 * Completely replaces the vertex index list with a new table. Chances are
1238 * good that you should also replace the ends list with set_ends() at the same
1239 * time.
1240 *
1241 * If num_vertices is not -1, it specifies an artificial limit to the number
1242 * of vertices in the array. Otherwise, all of the vertices in the array will
1243 * be used.
1244 *
1245 * Don't call this in a downstream thread unless you don't mind it blowing
1246 * away other changes you might have recently made in an upstream thread.
1247 *
1248 * This method is intended for low-level usage only. There are higher-level
1249 * methods for more common usage. We recommend you do not use this method
1250 * directly. If you do, be sure you know what you are doing!
1251 */
1253set_vertices(const GeomVertexArrayData *vertices, int num_vertices) {
1254 CDWriter cdata(_cycler, true);
1255 cdata->_vertices = (GeomVertexArrayData *)vertices;
1256 cdata->_num_vertices = num_vertices;
1257
1258 // Validate the format and make sure to copy its numeric type.
1259 const GeomVertexArrayFormat *format = vertices->get_array_format();
1260 nassertv(format->get_num_columns() == 1);
1261 cdata->_index_type = format->get_column(0)->get_numeric_type();
1262
1263 cdata->_modified = Geom::get_next_modified();
1264 cdata->_got_minmax = false;
1265}
1266
1267/**
1268 * Sets the primitive up as a nonindexed primitive, using the indicated vertex
1269 * range.
1270 *
1271 * Don't call this in a downstream thread unless you don't mind it blowing
1272 * away other changes you might have recently made in an upstream thread.
1273 *
1274 * This method is intended for low-level usage only. There are higher-level
1275 * methods for more common usage. We recommend you do not use this method
1276 * directly. If you do, be sure you know what you are doing!
1277 */
1279set_nonindexed_vertices(int first_vertex, int num_vertices) {
1280 nassertv(num_vertices != -1);
1281 CDWriter cdata(_cycler, true);
1282 cdata->_vertices = nullptr;
1283 cdata->_first_vertex = first_vertex;
1284 cdata->_num_vertices = num_vertices;
1285
1286 cdata->_modified = Geom::get_next_modified();
1287 cdata->_got_minmax = false;
1288
1289 // Force the minmax to be recomputed.
1290 recompute_minmax(cdata);
1291}
1292
1293/**
1294 * Returns a modifiable pointer to the primitive ends array, so application
1295 * code can directly fiddle with this data. Use with caution, since there are
1296 * no checks that the data will be left in a stable state.
1297 *
1298 * Note that simple primitive types, like triangles, do not have a ends array:
1299 * since all the primitives have the same number of vertices, it is not
1300 * needed.
1301 *
1302 * Don't call this in a downstream thread unless you don't mind it blowing
1303 * away other changes you might have recently made in an upstream thread.
1304 *
1305 * This method is intended for low-level usage only. There are higher-level
1306 * methods for more common usage. We recommend you do not use this method
1307 * directly. If you do, be sure you know what you are doing!
1308 */
1310modify_ends() {
1311 CDWriter cdata(_cycler, true);
1312
1313 cdata->_modified = Geom::get_next_modified();
1314 cdata->_got_minmax = false;
1315
1316 if (cdata->_ends.get_ref_count() > 1) {
1317 PTA_int new_ends;
1318 new_ends.v() = cdata->_ends.v();
1319 cdata->_ends = new_ends;
1320 }
1321 return cdata->_ends;
1322}
1323
1324/**
1325 * Completely replaces the primitive ends array with a new table. Chances are
1326 * good that you should also replace the vertices list with set_vertices() at
1327 * the same time.
1328 *
1329 * Note that simple primitive types, like triangles, do not have a ends array:
1330 * since all the primitives have the same number of vertices, it is not
1331 * needed.
1332 *
1333 * Don't call this in a downstream thread unless you don't mind it blowing
1334 * away other changes you might have recently made in an upstream thread.
1335 *
1336 * This method is intended for low-level usage only. There are higher-level
1337 * methods for more common usage. We recommend you do not use this method
1338 * directly. If you do, be sure you know what you are doing!
1339 */
1341set_ends(PTA_int ends) {
1342 CDWriter cdata(_cycler, true);
1343 cdata->_ends = ends;
1344
1345 cdata->_modified = Geom::get_next_modified();
1346 cdata->_got_minmax = false;
1347}
1348
1349/**
1350 * Explicitly specifies the minimum and maximum vertices, as well as the lists
1351 * of per-component min and max.
1352 *
1353 * Use this method with extreme caution. It's generally better to let the
1354 * GeomPrimitive compute these explicitly, unless for some reason you can do
1355 * it faster and you absolutely need the speed improvement.
1356 *
1357 * Note that any modification to the vertex array will normally cause this to
1358 * be recomputed, unless you set it immediately again.
1359 *
1360 * This method is intended for low-level usage only. There are higher-level
1361 * methods for more common usage. We recommend you do not use this method
1362 * directly. If you do, be sure you know what you are doing!
1363 */
1365set_minmax(int min_vertex, int max_vertex,
1367 CDWriter cdata(_cycler, true);
1368 cdata->_min_vertex = min_vertex;
1369 cdata->_max_vertex = max_vertex;
1370 cdata->_mins = mins;
1371 cdata->_maxs = maxs;
1372
1373 cdata->_modified = Geom::get_next_modified();
1374 cdata->_got_minmax = true;
1375}
1376
1377/**
1378 * Undoes a previous call to set_minmax(), and allows the minimum and maximum
1379 * values to be recomputed normally.
1380 *
1381 * This method is intended for low-level usage only. There are higher-level
1382 * methods for more common usage. We recommend you do not use this method
1383 * directly. If you do, be sure you know what you are doing!
1384 */
1386clear_minmax() {
1387 CDWriter cdata(_cycler, true);
1388 cdata->_got_minmax = false;
1389}
1390
1391/**
1392 * If the primitive type is a simple type in which all primitives have the
1393 * same number of vertices, like triangles, returns the number of vertices per
1394 * primitive. If the primitive type is a more complex type in which different
1395 * primitives might have different numbers of vertices, for instance a
1396 * triangle strip, returns 0.
1397 *
1398 * This method is intended for low-level usage only. There are higher-level
1399 * methods for more common usage. We recommend you do not use this method
1400 * directly. If you do, be sure you know what you are doing!
1401 */
1404 return 0;
1405}
1406
1407/**
1408 * Returns the minimum number of vertices that must be added before
1409 * close_primitive() may legally be called.
1410 *
1411 * This method is intended for low-level usage only. There are higher-level
1412 * methods for more common usage. We recommend you do not use this method
1413 * directly. If you do, be sure you know what you are doing!
1414 */
1417 return 3;
1418}
1419
1420/**
1421 * Returns the number of vertices that are added between primitives that
1422 * aren't, strictly speaking, part of the primitives themselves. This is
1423 * used, for instance, to define degenerate triangles to connect otherwise
1424 * disconnected triangle strips.
1425 *
1426 * This method is intended for low-level usage only. There are higher-level
1427 * methods for more common usage. We recommend you do not use this method
1428 * directly. If you do, be sure you know what you are doing!
1429 */
1432 return 0;
1433}
1434
1435/**
1436 * Indicates that the data should be enqueued to be prepared in the indicated
1437 * prepared_objects at the beginning of the next frame. This will ensure the
1438 * data is already loaded into the GSG if it is expected to be rendered soon.
1439 *
1440 * Use this function instead of prepare_now() to preload datas from a user
1441 * interface standpoint.
1442 */
1444prepare(PreparedGraphicsObjects *prepared_objects) {
1445 if (is_indexed()) {
1446 prepared_objects->enqueue_index_buffer(this);
1447 }
1448}
1449
1450/**
1451 * Returns true if the data has already been prepared or enqueued for
1452 * preparation on the indicated GSG, false otherwise.
1453 */
1455is_prepared(PreparedGraphicsObjects *prepared_objects) const {
1456 Contexts::const_iterator ci;
1457 ci = _contexts.find(prepared_objects);
1458 if (ci != _contexts.end()) {
1459 return true;
1460 }
1461 return prepared_objects->is_index_buffer_queued(this);
1462}
1463
1464/**
1465 * Creates a context for the data on the particular GSG, if it does not
1466 * already exist. Returns the new (or old) IndexBufferContext. This assumes
1467 * that the GraphicsStateGuardian is the currently active rendering context
1468 * and that it is ready to accept new datas. If this is not necessarily the
1469 * case, you should use prepare() instead.
1470 *
1471 * Normally, this is not called directly except by the GraphicsStateGuardian;
1472 * a data does not need to be explicitly prepared by the user before it may be
1473 * rendered.
1474 */
1476prepare_now(PreparedGraphicsObjects *prepared_objects,
1478 nassertr(is_indexed(), nullptr);
1479
1480 Contexts::const_iterator ci;
1481 ci = _contexts.find(prepared_objects);
1482 if (ci != _contexts.end()) {
1483 return (*ci).second;
1484 }
1485
1486 IndexBufferContext *ibc = prepared_objects->prepare_index_buffer_now(this, gsg);
1487 if (ibc != nullptr) {
1488 _contexts[prepared_objects] = ibc;
1489 }
1490 return ibc;
1491}
1492
1493/**
1494 * Frees the data context only on the indicated object, if it exists there.
1495 * Returns true if it was released, false if it had not been prepared.
1496 */
1498release(PreparedGraphicsObjects *prepared_objects) {
1499 Contexts::iterator ci;
1500 ci = _contexts.find(prepared_objects);
1501 if (ci != _contexts.end()) {
1502 IndexBufferContext *ibc = (*ci).second;
1503 prepared_objects->release_index_buffer(ibc);
1504 return true;
1505 }
1506
1507 // Maybe it wasn't prepared yet, but it's about to be.
1508 return prepared_objects->dequeue_index_buffer(this);
1509}
1510
1511/**
1512 * Frees the context allocated on all objects for which the data has been
1513 * declared. Returns the number of contexts which have been freed.
1514 */
1516release_all() {
1517 // We have to traverse a copy of the _contexts list, because the
1518 // PreparedGraphicsObjects object will call clear_prepared() in response to
1519 // each release_index_buffer(), and we don't want to be modifying the
1520 // _contexts list while we're traversing it.
1521 Contexts temp = _contexts;
1522 int num_freed = (int)_contexts.size();
1523
1524 Contexts::const_iterator ci;
1525 for (ci = temp.begin(); ci != temp.end(); ++ci) {
1526 PreparedGraphicsObjects *prepared_objects = (*ci).first;
1527 IndexBufferContext *ibc = (*ci).second;
1528 prepared_objects->release_index_buffer(ibc);
1529 }
1530
1531 // Now that we've called release_index_buffer() on every known context, the
1532 // _contexts list should have completely emptied itself.
1533 nassertr(_contexts.empty(), num_freed);
1534
1535 return num_freed;
1536}
1537
1538/**
1539 * Returns a registered GeomVertexArrayFormat of the indicated unsigned
1540 * integer numeric type for storing index values.
1541 */
1543get_index_format(NumericType index_type) {
1544 switch (index_type) {
1545 case NT_uint8:
1546 {
1547 static CPT(GeomVertexArrayFormat) cformat = nullptr;
1548 if (cformat == nullptr) {
1549 cformat = make_index_format(NT_uint8);
1550 }
1551 return cformat;
1552 }
1553 case NT_uint16:
1554 {
1555 static CPT(GeomVertexArrayFormat) cformat = nullptr;
1556 if (cformat == nullptr) {
1557 cformat = make_index_format(NT_uint16);
1558 }
1559 return cformat;
1560 }
1561 case NT_uint32:
1562 {
1563 static CPT(GeomVertexArrayFormat) cformat = nullptr;
1564 if (cformat == nullptr) {
1565 cformat = make_index_format(NT_uint32);
1566 }
1567 return cformat;
1568 }
1569
1570 default:
1571 gobj_cat.error()
1572 << "Not a valid index type: " << index_type << "\n";
1573 return nullptr;
1574 }
1575
1576 return nullptr;
1577}
1578
1579/**
1580 * Removes the indicated PreparedGraphicsObjects table from the data array's
1581 * table, without actually releasing the data array. This is intended to be
1582 * called only from PreparedGraphicsObjects::release_index_buffer(); it should
1583 * never be called by user code.
1584 */
1585void GeomPrimitive::
1586clear_prepared(PreparedGraphicsObjects *prepared_objects) {
1587 Contexts::iterator ci;
1588 ci = _contexts.find(prepared_objects);
1589 if (ci != _contexts.end()) {
1590 _contexts.erase(ci);
1591 } else {
1592 // If this assertion fails, clear_prepared() was given a prepared_objects
1593 // which the data array didn't know about.
1594 nassert_raise("unknown PreparedGraphicsObjects");
1595 }
1596}
1597
1598/**
1599 * Returns the largest index value that can be stored in an index of the
1600 * indicated type, minus one (to leave room for a potential strip cut index)
1601 */
1602int GeomPrimitive::
1603get_highest_index_value(NumericType index_type) {
1604 // Reserve the highest possible index because implementations use this as a
1605 // strip-cut index.
1606 switch (index_type) {
1607 case NT_uint8:
1608 return 0xff - 1;
1609
1610 case NT_uint16:
1611 return 0xffff - 1;
1612
1613 case NT_uint32:
1614 // We don't actually allow use of the sign bit, since all of our functions
1615 // receive an "int" instead of an "unsigned int".
1616 return 0x7fffffff - 1;
1617
1618 default:
1619 return 0;
1620 }
1621}
1622
1623/**
1624 * Returns the index of the indicated type that is reserved for use as a strip
1625 * cut index, if enabled for the primitive. When the renderer encounters this
1626 * index, it will restart the primitive. This is guaranteed not to point to
1627 * an actual vertex.
1628 */
1630get_strip_cut_index(NumericType index_type) {
1631 // Reserve the highest possible index because implementations use this as a
1632 // strip-cut index.
1633 switch (index_type) {
1634 case NT_uint8:
1635 return 0xff;
1636
1637 case NT_uint16:
1638 return 0xffff;
1639
1640 case NT_uint32:
1641 default:
1642 return -1;
1643 }
1644}
1645
1646/**
1647 * Expands min_point and max_point to include all of the vertices in the Geom,
1648 * if any (or the data of any point type, for instance, texture coordinates--
1649 * based on the column name). found_any is set true if any points are found.
1650 * It is the caller's responsibility to initialize min_point, max_point, and
1651 * found_any before calling this function. It also sets sq_center_dist, which
1652 * is the square of the maximum distance of the points to the center. This
1653 * can be useful when deciding whether a sphere volume might be more
1654 * appropriate.
1655 */
1657calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
1658 PN_stdfloat &sq_center_dist, bool &found_any,
1659 const GeomVertexData *vertex_data,
1660 bool got_mat, const LMatrix4 &mat,
1661 const InternalName *column_name,
1662 Thread *current_thread) const {
1663 GeomVertexReader reader(vertex_data, column_name, current_thread);
1664 if (!reader.has_column()) {
1665 // No vertex data.
1666 return;
1667 }
1668
1669 CDReader cdata(_cycler, current_thread);
1670 int i = 0;
1671
1672 if (cdata->_vertices.is_null()) {
1673 // Nonindexed case.
1674 nassertv(cdata->_num_vertices != -1);
1675 if (cdata->_num_vertices == 0) {
1676 return;
1677 }
1678
1679 if (got_mat) {
1680 // Find the first non-NaN vertex.
1681 while (!found_any && i < cdata->_num_vertices) {
1682 reader.set_row(cdata->_first_vertex + i);
1683 LPoint3 first_vertex = mat.xform_point_general(reader.get_data3());
1684 if (!first_vertex.is_nan()) {
1685 min_point = first_vertex;
1686 max_point = first_vertex;
1687 sq_center_dist = first_vertex.length_squared();
1688 found_any = true;
1689 }
1690 ++i;
1691 }
1692
1693 for (; i < cdata->_num_vertices; ++i) {
1694 reader.set_row_unsafe(cdata->_first_vertex + i);
1695 nassertv(!reader.is_at_end());
1696
1697 LPoint3 vertex = mat.xform_point_general(reader.get_data3());
1698
1699 min_point.set(min(min_point[0], vertex[0]),
1700 min(min_point[1], vertex[1]),
1701 min(min_point[2], vertex[2]));
1702 max_point.set(max(max_point[0], vertex[0]),
1703 max(max_point[1], vertex[1]),
1704 max(max_point[2], vertex[2]));
1705 sq_center_dist = max(sq_center_dist, vertex.length_squared());
1706 }
1707 } else {
1708 // Find the first non-NaN vertex.
1709 while (!found_any && i < cdata->_num_vertices) {
1710 reader.set_row(cdata->_first_vertex + i);
1711 LPoint3 first_vertex = reader.get_data3();
1712 if (!first_vertex.is_nan()) {
1713 min_point = first_vertex;
1714 max_point = first_vertex;
1715 sq_center_dist = first_vertex.length_squared();
1716 found_any = true;
1717 }
1718 ++i;
1719 }
1720
1721 for (; i < cdata->_num_vertices; ++i) {
1722 reader.set_row_unsafe(cdata->_first_vertex + i);
1723 nassertv(!reader.is_at_end());
1724
1725 const LVecBase3 &vertex = reader.get_data3();
1726
1727 min_point.set(min(min_point[0], vertex[0]),
1728 min(min_point[1], vertex[1]),
1729 min(min_point[2], vertex[2]));
1730 max_point.set(max(max_point[0], vertex[0]),
1731 max(max_point[1], vertex[1]),
1732 max(max_point[2], vertex[2]));
1733 sq_center_dist = max(sq_center_dist, vertex.length_squared());
1734 }
1735 }
1736
1737 } else {
1738 // Indexed case.
1739 GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
1740 if (index.is_at_end()) {
1741 return;
1742 }
1743
1744 int strip_cut_index = get_strip_cut_index(cdata->_index_type);
1745
1746 if (got_mat) {
1747 // Find the first non-NaN vertex.
1748 while (!found_any && !index.is_at_end()) {
1749 int ii = index.get_data1i();
1750 if (ii != strip_cut_index) {
1751 reader.set_row(ii);
1752 LPoint3 first_vertex = mat.xform_point_general(reader.get_data3());
1753 if (!first_vertex.is_nan()) {
1754 min_point = first_vertex;
1755 max_point = first_vertex;
1756 sq_center_dist = first_vertex.length_squared();
1757 found_any = true;
1758 }
1759 }
1760 }
1761
1762 while (!index.is_at_end()) {
1763 int ii = index.get_data1i();
1764 if (ii == strip_cut_index) {
1765 continue;
1766 }
1767 reader.set_row_unsafe(ii);
1768 nassertv(!reader.is_at_end());
1769
1770 LPoint3 vertex = mat.xform_point_general(reader.get_data3());
1771
1772 min_point.set(min(min_point[0], vertex[0]),
1773 min(min_point[1], vertex[1]),
1774 min(min_point[2], vertex[2]));
1775 max_point.set(max(max_point[0], vertex[0]),
1776 max(max_point[1], vertex[1]),
1777 max(max_point[2], vertex[2]));
1778 sq_center_dist = max(sq_center_dist, vertex.length_squared());
1779 }
1780 } else {
1781 // Find the first non-NaN vertex.
1782 while (!found_any && !index.is_at_end()) {
1783 int ii = index.get_data1i();
1784 if (ii != strip_cut_index) {
1785 reader.set_row(ii);
1786 LVecBase3 first_vertex = reader.get_data3();
1787 if (!first_vertex.is_nan()) {
1788 min_point = first_vertex;
1789 max_point = first_vertex;
1790 sq_center_dist = first_vertex.length_squared();
1791 found_any = true;
1792 }
1793 }
1794 }
1795
1796 while (!index.is_at_end()) {
1797 int ii = index.get_data1i();
1798 if (ii == strip_cut_index) {
1799 continue;
1800 }
1801 reader.set_row_unsafe(ii);
1802 nassertv(!reader.is_at_end());
1803
1804 const LVecBase3 &vertex = reader.get_data3();
1805
1806 min_point.set(min(min_point[0], vertex[0]),
1807 min(min_point[1], vertex[1]),
1808 min(min_point[2], vertex[2]));
1809 max_point.set(max(max_point[0], vertex[0]),
1810 max(max_point[1], vertex[1]),
1811 max(max_point[2], vertex[2]));
1812 sq_center_dist = max(sq_center_dist, vertex.length_squared());
1813 }
1814 }
1815 }
1816}
1817
1818/**
1819 * Expands radius so that a sphere with the given center point fits all of the
1820 * vertices.
1821 *
1822 * The center point is assumed to already have been transformed by the matrix,
1823 * if one is given.
1824 */
1826calc_sphere_radius(const LPoint3 &center, PN_stdfloat &sq_radius,
1827 bool &found_any, const GeomVertexData *vertex_data,
1828 Thread *current_thread) const {
1829 GeomVertexReader reader(vertex_data, InternalName::get_vertex(), current_thread);
1830 if (!reader.has_column()) {
1831 // No vertex data.
1832 return;
1833 }
1834
1835 if (!found_any) {
1836 sq_radius = 0.0;
1837 }
1838
1839 CDReader cdata(_cycler, current_thread);
1840
1841 if (cdata->_vertices.is_null()) {
1842 // Nonindexed case.
1843 nassertv(cdata->_num_vertices != -1);
1844 if (cdata->_num_vertices == 0) {
1845 return;
1846 }
1847 found_any = true;
1848
1849 for (int i = 0; i < cdata->_num_vertices; ++i) {
1850 reader.set_row_unsafe(cdata->_first_vertex + i);
1851 const LVecBase3 &vertex = reader.get_data3();
1852
1853 sq_radius = max(sq_radius, (vertex - center).length_squared());
1854 }
1855
1856 } else {
1857 // Indexed case.
1858 GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
1859 if (index.is_at_end()) {
1860 return;
1861 }
1862 found_any = true;
1863
1864 int strip_cut_index = get_strip_cut_index(cdata->_index_type);
1865
1866 while (!index.is_at_end()) {
1867 int ii = index.get_data1i();
1868 if (ii == strip_cut_index) {
1869 continue;
1870 }
1871 reader.set_row_unsafe(ii);
1872 const LVecBase3 &vertex = reader.get_data3();
1873
1874 sq_radius = max(sq_radius, (vertex - center).length_squared());
1875 }
1876 }
1877}
1878
1879/**
1880 * Decomposes a complex primitive type into a simpler primitive type, for
1881 * instance triangle strips to triangles, and returns a pointer to the new
1882 * primitive definition. If the decomposition cannot be performed, this might
1883 * return the original object.
1884 *
1885 * This method is useful for application code that wants to iterate through
1886 * the set of triangles on the primitive without having to write handlers for
1887 * each possible kind of primitive type.
1888 */
1889CPT(GeomPrimitive) GeomPrimitive::
1890decompose_impl() const {
1891 return this;
1892}
1893
1894/**
1895 * The virtual implementation of rotate().
1896 */
1897CPT(GeomVertexArrayData) GeomPrimitive::
1898rotate_impl() const {
1899 // The default implementation doesn't even try to do anything.
1900 nassertr(false, nullptr);
1901 return nullptr;
1902}
1903
1904/**
1905 * The virtual implementation of doubleside().
1906 */
1907CPT(GeomPrimitive) GeomPrimitive::
1908doubleside_impl() const {
1909 return this;
1910}
1911
1912/**
1913 * The virtual implementation of reverse().
1914 */
1915CPT(GeomPrimitive) GeomPrimitive::
1916reverse_impl() const {
1917 return this;
1918}
1919
1920/**
1921 * Should be redefined to return true in any primitive that implements
1922 * append_unused_vertices().
1923 */
1924bool GeomPrimitive::
1925requires_unused_vertices() const {
1926 return false;
1927}
1928
1929/**
1930 * Called when a new primitive is begun (other than the first primitive), this
1931 * should add some degenerate vertices between primitives, if the primitive
1932 * type requires that. The second parameter is the first vertex that begins
1933 * the new primitive.
1934 *
1935 * This method is only called if requires_unused_vertices(), above, returns
1936 * true.
1937 */
1938void GeomPrimitive::
1939append_unused_vertices(GeomVertexArrayData *, int) {
1940}
1941
1942/**
1943 * Recomputes the _min_vertex and _max_vertex values if necessary.
1944 */
1945void GeomPrimitive::
1946recompute_minmax(GeomPrimitive::CData *cdata) {
1947 if (cdata->_vertices.is_null()) {
1948 // In the nonindexed case, we don't need to do much (the minmax is
1949 // trivial).
1950 nassertv(cdata->_num_vertices != -1);
1951 cdata->_min_vertex = cdata->_first_vertex;
1952 cdata->_max_vertex = cdata->_first_vertex + cdata->_num_vertices - 1;
1953 cdata->_mins.clear();
1954 cdata->_maxs.clear();
1955
1956 } else {
1957 int num_vertices = cdata->_vertices.get_read_pointer()->get_num_rows();
1958
1959 if (num_vertices == 0) {
1960 // Or if we don't have any vertices, the minmax is also trivial.
1961 cdata->_min_vertex = 0;
1962 cdata->_max_vertex = 0;
1963 cdata->_mins.clear();
1964 cdata->_maxs.clear();
1965
1966 } else if (get_num_vertices_per_primitive() == 0) {
1967 // This is a complex primitive type like a triangle strip; compute the
1968 // minmax of each primitive (as well as the overall minmax).
1969 GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
1970
1971 cdata->_mins = make_index_data();
1972 cdata->_maxs = make_index_data();
1973
1974 GeomVertexArrayData *mins_data = cdata->_mins.get_write_pointer();
1975 GeomVertexArrayData *maxs_data = cdata->_maxs.get_write_pointer();
1976
1977 mins_data->unclean_set_num_rows(cdata->_ends.size());
1978 maxs_data->unclean_set_num_rows(cdata->_ends.size());
1979
1980 GeomVertexWriter mins(mins_data, 0);
1981 GeomVertexWriter maxs(maxs_data, 0);
1982
1983 int pi = 0;
1984
1985 unsigned int vertex = index.get_data1i();
1986 cdata->_min_vertex = vertex;
1987 cdata->_max_vertex = vertex;
1988 unsigned int min_prim = vertex;
1989 unsigned int max_prim = vertex;
1990
1991 int num_unused_vertices = get_num_unused_vertices_per_primitive();
1992
1993 for (int vi = 1; vi < num_vertices; ++vi) {
1994 nassertv(!index.is_at_end());
1995 nassertv(pi < (int)cdata->_ends.size());
1996
1997 unsigned int vertex;
1998
1999 if (vi == cdata->_ends[pi]) {
2000 // Skip unused vertices, since they won't be very relevant and may
2001 // contain a strip-cut index, which would distort the result.
2002 if (num_unused_vertices > 0) {
2003 vi += num_unused_vertices;
2004 index.set_row_unsafe(vi);
2005 }
2006 vertex = index.get_data1i();
2007
2008 mins.set_data1i(min_prim);
2009 maxs.set_data1i(max_prim);
2010 min_prim = vertex;
2011 max_prim = vertex;
2012 ++pi;
2013
2014 } else {
2015 vertex = index.get_data1i();
2016 min_prim = min(min_prim, vertex);
2017 max_prim = max(max_prim, vertex);
2018 }
2019
2020 cdata->_min_vertex = min(cdata->_min_vertex, vertex);
2021 cdata->_max_vertex = max(cdata->_max_vertex, vertex);
2022 }
2023
2024 mins.set_data1i(min_prim);
2025 maxs.set_data1i(max_prim);
2026 nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
2027
2028 } else {
2029 // This is a simple primitive type like a triangle; just compute the
2030 // overall minmax.
2031 GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
2032
2033 cdata->_mins.clear();
2034 cdata->_maxs.clear();
2035
2036 unsigned int vertex = index.get_data1i();
2037 cdata->_min_vertex = vertex;
2038 cdata->_max_vertex = vertex;
2039
2040 for (int vi = 1; vi < num_vertices; ++vi) {
2041 nassertv(!index.is_at_end());
2042 unsigned int vertex = index.get_data1i();
2043 cdata->_min_vertex = min(cdata->_min_vertex, vertex);
2044 cdata->_max_vertex = max(cdata->_max_vertex, vertex);
2045 }
2046 }
2047 }
2048
2049 cdata->_got_minmax = true;
2050}
2051
2052/**
2053 * The private implementation of make_indexed().
2054 */
2055void GeomPrimitive::
2056do_make_indexed(CData *cdata) {
2057 if (cdata->_vertices.is_null()) {
2058 if (gobj_cat.is_debug()) {
2059 gobj_cat.debug()
2060 << this << ".make_indexed()\n";
2061 }
2062
2063 nassertv(cdata->_num_vertices != -1);
2064 cdata->_vertices = make_index_data();
2065
2066 GeomVertexArrayData *array_data = cdata->_vertices.get_write_pointer();
2067 array_data->unclean_set_num_rows(cdata->_num_vertices);
2068 GeomVertexWriter index(array_data, 0);
2069
2070 for (int i = 0; i < cdata->_num_vertices; ++i) {
2071 index.set_data1i(i + cdata->_first_vertex);
2072 }
2073 cdata->_num_vertices = -1;
2074 }
2075}
2076
2077/**
2078 * If the indicated new vertex index won't fit in the specified index type,
2079 * automatically elevates the index type to the next available size.
2080 */
2081void GeomPrimitive::
2082consider_elevate_index_type(CData *cdata, int vertex) {
2083 // Note that we reserve the highest possible index of a particular index
2084 // type (ie. -1) because this is commonly used as a strip-cut (also known
2085 // as primitive restart) index.
2086 switch (cdata->_index_type) {
2087 case NT_uint8:
2088 if (vertex >= 0xff) {
2089 do_set_index_type(cdata, NT_uint16);
2090 }
2091 break;
2092
2093 case NT_uint16:
2094 if (vertex >= 0xffff) {
2095 do_set_index_type(cdata, NT_uint32);
2096 }
2097 break;
2098
2099 case NT_uint32:
2100 // Not much we can do here.
2101 nassertv(vertex < 0x7fffffff);
2102 break;
2103
2104 default:
2105 break;
2106 }
2107}
2108
2109/**
2110 * The private implementation of set_index_type().
2111 */
2112void GeomPrimitive::
2113do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
2114 int old_strip_cut_index = get_strip_cut_index(cdata->_index_type);
2115 int new_strip_cut_index = get_strip_cut_index(index_type);
2116
2117 cdata->_index_type = index_type;
2118
2119 if (gobj_cat.is_debug()) {
2120 gobj_cat.debug()
2121 << this << ".set_index_type(" << index_type << ")\n";
2122 }
2123
2124 if (!cdata->_vertices.is_null()) {
2125 CPT(GeomVertexArrayFormat) new_format = get_index_format();
2126
2127 CPT(GeomVertexArrayData) array_obj = cdata->_vertices.get_read_pointer();
2128 if (array_obj->get_array_format() != new_format) {
2129 PT(GeomVertexArrayData) new_vertices = make_index_data();
2130 new_vertices->set_num_rows(array_obj->get_num_rows());
2131
2132 GeomVertexReader from(array_obj, 0);
2133 GeomVertexWriter to(new_vertices, 0);
2134
2135 while (!from.is_at_end()) {
2136 int index = from.get_data1i();
2137 if (index == old_strip_cut_index) {
2138 index = new_strip_cut_index;
2139 }
2140 to.set_data1i(index);
2141 }
2142 cdata->_vertices = new_vertices;
2143 cdata->_got_minmax = false;
2144 }
2145 }
2146}
2147
2148/**
2149 * The private implementation of modify_vertices().
2150 */
2151PT(GeomVertexArrayData) GeomPrimitive::
2152do_modify_vertices(GeomPrimitive::CData *cdata) {
2153 if (cdata->_vertices.is_null()) {
2154 do_make_indexed(cdata);
2155 }
2156
2157 PT(GeomVertexArrayData) vertices = cdata->_vertices.get_write_pointer();
2158
2159 cdata->_modified = Geom::get_next_modified();
2160 cdata->_got_minmax = false;
2161 return vertices;
2162}
2163
2164/**
2165 * Writes the contents of this object to the datagram for shipping out to a
2166 * Bam file.
2167 */
2169write_datagram(BamWriter *manager, Datagram &dg) {
2170 TypedWritable::write_datagram(manager, dg);
2171
2172 manager->write_cdata(dg, _cycler);
2173}
2174
2175/**
2176 * Called by the BamReader to perform any final actions needed for setting up
2177 * the object after all objects have been read and all pointers have been
2178 * completed.
2179 */
2181finalize(BamReader *manager) {
2182 const GeomVertexArrayData *vertices = get_vertices();
2183 if (vertices != nullptr) {
2184 set_usage_hint(vertices->get_usage_hint());
2185 }
2186}
2187
2188/**
2189 * This internal function is called by make_from_bam to read in all of the
2190 * relevant data from the BamFile for the new GeomPrimitive.
2191 */
2192void GeomPrimitive::
2193fillin(DatagramIterator &scan, BamReader *manager) {
2194 TypedWritable::fillin(scan, manager);
2195
2196 manager->read_cdata(scan, _cycler);
2197 manager->register_finalize(this);
2198}
2199
2200/**
2201 *
2202 */
2203CycleData *GeomPrimitive::CData::
2204make_copy() const {
2205 return new CData(*this);
2206}
2207
2208/**
2209 * Writes the contents of this object to the datagram for shipping out to a
2210 * Bam file.
2211 */
2212void GeomPrimitive::CData::
2213write_datagram(BamWriter *manager, Datagram &dg) const {
2214 dg.add_uint8(_shade_model);
2215 dg.add_int32(_first_vertex);
2216 dg.add_int32(_num_vertices);
2217 dg.add_uint8(_index_type);
2218 dg.add_uint8(_usage_hint);
2219
2220 manager->write_pointer(dg, _vertices.get_read_pointer());
2221 WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
2222}
2223
2224/**
2225 * Receives an array of pointers, one for each time manager->read_pointer()
2226 * was called in fillin(). Returns the number of pointers processed.
2227 */
2228int GeomPrimitive::CData::
2229complete_pointers(TypedWritable **p_list, BamReader *manager) {
2230 int pi = CycleData::complete_pointers(p_list, manager);
2231
2232 _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);
2233
2234 if (manager->get_file_minor_ver() < 6 && !_vertices.is_null()) {
2235 // Older bam files might have a meaningless number in _num_vertices if the
2236 // primitive is indexed. Nowadays, this number is always considered
2237 // meaningful unless it is -1.
2238 _num_vertices = -1;
2239 }
2240
2241 return pi;
2242}
2243
2244/**
2245 * This internal function is called by make_from_bam to read in all of the
2246 * relevant data from the BamFile for the new GeomPrimitive.
2247 */
2248void GeomPrimitive::CData::
2249fillin(DatagramIterator &scan, BamReader *manager) {
2250 _shade_model = (ShadeModel)scan.get_uint8();
2251 _first_vertex = scan.get_int32();
2252 _num_vertices = scan.get_int32();
2253 _index_type = (NumericType)scan.get_uint8();
2254 _usage_hint = (UsageHint)scan.get_uint8();
2255
2256 manager->read_pointer(scan);
2257 READ_PTA(manager, scan, IPD_int::read_datagram, _ends);
2258
2259 _modified = Geom::get_next_modified();
2260 _got_minmax = false;
2261}
2262
2263/**
2264 * Ensures that the primitive's minmax cache has been computed.
2265 */
2267check_minmax() const {
2268 if (!_cdata->_got_minmax) {
2269 // We'll need to get a fresh pointer, since another thread might already
2270 // have modified the pointer on the object since we queried it.
2271 {
2272#ifdef DO_PIPELINING
2273 unref_delete((CycleData *)_cdata);
2274#endif
2275 GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler,
2276 false, _current_thread);
2277 ((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
2278#ifdef DO_PIPELINING
2279 _cdata->ref();
2280#endif
2281
2282 if (!fresh_cdata->_got_minmax) {
2283 // The cache is still stale. We have to do the work of freshening it.
2284 ((GeomPrimitive *)_object.p())->recompute_minmax(fresh_cdata);
2285 nassertv(fresh_cdata->_got_minmax);
2286 }
2287
2288 // When fresh_cdata goes out of scope, its write lock is released, and
2289 // _cdata reverts to our usual convention of an unlocked copy of the
2290 // data.
2291 }
2292 }
2293
2294 nassertv(_cdata->_got_minmax);
2295}
2296
2297/**
2298 *
2299 */
2300int GeomPrimitivePipelineReader::
2301get_first_vertex() const {
2302 if (_vertices.is_null()) {
2303 return _cdata->_first_vertex;
2304 }
2305
2306 size_t size = _vertices_cdata->_buffer.get_size();
2307 if (size == 0) {
2308 return 0;
2309 }
2310
2311 GeomVertexReader index(_vertices, 0);
2312 return index.get_data1i();
2313}
2314
2315/**
2316 * Returns the ith vertex index in the table.
2317 */
2319get_vertex(int i) const {
2320 if (!_vertices.is_null()) {
2321 // The indexed case.
2322 nassertr(i >= 0 && i < get_num_vertices(), -1);
2323
2324 const unsigned char *ptr = get_read_pointer(true);
2325 switch (_cdata->_index_type) {
2326 case GeomEnums::NT_uint8:
2327 return ((uint8_t *)ptr)[i];
2328 break;
2329 case GeomEnums::NT_uint16:
2330 return ((uint16_t *)ptr)[i];
2331 break;
2332 case GeomEnums::NT_uint32:
2333 return ((uint32_t *)ptr)[i];
2334 break;
2335 default:
2336 nassert_raise("unsupported index type");
2337 return -1;
2338 }
2339
2340 } else {
2341 // The nonindexed case.
2342 return _cdata->_first_vertex + i;
2343 }
2344}
2345
2346/**
2347 *
2348 */
2349int GeomPrimitivePipelineReader::
2350get_num_primitives() const {
2351 int num_vertices_per_primitive = _object->get_num_vertices_per_primitive();
2352
2353 if (num_vertices_per_primitive == 0) {
2354 // This is a complex primitive type like a triangle strip: each primitive
2355 // uses a different number of vertices.
2356 return _cdata->_ends.size();
2357
2358 } else {
2359 // This is a simple primitive type like a triangle: each primitive uses
2360 // the same number of vertices.
2361 return (get_num_vertices() / num_vertices_per_primitive);
2362 }
2363}
2364
2365/**
2366 * Turns on all the bits corresponding to the vertices that are referenced
2367 * by this GeomPrimitive.
2368 */
2370get_referenced_vertices(BitArray &bits) const {
2371 int num_vertices = get_num_vertices();
2372
2373 if (is_indexed()) {
2374 int strip_cut_index = get_strip_cut_index();
2375 const unsigned char *ptr = get_read_pointer(true);
2376 switch (get_index_type()) {
2377 case GeomEnums::NT_uint8:
2378 for (int vi = 0; vi < num_vertices; ++vi) {
2379 int index = ((const uint8_t *)ptr)[vi];
2380 if (index != strip_cut_index) {
2381 bits.set_bit(index);
2382 }
2383 }
2384 break;
2385 case GeomEnums::NT_uint16:
2386 for (int vi = 0; vi < num_vertices; ++vi) {
2387 int index = ((const uint16_t *)ptr)[vi];
2388 if (index != strip_cut_index) {
2389 bits.set_bit(index);
2390 }
2391 }
2392 break;
2393 case GeomEnums::NT_uint32:
2394 for (int vi = 0; vi < num_vertices; ++vi) {
2395 int index = ((const uint32_t *)ptr)[vi];
2396 if (index != strip_cut_index) {
2397 bits.set_bit(index);
2398 }
2399 }
2400 break;
2401 default:
2402 nassert_raise("unsupported index type");
2403 break;
2404 }
2405 } else {
2406 // Nonindexed case.
2407 bits.set_range(get_first_vertex(), num_vertices);
2408 }
2409}
2410
2411/**
2412 *
2413 */
2414bool GeomPrimitivePipelineReader::
2415check_valid(const GeomVertexDataPipelineReader *data_reader) const {
2416 if (get_num_vertices() != 0 &&
2417 data_reader->get_num_arrays() > 0 &&
2418 get_max_vertex() >= data_reader->get_num_rows()) {
2419
2420#ifndef NDEBUG
2421 gobj_cat.error()
2422 << get_object()->get_type() << " references vertices up to "
2423 << get_max_vertex() << ", but GeomVertexData has only "
2424 << data_reader->get_num_rows() << " rows!\n";
2425#endif
2426 return false;
2427 }
2428
2429 return true;
2430}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition bamReader.I:83
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
A dynamic array with an unlimited number of bits.
Definition bitArray.h:40
int get_num_on_bits() const
Returns the number of bits that are set to 1 in the array.
Definition bitArray.cxx:296
int get_next_higher_different_bit(int low_bit) const
Returns the index of the next bit in the array, above low_bit, whose value is different that the valu...
Definition bitArray.cxx:413
int get_lowest_on_bit() const
Returns the index of the lowest 1 bit in the array.
Definition bitArray.cxx:332
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition bitArray.cxx:195
void set_bit(int index)
Sets the nth bit on.
Definition bitArray.I:115
bool get_bit(int index) const
Returns true if the nth bit is set, false if it is cleared.
Definition bitArray.I:99
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition cycleData.h:50
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
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition datagram.I:67
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition datagram.I:50
Defines a series of disconnected line segments.
Definition geomLines.h:23
Defines a series of "patches", fixed-size groupings of vertices that must be processed by a tessellat...
Definition geomPatches.h:24
Defines a series of disconnected points.
Definition geomPoints.h:23
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
void get_referenced_vertices(BitArray &bits) const
Turns on all the bits corresponding to the vertices that are referenced by this GeomPrimitive.
void check_minmax() const
Ensures that the primitive's minmax cache has been computed.
int get_vertex(int i) const
Returns the ith vertex index in the table.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
get_min_num_vertices_per_primitive
Returns the minimum number of vertices that must be added before close_primitive() may legally be cal...
void clear_vertices()
Removes all of the vertices and primitives from the object, so they can be re-added.
int get_max_vertex() const
Returns the maximum vertex index number used by all the primitives in this object.
int get_num_primitives() const
Returns the number of individual primitives stored within this object.
void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, PN_stdfloat &sq_center_dist, bool &found_any, const GeomVertexData *vertex_data, bool got_mat, const LMatrix4 &mat, const InternalName *column_name, Thread *current_thread) const
Expands min_point and max_point to include all of the vertices in the Geom, if any (or the data of an...
get_geom_rendering
Returns the set of GeomRendering bits that represent the rendering properties required to properly re...
get_vertex
Returns the ith vertex index in the table.
void reserve_num_vertices(int num_vertices)
This ensures that enough memory space for n vertices is allocated, so that you may increase the numbe...
int get_num_used_vertices() const
Returns the number of vertices used by all of the primitives.
get_usage_hint
Returns the usage hint for this primitive.
void operator=(const GeomPrimitive &copy)
The copy assignment operator is not pipeline-safe.
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the data has already been prepared or enqueued for preparation on the indicated GSG,...
void make_indexed()
Converts the primitive from nonindexed form to indexed form.
void add_consecutive_vertices(int start, int num_vertices)
Adds a consecutive sequence of vertices, beginning at start, to the primitive.
get_num_vertices
Returns the number of indices used by all the primitives in this object.
bool is_composite() const
Returns true if the primitive is a composite primitive such as a tristrip or trifan,...
get_strip_cut_index
Returns the index of the indicated type that is reserved for use as a strip cut index,...
void pack_vertices(GeomVertexData *dest, const GeomVertexData *source)
Packs the vertices used by the primitive from the indicated source array onto the end of the indicate...
bool close_primitive()
Indicates that the previous n calls to add_vertex(), since the last call to close_primitive(),...
int get_first_vertex() const
Returns the first vertex number referenced by the primitive.
int get_primitive_max_vertex(int n) const
Returns the maximum vertex index number used by the nth primitive in this object.
get_index_type
Returns the numeric type of the index column.
const GeomVertexArrayFormat * get_index_format() const
Returns a registered format appropriate for using to store the index table.
void set_nonindexed_vertices(int first_vertex, int num_vertices)
Sets the primitive up as a nonindexed primitive, using the indicated vertex range.
get_num_unused_vertices_per_primitive
Returns the number of vertices that are added between primitives that aren't, strictly speaking,...
void set_index_type(NumericType index_type)
Changes the numeric type of the index column.
void set_vertices(const GeomVertexArrayData *vertices, int num_vertices=-1)
Completely replaces the vertex index list with a new table.
void set_ends(PTA_int ends)
Completely replaces the primitive ends array with a new table.
void offset_vertices(int offset)
Adds the indicated offset to all vertices used by the primitive.
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the data context only on the indicated object, if it exists there.
get_num_vertices_per_primitive
If the primitive type is a simple type in which all primitives have the same number of vertices,...
PTA_int modify_ends()
Returns a modifiable pointer to the primitive ends array, so application code can directly fiddle wit...
int get_primitive_min_vertex(int n) const
Returns the minimum vertex index number used by the nth primitive in this object.
void add_next_vertices(int num_vertices)
Adds the next n vertices in sequence, beginning from the last vertex added to the primitive + 1.
void make_nonindexed(GeomVertexData *dest, const GeomVertexData *source)
Converts the primitive from indexed to nonindexed by duplicating vertices as necessary into the indic...
void set_usage_hint(UsageHint usage_hint)
Changes the UsageHint hint for this primitive.
int get_primitive_start(int n) const
Returns the element within the _vertices list at which the nth primitive starts.
get_shade_model
Returns the ShadeModel hint for this primitive.
void clear_minmax()
Undoes a previous call to set_minmax(), and allows the minimum and maximum values to be recomputed no...
void calc_sphere_radius(const LPoint3 &center, PN_stdfloat &sq_radius, bool &found_any, const GeomVertexData *vertex_data, Thread *current_thread) const
Expands radius so that a sphere with the given center point fits all of the vertices.
int release_all()
Frees the context allocated on all objects for which the data has been declared.
int get_primitive_num_vertices(int n) const
Returns the number of vertices used by the nth primitive.
int get_primitive_end(int n) const
Returns the element within the _vertices list at which the nth primitive ends.
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type.
void prepare(PreparedGraphicsObjects *prepared_objects)
Indicates that the data should be enqueued to be prepared in the indicated prepared_objects at the be...
bool is_indexed() const
Returns true if the primitive is indexed, false otherwise.
IndexBufferContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the data on the particular GSG, if it does not already exist.
void set_minmax(int min_vertex, int max_vertex, GeomVertexArrayData *mins, GeomVertexArrayData *maxs)
Explicitly specifies the minimum and maximum vertices, as well as the lists of per-component min and ...
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
This is the data for one array of a GeomVertexData structure.
get_usage_hint
Returns the usage hint that describes to the rendering backend how often the vertex data will be modi...
get_array_format
Returns the format object that describes this array.
bool unclean_set_num_rows(int n)
This method behaves like set_num_rows(), except the new data is not initialized.
This describes the structure of a single array within a Geom data.
get_column
Returns the specification with the indicated name, or NULL if the name is not used.
get_num_columns
Returns the number of different columns in the array.
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
void copy_row_from(int dest_row, const GeomVertexDataPipelineReader &source, int source_row)
Copies a single row of the data from the other array into the indicated row of this array.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
void copy_row_from(int dest_row, const GeomVertexData *source, int source_row, Thread *current_thread)
Copies a single row of the data from the other array into the indicated row of this array.
int get_num_rows() const
Returns the number of rows stored within all the arrays.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
void set_row(int row)
Sets the start row to the indicated value.
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void add_data1i(int data)
Sets the write row to a particular 1-component value, and advances the write row.
void set_data1i(int data)
Sets the write row to a particular 1-component value, and advances the write row.
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition geom.cxx:1356
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
This is a special class object that holds all the information returned by a particular GSG to indicat...
Encodes a string name in a hash table, mapping it to a pointer.
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A table of objects that are saved within the graphics context for reference by handle later.
bool is_index_buffer_queued(const GeomPrimitive *data) const
Returns true if the index buffer has been queued on this GSG, false otherwise.
void release_index_buffer(IndexBufferContext *ibc)
Indicates that a data context, created by a previous call to prepare_index_buffer(),...
void enqueue_index_buffer(GeomPrimitive *data)
Indicates that a buffer would like to be put on the list to be prepared when the GSG is next ready to...
IndexBufferContext * prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg)
Immediately creates a new IndexBufferContext for the indicated data and returns it.
bool dequeue_index_buffer(GeomPrimitive *data)
Removes a buffer from the queued list of data arrays to be prepared.
A thread; that is, a lightweight process.
Definition thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition typedObject.I:38
Base class for objects that can be written to and read from Bam files.
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...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is our own Panda specialization on the default STL map.
Definition pmap.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...