Panda3D
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"
18 #include "geomVertexArrayFormat.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 
34 using std::max;
35 using std::min;
36 
37 TypeHandle GeomPrimitive::_type_handle;
38 TypeHandle GeomPrimitive::CData::_type_handle;
39 TypeHandle GeomPrimitivePipelineReader::_type_handle;
40 
41 PStatCollector GeomPrimitive::_decompose_pcollector("*:Munge:Decompose");
42 PStatCollector GeomPrimitive::_doubleside_pcollector("*:Munge:Doubleside");
43 PStatCollector GeomPrimitive::_reverse_pcollector("*:Munge:Reverse");
44 PStatCollector GeomPrimitive::_rotate_pcollector("*:Munge:Rotate");
45 
46 /**
47  * Constructs an invalid object. Only used when reading from bam.
48  */
49 GeomPrimitive::
50 GeomPrimitive() {
51 }
52 
53 /**
54  * Required to implement CopyOnWriteObject.
55  */
56 PT(CopyOnWriteObject) GeomPrimitive::
57 make_cow_copy() {
58  return make_copy();
59 }
60 
61 /**
62  *
63  */
64 GeomPrimitive::
65 GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
66  CDWriter cdata(_cycler, true);
67  cdata->_usage_hint = usage_hint;
68 }
69 
70 /**
71  *
72  */
73 GeomPrimitive::
74 GeomPrimitive(const GeomPrimitive &copy) :
75  CopyOnWriteObject(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  */
86 operator = (const GeomPrimitive &copy) {
87  CopyOnWriteObject::operator = (copy);
88  _cycler = copy._cycler;
89 }
90 
91 /**
92  *
93  */
94 GeomPrimitive::
95 ~GeomPrimitive() {
96  release_all();
97 }
98 
99 /**
100  * Returns the set of GeomRendering bits that represent the rendering
101  * properties required to properly render this primitive.
102  */
103 int GeomPrimitive::
104 get_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  */
119 set_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  */
142 set_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  */
161 add_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  */
241 add_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  */
308 add_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  */
333 reserve_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  */
355 close_primitive() {
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  */
405 clear_vertices() {
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  */
430 offset_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  */
512 offset_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  */
607 make_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  */
639 pack_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  */
692 make_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  */
708 get_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  */
735 get_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  */
758 get_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  */
787 get_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  */
803 get_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  */
821 get_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  */
844 CPT(GeomPrimitive) GeomPrimitive::
845 decompose() 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  */
864 CPT(GeomPrimitive) GeomPrimitive::
865 rotate() 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  */
908 CPT(GeomPrimitive) GeomPrimitive::
909 doubleside() 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  */
929 CPT(GeomPrimitive) GeomPrimitive::
930 reverse() 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  */
948 CPT(GeomPrimitive) GeomPrimitive::
949 match_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  */
982 CPT(GeomPrimitive) GeomPrimitive::
983 make_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  */
1025 CPT(GeomPrimitive) GeomPrimitive::
1026 make_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  */
1085 CPT(GeomPrimitive) GeomPrimitive::
1086 make_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  */
1112 CPT(GeomPrimitive) GeomPrimitive::
1113 make_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  */
1121 int GeomPrimitive::
1122 get_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  */
1137 bool GeomPrimitive::
1138 request_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  */
1165 void GeomPrimitive::
1166 output(std::ostream &out) const {
1167  out << get_type() << ", " << get_num_primitives()
1168  << ", " << get_num_vertices();
1169 }
1170 
1171 /**
1172  *
1173  */
1174 void GeomPrimitive::
1175 write(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  */
1228 PT(GeomVertexArrayData) GeomPrimitive::
1229 modify_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  */
1253 set_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  */
1279 set_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  */
1310 modify_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  */
1341 set_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  */
1365 set_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  */
1386 clear_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  */
1402 int GeomPrimitive::
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  */
1415 int GeomPrimitive::
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  */
1430 int GeomPrimitive::
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  */
1444 prepare(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  */
1455 is_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  */
1476 prepare_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  */
1498 release(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  */
1516 release_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  */
1543 get_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  */
1585 void GeomPrimitive::
1586 clear_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  */
1602 int GeomPrimitive::
1603 get_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  */
1629 int GeomPrimitive::
1630 get_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  */
1657 calc_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  */
1826 calc_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  */
1889 CPT(GeomPrimitive) GeomPrimitive::
1890 decompose_impl() const {
1891  return this;
1892 }
1893 
1894 /**
1895  * The virtual implementation of rotate().
1896  */
1897 CPT(GeomVertexArrayData) GeomPrimitive::
1898 rotate_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  */
1907 CPT(GeomPrimitive) GeomPrimitive::
1908 doubleside_impl() const {
1909  return this;
1910 }
1911 
1912 /**
1913  * The virtual implementation of reverse().
1914  */
1915 CPT(GeomPrimitive) GeomPrimitive::
1916 reverse_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  */
1924 bool GeomPrimitive::
1925 requires_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  */
1938 void GeomPrimitive::
1939 append_unused_vertices(GeomVertexArrayData *, int) {
1940 }
1941 
1942 /**
1943  * Recomputes the _min_vertex and _max_vertex values if necessary.
1944  */
1945 void GeomPrimitive::
1946 recompute_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  */
2055 void GeomPrimitive::
2056 do_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  */
2081 void GeomPrimitive::
2082 consider_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  */
2112 void GeomPrimitive::
2113 do_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  */
2151 PT(GeomVertexArrayData) GeomPrimitive::
2152 do_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  */
2169 write_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  */
2181 finalize(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  */
2192 void GeomPrimitive::
2193 fillin(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  */
2203 CycleData *GeomPrimitive::CData::
2204 make_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  */
2212 void GeomPrimitive::CData::
2213 write_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  */
2228 int GeomPrimitive::CData::
2229 complete_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  */
2248 void GeomPrimitive::CData::
2249 fillin(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  */
2267 check_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  */
2300 int GeomPrimitivePipelineReader::
2301 get_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  */
2319 get_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  */
2349 int GeomPrimitivePipelineReader::
2350 get_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  */
2370 get_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  */
2414 bool GeomPrimitivePipelineReader::
2415 check_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...
Definition: bamReader.cxx:808
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
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.
Definition: bamReader.cxx:695
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.
Definition: bamWriter.cxx:425
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
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...
Definition: geomPrimitive.h:56
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...
Definition: geomPrimitive.h:73
get_vertex
Returns the ith vertex index in the table.
Definition: geomPrimitive.h:99
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.
Definition: geomPrimitive.h:81
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.
Definition: geomPrimitive.h:99
bool is_composite() const
Returns true if the primitive is a composite primitive such as a tristrip or trifan,...
Definition: geomPrimitive.I:74
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_primitive_max_vertex(int n) const
Returns the maximum vertex index number used by the nth primitive in this object.
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.
Definition: geomPrimitive.h:77
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.
Definition: geomPrimitive.I:86
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:1337
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.
Definition: internalName.h:38
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.
Definition: typedWritable.h:35
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(CopyOnWriteObject) GeomPrimitive
Required to implement CopyOnWriteObject.
CPT(GeomPrimitive) GeomPrimitive
Decomposes a complex primitive type into a simpler primitive type, for instance triangle strips to tr...
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...