Panda3D
geomVertexData.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 geomVertexData.cxx
10  * @author drose
11  * @date 2005-03-06
12  */
13 
14 #include "geomVertexData.h"
15 #include "geom.h"
16 #include "geomVertexReader.h"
17 #include "geomVertexWriter.h"
18 #include "geomVertexRewriter.h"
19 #include "pStatTimer.h"
20 #include "bamReader.h"
21 #include "bamWriter.h"
22 #include "pset.h"
23 #include "indent.h"
24 
25 using std::ostream;
26 
27 TypeHandle GeomVertexData::_type_handle;
28 TypeHandle GeomVertexData::CDataCache::_type_handle;
29 TypeHandle GeomVertexData::CacheEntry::_type_handle;
30 TypeHandle GeomVertexData::CData::_type_handle;
31 TypeHandle GeomVertexDataPipelineReader::_type_handle;
32 TypeHandle GeomVertexDataPipelineWriter::_type_handle;
33 
34 PStatCollector GeomVertexData::_convert_pcollector("*:Munge:Convert");
35 PStatCollector GeomVertexData::_scale_color_pcollector("*:Munge:Scale color");
36 PStatCollector GeomVertexData::_set_color_pcollector("*:Munge:Set color");
37 PStatCollector GeomVertexData::_animation_pcollector("*:Animation");
38 
39 
40 /**
41  * Constructs an invalid object. This is only used when reading from the bam
42  * file.
43  */
44 GeomVertexData::
45 GeomVertexData() :
46  _char_pcollector(_animation_pcollector, "unnamed"),
47  _skinning_pcollector(_char_pcollector, "Skinning"),
48  _morphs_pcollector(_char_pcollector, "Morphs"),
49  _blends_pcollector(_char_pcollector, "Calc blends")
50 {
51 }
52 
53 /**
54  * Required to implement CopyOnWriteObject.
55  */
56 PT(CopyOnWriteObject) GeomVertexData::
57 make_cow_copy() {
58  return new GeomVertexData(*this);
59 }
60 
61 /**
62  *
63  */
64 GeomVertexData::
65 GeomVertexData(const std::string &name,
66  const GeomVertexFormat *format,
67  GeomVertexData::UsageHint usage_hint) :
68  _name(name),
69  _char_pcollector(PStatCollector(_animation_pcollector, name)),
70  _skinning_pcollector(_char_pcollector, "Skinning"),
71  _morphs_pcollector(_char_pcollector, "Morphs"),
72  _blends_pcollector(_char_pcollector, "Calc blends"),
73  _cycler(GeomVertexData::CData(format, usage_hint))
74 {
75  nassertv(format->is_registered());
76 }
77 
78 /**
79  *
80  */
81 GeomVertexData::
82 GeomVertexData(const GeomVertexData &copy) :
83  CopyOnWriteObject(copy),
84  _name(copy._name),
85  _cycler(copy._cycler),
86  _char_pcollector(copy._char_pcollector),
87  _skinning_pcollector(copy._skinning_pcollector),
88  _morphs_pcollector(copy._morphs_pcollector),
89  _blends_pcollector(copy._blends_pcollector)
90 {
91  OPEN_ITERATE_ALL_STAGES(_cycler) {
92  CDStageWriter cdata(_cycler, pipeline_stage);
93  // It's important that we *not* copy the animated_vertices pointer.
94  cdata->_animated_vertices = nullptr;
95  cdata->_animated_vertices_modified = UpdateSeq();
96  }
97  CLOSE_ITERATE_ALL_STAGES(_cycler);
98 }
99 
100 /**
101  * This constructor copies all of the basic properties of the source
102  * VertexData, like usage_hint and animation tables, but does not copy the
103  * actual data, and it allows you to specify a different format.
104  */
105 GeomVertexData::
106 GeomVertexData(const GeomVertexData &copy,
107  const GeomVertexFormat *format) :
108  CopyOnWriteObject(copy),
109  _name(copy._name),
110  _cycler(copy._cycler),
111  _char_pcollector(copy._char_pcollector),
112  _skinning_pcollector(copy._skinning_pcollector),
113  _morphs_pcollector(copy._morphs_pcollector),
114  _blends_pcollector(copy._blends_pcollector)
115 {
116  nassertv(format->is_registered());
117 
118  // Create some empty arrays as required by the format.
119  OPEN_ITERATE_ALL_STAGES(_cycler) {
120  CDStageWriter cdata(_cycler, pipeline_stage);
121 
122  UsageHint usage_hint = cdata->_usage_hint;
123  cdata->_arrays.clear();
124  cdata->_format = format;
125  int num_arrays = format->get_num_arrays();
126  for (int i = 0; i < num_arrays; i++) {
128  (format->get_array(i), usage_hint);
129  cdata->_arrays.push_back(array.p());
130  }
131 
132  // It's important that we *not* copy the animated_vertices pointer.
133  cdata->_animated_vertices = nullptr;
134  cdata->_animated_vertices_modified = UpdateSeq();
135  }
136  CLOSE_ITERATE_ALL_STAGES(_cycler);
137 }
138 
139 /**
140  * The copy assignment operator is not pipeline-safe. This will completely
141  * obliterate all stages of the pipeline, so don't do it for a GeomVertexData
142  * that is actively being used for rendering.
143  */
145 operator = (const GeomVertexData &copy) {
146  CopyOnWriteObject::operator = (copy);
147 
148  clear_cache();
149 
150  _name = copy._name;
151  _cycler = copy._cycler;
152  _char_pcollector = copy._char_pcollector;
153  _skinning_pcollector = copy._skinning_pcollector;
154  _morphs_pcollector = copy._morphs_pcollector;
155  _blends_pcollector = copy._blends_pcollector;
156 
157  OPEN_ITERATE_ALL_STAGES(_cycler) {
158  CDStageWriter cdata(_cycler, pipeline_stage);
159  cdata->_modified = Geom::get_next_modified();
160  cdata->_animated_vertices = nullptr;
161  cdata->_animated_vertices_modified = UpdateSeq();
162  }
163  CLOSE_ITERATE_ALL_STAGES(_cycler);
164 }
165 
166 /**
167  *
168  */
169 GeomVertexData::
170 ~GeomVertexData() {
171  clear_cache();
172 }
173 
174 /**
175  * Returns 0 if the two objects are equivalent, even if they are not the same
176  * pointer.
177  */
179 compare_to(const GeomVertexData &other) const {
180  CDReader cdata(_cycler);
181  CDReader other_cdata(other._cycler);
182 
183  if (cdata->_usage_hint != other_cdata->_usage_hint) {
184  return (int)cdata->_usage_hint - (int)other_cdata->_usage_hint;
185  }
186  if (cdata->_format != other_cdata->_format) {
187  return cdata->_format < other_cdata->_format ? -1 : 1;
188  }
189  if (cdata->_transform_table != other_cdata->_transform_table) {
190  return cdata->_transform_table < other_cdata->_transform_table ? -1 : 1;
191  }
192  if (cdata->_transform_blend_table != other_cdata->_transform_blend_table) {
193  return cdata->_transform_blend_table < other_cdata->_transform_blend_table ? -1 : 1;
194  }
195  if (cdata->_slider_table != other_cdata->_slider_table) {
196  return cdata->_slider_table < other_cdata->_slider_table ? -1 : 1;
197  }
198  if (cdata->_arrays.size() != other_cdata->_arrays.size()) {
199  return (int)cdata->_arrays.size() - (int)other_cdata->_arrays.size();
200  }
201  for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
202  if (cdata->_arrays[i] != other_cdata->_arrays[i]) {
203  return cdata->_arrays[i] < other_cdata->_arrays[i] ? -1 : 1;
204  }
205  }
206 
207  return 0;
208 }
209 
210 /**
211  * Changes the name of the vertex data. This name is reported on the PStats
212  * graph for vertex computations.
213  */
214 void GeomVertexData::
215 set_name(const std::string &name) {
216  _name = name;
217  _char_pcollector = PStatCollector(_animation_pcollector, name);
218  _skinning_pcollector = PStatCollector(_char_pcollector, "Skinning");
219  _morphs_pcollector = PStatCollector(_char_pcollector, "Morphs");
220  _blends_pcollector = PStatCollector(_char_pcollector, "Calc blends");
221 }
222 
223 /**
224  * Changes the UsageHint hint for this vertex data, and for all of the arrays
225  * that share this data. See get_usage_hint().
226  *
227  * Don't call this in a downstream thread unless you don't mind it blowing
228  * away other changes you might have recently made in an upstream thread.
229  */
230 void GeomVertexData::
231 set_usage_hint(GeomVertexData::UsageHint usage_hint) {
232  CDWriter cdata(_cycler, true);
233  cdata->_usage_hint = usage_hint;
234 
235  Arrays::iterator ai;
236  for (ai = cdata->_arrays.begin();
237  ai != cdata->_arrays.end();
238  ++ai) {
239  PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
240  array_obj->set_usage_hint(usage_hint);
241  }
243  cdata->_modified = Geom::get_next_modified();
244  cdata->_animated_vertices_modified = UpdateSeq();
245 }
246 
247 /**
248  * Changes the format of the vertex data. If the data is not empty, this will
249  * implicitly change every row to match the new format.
250  *
251  * Don't call this in a downstream thread unless you don't mind it blowing
252  * away other changes you might have recently made in an upstream thread.
253  */
254 void GeomVertexData::
255 set_format(const GeomVertexFormat *format) {
256  Thread *current_thread = Thread::get_current_thread();
257  nassertv(format->is_registered());
258 
259  CDLockedReader cdata(_cycler, current_thread);
260 
261  if (format == cdata->_format) {
262  // Trivially no-op.
263  return;
264  }
265 
266  CDWriter cdataw(_cycler, cdata, true);
267 
268  // Put the current data aside, so we can copy it back in below.
269  CPT(GeomVertexData) orig_data = new GeomVertexData(*this);
270 
271  // Assign the new format. This means clearing out all of our current arrays
272  // and replacing them with new, empty arrays.
273  cdataw->_format = format;
274 
275  UsageHint usage_hint = cdataw->_usage_hint;
276  cdataw->_arrays.clear();
277  int num_arrays = cdataw->_format->get_num_arrays();
278  for (int i = 0; i < num_arrays; i++) {
280  (cdataw->_format->get_array(i), usage_hint);
281  cdataw->_arrays.push_back(array.p());
282  }
283 
284  // Now copy the original data back in. This will automatically convert it
285  // to the new format.
286  copy_from(orig_data, false, current_thread);
287 
289  cdataw->_modified = Geom::get_next_modified();
290  cdataw->_animated_vertices.clear();
291 }
292 
293 /**
294  * Changes the format of the vertex data, without reformatting the data to
295  * match. The data is exactly the same after this operation, but will be
296  * reinterpreted according to the new format. This assumes that the new
297  * format is fundamentally compatible with the old format; in particular, it
298  * must have the same number of arrays with the same stride in each one. No
299  * checking is performed that the data remains sensible.
300  */
302 unclean_set_format(const GeomVertexFormat *format) {
303  Thread *current_thread = Thread::get_current_thread();
304  nassertv(format->is_registered());
305 
306  CDLockedReader cdata(_cycler, current_thread);
307 
308  if (format == cdata->_format) {
309  // Trivially no-op.
310  return;
311  }
312 
313 #ifndef NDEBUG
314  nassertv(format->get_num_arrays() == cdata->_format->get_num_arrays());
315  for (size_t ai = 0; ai < format->get_num_arrays(); ++ai) {
316  nassertv(format->get_array(ai)->get_stride() == cdata->_format->get_array(ai)->get_stride());
317  }
318  nassertv(cdata->_arrays.size() == cdata->_format->get_num_arrays());
319 #endif // NDEBUG
320 
321  CDWriter cdataw(_cycler, cdata, true);
322 
323  // Assign the new format.
324  cdataw->_format = format;
325 
326  for (size_t ai = 0; ai < cdataw->_arrays.size(); ++ai) {
327  PT(GeomVertexArrayData) array_obj = cdataw->_arrays[ai].get_write_pointer();
328  array_obj->_array_format = format->get_array(ai);
329  }
330 
332  cdataw->_modified = Geom::get_next_modified();
333  cdataw->_animated_vertices.clear();
334 }
335 
336 /**
337  * Removes all of the rows from the arrays; functionally equivalent to
338  * set_num_rows(0) (but faster).
339  *
340  * Don't call this in a downstream thread unless you don't mind it blowing
341  * away other changes you might have recently made in an upstream thread.
342  */
344 clear_rows() {
345  Thread *current_thread = Thread::get_current_thread();
346  CDWriter cdata(_cycler, true, current_thread);
347  nassertv(cdata->_format->get_num_arrays() == cdata->_arrays.size());
348 
349  Arrays::iterator ai;
350  for (ai = cdata->_arrays.begin();
351  ai != cdata->_arrays.end();
352  ++ai) {
353  PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
354  array_obj->clear_rows();
355  }
357  cdata->_modified = Geom::get_next_modified();
358  cdata->_animated_vertices.clear();
359 }
360 
361 /**
362  * Replaces the TransformTable on this vertex data with the indicated table.
363  * The length of this table should be consistent with the maximum table index
364  * assigned to the vertices under the "transform_index" name.
365  *
366  * Don't call this in a downstream thread unless you don't mind it blowing
367  * away other changes you might have recently made in an upstream thread.
368  */
369 void GeomVertexData::
370 set_transform_table(const TransformTable *table) {
371  Thread *current_thread = Thread::get_current_thread();
372  nassertv(table == nullptr || table->is_registered());
373 
374  CDWriter cdata(_cycler, true, current_thread);
375  cdata->_transform_table = (TransformTable *)table;
377  cdata->_modified = Geom::get_next_modified();
378  cdata->_animated_vertices_modified = UpdateSeq();
379 }
380 
381 /**
382  * Returns a modifiable pointer to the current TransformBlendTable on this
383  * vertex data, if any, or NULL if there is not a TransformBlendTable. See
384  * get_transform_blend_table().
385  *
386  * Don't call this in a downstream thread unless you don't mind it blowing
387  * away other changes you might have recently made in an upstream thread.
388  */
389 PT(TransformBlendTable) GeomVertexData::
390 modify_transform_blend_table() {
391  CDWriter cdata(_cycler, true);
392 
394  cdata->_modified = Geom::get_next_modified();
395  cdata->_animated_vertices_modified = UpdateSeq();
396 
397  return cdata->_transform_blend_table.get_write_pointer();
398 }
399 
400 /**
401  * Replaces the TransformBlendTable on this vertex data with the indicated
402  * table. The length of this table should be consistent with the maximum
403  * table index assigned to the vertices under the "transform_blend" name.
404  *
405  * Don't call this in a downstream thread unless you don't mind it blowing
406  * away other changes you might have recently made in an upstream thread.
407  */
410  CDWriter cdata(_cycler, true);
411  cdata->_transform_blend_table = (TransformBlendTable *)table;
413  cdata->_modified = Geom::get_next_modified();
414  cdata->_animated_vertices_modified = UpdateSeq();
415 }
416 
417 /**
418  * Replaces the SliderTable on this vertex data with the indicated table.
419  * There should be an entry in this table for each kind of morph offset
420  * defined in the vertex data.
421  *
422  * The SliderTable object must have been registered prior to setting it on the
423  * GeomVertexData.
424  *
425  * Don't call this in a downstream thread unless you don't mind it blowing
426  * away other changes you might have recently made in an upstream thread.
427  */
428 void GeomVertexData::
429 set_slider_table(const SliderTable *table) {
430  nassertv(table == nullptr || table->is_registered());
431 
432  CDWriter cdata(_cycler, true);
433  cdata->_slider_table = (SliderTable *)table;
435  cdata->_modified = Geom::get_next_modified();
436  cdata->_animated_vertices_modified = UpdateSeq();
437 }
438 
439 /**
440  * Returns true if the vertex data is currently resident in memory. If this
441  * returns false, the vertex data will be brought back into memory shortly;
442  * try again later.
443  */
445 request_resident() const {
446  CDReader cdata(_cycler);
447 
448  bool resident = true;
449 
450  Arrays::const_iterator ai;
451  for (ai = cdata->_arrays.begin();
452  ai != cdata->_arrays.end();
453  ++ai) {
454  if (!(*ai).get_read_pointer()->request_resident()) {
455  resident = false;
456  }
457  }
458 
459  return resident;
460 }
461 
462 /**
463  * Copies all the data from the other array into the corresponding data types
464  * in this array, by matching data types name-by-name.
465  *
466  * keep_data_objects specifies what to do when one or more of the arrays can
467  * be copied without the need to apply any conversion operation. If it is
468  * true, the original GeomVertexArrayData objects in this object are retained,
469  * and their data arrays are copied byte-by-byte from the source; if it is
470  * false, then the GeomVertexArrayData objects are copied pointerwise from the
471  * source.
472  *
473  * Don't call this in a downstream thread unless you don't mind it blowing
474  * away other changes you might have recently made in an upstream thread.
475  */
477 copy_from(const GeomVertexData *source, bool keep_data_objects,
478  Thread *current_thread) {
479  const GeomVertexFormat *source_format = source->get_format();
480  const GeomVertexFormat *dest_format = get_format();
481 
482  int num_rows = source->get_num_rows();
483  int num_arrays = source_format->get_num_arrays();
484  int source_i;
485 
486  // First, check to see if any arrays can be simply appropriated for the new
487  // format, without changing the data.
488  pset<int> done_arrays;
489 
490  for (source_i = 0; source_i < num_arrays; ++source_i) {
491  const GeomVertexArrayFormat *source_array_format =
492  source_format->get_array(source_i);
493 
494  bool array_done = false;
495 
496  int dest_num_arrays = dest_format->get_num_arrays();
497  for (int dest_i = 0;
498  dest_i < dest_num_arrays && !array_done;
499  ++dest_i) {
500  const GeomVertexArrayFormat *dest_array_format =
501  dest_format->get_array(dest_i);
502  if (dest_array_format->is_data_subset_of(*source_array_format)) {
503  // Great! Just use the same data for this one.
504  if (keep_data_objects) {
505  // Copy the data, but keep the same GeomVertexArrayData object.
506 
507  modify_array_handle(dest_i)->copy_data_from(source->get_array_handle(source_i));
508  } else {
509  // Copy the GeomVertexArrayData object.
510  if (get_array(dest_i) != source->get_array(source_i)) {
511  set_array(dest_i, source->get_array(source_i));
512  }
513  }
514 
515  array_done = true;
516  done_arrays.insert(dest_i);
517  }
518  }
519  }
520 
521  // Now make sure the arrays we didn't share are all filled in.
522  {
524  writer.check_array_writers();
525  writer.reserve_num_rows(num_rows);
526  writer.set_num_rows(num_rows);
527  }
528 
529  // Now go back through and copy any data that's left over.
530  for (source_i = 0; source_i < num_arrays; ++source_i) {
531  CPT(GeomVertexArrayDataHandle) array_handle = source->get_array_handle(source_i);
532  const unsigned char *array_data = array_handle->get_read_pointer(true);
533  const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
534  int num_columns = source_array_format->get_num_columns();
535  for (int di = 0; di < num_columns; ++di) {
536  const GeomVertexColumn *source_column = source_array_format->get_column(di);
537 
538  int dest_i = dest_format->get_array_with(source_column->get_name());
539  if (dest_i >= 0 && done_arrays.count(dest_i) == 0) {
540  // The data type exists in the new format; we have to copy it.
541  const GeomVertexArrayFormat *dest_array_format =
542  dest_format->get_array(dest_i);
543  const GeomVertexColumn *dest_column =
544  dest_array_format->get_column(source_column->get_name());
545  nassertv(dest_column != nullptr);
546 
547  if (dest_column->is_bytewise_equivalent(*source_column)) {
548  // We can do a quick bytewise copy.
549  PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i);
550  unsigned char *dest_array_data = dest_handle->get_write_pointer();
551 
552  bytewise_copy(dest_array_data + dest_column->get_start(),
553  dest_array_format->get_stride(),
554  array_data + source_column->get_start(), source_array_format->get_stride(),
555  source_column, num_rows);
556 
557  } else if (dest_column->is_packed_argb() &&
558  source_column->is_uint8_rgba()) {
559  // A common special case: OpenGL color to DirectX color.
560  PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i);
561  unsigned char *dest_array_data = dest_handle->get_write_pointer();
562 
563  uint8_rgba_to_packed_argb
564  (dest_array_data + dest_column->get_start(),
565  dest_array_format->get_stride(),
566  array_data + source_column->get_start(), source_array_format->get_stride(),
567  num_rows);
568 
569  } else if (dest_column->is_uint8_rgba() &&
570  source_column->is_packed_argb()) {
571  // Another common special case: DirectX color to OpenGL color.
572  PT(GeomVertexArrayDataHandle) dest_handle = modify_array_handle(dest_i);
573  unsigned char *dest_array_data = dest_handle->get_write_pointer();
574 
575  packed_argb_to_uint8_rgba
576  (dest_array_data + dest_column->get_start(),
577  dest_array_format->get_stride(),
578  array_data + source_column->get_start(), source_array_format->get_stride(),
579  num_rows);
580 
581  } else {
582  // A generic copy.
583  if (gobj_cat.is_debug()) {
584  gobj_cat.debug()
585  << "generic copy " << *dest_column << " from "
586  << *source_column << "\n";
587  }
588  GeomVertexWriter to(this);
589  to.set_column(dest_i, dest_column);
590  GeomVertexReader from(source);
591  from.set_column(source_i, source_column);
592 
593  while (!from.is_at_end()) {
594  to.set_data4(from.get_data4());
595  }
596  }
597  }
598  }
599  }
600 
601  // Also convert the animation tables as necessary.
602  const GeomVertexAnimationSpec &source_animation = source_format->get_animation();
603  const GeomVertexAnimationSpec &dest_animation = dest_format->get_animation();
604  if (source_animation != dest_animation) {
605  if (dest_animation.get_animation_type() == AT_hardware) {
606  // Convert Panda-style animation tables to hardware-style animation
607  // tables.
608  CPT(TransformBlendTable) blend_table = source->get_transform_blend_table();
609  if (blend_table != nullptr) {
610  PT(TransformTable) transform_table = new TransformTable;
611  TransformMap already_added;
612 
613  if (dest_animation.get_indexed_transforms()) {
614  // Build an indexed transform array. This is easier; this means we
615  // can put the blends in any order.
616  GeomVertexWriter weight(this, InternalName::get_transform_weight());
617  GeomVertexWriter index(this, InternalName::get_transform_index());
618  GeomVertexReader from(source, InternalName::get_transform_blend());
619 
620  while (!from.is_at_end()) {
621  const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
622  LVecBase4 weights = LVecBase4::zero();
623  LVecBase4i indices(0, 0, 0, 0);
624 
625  if (blend.get_num_transforms() <= 4) {
626  for (size_t i = 0; i < blend.get_num_transforms(); i++) {
627  weights[i] = blend.get_weight(i);
628  indices[i] = add_transform(transform_table, blend.get_transform(i),
629  already_added);
630  }
631  } else {
632  // Limit the number of blends to the four with highest weights.
633  TransformBlend blend2(blend);
634  blend2.limit_transforms(4);
635  blend2.normalize_weights();
636 
637  for (size_t i = 0; i < 4; i++) {
638  weights[i] = blend2.get_weight(i);
639  indices[i] = add_transform(transform_table, blend2.get_transform(i),
640  already_added);
641  }
642  }
643 
644  if (weight.has_column()) {
645  weight.set_data4(weights);
646  }
647  index.set_data4i(indices);
648  }
649  } else {
650  // Build a nonindexed transform array. This means we have to use
651  // the same n transforms, in the same order, for each vertex.
652  GeomVertexWriter weight(this, InternalName::get_transform_weight());
653  GeomVertexReader from(source, InternalName::get_transform_blend());
654 
655  while (!from.is_at_end()) {
656  const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
657  LVecBase4 weights = LVecBase4::zero();
658 
659  for (size_t i = 0; i < blend.get_num_transforms(); i++) {
660  int index = add_transform(transform_table, blend.get_transform(i),
661  already_added);
662  nassertv(index < 4);
663  weights[index] = blend.get_weight(i);
664  }
665  if (weight.has_column()) {
666  weight.set_data4(weights);
667  }
668  }
669  }
670 
671  clear_transform_blend_table();
672  set_transform_table(TransformTable::register_table(transform_table));
673  }
674  }
675  }
676 }
677 
678 /**
679  * Copies a single row of the data from the other array into the indicated row
680  * of this array. In this case, the source format must exactly match the
681  * destination format.
682  *
683  * Don't call this in a downstream thread unless you don't mind it blowing
684  * away other changes you might have recently made in an upstream thread.
685  */
687 copy_row_from(int dest_row, const GeomVertexData *source,
688  int source_row, Thread *current_thread) {
689 
690  GeomVertexDataPipelineReader reader(source, current_thread);
691  reader.check_array_readers();
692 
693  GeomVertexDataPipelineWriter writer(this, true, current_thread);
694  writer.check_array_writers();
695  writer.copy_row_from(dest_row, reader, source_row);
696 }
697 
698 /**
699  * Returns a new GeomVertexData that represents the same contents as this one,
700  * with all data types matched up name-by-name to the indicated new format.
701  */
702 CPT(GeomVertexData) GeomVertexData::
703 convert_to(const GeomVertexFormat *new_format) const {
704  Thread *current_thread = Thread::get_current_thread();
705 
706  if (new_format == get_format()) {
707  // Trivial case: no change is needed.
708  return this;
709  }
710 
711  // Look up the new format in our cache--maybe we've recently applied it.
712  PT(CacheEntry) entry;
713 
714  CacheKey key(new_format);
715 
716  _cache_lock.acquire();
717  Cache::const_iterator ci = _cache.find(&key);
718  if (ci == _cache.end()) {
719  _cache_lock.release();
720 
721  } else {
722  entry = (*ci).second;
723  _cache_lock.release();
724  nassertr(entry->_source == this, nullptr);
725 
726  // Here's an element in the cache for this computation. Record a cache
727  // hit, so this element will stay in the cache a while longer.
728  entry->refresh(current_thread);
729 
730  CDCacheReader cdata(entry->_cycler);
731  if (cdata->_result != nullptr) {
732  return cdata->_result;
733  }
734 
735  // The cache entry is stale, but we'll recompute it below. Note that
736  // there's a small race condition here; another thread might recompute the
737  // cache at the same time. No big deal, since it'll compute the same
738  // result.
739  }
740 
741  // Okay, convert the data to the new format.
742  if (gobj_cat.is_debug()) {
743  gobj_cat.debug()
744  << "Converting " << get_num_rows() << " rows from " << *get_format()
745  << " to " << *new_format << "\n";
746  }
747  PStatTimer timer(_convert_pcollector);
748 
749  PT(GeomVertexData) new_data =
750  new GeomVertexData(get_name(), new_format, get_usage_hint());
751  new_data->set_transform_blend_table(get_transform_blend_table());
752  new_data->set_slider_table(get_slider_table());
753 
754  new_data->copy_from(this, false);
755 
756  // Record the new result in the cache.
757  if (entry == nullptr) {
758  // Create a new entry for the result.
759  // We don't need the key anymore, move the pointers into the CacheEntry.
760  entry = new CacheEntry((GeomVertexData *)this, std::move(key));
761 
762  {
763  LightMutexHolder holder(_cache_lock);
764  bool inserted = ((GeomVertexData *)this)->_cache.insert(Cache::value_type(&entry->_key, entry)).second;
765  if (!inserted) {
766  // Some other thread must have beat us to the punch. Never mind.
767  return new_data;
768  }
769  }
770 
771  // And tell the cache manager about the new entry. (It might immediately
772  // request a delete from the cache of the thing we just added.)
773  entry->record(current_thread);
774  }
775 
776  // Finally, store the cached result on the entry.
777  CDCacheWriter cdata(entry->_cycler, true, current_thread);
778  cdata->_result = new_data;
779 
780  return new_data;
781 }
782 
783 /**
784  * Returns a new GeomVertexData object with the color table modified in-place
785  * to apply the indicated scale.
786  *
787  * If the vertex data does not include a color column, a new one will not be
788  * added.
789  */
790 CPT(GeomVertexData) GeomVertexData::
791 scale_color(const LVecBase4 &color_scale) const {
792  const GeomVertexColumn *old_column =
793  get_format()->get_column(InternalName::get_color());
794  if (old_column == nullptr) {
795  return this;
796  }
797 
798  PT(GeomVertexData) new_data = new GeomVertexData(*this);
799  GeomVertexRewriter data(new_data, InternalName::get_color());
800  while (!data.is_at_end()) {
801  LColor color = data.get_data4();
802  data.set_data4(color[0] * color_scale[0],
803  color[1] * color_scale[1],
804  color[2] * color_scale[2],
805  color[3] * color_scale[3]);
806  }
807 
808  return new_data;
809 }
810 
811 /**
812  * Returns a new GeomVertexData object with the color table replaced with a
813  * new color table that has been scaled by the indicated value. The new color
814  * table will be added as a new array; if the old color table was interleaved
815  * with a previous array, the previous array will not be repacked.
816  */
817 CPT(GeomVertexData) GeomVertexData::
818 scale_color(const LVecBase4 &color_scale, int num_components,
819  GeomVertexData::NumericType numeric_type,
820  GeomVertexData::Contents contents) const {
821  int old_color_array = get_format()->get_array_with(InternalName::get_color());
822  if (old_color_array == -1) {
823  // Oops, no color anyway.
824  return set_color(color_scale, num_components, numeric_type, contents);
825  }
826 
827  int num_rows = get_num_rows();
828 
829  if (gobj_cat.is_debug()) {
830  gobj_cat.debug()
831  << "Scaling color for " << num_rows << " vertices by "
832  << color_scale << ".\n";
833  }
834  PStatTimer timer(_scale_color_pcollector);
835 
836  PT(GeomVertexData) new_data = replace_column
837  (InternalName::get_color(), num_components, numeric_type, contents);
838 
839  // Now go through and apply the scale, copying it to the new data.
840  GeomVertexWriter to(new_data, InternalName::get_color());
841  GeomVertexReader from(this, InternalName::get_color());
842 
843  for (int i = 0; i < num_rows; i++) {
844  LColor color = from.get_data4();
845  to.set_data4(color[0] * color_scale[0],
846  color[1] * color_scale[1],
847  color[2] * color_scale[2],
848  color[3] * color_scale[3]);
849  }
850 
851  return new_data;
852 }
853 
854 /**
855  * Returns a new GeomVertexData object with the color data modified in-place
856  * with the new value.
857  *
858  * If the vertex data does not include a color column, a new one will not be
859  * added.
860  */
861 CPT(GeomVertexData) GeomVertexData::
862 set_color(const LColor &color) const {
863  const GeomVertexColumn *old_column =
864  get_format()->get_column(InternalName::get_color());
865  if (old_column == nullptr) {
866  return this;
867  }
868 
869  PT(GeomVertexData) new_data = new GeomVertexData(*this);
870  do_set_color(new_data, color);
871  return new_data;
872 }
873 
874 /**
875  * Returns a new GeomVertexData object with the color table replaced with a
876  * new color table for which each vertex has the indicated value. The new
877  * color table will be added as a new array; if the old color table was
878  * interleaved with a previous array, the previous array will not be repacked.
879  */
880 CPT(GeomVertexData) GeomVertexData::
881 set_color(const LColor &color, int num_components,
882  GeomVertexData::NumericType numeric_type,
883  GeomVertexData::Contents contents) const {
884  if (gobj_cat.is_debug()) {
885  gobj_cat.debug()
886  << "Setting color for " << get_num_rows() << " vertices to "
887  << color << ".\n";
888  }
889  PStatTimer timer(_set_color_pcollector);
890 
891  PT(GeomVertexData) new_data = replace_column
892  (InternalName::get_color(), num_components, numeric_type, contents);
893 
894  do_set_color(new_data, color);
895  return new_data;
896 }
897 
898 /**
899  * Returns a new GeomVertexData object with the normal data modified in-place,
900  * so that each lighting normal is now facing in the opposite direction.
901  *
902  * If the vertex data does not include a normal column, this returns the
903  * original GeomVertexData object, unchanged.
904  */
905 CPT(GeomVertexData) GeomVertexData::
906 reverse_normals() const {
907  const GeomVertexColumn *old_column =
908  get_format()->get_column(InternalName::get_normal());
909  if (old_column == nullptr) {
910  return this;
911  }
912 
913  PT(GeomVertexData) new_data = new GeomVertexData(*this);
914  GeomVertexRewriter to(new_data, InternalName::get_normal());
915  while (!to.is_at_end()) {
916  to.set_data3(-to.get_data3());
917  }
918 
919  return new_data;
920 }
921 
922 /**
923  * Returns a GeomVertexData that represents the results of computing the
924  * vertex animation on the CPU for this GeomVertexData.
925  *
926  * If there is no CPU-defined vertex animation on this object, this just
927  * returns the original object.
928  *
929  * If there is vertex animation, but the VertexTransform values have not
930  * changed since last time, this may return the same pointer it returned
931  * previously. Even if the VertexTransform values have changed, it may still
932  * return the same pointer, but with its contents modified (this is preferred,
933  * since it allows the graphics backend to update vertex buffers optimally).
934  *
935  * If force is false, this method may return immediately with stale data, if
936  * the vertex data is not completely resident. If force is true, this method
937  * will never return stale data, but may block until the data is available.
938  */
939 CPT(GeomVertexData) GeomVertexData::
940 animate_vertices(bool force, Thread *current_thread) const {
941 #ifdef DO_PIPELINING
942  {
943  // In the pipelining case, we take a simple short-route optimization: if
944  // the vdata isn't animated, we don't need to grab any mutex first.
945  CDReader cdata(_cycler, current_thread);
946  if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
947  return this;
948  }
949  }
950 #endif // DO_PIPELINING
951 
952  PStatTimer timer(((GeomVertexData *)this)->_char_pcollector, current_thread);
953 
954  // Now that we've short-circuited the short route, we reasonably believe the
955  // vdata is animated. Grab the mutex and make sure it's still animated
956  // after we've acquired it.
957  CDLockedReader cdata(_cycler, current_thread);
958  if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
959  return this;
960  }
961 
962  UpdateSeq modified;
963  {
964  PStatTimer timer2(((GeomVertexData *)this)->_blends_pcollector, current_thread);
965  if (!cdata->_transform_blend_table.is_null()) {
966  if (cdata->_slider_table != nullptr) {
967  modified =
968  std::max(cdata->_transform_blend_table.get_read_pointer()->get_modified(current_thread),
969  cdata->_slider_table->get_modified(current_thread));
970  } else {
971  modified = cdata->_transform_blend_table.get_read_pointer()->get_modified(current_thread);
972  }
973 
974  } else if (cdata->_slider_table != nullptr) {
975  modified = cdata->_slider_table->get_modified(current_thread);
976 
977  } else {
978  // No transform blend table or slider table--ergo, no vertex animation.
979  return this;
980  }
981  }
982 
983  if (cdata->_animated_vertices_modified == modified &&
984  cdata->_animated_vertices != nullptr) {
985  // No changes.
986  return cdata->_animated_vertices;
987  }
988 
989  if (!force && !request_resident()) {
990  // The vertex data isn't resident. Return the best information we've got.
991  if (cdata->_animated_vertices != nullptr) {
992  return cdata->_animated_vertices;
993  }
994  return this;
995  }
996 
997  CDWriter cdataw(((GeomVertexData *)this)->_cycler, cdata, false);
998  cdataw->_animated_vertices_modified = modified;
999  ((GeomVertexData *)this)->update_animated_vertices(cdataw, current_thread);
1000 
1001  return cdataw->_animated_vertices;
1002 }
1003 
1004 /**
1005  * Removes the cache of animated vertices computed by a previous call to
1006  * animate_vertices() within the same frame. This will force the next call to
1007  * animate_vertices() to recompute these values from scratch. Normally it is
1008  * not necessary to call this.
1009  */
1010 void GeomVertexData::
1011 clear_animated_vertices() {
1012  CDWriter cdata(_cycler, true);
1013  cdata->_animated_vertices_modified.clear();
1014  cdata->_animated_vertices.clear();
1015 }
1016 
1017 
1018 /**
1019  * Applies the indicated transform matrix to all of the vertices in the
1020  * GeomVertexData. The transform is applied to all "point" and "vector" type
1021  * columns described in the format.
1022  */
1024 transform_vertices(const LMatrix4 &mat) {
1025  transform_vertices(mat, 0, get_num_rows());
1026 }
1027 
1028 /**
1029  * Applies the indicated transform matrix to all of the vertices from
1030  * begin_row up to but not including end_row. The transform is applied to all
1031  * "point" and "vector" type columns described in the format.
1032  */
1034 transform_vertices(const LMatrix4 &mat, int begin_row, int end_row) {
1035  if (end_row <= begin_row) {
1036  // Trivial no-op.
1037  return;
1038  }
1039 
1040  const GeomVertexFormat *format = get_format();
1041 
1042  size_t ci;
1043  for (ci = 0; ci < format->get_num_points(); ci++) {
1044  GeomVertexRewriter data(this, format->get_point(ci));
1045  do_transform_point_column(format, data, mat, begin_row, end_row);
1046  }
1047 
1048  for (ci = 0; ci < format->get_num_vectors(); ci++) {
1049  GeomVertexRewriter data(this, format->get_vector(ci));
1050  do_transform_vector_column(format, data, mat, begin_row, end_row);
1051  }
1052 }
1053 
1054 /**
1055  * Applies the indicated transform matrix to all of the vertices mentioned in
1056  * the sparse array. The transform is applied to all "point" and "vector"
1057  * type columns described in the format.
1058  */
1060 transform_vertices(const LMatrix4 &mat, const SparseArray &rows) {
1061  if (rows.is_zero()) {
1062  // Trivial no-op.
1063  return;
1064  }
1065 
1066  const GeomVertexFormat *format = get_format();
1067 
1068  size_t ci;
1069  for (ci = 0; ci < format->get_num_points(); ci++) {
1070  GeomVertexRewriter data(this, format->get_point(ci));
1071 
1072  for (size_t i = 0; i < rows.get_num_subranges(); ++i) {
1073  int begin_row = rows.get_subrange_begin(i);
1074  int end_row = rows.get_subrange_end(i);
1075  do_transform_point_column(format, data, mat, begin_row, end_row);
1076  }
1077  }
1078 
1079  for (ci = 0; ci < format->get_num_vectors(); ci++) {
1080  GeomVertexRewriter data(this, format->get_vector(ci));
1081 
1082  for (size_t i = 0; i < rows.get_num_subranges(); ++i) {
1083  int begin_row = rows.get_subrange_begin(i);
1084  int end_row = rows.get_subrange_end(i);
1085  do_transform_vector_column(format, data, mat, begin_row, end_row);
1086  }
1087  }
1088 }
1089 
1090 /**
1091  * Fills in the color column of the given vertex data object with a constant
1092  * color. Assumes that there is already a color column present.
1093  */
1094 void GeomVertexData::
1095 do_set_color(GeomVertexData *vdata, const LColor &color) {
1096  // This function is used relatively often (by the SceneGraphReducer, when
1097  // flattening colors, and by the munger, when munging colors), so I've
1098  // written out a version that avoids the performance overhead of the packer
1099  // and GeomVertexWriter.
1100 
1101  const GeomVertexFormat *format = vdata->get_format();
1102  const GeomVertexColumn *column;
1103  int array_index;
1104  if (!format->get_array_info(InternalName::get_color(), array_index, column)) {
1105  nassert_raise("no color column");
1106  return;
1107  }
1108 
1109  size_t stride = format->get_array(array_index)->get_stride();
1110 
1111  GeomVertexColumn::Packer *packer = column->_packer;
1112  nassertv(packer != nullptr);
1113 
1114  // Pack into a buffer, which we will then copy.
1115  unsigned char buffer[32];
1116  size_t bufsize = column->get_total_bytes();
1117 #ifdef STDFLOAT_DOUBLE
1118  packer->set_data4d(buffer, color);
1119 #else
1120  packer->set_data4f(buffer, color);
1121 #endif
1122 
1123  PT(GeomVertexArrayDataHandle) handle = vdata->modify_array_handle(array_index);
1124  unsigned char *write_ptr = handle->get_write_pointer();
1125  unsigned char *end_ptr = write_ptr + handle->get_data_size_bytes();
1126  write_ptr += column->get_start();
1127 
1128  if (bufsize == 4) {
1129  // Most common case.
1130  while (write_ptr < end_ptr) {
1131  write_ptr[0] = buffer[0];
1132  write_ptr[1] = buffer[1];
1133  write_ptr[2] = buffer[2];
1134  write_ptr[3] = buffer[3];
1135  write_ptr += stride;
1136  }
1137  } else {
1138  while (write_ptr < end_ptr) {
1139  memcpy(write_ptr, buffer, bufsize);
1140  write_ptr += stride;
1141  }
1142  }
1143 }
1144 
1145 /**
1146  * Quickly copies data without the need to convert it.
1147  */
1148 void GeomVertexData::
1149 bytewise_copy(unsigned char *to, int to_stride,
1150  const unsigned char *from, int from_stride,
1151  const GeomVertexColumn *from_type,
1152  int num_records) {
1153  if (gobj_cat.is_debug()) {
1154  gobj_cat.debug()
1155  << "bytewise_copy(" << (void *)to << ", " << to_stride
1156  << ", " << (const void *)from << ", " << from_stride
1157  << ", " << *from_type << ", " << num_records << ")\n";
1158  }
1159  if (to_stride == from_type->get_total_bytes() &&
1160  from_stride == from_type->get_total_bytes()) {
1161  // Fantastic! It's just a linear array of this one data type. Copy the
1162  // whole thing all at once.
1163  memcpy(to, from, num_records * from_type->get_total_bytes());
1164 
1165  } else {
1166  // Ok, it's interleaved in with other data. Copy them one record at a
1167  // time.
1168  while (num_records > 0) {
1169  memcpy(to, from, from_type->get_total_bytes());
1170  to += to_stride;
1171  from += from_stride;
1172  num_records--;
1173  }
1174  }
1175 }
1176 
1177 /**
1178  * Returns a new GeomVertexData object, suitable for modification, with the
1179  * indicated data type replaced with a new table filled with undefined values.
1180  * The new table will be added as a new array; if the old table was
1181  * interleaved with a previous array, the previous array will not be repacked.
1182  *
1183  * If num_components is 0, the indicated name is simply removed from the type,
1184  * without replacing it with anything else.
1185  */
1186 PT(GeomVertexData) GeomVertexData::
1187 replace_column(InternalName *name, int num_components,
1188  GeomVertexData::NumericType numeric_type,
1189  GeomVertexData::Contents contents) const {
1190  CDReader cdata(_cycler);
1191  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*cdata->_format);
1192 
1193  // Remove the old description of the type from the format.
1194  bool removed_type_array = false;
1195  int old_type_array = cdata->_format->get_array_with(name);
1196  if (old_type_array != -1) {
1197  GeomVertexArrayFormat *array_format = new_format->modify_array(old_type_array);
1198  if (array_format->get_num_columns() == 1) {
1199  // Actually, this array didn't have any other data types, so just drop
1200  // the whole array.
1201  new_format->remove_array(old_type_array);
1202  removed_type_array = true;
1203 
1204  } else {
1205  // Remove the description for the type, but don't bother to repack the
1206  // array.
1207  array_format->remove_column(name);
1208  }
1209  }
1210 
1211  // Now define a new array to contain just the type.
1212  int new_type_array = -1;
1213  if (num_components != 0) {
1214  PT(GeomVertexArrayFormat) type_array_format =
1215  new GeomVertexArrayFormat(name, num_components, numeric_type, contents);
1216  new_type_array = new_format->add_array(type_array_format);
1217  }
1218 
1219  CPT(GeomVertexFormat) format =
1220  GeomVertexFormat::register_format(new_format);
1221 
1222  if (gobj_cat.is_debug()) {
1223  gobj_cat.debug()
1224  << "Replacing data type " << *name << "; converting "
1225  << get_num_rows() << " rows from "
1226  << *cdata->_format << " to " << *format << "\n";
1227  }
1228 
1229  PT(GeomVertexData) new_data = new GeomVertexData(*this, format);
1230 
1231  int j = 0;
1232  int num_arrays = get_num_arrays();
1233  for (int i = 0; i < num_arrays; ++i) {
1234  if (i == old_type_array) {
1235  if (!removed_type_array) {
1236  // Pointer-copy the original array that includes the type (since it
1237  // also includes other data).
1238  new_data->set_array(j, get_array(i));
1239  ++j;
1240  }
1241 
1242  } else {
1243  // Just pointer-copy any arrays other than type.
1244  new_data->set_array(j, get_array(i));
1245  ++j;
1246  }
1247  }
1248 
1249  if (new_type_array != -1) {
1250  nassertr(j == new_type_array, new_data);
1251 
1252  // For the new type array, we set up a temporary array that has room for
1253  // the right number of rows.
1254  PT(GeomVertexArrayData) new_array = new GeomVertexArrayData
1255  (format->get_array(j), get_usage_hint());
1256  new_array->set_num_rows(get_num_rows());
1257  new_data->set_array(j, new_array);
1258  }
1259 
1260  return new_data;
1261 }
1262 
1263 /**
1264  *
1265  */
1266 void GeomVertexData::
1267 output(ostream &out) const {
1268  if (!get_name().empty()) {
1269  out << get_name() << " ";
1270  }
1271  out << get_num_rows() << " rows: " << *get_format();
1272 }
1273 
1274 /**
1275  *
1276  */
1277 void GeomVertexData::
1278 write(ostream &out, int indent_level) const {
1279  if (!get_name().empty()) {
1280  indent(out, indent_level) << get_name() << "\n";
1281  }
1282  get_format()->write_with_data(out, indent_level + 2, this);
1283  CPT(TransformBlendTable) table = get_transform_blend_table();
1284  if (table != nullptr) {
1285  indent(out, indent_level)
1286  << "Transform blend table:\n";
1287  table->write(out, indent_level + 2);
1288  }
1289 }
1290 
1291 /**
1292  * Writes a verbose, human-friendly description of the indicated vertex
1293  * number.
1294  */
1295 void GeomVertexData::
1296 describe_vertex(ostream &out, int row) const {
1297  nassertv_always(row >= 0 && row < get_num_rows());
1298 
1299  out << "Vertex " << row << ":\n";
1300 
1301  GeomVertexReader reader(this);
1302  reader.set_row_unsafe(row);
1303  const GeomVertexFormat *format = get_format();
1304 
1305  const TransformBlendTable *tb_table = nullptr;
1306  if (format->get_animation().get_animation_type() == AT_panda) {
1307  tb_table = get_transform_blend_table();
1308  }
1309 
1310  int num_columns = format->get_num_columns();
1311  for (int ci = 0; ci < num_columns; ++ci) {
1312  int ai = format->get_array_with(ci);
1313  const GeomVertexColumn *column = format->get_column(ci);
1314  reader.set_column(ai, column);
1315 
1316  int num_values = std::min(column->get_num_values(), 4);
1317  const LVecBase4 &d = reader.get_data4();
1318 
1319  out << " " << *column->get_name();
1320  for (int v = 0; v < num_values; v++) {
1321  out << " " << d[v];
1322  }
1323  out << "\n";
1324 
1325  if (column->get_name() == InternalName::get_transform_blend() &&
1326  tb_table != nullptr) {
1327  // This is an index into the transform blend table. Look up the index
1328  // and report the vertex weighting.
1329  reader.set_column(ai, column);
1330  int bi = reader.get_data1i();
1331  if (bi >= 0 && (size_t)bi < tb_table->get_num_blends()) {
1332  const TransformBlend &blend = tb_table->get_blend(bi);
1333  out << " " << blend << "\n";
1334  }
1335  }
1336  }
1337 
1338  // Also show the raw vertex data, why not?
1339  out << "\nraw data:\n";
1340  int num_arrays = format->get_num_arrays();
1341  for (int ai = 0; ai < num_arrays; ++ai) {
1342  const GeomVertexArrayData *array = get_array(ai);
1343  const GeomVertexArrayFormat *aformat = format->get_array(ai);
1344  nassertv(array != nullptr && aformat != nullptr);
1345  out << " " << *aformat << "\n";
1346  CPT(GeomVertexArrayDataHandle) handle = array->get_handle();
1347  nassertv(handle != nullptr);
1348  const unsigned char *data = handle->get_read_pointer(true);
1349  nassertv(data != nullptr);
1350  int stride = aformat->get_stride();
1351  int start = stride * row;
1352  if (data != nullptr) {
1353  Datagram dg(data + start, stride);
1354  dg.dump_hex(out, 4);
1355  }
1356  }
1357 }
1358 
1359 /**
1360  * Removes all of the previously-cached results of convert_to().
1361  *
1362  * This blows away the entire cache, upstream and downstream the pipeline.
1363  * Use clear_cache_stage() instead if you only want to blow away the cache at
1364  * the current stage and upstream.
1365  */
1366 void GeomVertexData::
1367 clear_cache() {
1368  LightMutexHolder holder(_cache_lock);
1369  for (Cache::iterator ci = _cache.begin();
1370  ci != _cache.end();
1371  ++ci) {
1372  CacheEntry *entry = (*ci).second;
1373  entry->erase();
1374  }
1375  _cache.clear();
1376 }
1377 
1378 /**
1379  * Removes all of the previously-cached results of convert_to(), at the
1380  * current pipeline stage and upstream. Does not affect the downstream cache.
1381  *
1382  * Don't call this in a downstream thread unless you don't mind it blowing
1383  * away other changes you might have recently made in an upstream thread.
1384  */
1387  LightMutexHolder holder(_cache_lock);
1388  for (Cache::iterator ci = _cache.begin();
1389  ci != _cache.end();
1390  ++ci) {
1391  CacheEntry *entry = (*ci).second;
1392  CDCacheWriter cdata(entry->_cycler);
1393  cdata->_result = nullptr;
1394  }
1395 }
1396 
1397 /**
1398  * Quickly converts DirectX-style color to OpenGL-style color.
1399  */
1400 void GeomVertexData::
1401 packed_argb_to_uint8_rgba(unsigned char *to, int to_stride,
1402  const unsigned char *from, int from_stride,
1403  int num_records) {
1404  if (gobj_cat.is_debug()) {
1405  gobj_cat.debug()
1406  << "packed_argb_to_uint8_rgba(" << (void *)to << ", " << to_stride
1407  << ", " << (const void *)from << ", " << from_stride
1408  << ", " << num_records << ")\n";
1409  }
1410 
1411  while (num_records > 0) {
1412  uint32_t dword = *(const uint32_t *)from;
1413  to[0] = unpack_abcd_b(dword);
1414  to[1] = unpack_abcd_c(dword);
1415  to[2] = unpack_abcd_d(dword);
1416  to[3] = unpack_abcd_a(dword);
1417 
1418  to += to_stride;
1419  from += from_stride;
1420  num_records--;
1421  }
1422 }
1423 
1424 /**
1425  * Quickly converts OpenGL-style color to DirectX-style color.
1426  */
1427 void GeomVertexData::
1428 uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
1429  const unsigned char *from, int from_stride,
1430  int num_records) {
1431  if (gobj_cat.is_debug()) {
1432  gobj_cat.debug()
1433  << "uint8_rgba_to_packed_argb(" << (void *)to << ", " << to_stride
1434  << ", " << (const void *)from << ", " << from_stride
1435  << ", " << num_records << ")\n";
1436  }
1437 
1438  while (num_records > 0) {
1439  *(uint32_t *)to = pack_abcd(from[3], from[0], from[1], from[2]);
1440 
1441  to += to_stride;
1442  from += from_stride;
1443  num_records--;
1444  }
1445 }
1446 
1447 /**
1448  * Recomputes the results of computing the vertex animation on the CPU, and
1449  * applies them to the existing animated_vertices object.
1450  */
1451 void GeomVertexData::
1452 update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) {
1453  PStatTimer timer(_char_pcollector, current_thread);
1454 
1455  int num_rows = get_num_rows();
1456 
1457  if (gobj_cat.is_debug()) {
1458  gobj_cat.debug()
1459  << "Animating " << num_rows << " vertices for " << get_name()
1460  << "\n";
1461  }
1462 
1463  const GeomVertexFormat *orig_format = cdata->_format;
1464  CPT(GeomVertexFormat) new_format = orig_format;
1465 
1466  if (cdata->_animated_vertices == nullptr) {
1467  new_format = orig_format->get_post_animated_format();
1468  cdata->_animated_vertices =
1469  new GeomVertexData(get_name(), new_format,
1470  std::min(get_usage_hint(), UH_dynamic));
1471  }
1472  PT(GeomVertexData) new_data = cdata->_animated_vertices;
1473 
1474  // We have to make a complete copy of the data first so we can modify it.
1475  // If we were clever, we could maybe just figure out the subset of the data
1476  // that might have changed since last frame, but that's too much trouble
1477  // (and isn't obviously faster than just copying the whole thing).
1478  new_data->copy_from(this, true);
1479 
1480  // First, apply all of the morphs.
1481  CPT(SliderTable) slider_table = cdata->_slider_table;
1482  if (slider_table != nullptr) {
1483  PStatTimer timer2(_morphs_pcollector);
1484  int num_morphs = orig_format->get_num_morphs();
1485  for (int mi = 0; mi < num_morphs; mi++) {
1486  CPT(InternalName) slider_name = orig_format->get_morph_slider(mi);
1487 
1488  const SparseArray &sliders = slider_table->find_sliders(slider_name);
1489  if (!sliders.is_zero()) {
1490  nassertv(!sliders.is_inverse());
1491  int num_slider_subranges = sliders.get_num_subranges();
1492  for (int sni = 0; sni < num_slider_subranges; ++sni) {
1493  int slider_begin = sliders.get_subrange_begin(sni);
1494  int slider_end = sliders.get_subrange_end(sni);
1495  for (int sn = slider_begin; sn < slider_end; ++sn) {
1496  const VertexSlider *slider = slider_table->get_slider(sn);
1497  const SparseArray &rows = slider_table->get_slider_rows(sn);
1498  nassertv(!rows.is_inverse());
1499 
1500  PN_stdfloat slider_value = slider->get_slider();
1501  if (slider_value != 0.0f) {
1502  CPT(InternalName) base_name = orig_format->get_morph_base(mi);
1503  CPT(InternalName) delta_name = orig_format->get_morph_delta(mi);
1504 
1505  GeomVertexRewriter data(new_data, base_name);
1506  GeomVertexReader delta(this, delta_name);
1507  int num_subranges = rows.get_num_subranges();
1508 
1509  if (data.get_column()->get_num_values() == 4) {
1510  if (data.get_column()->has_homogeneous_coord()) {
1511  // Scale the delta by the homogeneous coordinate.
1512  for (int i = 0; i < num_subranges; ++i) {
1513  int begin = rows.get_subrange_begin(i);
1514  int end = rows.get_subrange_end(i);
1515  data.set_row_unsafe(begin);
1516  delta.set_row_unsafe(begin);
1517  for (int j = begin; j < end; ++j) {
1518  LPoint4 vertex = data.get_data4();
1519  LPoint3 d = delta.get_data3();
1520  d *= slider_value * vertex[3];
1521  data.set_data4(vertex[0] + d[0],
1522  vertex[1] + d[1],
1523  vertex[2] + d[2],
1524  vertex[3]);
1525  }
1526  }
1527  } else {
1528  // Just apply the four-component delta.
1529  for (int i = 0; i < num_subranges; ++i) {
1530  int begin = rows.get_subrange_begin(i);
1531  int end = rows.get_subrange_end(i);
1532  data.set_row_unsafe(begin);
1533  delta.set_row_unsafe(begin);
1534  for (int j = begin; j < end; ++j) {
1535  const LPoint4 &vertex = data.get_data4();
1536  LPoint4 d = delta.get_data4();
1537  data.set_data4(vertex + d * slider_value);
1538  }
1539  }
1540  }
1541  } else {
1542  // 3-component or smaller values; don't worry about a
1543  // homogeneous coordinate.
1544  for (int i = 0; i < num_subranges; ++i) {
1545  int begin = rows.get_subrange_begin(i);
1546  int end = rows.get_subrange_end(i);
1547  data.set_row_unsafe(begin);
1548  delta.set_row_unsafe(begin);
1549  for (int j = begin; j < end; ++j) {
1550  const LPoint3 &vertex = data.get_data3();
1551  LPoint3 d = delta.get_data3();
1552  data.set_data3(vertex + d * slider_value);
1553  }
1554  }
1555  }
1556  }
1557  }
1558  }
1559  }
1560  }
1561  }
1562 
1563  // Then apply the transforms.
1564  CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer(current_thread);
1565  if (tb_table != nullptr) {
1566  // Recompute all the blends up front, so we don't have to test each one
1567  // for staleness at each vertex.
1568  {
1569  PStatTimer timer4(_blends_pcollector);
1570  int num_blends = tb_table->get_num_blends();
1571  for (int bi = 0; bi < num_blends; bi++) {
1572  tb_table->get_blend(bi).update_blend(current_thread);
1573  }
1574  }
1575 
1576  // Now go through and apply the transforms.
1577  PStatTimer timer3(_skinning_pcollector);
1578 
1579  const SparseArray &rows = tb_table->get_rows();
1580  int num_subranges = rows.get_num_subranges();
1581 
1582  int blend_array_index = orig_format->get_array_with(InternalName::get_transform_blend());
1583  if (blend_array_index < 0) {
1584  gobj_cat.warning()
1585  << "Vertex data " << get_name()
1586  << " has a transform_blend_table, but no transform_blend data.\n";
1587  return;
1588  }
1589 
1590  CPT(GeomVertexArrayFormat) blend_array_format = orig_format->get_array(blend_array_index);
1591 
1592  if (blend_array_format->get_stride() == 2 &&
1593  blend_array_format->get_column(0)->get_component_bytes() == 2) {
1594  // The blend indices are a table of ushorts. Optimize this common case.
1595  CPT(GeomVertexArrayDataHandle) blend_array_handle =
1596  new GeomVertexArrayDataHandle(cdata->_arrays[blend_array_index].get_read_pointer(current_thread), current_thread);
1597  const unsigned short *blendt = (const unsigned short *)blend_array_handle->get_read_pointer(true);
1598 
1599  size_t ci;
1600  for (ci = 0; ci < new_format->get_num_points(); ci++) {
1601  GeomVertexRewriter data(new_data, new_format->get_point(ci));
1602 
1603  for (int i = 0; i < num_subranges; ++i) {
1604  int begin = rows.get_subrange_begin(i);
1605  int end = rows.get_subrange_end(i);
1606  nassertv(begin < end);
1607 
1608  int first_vertex = begin;
1609  int first_bi = blendt[first_vertex];
1610 
1611  while (first_vertex < end) {
1612  // At this point, first_vertex is the first of a series of
1613  // vertices that shares the blend index first_bi.
1614 
1615  // Scan for the end of this series of vertices--we're looking for
1616  // the next vertex with a different blend index.
1617  int next_vertex = first_vertex;
1618  int next_bi = first_bi;
1619  ++next_vertex;
1620  while (next_vertex < end) {
1621  next_bi = blendt[next_vertex];
1622  if (next_bi != first_bi) {
1623  break;
1624  }
1625  ++next_vertex;
1626  }
1627 
1628  // We've just reached the end of the vertices with a matching
1629  // blend index. Transform all those vertices as a block.
1630  LMatrix4 mat;
1631  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1632  new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex);
1633 
1634  first_vertex = next_vertex;
1635  first_bi = next_bi;
1636  }
1637  }
1638  }
1639 
1640  for (ci = 0; ci < new_format->get_num_vectors(); ci++) {
1641  GeomVertexRewriter data(new_data, new_format->get_vector(ci));
1642 
1643  for (int i = 0; i < num_subranges; ++i) {
1644  int begin = rows.get_subrange_begin(i);
1645  int end = rows.get_subrange_end(i);
1646  nassertv(begin < end);
1647 
1648  int first_vertex = begin;
1649  int first_bi = blendt[first_vertex];
1650 
1651  while (first_vertex < end) {
1652  // At this point, first_vertex is the first of a series of
1653  // vertices that shares the blend index first_bi.
1654 
1655  // Scan for the end of this series of vertices--we're looking for
1656  // the next vertex with a different blend index.
1657  int next_vertex = first_vertex;
1658  int next_bi = first_bi;
1659  ++next_vertex;
1660  while (next_vertex < end) {
1661  next_bi = blendt[next_vertex];
1662  if (next_bi != first_bi) {
1663  break;
1664  }
1665  ++next_vertex;
1666  }
1667 
1668  // We've just reached the end of the vertices with a matching
1669  // blend index. Transform all those vertices as a block.
1670  LMatrix4 mat;
1671  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1672  new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex);
1673 
1674  first_vertex = next_vertex;
1675  first_bi = next_bi;
1676  }
1677  }
1678  }
1679 
1680  } else {
1681  // The blend indices are anything else. Use the GeomVertexReader to
1682  // iterate through them.
1683  GeomVertexReader blendi(this, InternalName::get_transform_blend());
1684  nassertv(blendi.has_column());
1685 
1686  size_t ci;
1687  for (ci = 0; ci < new_format->get_num_points(); ci++) {
1688  GeomVertexRewriter data(new_data, new_format->get_point(ci));
1689 
1690  for (int i = 0; i < num_subranges; ++i) {
1691  int begin = rows.get_subrange_begin(i);
1692  int end = rows.get_subrange_end(i);
1693  nassertv(begin < end);
1694  blendi.set_row_unsafe(begin);
1695 
1696  int first_vertex = begin;
1697  int first_bi = blendi.get_data1i();
1698 
1699  while (first_vertex < end) {
1700  // At this point, first_vertex is the first of a series of
1701  // vertices that shares the blend index first_bi.
1702 
1703  // Scan for the end of this series of vertices--we're looking for
1704  // the next vertex with a different blend index.
1705  int next_vertex = first_vertex;
1706  int next_bi = first_bi;
1707  ++next_vertex;
1708  while (next_vertex < end) {
1709  next_bi = blendi.get_data1i();
1710  if (next_bi != first_bi) {
1711  break;
1712  }
1713  ++next_vertex;
1714  }
1715 
1716  // We've just reached the end of the vertices with a matching
1717  // blend index. Transform all those vertices as a block.
1718  LMatrix4 mat;
1719  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1720  new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex);
1721 
1722  first_vertex = next_vertex;
1723  first_bi = next_bi;
1724  }
1725  }
1726  }
1727 
1728  for (ci = 0; ci < new_format->get_num_vectors(); ci++) {
1729  GeomVertexRewriter data(new_data, new_format->get_vector(ci));
1730 
1731  for (int i = 0; i < num_subranges; ++i) {
1732  int begin = rows.get_subrange_begin(i);
1733  int end = rows.get_subrange_end(i);
1734  nassertv(begin != end);
1735  blendi.set_row_unsafe(begin);
1736 
1737  int first_vertex = begin;
1738  int first_bi = blendi.get_data1i();
1739 
1740  while (first_vertex < end) {
1741  // At this point, first_vertex is the first of a series of
1742  // vertices that shares the blend index first_bi.
1743 
1744  // Scan for the end of this series of vertices--we're looking for
1745  // the next vertex with a different blend index.
1746  int next_vertex = first_vertex;
1747  int next_bi = first_bi;
1748  ++next_vertex;
1749  while (next_vertex < end) {
1750  next_bi = blendi.get_data1i();
1751  if (next_bi != first_bi) {
1752  break;
1753  }
1754  ++next_vertex;
1755  }
1756 
1757  // We've just reached the end of the vertices with a matching
1758  // blend index. Transform all those vertices as a block.
1759  LMatrix4 mat;
1760  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1761  new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex);
1762 
1763  first_vertex = next_vertex;
1764  first_bi = next_bi;
1765  }
1766  }
1767  }
1768  }
1769  }
1770 }
1771 
1772 
1773 /**
1774  * Transforms a range of vertices for one particular column, as a point.
1775  */
1776 void GeomVertexData::
1777 do_transform_point_column(const GeomVertexFormat *format, GeomVertexRewriter &data,
1778  const LMatrix4 &mat, int begin_row, int end_row) {
1779  const GeomVertexColumn *data_column = data.get_column();
1780  int num_values = data_column->get_num_values();
1781 
1782  if ((num_values == 3 || num_values == 4) &&
1783  data_column->get_numeric_type() == NT_float32) {
1784  // The table of points is a table of LPoint3f's or LPoint4f's. Optimize
1785  // this common case.
1786  GeomVertexArrayDataHandle *data_handle = data.get_array_handle();
1787 
1788  size_t stride = data.get_stride();
1789  size_t num_rows = end_row - begin_row;
1790  unsigned char *datat = data_handle->get_write_pointer();
1791  datat += data_column->get_start() + begin_row * stride;
1792  LMatrix4f matf = LCAST(float, mat);
1793 
1794  if (num_values == 3) {
1795  table_xform_point3f(datat, num_rows, stride, matf);
1796  } else {
1797  table_xform_vecbase4f(datat, num_rows, stride, matf);
1798  }
1799 
1800  } else if (num_values == 4) {
1801  // Use the GeomVertexRewriter to adjust the 4-component points.
1802 
1803  data.set_row_unsafe(begin_row);
1804  for (int j = begin_row; j < end_row; ++j) {
1805  LPoint4 vertex = data.get_data4();
1806  data.set_data4(vertex * mat);
1807  }
1808 
1809  } else {
1810  // Use the GeomVertexRewriter to adjust the 3-component points.
1811 
1812  data.set_row_unsafe(begin_row);
1813  for (int j = begin_row; j < end_row; ++j) {
1814  LPoint3 vertex = data.get_data3();
1815  data.set_data3(vertex * mat);
1816  }
1817  }
1818 }
1819 
1820 /**
1821  * Transforms a range of vertices for one particular column, as a vector.
1822  */
1823 void GeomVertexData::
1824 do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &data,
1825  const LMatrix4 &mat, int begin_row, int end_row) {
1826  const GeomVertexColumn *data_column = data.get_column();
1827  int num_values = data_column->get_num_values();
1828 
1829  LMatrix4 xform;
1830  bool normalize = false;
1831  if (data_column->get_contents() == C_normal) {
1832  // This is to preserve perpendicularity to the surface.
1833  LVecBase3 scale_sq(mat.get_row3(0).length_squared(),
1834  mat.get_row3(1).length_squared(),
1835  mat.get_row3(2).length_squared());
1836  if (IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[1], 2.0e-3f) &&
1837  IS_THRESHOLD_EQUAL(scale_sq[0], scale_sq[2], 2.0e-3f)) {
1838  // There is a uniform scale.
1839  LVecBase3 scale, shear, hpr;
1840  if (IS_THRESHOLD_EQUAL(scale_sq[0], 1, 2.0e-3f)) {
1841  // No scale to worry about.
1842  xform = mat;
1843  } else if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr)) {
1844  // Make a new matrix with scale/translate taken out of the equation.
1845  compose_matrix(xform, LVecBase3(1, 1, 1), shear, hpr, LVecBase3::zero());
1846  } else {
1847  normalize = true;
1848  }
1849  } else {
1850  // There is a non-uniform scale, so we need to do all this to preserve
1851  // orthogonality to the surface.
1852  xform.invert_from(mat);
1853  xform.transpose_in_place();
1854  normalize = true;
1855  }
1856  } else {
1857  xform = mat;
1858  }
1859 
1860  if ((num_values == 3 || num_values == 4) &&
1861  data_column->get_numeric_type() == NT_float32) {
1862  // The table of vectors is a table of LVector3f's or LVector4f's.
1863  // Optimize this common case.
1864  GeomVertexArrayDataHandle *data_handle = data.get_array_handle();
1865 
1866  size_t stride = data.get_stride();
1867  size_t num_rows = end_row - begin_row;
1868  unsigned char *datat = data_handle->get_write_pointer();
1869  datat += data_column->get_start() + begin_row * stride;
1870  LMatrix4f matf = LCAST(float, xform);
1871 
1872  if (normalize) {
1873  table_xform_normal3f(datat, num_rows, stride, matf);
1874  } else if (num_values == 3) {
1875  table_xform_vector3f(datat, num_rows, stride, matf);
1876  } else {
1877  table_xform_vecbase4f(datat, num_rows, stride, matf);
1878  }
1879 
1880  } else {
1881  // Use the GeomVertexRewriter to transform the vectors.
1882  data.set_row_unsafe(begin_row);
1883 
1884  if (normalize) {
1885  for (int j = begin_row; j < end_row; ++j) {
1886  LVector3 vector = data.get_data3();
1887  vector *= xform;
1888  vector.normalize();
1889  data.set_data3(vector);
1890  }
1891  } else {
1892  for (int j = begin_row; j < end_row; ++j) {
1893  LVector3 vector = data.get_data3();
1894  data.set_data3(vector * xform);
1895  }
1896  }
1897  }
1898 }
1899 
1900 /**
1901  * Transforms each of the LPoint3f objects in the indicated table by the
1902  * indicated matrix.
1903  */
1904 void GeomVertexData::
1905 table_xform_point3f(unsigned char *datat, size_t num_rows, size_t stride,
1906  const LMatrix4f &matf) {
1907  // We don't bother checking for the unaligned case here, because in practice
1908  // it doesn't matter with a 3-component point.
1909  for (size_t i = 0; i < num_rows; ++i) {
1910  LPoint3f &vertex = *(LPoint3f *)(&datat[i * stride]);
1911  vertex *= matf;
1912  }
1913 }
1914 
1915 /**
1916  * Transforms each of the LVector3f objects in the indicated table by the
1917  * indicated matrix, and also normalizes them.
1918  */
1919 void GeomVertexData::
1920 table_xform_normal3f(unsigned char *datat, size_t num_rows, size_t stride,
1921  const LMatrix4f &matf) {
1922  // We don't bother checking for the unaligned case here, because in practice
1923  // it doesn't matter with a 3-component vector.
1924  for (size_t i = 0; i < num_rows; ++i) {
1925  LNormalf &vertex = *(LNormalf *)(&datat[i * stride]);
1926  vertex *= matf;
1927  vertex.normalize();
1928  }
1929 }
1930 
1931 /**
1932  * Transforms each of the LVector3f objects in the indicated table by the
1933  * indicated matrix.
1934  */
1935 void GeomVertexData::
1936 table_xform_vector3f(unsigned char *datat, size_t num_rows, size_t stride,
1937  const LMatrix4f &matf) {
1938  // We don't bother checking for the unaligned case here, because in practice
1939  // it doesn't matter with a 3-component vector.
1940  for (size_t i = 0; i < num_rows; ++i) {
1941  LVector3f &vertex = *(LVector3f *)(&datat[i * stride]);
1942  vertex *= matf;
1943  }
1944 }
1945 
1946 /**
1947  * Transforms each of the LVecBase4f objects in the indicated table by the
1948  * indicated matrix.
1949  */
1950 void GeomVertexData::
1951 table_xform_vecbase4f(unsigned char *datat, size_t num_rows, size_t stride,
1952  const LMatrix4f &matf) {
1953 #if defined(HAVE_EIGEN) && defined(LINMATH_ALIGN)
1954  // Check if the table is unaligned. If it is, we can't use the LVecBase4f
1955  // object directly, which assumes 16-byte alignment.
1956  if (((size_t)datat & 0xf) != 0 || (stride & 0xf) != 0) {
1957  // Instead, we'll use low-level Eigen calls to multiply out the unaligned
1958  // memory.
1959  Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, 4, Eigen::RowMajor>, Eigen::Unaligned, Eigen::OuterStride<> > table((float *)datat, num_rows, 4, Eigen::OuterStride<>(stride / sizeof(float)));
1960  for (size_t i = 0; i < num_rows; ++i) {
1961  table.row(i) *= matf._m;
1962  }
1963  return;
1964  }
1965 #endif // HAVE_EIGEN
1966 
1967  // If the table is properly aligned (or we don't require alignment), we can
1968  // directly use the high-level LVecBase4f object, which will do the right
1969  // thing.
1970  for (size_t i = 0; i < num_rows; ++i) {
1971  LVecBase4f &vertex = *(LVecBase4f *)(&datat[i * stride]);
1972  vertex *= matf;
1973  }
1974 }
1975 
1976 /**
1977  * Tells the BamReader how to create objects of type GeomVertexData.
1978  */
1981  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1982 }
1983 
1984 /**
1985  * Writes the contents of this object to the datagram for shipping out to a
1986  * Bam file.
1987  */
1989 write_datagram(BamWriter *manager, Datagram &dg) {
1990  CopyOnWriteObject::write_datagram(manager, dg);
1991 
1992  dg.add_string(_name);
1993  manager->write_cdata(dg, _cycler);
1994 }
1995 
1996 /**
1997  * This function is called by the BamReader's factory when a new object of
1998  * type GeomVertexData is encountered in the Bam file. It should create the
1999  * GeomVertexData and extract its information from the file.
2000  */
2001 TypedWritable *GeomVertexData::
2002 make_from_bam(const FactoryParams &params) {
2003  GeomVertexData *object = new GeomVertexData;
2004  DatagramIterator scan;
2005  BamReader *manager;
2006 
2007  parse_params(params, scan, manager);
2008  object->fillin(scan, manager);
2009  manager->register_finalize(object);
2010 
2011  return object;
2012 }
2013 
2014 /**
2015  * Receives an array of pointers, one for each time manager->read_pointer()
2016  * was called in fillin(). Returns the number of pointers processed.
2017  */
2019 complete_pointers(TypedWritable **p_list, BamReader *manager) {
2020  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
2021  return pi;
2022 }
2023 
2024 /**
2025  * Some objects require all of their nested pointers to have been completed
2026  * before the objects themselves can be completed. If this is the case,
2027  * override this method to return true, and be careful with circular
2028  * references (which would make the object unreadable from a bam file).
2029  */
2031 require_fully_complete() const {
2032  return true;
2033 }
2034 
2035 /**
2036  * Called by the BamReader to perform any final actions needed for setting up
2037  * the object after all objects have been read and all pointers have been
2038  * completed.
2039  */
2041 finalize(BamReader *manager) {
2042  // NOTE: This method may be called more than once, because the
2043  // Geom::finalize() will call it explicitly. We have to be prepared to
2044  // accept multiple finalize() calls.
2045 
2046  // Now we need to register the format that we have read from the bam file
2047  // (since it doesn't come out of the bam file automatically registered).
2048  // This may change the format's pointer, which we should then update our own
2049  // data to reflect. But since this may cause the unregistered object to
2050  // destruct, we have to also tell the BamReader to return the new object
2051  // from now on.
2052 
2053  // This extends to the nested array datas, as well as the transform table
2054  // and slider tables, as well.
2055 
2056  CDWriter cdata(_cycler, true);
2057 
2058  for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
2059  CPT(GeomVertexFormat) new_format =
2060  GeomVertexFormat::register_format(cdata->_format);
2061  manager->change_pointer(cdata->_format, new_format);
2062  cdata->_format = new_format;
2063 
2064  CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
2065  PT(GeomVertexArrayData) array_obj = cdata->_arrays[i].get_unsafe_pointer();
2066  nassertv(new_array_format->is_data_subset_of(*array_obj->_array_format));
2067 
2068  manager->change_pointer(array_obj->_array_format, new_array_format);
2069  array_obj->_array_format = new_array_format;
2070  }
2071 
2072  if (cdata->_transform_table != nullptr) {
2073  CPT(TransformTable) new_transform_table =
2074  TransformTable::register_table(cdata->_transform_table);
2075  manager->change_pointer(cdata->_transform_table, new_transform_table);
2076  cdata->_transform_table = new_transform_table;
2077  }
2078 
2079  if (cdata->_slider_table != nullptr) {
2080  CPT(SliderTable) new_slider_table =
2081  SliderTable::register_table(cdata->_slider_table);
2082  manager->change_pointer(cdata->_slider_table, new_slider_table);
2083  cdata->_slider_table = new_slider_table;
2084  }
2085 }
2086 
2087 /**
2088  * This internal function is called by make_from_bam to read in all of the
2089  * relevant data from the BamFile for the new GeomVertexData.
2090  */
2091 void GeomVertexData::
2092 fillin(DatagramIterator &scan, BamReader *manager) {
2093  CopyOnWriteObject::fillin(scan, manager);
2094 
2095  set_name(scan.get_string());
2096  manager->read_cdata(scan, _cycler);
2097 }
2098 
2099 /**
2100  *
2101  */
2102 CycleData *GeomVertexData::CDataCache::
2103 make_copy() const {
2104  return new CDataCache(*this);
2105 }
2106 
2107 /**
2108  * Called when the entry is evicted from the cache, this should clean up the
2109  * owning object appropriately.
2110  */
2112 evict_callback() {
2113  LightMutexHolder holder(_source->_cache_lock);
2114  Cache::iterator ci = _source->_cache.find(&_key);
2115  nassertv(ci != _source->_cache.end());
2116  nassertv((*ci).second == this);
2117  _source->_cache.erase(ci);
2118 }
2119 
2120 /**
2121  *
2122  */
2123 void GeomVertexData::CacheEntry::
2124 output(ostream &out) const {
2125  out << "vertex data " << (void *)_source << " to "
2126  << *_key._modifier;
2127 }
2128 
2129 /**
2130  *
2131  */
2132 CycleData *GeomVertexData::CData::
2133 make_copy() const {
2134  return new CData(*this);
2135 }
2136 
2137 /**
2138  * Writes the contents of this object to the datagram for shipping out to a
2139  * Bam file.
2140  */
2141 void GeomVertexData::CData::
2142 write_datagram(BamWriter *manager, Datagram &dg) const {
2143  manager->write_pointer(dg, _format);
2144  dg.add_uint8(_usage_hint);
2145 
2146  dg.add_uint16(_arrays.size());
2147  Arrays::const_iterator ai;
2148  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
2149  manager->write_pointer(dg, (*ai).get_read_pointer());
2150  }
2151 
2152  manager->write_pointer(dg, _transform_table);
2153  manager->write_pointer(dg, _transform_blend_table.get_read_pointer());
2154  manager->write_pointer(dg, _slider_table);
2155 }
2156 
2157 /**
2158  * Receives an array of pointers, one for each time manager->read_pointer()
2159  * was called in fillin(). Returns the number of pointers processed.
2160  */
2161 int GeomVertexData::CData::
2162 complete_pointers(TypedWritable **p_list, BamReader *manager) {
2163  int pi = CycleData::complete_pointers(p_list, manager);
2164 
2165  _format = DCAST(GeomVertexFormat, p_list[pi++]);
2166 
2167  Arrays::iterator ai;
2168  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
2169  (*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);
2170  }
2171 
2172  _transform_table = DCAST(TransformTable, p_list[pi++]);
2173  _transform_blend_table = DCAST(TransformBlendTable, p_list[pi++]);
2174  _slider_table = DCAST(SliderTable, p_list[pi++]);
2175 
2176  _modified = Geom::get_next_modified();
2177 
2178  if (!_arrays.empty() && manager->get_file_minor_ver() < 7) {
2179  // Bam files prior to 6.7 did not store a SparseArray in the SliderTable
2180  // or TransformBlendTable entries. We need to make up a SparseArray for
2181  // each of them that reflects the complete number of rows in the data.
2182  SparseArray all_rows;
2183  CPT(GeomVertexArrayData) adata = _arrays[0].get_read_pointer();
2184  all_rows.set_range(0, adata->get_num_rows());
2185 
2186  if (_slider_table != nullptr) {
2187  int num_sliders = _slider_table->get_num_sliders();
2188  for (int i = 0; i < num_sliders; ++i) {
2189  ((SliderTable *)_slider_table.p())->set_slider_rows(i, all_rows);
2190  }
2191  }
2192  if (!_transform_blend_table.is_null()) {
2193  _transform_blend_table.get_unsafe_pointer()->set_rows(all_rows);
2194  }
2195  }
2196 
2197  return pi;
2198 }
2199 
2200 /**
2201  * This internal function is called by make_from_bam to read in all of the
2202  * relevant data from the BamFile for the new GeomVertexData.
2203  */
2204 void GeomVertexData::CData::
2205 fillin(DatagramIterator &scan, BamReader *manager) {
2206  manager->read_pointer(scan);
2207  _usage_hint = (UsageHint)scan.get_uint8();
2208 
2209  size_t num_arrays = scan.get_uint16();
2210  _arrays.reserve(num_arrays);
2211  for (size_t i = 0; i < num_arrays; ++i) {
2212  manager->read_pointer(scan);
2213  _arrays.push_back(nullptr);
2214  }
2215 
2216  manager->read_pointer(scan);
2217  manager->read_pointer(scan);
2218  manager->read_pointer(scan);
2219 }
2220 
2221 /**
2222  *
2223  */
2224 int GeomVertexDataPipelineBase::
2225 get_num_bytes() const {
2226  int num_bytes = sizeof(GeomVertexData);
2227 
2228  GeomVertexData::Arrays::const_iterator ai;
2229  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2230  num_bytes += (*ai).get_read_pointer()->get_data_size_bytes();
2231  }
2232 
2233  return num_bytes;
2234 }
2235 
2236 /**
2237  *
2238  */
2239 int GeomVertexDataPipelineReader::
2240 get_num_rows() const {
2241  nassertr(_cdata->_format->get_num_arrays() == _cdata->_arrays.size(), 0);
2242  nassertr(_got_array_readers, 0);
2243 
2244  if (_cdata->_format->get_num_arrays() == 0) {
2245  // No arrays means no rows. Weird but legal.
2246  return 0;
2247  }
2248 
2249  // Look up the answer on the first array (since any array will do).
2250  int stride = _cdata->_format->get_array(0)->get_stride();
2251  return _array_readers[0]->get_data_size_bytes() / stride;
2252 }
2253 
2254 /**
2255  *
2256  */
2257 bool GeomVertexDataPipelineReader::
2258 get_array_info(const InternalName *name,
2259  const GeomVertexArrayDataHandle *&array_reader,
2260  int &num_values,
2261  GeomVertexDataPipelineReader::NumericType &numeric_type,
2262  int &start, int &stride) const {
2263  nassertr(_got_array_readers, false);
2264  int array_index;
2265  const GeomVertexColumn *column;
2266  if (_cdata->_format->get_array_info(name, array_index, column)) {
2267  array_reader = _array_readers[array_index];
2268  num_values = column->get_num_values();
2269  numeric_type = column->get_numeric_type();
2270  start = column->get_start();
2271  stride = _cdata->_format->get_array(array_index)->get_stride();
2272  return true;
2273  }
2274  return false;
2275 }
2276 
2277 /**
2278  *
2279  */
2280 bool GeomVertexDataPipelineReader::
2281 get_array_info(const InternalName *name,
2282  const GeomVertexArrayDataHandle *&array_reader,
2283  int &num_values,
2284  GeomVertexDataPipelineReader::NumericType &numeric_type,
2285  bool &normalized, int &start, int &stride, int &divisor,
2286  int &num_elements, int &element_stride) const {
2287  nassertr(_got_array_readers, false);
2288  int array_index;
2289  const GeomVertexColumn *column;
2290  if (_cdata->_format->get_array_info(name, array_index, column)) {
2291  array_reader = _array_readers[array_index];
2292  num_values = column->get_num_values();
2293  numeric_type = column->get_numeric_type();
2294  normalized = (column->get_contents() == GeomEnums::C_color);
2295  start = column->get_start();
2296  stride = _cdata->_format->get_array(array_index)->get_stride();
2297  divisor = _cdata->_format->get_array(array_index)->get_divisor();
2298  num_elements = column->get_num_elements();
2299  element_stride = column->get_element_stride();
2300  return true;
2301  }
2302  return false;
2303 }
2304 
2305 /**
2306  *
2307  */
2308 bool GeomVertexDataPipelineReader::
2309 get_vertex_info(const GeomVertexArrayDataHandle *&array_reader,
2310  int &num_values,
2311  GeomVertexDataPipelineReader::NumericType &numeric_type,
2312  int &start, int &stride) const {
2313  nassertr(_got_array_readers, false);
2314  int array_index = _cdata->_format->get_vertex_array_index();
2315  if (array_index >= 0) {
2316  const GeomVertexColumn *column = _cdata->_format->get_vertex_column();
2317 
2318  array_reader = _array_readers[array_index];
2319  num_values = column->get_num_values();
2320  numeric_type = column->get_numeric_type();
2321  start = column->get_start();
2322  stride = _cdata->_format->get_array(array_index)->get_stride();
2323  return true;
2324  }
2325  return false;
2326 }
2327 
2328 /**
2329  *
2330  */
2331 bool GeomVertexDataPipelineReader::
2332 get_normal_info(const GeomVertexArrayDataHandle *&array_reader,
2333  GeomVertexDataPipelineReader::NumericType &numeric_type,
2334  int &start, int &stride) const {
2335  nassertr(_got_array_readers, false);
2336  int array_index = _cdata->_format->get_normal_array_index();
2337  if (array_index >= 0) {
2338  const GeomVertexColumn *column = _cdata->_format->get_normal_column();
2339 
2340  array_reader = _array_readers[array_index];
2341  numeric_type = column->get_numeric_type();
2342  start = column->get_start();
2343  stride = _cdata->_format->get_array(array_index)->get_stride();
2344  return true;
2345  }
2346  return false;
2347 }
2348 
2349 /**
2350  *
2351  */
2352 bool GeomVertexDataPipelineReader::
2353 get_color_info(const GeomVertexArrayDataHandle *&array_reader,
2354  int &num_values,
2355  GeomVertexDataPipelineReader::NumericType &numeric_type,
2356  int &start, int &stride) const {
2357  nassertr(_got_array_readers, false);
2358  int array_index = _cdata->_format->get_color_array_index();
2359  if (array_index >= 0) {
2360  const GeomVertexColumn *column = _cdata->_format->get_color_column();
2361 
2362  array_reader = _array_readers[array_index];
2363  num_values = column->get_num_values();
2364  numeric_type = column->get_numeric_type();
2365  start = column->get_start();
2366  stride = _cdata->_format->get_array(array_index)->get_stride();
2367  return true;
2368  }
2369  return false;
2370 }
2371 
2372 /**
2373  *
2374  */
2375 void GeomVertexDataPipelineReader::
2376 make_array_readers() {
2377  nassertv(!_got_array_readers);
2378 
2379  _array_readers.reserve(_cdata->_arrays.size());
2380  GeomVertexData::Arrays::const_iterator ai;
2381  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2382  _array_readers.push_back(new GeomVertexArrayDataHandle((*ai).get_read_pointer(_current_thread), _current_thread));
2383  }
2384 
2385  _got_array_readers = true;
2386 }
2387 
2388 /**
2389  *
2390  */
2391 int GeomVertexDataPipelineWriter::
2392 get_num_rows() const {
2393  nassertr(_cdata->_format->get_num_arrays() == _cdata->_arrays.size(), 0);
2394  nassertr(_got_array_writers, 0);
2395 
2396  if (_cdata->_format->get_num_arrays() == 0) {
2397  // No arrays means no rows. Weird but legal.
2398  return 0;
2399  }
2400 
2401  // Look up the answer on the first array (since any array will do).
2402  int stride = _cdata->_format->get_array(0)->get_stride();
2403  return _array_writers[0]->get_data_size_bytes() / stride;
2404 }
2405 
2406 /**
2407  *
2408  */
2409 bool GeomVertexDataPipelineWriter::
2410 set_num_rows(int n) {
2411  nassertr(_got_array_writers, false);
2412  nassertr(_cdata->_format->get_num_arrays() == _cdata->_arrays.size(), false);
2413 
2414  bool any_changed = false;
2415 
2416  int color_array = -1;
2417  int orig_color_rows = -1;
2418 
2419  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2420  if (_array_writers[i]->get_num_rows() != n) {
2421  if (_array_writers[i]->get_object()->has_column(InternalName::get_color())) {
2422  color_array = i;
2423  orig_color_rows = _array_writers[i]->get_num_rows();
2424  }
2425  _array_writers[i]->set_num_rows(n);
2426  any_changed = true;
2427  }
2428  }
2429 
2430  if (color_array >= 0 && orig_color_rows < n) {
2431  // We have just added some rows; fill the "color" column with (1, 1, 1,
2432  // 1), for the programmer's convenience.
2433  GeomVertexArrayDataHandle *array_writer = _array_writers[color_array];
2434  const GeomVertexArrayFormat *array_format = array_writer->get_array_format();
2435  const GeomVertexColumn *column =
2436  array_format->get_column(InternalName::get_color());
2437  int stride = array_format->get_stride();
2438  unsigned char *start =
2439  array_writer->get_write_pointer() + column->get_start();
2440  unsigned char *stop = start + array_writer->get_data_size_bytes();
2441  unsigned char *pointer = start + stride * orig_color_rows;
2442  int num_values = column->get_num_values();
2443 
2444  switch (column->get_numeric_type()) {
2445  case NT_uint8:
2446  case NT_uint16:
2447  case NT_uint32:
2448  case NT_packed_dcba:
2449  case NT_packed_dabc:
2450  while (pointer < stop) {
2451  memset(pointer, 0xff, column->get_total_bytes());
2452  pointer += stride;
2453  }
2454  break;
2455 
2456  case NT_float32:
2457  while (pointer < stop) {
2458  PN_float32 *pi = (PN_float32 *)pointer;
2459  for (int i = 0; i < num_values; i++) {
2460  pi[i] = 1.0f;
2461  }
2462  pointer += stride;
2463  }
2464  break;
2465 
2466  case NT_float64:
2467  while (pointer < stop) {
2468  PN_float64 *pi = (PN_float64 *)pointer;
2469  for (int i = 0; i < num_values; i++) {
2470  pi[i] = 1.0;
2471  }
2472  pointer += stride;
2473  }
2474  break;
2475 
2476  case NT_stdfloat:
2477  case NT_int8:
2478  case NT_int16:
2479  case NT_int32:
2480  // Shouldn't have this type in the format.
2481  nassertr(false, false);
2482  break;
2483 
2484  case NT_packed_ufloat:
2485  while (pointer < stop) {
2486  *(int32_t *)pointer = 0x781e03c0;
2487  pointer += stride;
2488  }
2489  break;
2490  }
2491  }
2492 
2493  if (any_changed) {
2494  _object->clear_cache_stage();
2495  _cdata->_modified = Geom::get_next_modified();
2496  _cdata->_animated_vertices.clear();
2497  }
2498 
2499  return any_changed;
2500 }
2501 
2502 /**
2503  *
2504  */
2505 bool GeomVertexDataPipelineWriter::
2506 unclean_set_num_rows(int n) {
2507  nassertr(_got_array_writers, false);
2508  nassertr(_cdata->_format->get_num_arrays() == _cdata->_arrays.size(), false);
2509 
2510  bool any_changed = false;
2511 
2512  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2513  if (_array_writers[i]->get_num_rows() != n) {
2514  if (_array_writers[i]->unclean_set_num_rows(n)) {
2515  any_changed = true;
2516  }
2517  }
2518  }
2519 
2520  if (any_changed) {
2521  _object->clear_cache_stage();
2522  _cdata->_modified = Geom::get_next_modified();
2523  _cdata->_animated_vertices.clear();
2524  }
2525 
2526  return any_changed;
2527 }
2528 
2529 /**
2530  *
2531  */
2532 bool GeomVertexDataPipelineWriter::
2533 reserve_num_rows(int n) {
2534  nassertr(_got_array_writers, false);
2535  nassertr(_cdata->_format->get_num_arrays() == _cdata->_arrays.size(), false);
2536 
2537  bool any_changed = false;
2538 
2539  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2540  if (_array_writers[i]->reserve_num_rows(n)) {
2541  any_changed = true;
2542  }
2543  }
2544 
2545  return any_changed;
2546 }
2547 
2548 /**
2549  *
2550  */
2551 PT(GeomVertexArrayData) GeomVertexDataPipelineWriter::
2552 modify_array(size_t i) {
2553  nassertr(i < _cdata->_arrays.size(), nullptr);
2554 
2555  PT(GeomVertexArrayData) new_data;
2556  if (_got_array_writers) {
2557  new_data = _array_writers[i]->get_object();
2558  } else {
2559  new_data = _cdata->_arrays[i].get_write_pointer();
2560  }
2561 
2562  _object->clear_cache_stage();
2563  _cdata->_modified = Geom::get_next_modified();
2564  _cdata->_animated_vertices_modified = UpdateSeq();
2565 
2566  return new_data;
2567 }
2568 
2569 /**
2570  *
2571  */
2572 void GeomVertexDataPipelineWriter::
2573 set_array(size_t i, const GeomVertexArrayData *array) {
2574  nassertv(i < _cdata->_arrays.size());
2575  _cdata->_arrays[i] = (GeomVertexArrayData *)array;
2576  _object->clear_cache_stage();
2577  _cdata->_modified = Geom::get_next_modified();
2578  _cdata->_animated_vertices_modified = UpdateSeq();
2579 
2580  if (_got_array_writers) {
2581  _array_writers[i] = new GeomVertexArrayDataHandle(_cdata->_arrays[i].get_write_pointer(), _current_thread);
2582  }
2583 }
2584 
2585 /**
2586  * Copies a single row of the data from the other array into the indicated row
2587  * of this array. In this case, the source format must exactly match the
2588  * destination format.
2589  *
2590  * Don't call this in a downstream thread unless you don't mind it blowing
2591  * away other changes you might have recently made in an upstream thread.
2592  */
2594 copy_row_from(int dest_row, const GeomVertexDataPipelineReader &source,
2595  int source_row) {
2596  const GeomVertexFormat *source_format = source.get_format();
2597  const GeomVertexFormat *dest_format = get_format();
2598  nassertv(source_format == dest_format);
2599  nassertv(source_row >= 0 && source_row < source.get_num_rows());
2600  nassertv(_got_array_writers);
2601 
2602  if (dest_row >= get_num_rows()) {
2603  // Implicitly add enough rows to get to the indicated row.
2604  set_num_rows(dest_row + 1);
2605  }
2606 
2607  size_t num_arrays = source_format->get_num_arrays();
2608  for (size_t i = 0; i < num_arrays; ++i) {
2609  GeomVertexArrayDataHandle *dest_handle = get_array_writer(i);
2610  unsigned char *dest_array_data = dest_handle->get_write_pointer();
2611 
2612  const GeomVertexArrayDataHandle *source_array_handle = source.get_array_reader(i);
2613  const unsigned char *source_array_data = source_array_handle->get_read_pointer(true);
2614 
2615  const GeomVertexArrayFormat *array_format = source_format->get_array(i);
2616  int stride = array_format->get_stride();
2617 
2618  memcpy(dest_array_data + stride * dest_row,
2619  source_array_data + stride * source_row,
2620  stride);
2621  }
2622 }
2623 
2624 /**
2625  *
2626  */
2627 void GeomVertexDataPipelineWriter::
2628 make_array_writers() {
2629  nassertv(!_got_array_writers);
2630 
2631  _array_writers.reserve(_cdata->_arrays.size());
2632  GeomVertexData::Arrays::iterator ai;
2633  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2634  _array_writers.push_back(new GeomVertexArrayDataHandle((*ai).get_write_pointer(), _current_thread));
2635  }
2636 
2637  _object->clear_cache_stage();
2638  _cdata->_modified = Geom::get_next_modified();
2639  _cdata->_animated_vertices_modified = UpdateSeq();
2640 
2641  _got_array_writers = true;
2642 }
2643 
2644 /**
2645  *
2646  */
2647 void GeomVertexDataPipelineWriter::
2648 delete_array_writers() {
2649  nassertv(_got_array_writers);
2650 
2651  _array_writers.clear();
2652  _got_array_writers = false;
2653 }
LightMutexDirect::acquire
void acquire() const
Grabs the lightMutex if it is available.
Definition: lightMutexDirect.I:56
GeomVertexData::set_format
set_format
Changes the format of the vertex data.
Definition: geomVertexData.h:98
geomVertexData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DatagramIterator::get_string
std::string get_string()
Extracts a variable-length string.
Definition: datagramIterator.cxx:26
LightMutexHolder
Similar to MutexHolder, but for a light mutex.
Definition: lightMutexHolder.h:25
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
GeomVertexColumn::get_start
int get_start() const
Returns the byte within the array record at which this column starts.
Definition: geomVertexColumn.I:133
GeomVertexData::finalize
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
Definition: geomVertexData.cxx:2041
GeomVertexFormat::get_morph_base
get_morph_base
Returns the name of the base column that the nth morph modifies.
Definition: geomVertexFormat.h:121
UpdateSeq
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
SliderTable
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:37
GeomVertexArrayData
This is the data for one array of a GeomVertexData structure.
Definition: geomVertexArrayData.h:58
TransformBlendTable::get_num_blends
get_num_blends
Returns the total number of different blend combinations in the table.
Definition: transformBlendTable.h:57
GeomVertexWriter::set_data4
void set_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row.
Definition: geomVertexWriter.I:670
GeomVertexFormat::is_registered
is_registered
Returns true if this format has been registered, false if it has not.
Definition: geomVertexFormat.h:68
CycleData
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
geomVertexWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexFormat::get_num_columns
get_num_columns
Returns the total number of different columns in the specification, across all arrays.
Definition: geomVertexFormat.h:97
DatagramIterator::get_uint16
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
Definition: datagramIterator.I:145
GeomVertexArrayFormat::remove_column
void remove_column(const InternalName *name)
Removes the column with the indicated name, if any.
Definition: geomVertexArrayFormat.cxx:280
GeomVertexReader::get_data1i
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
Definition: geomVertexReader.I:631
GeomVertexRewriter
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
Definition: geomVertexRewriter.h:33
GeomVertexData
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
Definition: geomVertexData.h:68
GeomVertexColumn::get_num_values
int get_num_values() const
Returns the number of numeric values of the column: the number of distinct numeric values that go int...
Definition: geomVertexColumn.I:99
GeomVertexData::clear_cache_stage
void clear_cache_stage()
Removes all of the previously-cached results of convert_to(), at the current pipeline stage and upstr...
Definition: geomVertexData.cxx:1386
TransformBlend
This defines a single entry in a TransformBlendTable.
Definition: transformBlend.h:32
TransformBlend::get_num_transforms
get_num_transforms
Returns the number of transforms stored in the blend object.
Definition: transformBlend.h:63
GeomVertexData::copy_from
void copy_from(const GeomVertexData *source, bool keep_data_objects, Thread *current_thread=Thread::get_current_thread())
Copies all the data from the other array into the corresponding data types in this array,...
Definition: geomVertexData.cxx:477
GeomVertexFormat::get_array_info
bool get_array_info(const InternalName *name, int &array_index, const GeomVertexColumn *&column) const
Quickly looks up the indicated column within all of the nested arrays and sets array_index and column...
Definition: geomVertexFormat.cxx:632
GeomVertexArrayFormat::get_column
get_column
Returns the specification with the indicated name, or NULL if the name is not used.
Definition: geomVertexArrayFormat.h:106
GeomVertexAnimationSpec::get_animation_type
get_animation_type
Returns the type of animation represented by this spec.
Definition: geomVertexAnimationSpec.h:45
Datagram::add_uint8
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
DatagramIterator
A class to retrieve the individual data elements previously stored in a Datagram.
Definition: datagramIterator.h:27
geomVertexReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
GeomVertexData::set_transform_table
set_transform_table
Replaces the TransformTable on this vertex data with the indicated table.
Definition: geomVertexData.h:120
TypedWritable::complete_pointers
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: typedWritable.cxx:81
TransformTable
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
Definition: transformTable.h:38
BamReader
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
GeomVertexArrayFormat::get_num_columns
get_num_columns
Returns the number of different columns in the array.
Definition: geomVertexArrayFormat.h:106
GeomVertexData::reserve_num_rows
bool reserve_num_rows(int n)
This ensures that enough memory space for n rows is allocated, so that you may increase the number of...
Definition: geomVertexData.I:133
GeomVertexArrayFormat::is_data_subset_of
bool is_data_subset_of(const GeomVertexArrayFormat &other) const
Returns true if all of the fields in this array format are also present and equivalent in the other a...
Definition: geomVertexArrayFormat.cxx:416
GeomVertexData::get_format
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
Definition: geomVertexData.h:98
GeomVertexFormat::get_column
get_column
Returns the ith column of the specification, across all arrays.
Definition: geomVertexFormat.h:97
pStatTimer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TransformTable::is_registered
is_registered
Returns true if this table has been registered.
Definition: transformTable.h:60
BamWriter
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
GeomVertexWriter
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
Definition: geomVertexWriter.h:55
BamWriter::write_pointer
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
GeomVertexData::set_array
set_array
Replaces the indicated vertex data array with a completely new array.
Definition: geomVertexData.h:115
GeomVertexWriter::set_column
bool set_column(int column)
Sets up the writer to use the nth data type of the GeomVertexFormat, numbering from 0.
Definition: geomVertexWriter.I:201
InternalName
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
GeomVertexReader
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
Definition: geomVertexReader.h:47
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
GeomVertexWriter::has_column
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
Definition: geomVertexWriter.I:253
SparseArray::is_zero
bool is_zero() const
Returns true if the entire bitmask is zero, false otherwise.
Definition: sparseArray.I:170
GeomVertexData::set_slider_table
set_slider_table
Replaces the SliderTable on this vertex data with the indicated table.
Definition: geomVertexData.h:130
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Geom::get_next_modified
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition: geom.cxx:1324
GeomVertexData::unclean_set_format
void unclean_set_format(const GeomVertexFormat *format)
Changes the format of the vertex data, without reformatting the data to match.
Definition: geomVertexData.cxx:302
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
Datagram
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
GeomVertexDataPipelineWriter::copy_row_from
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.
Definition: geomVertexData.cxx:2594
GeomVertexData::unpack_abcd_c
static unsigned int unpack_abcd_c(uint32_t data)
Returns the third packed value from a DirectX-style NT_packed_abcd.
Definition: geomVertexData.I:337
GeomVertexFormat::get_vector
get_vector
Returns the name of the nth vector column.
Definition: geomVertexFormat.h:110
GeomVertexData::request_resident
bool request_resident() const
Returns true if the vertex data is currently resident in memory.
Definition: geomVertexData.cxx:445
GeomVertexData::unpack_abcd_b
static unsigned int unpack_abcd_b(uint32_t data)
Returns the second packed value from a DirectX-style NT_packed_abcd.
Definition: geomVertexData.I:329
GeomVertexArrayDataHandle::get_read_pointer
const unsigned char * get_read_pointer(bool force) const
Returns a readable pointer to the beginning of the actual data stream, or NULL if the data is not cur...
Definition: geomVertexArrayData.I:420
Thread::get_current_thread
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
TransformBlend::normalize_weights
void normalize_weights()
Rescales all of the weights on the various transforms so that they sum to 1.0.
Definition: transformBlend.cxx:124
PStatTimer
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Datagram::add_string
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
GeomVertexReader::is_at_end
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
Definition: geomVertexReader.I:362
Datagram::add_uint16
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
GeomVertexFormat::get_animation
get_animation
Returns the GeomVertexAnimationSpec that indicates how this format's vertices are set up for animatio...
Definition: geomVertexFormat.h:72
BamReader::register_finalize
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
GeomVertexData::get_num_rows
int get_num_rows() const
Returns the number of rows stored within all the arrays.
Definition: geomVertexData.I:62
GeomVertexFormat::get_num_morphs
get_num_morphs
Returns the number of columns within the format that represent morph deltas.
Definition: geomVertexFormat.h:120
GeomVertexData::transform_vertices
void transform_vertices(const LMatrix4 &mat)
Applies the indicated transform matrix to all of the vertices in the GeomVertexData.
Definition: geomVertexData.cxx:1024
GeomVertexData::require_fully_complete
virtual bool require_fully_complete() const
Some objects require all of their nested pointers to have been completed before the objects themselve...
Definition: geomVertexData.cxx:2031
GeomVertexAnimationSpec
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
Definition: geomVertexAnimationSpec.h:38
GeomVertexFormat::get_point
get_point
Returns the name of the nth point column.
Definition: geomVertexFormat.h:106
BamReader::read_cdata
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
GeomVertexData::get_usage_hint
get_usage_hint
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
Definition: geomVertexData.h:93
SparseArray::is_inverse
bool is_inverse() const
If this is true, the SparseArray is actually defined as a list of subranges of integers that are *not...
Definition: sparseArray.I:382
CycleDataWriter
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
Definition: cycleDataWriter.h:34
GeomVertexData::operator=
void operator=(const GeomVertexData &copy)
The copy assignment operator is not pipeline-safe.
Definition: geomVertexData.cxx:145
TransformBlendTable::get_blend
get_blend
Returns the nth blend in the table.
Definition: transformBlendTable.h:57
TypedWritable::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: typedWritable.cxx:54
GeomVertexData::complete_pointers
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: geomVertexData.cxx:2019
GeomVertexData::clear_rows
void clear_rows()
Removes all of the rows from the arrays; functionally equivalent to set_num_rows(0) (but faster).
Definition: geomVertexData.cxx:344
SparseArray
This class records a set of integers, where each integer is either present or not present in the set.
Definition: sparseArray.h:43
CycleDataReader
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
Definition: cycleDataReader.h:35
GeomVertexData::set_transform_blend_table
void set_transform_blend_table(const TransformBlendTable *table)
Replaces the TransformBlendTable on this vertex data with the indicated table.
Definition: geomVertexData.cxx:409
SparseArray::get_num_subranges
size_t get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:394
PStatCollector
A lightweight class that represents a single element that may be timed and/or counted via stats.
Definition: pStatCollector.h:43
GeomVertexData::set_num_rows
bool set_num_rows(int n)
Sets the length of the array to n rows in all of the various arrays (presumably by adding rows).
Definition: geomVertexData.I:97
CopyOnWriteObject
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
Definition: copyOnWriteObject.h:41
GeomVertexColumn::get_num_elements
int get_num_elements() const
Returns the number of times this column is repeated.
Definition: geomVertexColumn.I:108
geomVertexRewriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SparseArray::set_range
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition: sparseArray.I:218
GeomVertexColumn::get_name
const InternalName * get_name() const
Returns the name of this particular data field, e.g.
Definition: geomVertexColumn.I:77
GeomVertexData::copy_row_from
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.
Definition: geomVertexData.cxx:687
GeomVertexReader::get_data4
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
Definition: geomVertexReader.I:590
GeomVertexData::write_datagram
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: geomVertexData.cxx:1989
GeomVertexFormat::get_num_vectors
get_num_vectors
Returns the number of columns within the format that represent directional vectors.
Definition: geomVertexFormat.h:110
GeomVertexData::CacheEntry
Definition: geomVertexData.h:256
GeomVertexColumn::get_total_bytes
int get_total_bytes() const
Returns the number of bytes used by each element of the column: component_bytes * num_components.
Definition: geomVertexColumn.I:171
GeomVertexData::has_column
bool has_column(const InternalName *name) const
Returns true if the data has the named column, false otherwise.
Definition: geomVertexData.I:52
GeomVertexData::set_name
set_name
Changes the name of the vertex data.
Definition: geomVertexData.h:89
GeomVertexColumn::is_uint8_rgba
bool is_uint8_rgba() const
Returns true if this column is the standard OpenGL representation of 4-component color: C_color,...
Definition: geomVertexColumn.I:232
GeomVertexData::get_name
get_name
Returns the name passed to the constructor, if any.
Definition: geomVertexData.h:89
VertexSlider
This is an abstract base class that retains some slider value, which is a linear value that typically...
Definition: vertexSlider.h:37
BamReader::change_pointer
bool change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer)
Indicates that an object recently read from the bam stream should be replaced with a new object.
Definition: bamReader.cxx:464
geom.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexColumn::is_packed_argb
bool is_packed_argb() const
Returns true if this column is the standard DirectX representation of 4-component color: C_color,...
Definition: geomVertexColumn.I:221
TransformBlend::get_transform
get_transform
Returns the nth transform stored in the blend object.
Definition: transformBlend.h:63
GeomVertexWriter::set_data4i
void set_data4i(int a, int b, int c, int d)
Sets the write row to a particular 4-component value, and advances the write row.
Definition: geomVertexWriter.I:788
GeomVertexColumn::get_numeric_type
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
Definition: geomVertexColumn.I:116
GeomVertexColumn::get_contents
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
Definition: geomVertexColumn.I:124
GeomVertexArrayDataHandle
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
Definition: geomVertexArrayData.h:250
GeomVertexFormat
This class defines the physical layout of the vertex data stored within a Geom.
Definition: geomVertexFormat.h:55
Factory::register_factory
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
BamWriter::write_cdata
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
GeomVertexData::pack_abcd
static uint32_t pack_abcd(unsigned int a, unsigned int b, unsigned int c, unsigned int d)
Packs four values in a DirectX-style NT_packed_abcd value.
Definition: geomVertexData.I:309
GeomVertexWriter::get_array
int get_array() const
Returns the array index containing the data type that the writer is working on.
Definition: geomVertexWriter.I:262
CycleDataStageWriter
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
Definition: cycleDataStageWriter.h:31
GeomVertexFormat::get_array
get_array
Returns the description of the nth array used by the format.
Definition: geomVertexFormat.h:79
GeomVertexAnimationSpec::get_indexed_transforms
get_indexed_transforms
This is only meaningful for animation_type AT_hardware.
Definition: geomVertexAnimationSpec.h:50
GeomVertexFormat::get_num_points
get_num_points
Returns the number of columns within the format that represent points in space.
Definition: geomVertexFormat.h:106
CPT
CPT(GeomVertexData) GeomVertexData
Returns a new GeomVertexData that represents the same contents as this one, with all data types match...
Definition: geomVertexData.cxx:702
CycleDataLockedReader
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
Definition: cycleDataLockedReader.h:40
PT
PT(CopyOnWriteObject) GeomVertexData
Required to implement CopyOnWriteObject.
Definition: geomVertexData.cxx:56
GeomVertexData::get_slider_table
get_slider_table
Returns a const pointer to the SliderTable assigned to this data.
Definition: geomVertexData.h:130
GeomVertexFormat::get_morph_slider
get_morph_slider
Returns the slider name associated with the nth morph column.
Definition: geomVertexFormat.h:120
BamReader::read_pointer
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
GeomVertexData::get_num_arrays
get_num_arrays
Returns the number of individual arrays stored within the data.
Definition: geomVertexData.h:111
GeomVertexData::unpack_abcd_a
static unsigned int unpack_abcd_a(uint32_t data)
Returns the first packed value from a DirectX-style NT_packed_abcd.
Definition: geomVertexData.I:321
pset.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
GeomVertexData::compare_to
int compare_to(const GeomVertexData &other) const
Returns 0 if the two objects are equivalent, even if they are not the same pointer.
Definition: geomVertexData.cxx:179
SparseArray::get_subrange_end
int get_subrange_end(size_t n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:415
SparseArray::get_subrange_begin
int get_subrange_begin(size_t n) const
Returns the first numeric element in the nth subrange.
Definition: sparseArray.I:404
SliderTable::is_registered
bool is_registered() const
Returns true if this table has been registered.
Definition: sliderTable.I:20
GeomVertexColumn
This defines how a single column is interleaved within a vertex array stored within a Geom.
Definition: geomVertexColumn.h:37
GeomVertexData::set_usage_hint
set_usage_hint
Changes the UsageHint hint for this vertex data, and for all of the arrays that share this data.
Definition: geomVertexData.h:93
TransformBlend::get_blend
void get_blend(LMatrix4 &result, Thread *current_thread) const
Returns the current value of the blend, based on the current value of all of the nested transform obj...
Definition: transformBlend.I:196
TransformBlend::limit_transforms
void limit_transforms(int max_transforms)
If the total number of transforms in the blend exceeds max_transforms, removes the n least-important ...
Definition: transformBlend.cxx:95
GeomVertexArrayFormat::get_stride
get_stride
Returns the total number of bytes reserved in the array for each vertex.
Definition: geomVertexArrayFormat.h:82
BamReader::get_file_minor_ver
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
GeomVertexColumn::get_element_stride
int get_element_stride() const
This value is only relevant for matrix types.
Definition: geomVertexColumn.I:153
TransformBlend::get_weight
get_weight
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
Definition: transformBlend.h:71
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DatagramIterator::get_uint8
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
Definition: datagramIterator.I:72
TypedWritable::fillin
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
Definition: typedWritable.cxx:103
GeomVertexData::unpack_abcd_d
static unsigned int unpack_abcd_d(uint32_t data)
Returns the fourth packed value from a DirectX-style NT_packed_abcd.
Definition: geomVertexData.I:345
TransformBlendTable::get_rows
get_rows
Returns the subset of rows (vertices) in the associated GeomVertexData that this TransformBlendTable ...
Definition: transformBlendTable.h:77
GeomVertexData::register_with_read_factory
static void register_with_read_factory()
Tells the BamReader how to create objects of type GeomVertexData.
Definition: geomVertexData.cxx:1980
bamWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
CycleData::complete_pointers
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: cycleData.cxx:48
LightMutexDirect::release
void release() const
Releases the lightMutex.
Definition: lightMutexDirect.I:69
GeomVertexDataPipelineWriter
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Definition: geomVertexData.h:508
GeomVertexFormat::get_num_arrays
get_num_arrays
Returns the number of individual arrays required by the format.
Definition: geomVertexFormat.h:79
GeomVertexColumn::is_bytewise_equivalent
bool is_bytewise_equivalent(const GeomVertexColumn &other) const
Returns true if the data store of this column is exactly the same as that of the other,...
Definition: geomVertexColumn.I:209
TransformBlendTable
This structure collects together the different combinations of transforms and blend amounts used by a...
Definition: transformBlendTable.h:45
GeomVertexData::unclean_set_num_rows
bool unclean_set_num_rows(int n)
This method behaves like set_num_rows(), except the new data is not initialized.
Definition: geomVertexData.I:116
parse_params
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
GeomVertexArrayFormat
This describes the structure of a single array within a Geom data.
Definition: geomVertexArrayFormat.h:47
GeomVertexArrayDataHandle::get_write_pointer
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
Definition: geomVertexArrayData.cxx:605
GeomVertexData::CacheEntry::evict_callback
virtual void evict_callback()
Called when the entry is evicted from the cache, this should clean up the owning object appropriately...
Definition: geomVertexData.cxx:2112
GeomVertexFormat::get_array_with
int get_array_with(size_t i) const
Returns the index number of the array with the ith column.
Definition: geomVertexFormat.cxx:406
GeomVertexDataPipelineReader
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
Definition: geomVertexData.h:442
GeomVertexReader::set_column
bool set_column(int column)
Sets up the reader to use the nth data type of the GeomVertexFormat, numbering from 0.
Definition: geomVertexReader.I:229
GeomVertexFormat::get_morph_delta
get_morph_delta
Returns the name of the column that defines the nth morph.
Definition: geomVertexFormat.h:122
pset< int >