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