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  */
144 void GeomVertexData::
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  */
301 void GeomVertexData::
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  */
343 void GeomVertexData::
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  */
408 void GeomVertexData::
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  */
444 bool GeomVertexData::
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  */
476 void GeomVertexData::
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  {
523  GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread());
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  */
686 void GeomVertexData::
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  */
1023 void GeomVertexData::
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  */
1033 void GeomVertexData::
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  */
1059 void GeomVertexData::
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  */
1385 void GeomVertexData::
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  */
1979 void GeomVertexData::
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  */
1988 void GeomVertexData::
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  */
2018 int GeomVertexData::
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  */
2030 bool GeomVertexData::
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  */
2040 void GeomVertexData::
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  */
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 }
get_vector
Returns the name of the nth vector column.
get_animation_type
Returns the type of animation represented by this spec.
get_blend
Returns the nth blend in the table.
get_morph_slider
Returns the slider name associated with the nth morph column.
set_transform_table
Replaces the TransformTable on this vertex data with the indicated table.
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,...
static unsigned int unpack_abcd_d(uint32_t data)
Returns the fourth packed value from a DirectX-style NT_packed_abcd.
get_num_columns
Returns the number of different columns in the array.
This class records a set of integers, where each integer is either present or not present in the set.
Definition: sparseArray.h:42
bool is_uint8_rgba() const
Returns true if this column is the standard OpenGL representation of 4-component color: C_color,...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_animation
Returns the GeomVertexAnimationSpec that indicates how this format's vertices are set up for animatio...
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
void transform_vertices(const LMatrix4 &mat)
Applies the indicated transform matrix to all of the vertices in the GeomVertexData.
get_num_transforms
Returns the number of transforms stored in the blend object.
bool unclean_set_num_rows(int n)
This method behaves like set_num_rows(), except the new data is not initialized.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
void unclean_set_format(const GeomVertexFormat *format)
Changes the format of the vertex data, without reformatting the data to match.
bool set_column(int column)
Sets up the reader to use the nth data type of the GeomVertexFormat, numbering from 0.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
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.
get_rows
Returns the subset of rows (vertices) in the associated GeomVertexData that this TransformBlendTable ...
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
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,...
int get_array() const
Returns the array index containing the data type that the writer is working on.
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
bool is_zero() const
Returns true if the entire bitmask is zero, false otherwise.
Definition: sparseArray.I:170
bool is_packed_argb() const
Returns true if this column is the standard DirectX representation of 4-component color: C_color,...
static unsigned int unpack_abcd_b(uint32_t data)
Returns the second packed value from a DirectX-style NT_packed_abcd.
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
get_weight
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
This is an abstract base class that retains some slider value, which is a linear value that typically...
Definition: vertexSlider.h:37
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void remove_column(const InternalName *name)
Removes the column with the indicated name, if any.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
get_num_vectors
Returns the number of columns within the format that represent directional vectors.
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
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...
static unsigned int unpack_abcd_c(uint32_t data)
Returns the third packed value from a DirectX-style NT_packed_abcd.
bool request_resident() const
Returns true if the vertex data is currently resident in memory.
get_transform
Returns the nth transform stored in the blend object.
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
set_format
Changes the format of the vertex data.
std::string get_string()
Extracts a variable-length string.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
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...
This defines how a single column is interleaved within a vertex array stored within a Geom.
set_array
Replaces the indicated vertex data array with a completely new array.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
set_name
Changes the name of the vertex data.
void limit_transforms(int max_transforms)
If the total number of transforms in the blend exceeds max_transforms, removes the n least-important ...
int get_num_elements() const
Returns the number of times this column is repeated.
CPT(GeomVertexData) GeomVertexData
Returns a new GeomVertexData that represents the same contents as this one, with all data types match...
is_registered
Returns true if this format has been registered, false if it has not.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
void set_transform_blend_table(const TransformBlendTable *table)
Replaces the TransformBlendTable on this vertex data with the indicated table.
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...
set_slider_table
Replaces the SliderTable on this vertex data with the indicated table.
A lightweight class that represents a single element that may be timed and/or counted via stats.
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:37
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...
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
int get_element_stride() const
This value is only relevant for matrix types.
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.
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().
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...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
Similar to MutexHolder, but for a light mutex.
void acquire() const
Grabs the lightMutex if it is available.
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
get_column
Returns the ith column of the specification, across all arrays.
virtual bool require_fully_complete() const
Some objects require all of their nested pointers to have been completed before the objects themselve...
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
get_num_arrays
Returns the number of individual arrays required by the format.
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
void release() const
Releases the lightMutex.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
int get_num_values() const
Returns the number of numeric values of the column: the number of distinct numeric values that go int...
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
void operator=(const GeomVertexData &copy)
The copy assignment operator is not pipeline-safe.
PT(CopyOnWriteObject) GeomVertexData
Required to implement CopyOnWriteObject.
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.
virtual void evict_callback()
Called when the entry is evicted from the cache, this should clean up the owning object appropriately...
size_t get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:394
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).
int get_array_with(size_t i) const
Returns the index number of the array with the ith column.
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
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
get_indexed_transforms
This is only meaningful for animation_type AT_hardware.
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
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_rows() const
Returns the number of rows stored within all the arrays.
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
This defines a single entry in a TransformBlendTable.
int get_start() const
Returns the byte within the array record at which this column starts.
This class defines the physical layout of the vertex data stored within a Geom.
static unsigned int unpack_abcd_a(uint32_t data)
Returns the first packed value from a DirectX-style NT_packed_abcd.
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.
bool is_registered() const
Returns true if this table has been registered.
Definition: sliderTable.I:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition: sparseArray.I:218
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
get_num_blends
Returns the total number of different blend combinations in the table.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_morph_delta
Returns the name of the column that defines the nth morph.
set_usage_hint
Changes the UsageHint hint for this vertex data, and for all of the arrays that share this data.
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.
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
int get_subrange_begin(size_t n) const
Returns the first numeric element in the nth subrange.
Definition: sparseArray.I:404
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
get_stride
Returns the total number of bytes reserved in the array for each vertex.
A thread; that is, a lightweight process.
Definition: thread.h:46
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
void clear_rows()
Removes all of the rows from the arrays; functionally equivalent to set_num_rows(0) (but faster).
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition: geom.cxx:1324
get_morph_base
Returns the name of the base column that the nth morph modifies.
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise.
This describes the structure of a single array within a Geom data.
get_num_morphs
Returns the number of columns within the format that represent morph deltas.
int compare_to(const GeomVertexData &other) const
Returns 0 if the two objects are equivalent, even if they are not the same pointer.
get_point
Returns the name of the nth point column.
get_usage_hint
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
This structure collects together the different combinations of transforms and blend amounts used by a...
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
bool set_column(int column)
Sets up the writer to use the nth data type of the GeomVertexFormat, numbering from 0.
get_num_columns
Returns the total number of different columns in the specification, across all arrays.
int get_subrange_end(size_t n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:415
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
void normalize_weights()
Rescales all of the weights on the various transforms so that they sum to 1.0.
A class to retrieve the individual data elements previously stored in a Datagram.
const InternalName * get_name() const
Returns the name of this particular data field, e.g.
get_name
Returns the name passed to the constructor, if any.
int get_total_bytes() const
Returns the number of bytes used by each element of the column: component_bytes * num_components.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
is_registered
Returns true if this table has been registered.
bool set_num_rows(int n)
Sets the length of the array to n rows.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
get_array
Returns the description of the nth array used by the format.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_points
Returns the number of columns within the format that represent points in space.
get_column
Returns the specification with the indicated name, or NULL if the name is not used.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This object provides the functionality of both a GeomVertexReader and a GeomVertexWriter,...
This is the data for one array of a GeomVertexData structure.
void clear_cache_stage()
Removes all of the previously-cached results of convert_to(), at the current pipeline stage and upstr...
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void register_with_read_factory()
Tells the BamReader how to create objects of type GeomVertexData.