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