Panda3D
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 ////////////////////////////////////////////////////////////////////
59 PT(CopyOnWriteObject) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
102 GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
448 PT(TransformBlendTable) GeomVertexData::
449 modify_transform_blend_table() {
450  CDWriter cdata(_cycler, true);
451 
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 
737  clear_transform_blend_table();
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 ////////////////////////////////////////////////////////////////////
796 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
893 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
924 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
970 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
997 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
1031 CPT(GeomVertexData) GeomVertexData::
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 ////////////////////////////////////////////////////////////////////
1072 CPT(GeomVertexData) GeomVertexData::
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::
1150 clear_animated_vertices() {
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::transform_vertices
1201 // Access: Published
1202 // Description: Applies the indicated transform matrix to all of the
1203 // vertices mentioned in the sparse array. The
1204 // transform is applied to all "point" and "vector"
1205 // type columns described in the format.
1206 ////////////////////////////////////////////////////////////////////
1207 void GeomVertexData::
1208 transform_vertices(const LMatrix4 &mat, const SparseArray &rows) {
1209  if (rows.is_zero()) {
1210  // Trivial no-op.
1211  return;
1212  }
1213 
1214  const GeomVertexFormat *format = get_format();
1215 
1216  size_t ci;
1217  for (ci = 0; ci < format->get_num_points(); ci++) {
1218  GeomVertexRewriter data(this, format->get_point(ci));
1219 
1220  for (size_t i = 0; i < rows.get_num_subranges(); ++i) {
1221  int begin_row = rows.get_subrange_begin(i);
1222  int end_row = rows.get_subrange_end(i);
1223  do_transform_point_column(format, data, mat, begin_row, end_row);
1224  }
1225  }
1226 
1227  for (ci = 0; ci < format->get_num_vectors(); ci++) {
1228  GeomVertexRewriter data(this, format->get_vector(ci));
1229 
1230  for (size_t i = 0; i < rows.get_num_subranges(); ++i) {
1231  int begin_row = rows.get_subrange_begin(i);
1232  int end_row = rows.get_subrange_end(i);
1233  do_transform_vector_column(format, data, mat, begin_row, end_row);
1234  }
1235  }
1236 }
1237 
1238 ////////////////////////////////////////////////////////////////////
1239 // Function: GeomVertexData::bytewise_copy
1240 // Access: Private, Static
1241 // Description: Quickly copies data without the need to convert it.
1242 ////////////////////////////////////////////////////////////////////
1243 void GeomVertexData::
1244 bytewise_copy(unsigned char *to, int to_stride,
1245  const unsigned char *from, int from_stride,
1246  const GeomVertexColumn *from_type,
1247  int num_records) {
1248  if (gobj_cat.is_debug()) {
1249  gobj_cat.debug()
1250  << "bytewise_copy(" << (void *)to << ", " << to_stride
1251  << ", " << (const void *)from << ", " << from_stride
1252  << ", " << *from_type << ", " << num_records << ")\n";
1253  }
1254  if (to_stride == from_type->get_total_bytes() &&
1255  from_stride == from_type->get_total_bytes()) {
1256  // Fantastic! It's just a linear array of this one data type.
1257  // Copy the whole thing all at once.
1258  memcpy(to, from, num_records * from_type->get_total_bytes());
1259 
1260  } else {
1261  // Ok, it's interleaved in with other data. Copy them one record
1262  // at a time.
1263  while (num_records > 0) {
1264  memcpy(to, from, from_type->get_total_bytes());
1265  to += to_stride;
1266  from += from_stride;
1267  num_records--;
1268  }
1269  }
1270 }
1271 
1272 ////////////////////////////////////////////////////////////////////
1273 // Function: GeomVertexData::replace_column
1274 // Access: Published
1275 // Description: Returns a new GeomVertexData object, suitable for
1276 // modification, with the indicated data type replaced
1277 // with a new table filled with undefined values. The
1278 // new table will be added as a new array; if the old
1279 // table was interleaved with a previous array, the
1280 // previous array will not be repacked.
1281 //
1282 // If num_components is 0, the indicated name is simply
1283 // removed from the type, without replacing it with
1284 // anything else.
1285 ////////////////////////////////////////////////////////////////////
1286 PT(GeomVertexData) GeomVertexData::
1287 replace_column(InternalName *name, int num_components,
1288  GeomVertexData::NumericType numeric_type,
1289  GeomVertexData::Contents contents) const {
1290  CDReader cdata(_cycler);
1291  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*cdata->_format);
1292 
1293  // Remove the old description of the type from the format.
1294  bool removed_type_array = false;
1295  int old_type_array = cdata->_format->get_array_with(name);
1296  if (old_type_array != -1) {
1297  GeomVertexArrayFormat *array_format = new_format->modify_array(old_type_array);
1298  if (array_format->get_num_columns() == 1) {
1299  // Actually, this array didn't have any other data types, so
1300  // just drop the whole array.
1301  new_format->remove_array(old_type_array);
1302  removed_type_array = true;
1303 
1304  } else {
1305  // Remove the description for the type, but don't bother to
1306  // repack the array.
1307  array_format->remove_column(name);
1308  }
1309  }
1310 
1311  // Now define a new array to contain just the type.
1312  int new_type_array = -1;
1313  if (num_components != 0) {
1314  PT(GeomVertexArrayFormat) type_array_format =
1315  new GeomVertexArrayFormat(name, num_components, numeric_type, contents);
1316  new_type_array = new_format->add_array(type_array_format);
1317  }
1318 
1319  CPT(GeomVertexFormat) format =
1320  GeomVertexFormat::register_format(new_format);
1321 
1322  if (gobj_cat.is_debug()) {
1323  gobj_cat.debug()
1324  << "Replacing data type " << *name << "; converting "
1325  << get_num_rows() << " rows from "
1326  << *cdata->_format << " to " << *format << "\n";
1327  }
1328 
1329  PT(GeomVertexData) new_data = new GeomVertexData(*this, format);
1330 
1331  int j = 0;
1332  int num_arrays = get_num_arrays();
1333  for (int i = 0; i < num_arrays; ++i) {
1334  if (i == old_type_array) {
1335  if (!removed_type_array) {
1336  // Pointer-copy the original array that includes the type
1337  // (since it also includes other data).
1338  new_data->set_array(j, get_array(i));
1339  ++j;
1340  }
1341 
1342  } else {
1343  // Just pointer-copy any arrays other than type.
1344  new_data->set_array(j, get_array(i));
1345  ++j;
1346  }
1347  }
1348 
1349  if (new_type_array != -1) {
1350  nassertr(j == new_type_array, new_data);
1351 
1352  // For the new type array, we set up a temporary array that has
1353  // room for the right number of rows.
1354  PT(GeomVertexArrayData) new_array = new GeomVertexArrayData
1355  (format->get_array(j), get_usage_hint());
1356  new_array->set_num_rows(get_num_rows());
1357  new_data->set_array(j, new_array);
1358  }
1359 
1360  return new_data;
1361 }
1362 
1363 ////////////////////////////////////////////////////////////////////
1364 // Function: GeomVertexData::output
1365 // Access: Published
1366 // Description:
1367 ////////////////////////////////////////////////////////////////////
1368 void GeomVertexData::
1369 output(ostream &out) const {
1370  if (!get_name().empty()) {
1371  out << get_name() << " ";
1372  }
1373  out << get_num_rows() << " rows: " << *get_format();
1374 }
1375 
1376 ////////////////////////////////////////////////////////////////////
1377 // Function: GeomVertexData::write
1378 // Access: Published
1379 // Description:
1380 ////////////////////////////////////////////////////////////////////
1381 void GeomVertexData::
1382 write(ostream &out, int indent_level) const {
1383  if (!get_name().empty()) {
1384  indent(out, indent_level) << get_name() << "\n";
1385  }
1386  get_format()->write_with_data(out, indent_level + 2, this);
1387  CPT(TransformBlendTable) table = get_transform_blend_table();
1388  if (table != (TransformBlendTable *)NULL) {
1389  indent(out, indent_level)
1390  << "Transform blend table:\n";
1391  table->write(out, indent_level + 2);
1392  }
1393 }
1394 
1395 ////////////////////////////////////////////////////////////////////
1396 // Function: GeomVertexData::describe_vertex
1397 // Access: Published
1398 // Description: Writes a verbose, human-friendly description of the
1399 // indicated vertex number.
1400 ////////////////////////////////////////////////////////////////////
1401 void GeomVertexData::
1402 describe_vertex(ostream &out, int row) const {
1403  nassertv_always(row >= 0 && row < get_num_rows());
1404 
1405  out << "Vertex " << row << ":\n";
1406 
1407  GeomVertexReader reader(this);
1408  reader.set_row_unsafe(row);
1409  const GeomVertexFormat *format = get_format();
1410 
1411  const TransformBlendTable *tb_table = NULL;
1412  if (format->get_animation().get_animation_type() == AT_panda) {
1413  tb_table = get_transform_blend_table();
1414  }
1415 
1416  int num_columns = format->get_num_columns();
1417  for (int ci = 0; ci < num_columns; ++ci) {
1418  int ai = format->get_array_with(ci);
1419  const GeomVertexColumn *column = format->get_column(ci);
1420  reader.set_column(ai, column);
1421 
1422  int num_values = min(column->get_num_values(), 4);
1423  const LVecBase4 &d = reader.get_data4();
1424 
1425  out << " " << *column->get_name();
1426  for (int v = 0; v < num_values; v++) {
1427  out << " " << d[v];
1428  }
1429  out << "\n";
1430 
1431  if (column->get_name() == InternalName::get_transform_blend() &&
1432  tb_table != NULL) {
1433  // This is an index into the transform blend table. Look up the
1434  // index and report the vertex weighting.
1435  reader.set_column(ai, column);
1436  int bi = reader.get_data1i();
1437  if (bi >= 0 && bi < tb_table->get_num_blends()) {
1438  const TransformBlend &blend = tb_table->get_blend(bi);
1439  out << " " << blend << "\n";
1440  }
1441  }
1442  }
1443 
1444  // Also show the raw vertex data, why not?
1445  out << "\nraw data:\n";
1446  int num_arrays = format->get_num_arrays();
1447  for (int ai = 0; ai < num_arrays; ++ai) {
1448  const GeomVertexArrayData *array = get_array(ai);
1449  const GeomVertexArrayFormat *aformat = format->get_array(ai);
1450  nassertv(array != NULL && aformat != NULL);
1451  out << " " << *aformat << "\n";
1452  CPT(GeomVertexArrayDataHandle) handle = array->get_handle();
1453  nassertv(handle != (const GeomVertexArrayDataHandle *)NULL);
1454  const unsigned char *data = handle->get_read_pointer(true);
1455  nassertv(data != NULL);
1456  int stride = aformat->get_stride();
1457  int start = stride * row;
1458  if (data != NULL) {
1459  Datagram dg(data + start, stride);
1460  dg.dump_hex(out, 4);
1461  }
1462  }
1463 }
1464 
1465 ////////////////////////////////////////////////////////////////////
1466 // Function: GeomVertexData::clear_cache
1467 // Access: Published
1468 // Description: Removes all of the previously-cached results of
1469 // convert_to().
1470 //
1471 // This blows away the entire cache, upstream and
1472 // downstream the pipeline. Use clear_cache_stage()
1473 // instead if you only want to blow away the cache at
1474 // the current stage and upstream.
1475 ////////////////////////////////////////////////////////////////////
1476 void GeomVertexData::
1477 clear_cache() {
1478  LightMutexHolder holder(_cache_lock);
1479  for (Cache::iterator ci = _cache.begin();
1480  ci != _cache.end();
1481  ++ci) {
1482  CacheEntry *entry = (*ci).second;
1483  entry->erase();
1484  }
1485  _cache.clear();
1486 }
1487 
1488 ////////////////////////////////////////////////////////////////////
1489 // Function: GeomVertexData::clear_cache_stage
1490 // Access: Published
1491 // Description: Removes all of the previously-cached results of
1492 // convert_to(), at the current pipeline stage and
1493 // upstream. Does not affect the downstream cache.
1494 //
1495 // Don't call this in a downstream thread unless you
1496 // don't mind it blowing away other changes you might
1497 // have recently made in an upstream thread.
1498 ////////////////////////////////////////////////////////////////////
1499 void GeomVertexData::
1501  LightMutexHolder holder(_cache_lock);
1502  for (Cache::iterator ci = _cache.begin();
1503  ci != _cache.end();
1504  ++ci) {
1505  CacheEntry *entry = (*ci).second;
1506  CDCacheWriter cdata(entry->_cycler);
1507  cdata->_result = NULL;
1508  }
1509 }
1510 
1511 ////////////////////////////////////////////////////////////////////
1512 // Function: GeomVertexData::packed_argb_to_uint8_rgba
1513 // Access: Private, Static
1514 // Description: Quickly converts DirectX-style color to OpenGL-style
1515 // color.
1516 ////////////////////////////////////////////////////////////////////
1517 void GeomVertexData::
1518 packed_argb_to_uint8_rgba(unsigned char *to, int to_stride,
1519  const unsigned char *from, int from_stride,
1520  int num_records) {
1521  if (gobj_cat.is_debug()) {
1522  gobj_cat.debug()
1523  << "packed_argb_to_uint8_rgba(" << (void *)to << ", " << to_stride
1524  << ", " << (const void *)from << ", " << from_stride
1525  << ", " << num_records << ")\n";
1526  }
1527 
1528  while (num_records > 0) {
1529  PN_uint32 dword = *(const PN_uint32 *)from;
1530  to[0] = unpack_abcd_b(dword);
1531  to[1] = unpack_abcd_c(dword);
1532  to[2] = unpack_abcd_d(dword);
1533  to[3] = unpack_abcd_a(dword);
1534 
1535  to += to_stride;
1536  from += from_stride;
1537  num_records--;
1538  }
1539 }
1540 
1541 ////////////////////////////////////////////////////////////////////
1542 // Function: GeomVertexData::uint8_rgba_to_packed_argb
1543 // Access: Private, Static
1544 // Description: Quickly converts OpenGL-style color to DirectX-style
1545 // color.
1546 ////////////////////////////////////////////////////////////////////
1547 void GeomVertexData::
1548 uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
1549  const unsigned char *from, int from_stride,
1550  int num_records) {
1551  if (gobj_cat.is_debug()) {
1552  gobj_cat.debug()
1553  << "uint8_rgba_to_packed_argb(" << (void *)to << ", " << to_stride
1554  << ", " << (const void *)from << ", " << from_stride
1555  << ", " << num_records << ")\n";
1556  }
1557 
1558  while (num_records > 0) {
1559  *(PN_uint32 *)to = pack_abcd(from[3], from[0], from[1], from[2]);
1560 
1561  to += to_stride;
1562  from += from_stride;
1563  num_records--;
1564  }
1565 }
1566 
1567 ////////////////////////////////////////////////////////////////////
1568 // Function: GeomVertexData::update_animated_vertices
1569 // Access: Private
1570 // Description: Recomputes the results of computing the vertex
1571 // animation on the CPU, and applies them to the
1572 // existing animated_vertices object.
1573 ////////////////////////////////////////////////////////////////////
1574 void GeomVertexData::
1575 update_animated_vertices(GeomVertexData::CData *cdata, Thread *current_thread) {
1576  PStatTimer timer(_char_pcollector, current_thread);
1577 
1578  int num_rows = get_num_rows();
1579 
1580  if (gobj_cat.is_debug()) {
1581  gobj_cat.debug()
1582  << "Animating " << num_rows << " vertices for " << get_name()
1583  << "\n";
1584  }
1585 
1586  const GeomVertexFormat *orig_format = cdata->_format;
1587  CPT(GeomVertexFormat) new_format = orig_format;
1588 
1589  if (cdata->_animated_vertices == (GeomVertexData *)NULL) {
1590  new_format = orig_format->get_post_animated_format();
1591  cdata->_animated_vertices =
1592  new GeomVertexData(get_name(), new_format,
1593  min(get_usage_hint(), UH_dynamic));
1594  }
1595  PT(GeomVertexData) new_data = cdata->_animated_vertices;
1596 
1597  // We have to make a complete copy of the data first so we can
1598  // modify it. If we were clever, we could maybe just figure out the
1599  // subset of the data that might have changed since last frame, but
1600  // that's too much trouble (and isn't obviously faster than just
1601  // copying the whole thing).
1602  new_data->copy_from(this, true);
1603 
1604  // First, apply all of the morphs.
1605  CPT(SliderTable) slider_table = cdata->_slider_table;
1606  if (slider_table != (SliderTable *)NULL) {
1607  PStatTimer timer2(_morphs_pcollector);
1608  int num_morphs = orig_format->get_num_morphs();
1609  for (int mi = 0; mi < num_morphs; mi++) {
1610  CPT(InternalName) slider_name = orig_format->get_morph_slider(mi);
1611 
1612  const SparseArray &sliders = slider_table->find_sliders(slider_name);
1613  if (!sliders.is_zero()) {
1614  nassertv(!sliders.is_inverse());
1615  int num_slider_subranges = sliders.get_num_subranges();
1616  for (int sni = 0; sni < num_slider_subranges; ++sni) {
1617  int slider_begin = sliders.get_subrange_begin(sni);
1618  int slider_end = sliders.get_subrange_end(sni);
1619  for (int sn = slider_begin; sn < slider_end; ++sn) {
1620  const VertexSlider *slider = slider_table->get_slider(sn);
1621  const SparseArray &rows = slider_table->get_slider_rows(sn);
1622  nassertv(!rows.is_inverse());
1623 
1624  PN_stdfloat slider_value = slider->get_slider();
1625  if (slider_value != 0.0f) {
1626  CPT(InternalName) base_name = orig_format->get_morph_base(mi);
1627  CPT(InternalName) delta_name = orig_format->get_morph_delta(mi);
1628 
1629  GeomVertexRewriter data(new_data, base_name);
1630  GeomVertexReader delta(this, delta_name);
1631  int num_subranges = rows.get_num_subranges();
1632 
1633  if (data.get_column()->get_num_values() == 4) {
1634  if (data.get_column()->has_homogeneous_coord()) {
1635  // Scale the delta by the homogeneous coordinate.
1636  for (int i = 0; i < num_subranges; ++i) {
1637  int begin = rows.get_subrange_begin(i);
1638  int end = rows.get_subrange_end(i);
1639  data.set_row_unsafe(begin);
1640  delta.set_row_unsafe(begin);
1641  for (int j = begin; j < end; ++j) {
1642  LPoint4 vertex = data.get_data4();
1643  LPoint3 d = delta.get_data3();
1644  d *= slider_value * vertex[3];
1645  data.set_data4(vertex[0] + d[0],
1646  vertex[1] + d[1],
1647  vertex[2] + d[2],
1648  vertex[3]);
1649  }
1650  }
1651  } else {
1652  // Just apply the four-component delta.
1653  for (int i = 0; i < num_subranges; ++i) {
1654  int begin = rows.get_subrange_begin(i);
1655  int end = rows.get_subrange_end(i);
1656  data.set_row_unsafe(begin);
1657  delta.set_row_unsafe(begin);
1658  for (int j = begin; j < end; ++j) {
1659  const LPoint4 &vertex = data.get_data4();
1660  LPoint4 d = delta.get_data4();
1661  data.set_data4(vertex + d * slider_value);
1662  }
1663  }
1664  }
1665  } else {
1666  // 3-component or smaller values; don't worry about a
1667  // homogeneous coordinate.
1668  for (int i = 0; i < num_subranges; ++i) {
1669  int begin = rows.get_subrange_begin(i);
1670  int end = rows.get_subrange_end(i);
1671  data.set_row_unsafe(begin);
1672  delta.set_row_unsafe(begin);
1673  for (int j = begin; j < end; ++j) {
1674  const LPoint3 &vertex = data.get_data3();
1675  LPoint3 d = delta.get_data3();
1676  data.set_data3(vertex + d * slider_value);
1677  }
1678  }
1679  }
1680  }
1681  }
1682  }
1683  }
1684  }
1685  }
1686 
1687  // Then apply the transforms.
1688  CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table.get_read_pointer();
1689  if (tb_table != (TransformBlendTable *)NULL) {
1690  // Recompute all the blends up front, so we don't have to test
1691  // each one for staleness at each vertex.
1692  {
1693  PStatTimer timer4(_blends_pcollector);
1694  int num_blends = tb_table->get_num_blends();
1695  for (int bi = 0; bi < num_blends; bi++) {
1696  tb_table->get_blend(bi).update_blend(current_thread);
1697  }
1698  }
1699 
1700  // Now go through and apply the transforms.
1701  PStatTimer timer3(_skinning_pcollector);
1702 
1703  const SparseArray &rows = tb_table->get_rows();
1704  int num_subranges = rows.get_num_subranges();
1705 
1706  int blend_array_index = orig_format->get_array_with(InternalName::get_transform_blend());
1707  if (blend_array_index < 0) {
1708  gobj_cat.warning()
1709  << "Vertex data " << get_name()
1710  << " has a transform_blend_table, but no transform_blend data.\n";
1711  return;
1712  }
1713 
1714  CPT(GeomVertexArrayFormat) blend_array_format = orig_format->get_array(blend_array_index);
1715 
1716  if (blend_array_format->get_stride() == 2 &&
1717  blend_array_format->get_column(0)->get_component_bytes() == 2) {
1718  // The blend indices are a table of ushorts. Optimize this
1719  // common case.
1720  CPT(GeomVertexArrayDataHandle) blend_array_handle = cdata->_arrays[blend_array_index].get_read_pointer()->get_handle(current_thread);
1721  const unsigned short *blendt = (const unsigned short *)blend_array_handle->get_read_pointer(true);
1722 
1723  int ci;
1724  for (ci = 0; ci < new_format->get_num_points(); ci++) {
1725  GeomVertexRewriter data(new_data, new_format->get_point(ci));
1726 
1727  for (int i = 0; i < num_subranges; ++i) {
1728  int begin = rows.get_subrange_begin(i);
1729  int end = rows.get_subrange_end(i);
1730  nassertv(begin < end);
1731 
1732  int first_vertex = begin;
1733  int first_bi = blendt[first_vertex];
1734 
1735  while (first_vertex < end) {
1736  // At this point, first_vertex is the first of a series of
1737  // vertices that shares the blend index first_bi.
1738 
1739  // Scan for the end of this series of vertices--we're
1740  // looking for the next vertex with a different blend index.
1741  int next_vertex = first_vertex;
1742  int next_bi = first_bi;
1743  ++next_vertex;
1744  while (next_vertex < end) {
1745  next_bi = blendt[next_vertex];
1746  if (next_bi != first_bi) {
1747  break;
1748  }
1749  ++next_vertex;
1750  }
1751 
1752  // We've just reached the end of the vertices with a matching
1753  // blend index. Transform all those vertices as a block.
1754  LMatrix4 mat;
1755  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1756  new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex);
1757 
1758  first_vertex = next_vertex;
1759  first_bi = next_bi;
1760  }
1761  }
1762  }
1763 
1764  for (ci = 0; ci < new_format->get_num_vectors(); ci++) {
1765  GeomVertexRewriter data(new_data, new_format->get_vector(ci));
1766 
1767  for (int i = 0; i < num_subranges; ++i) {
1768  int begin = rows.get_subrange_begin(i);
1769  int end = rows.get_subrange_end(i);
1770  nassertv(begin < end);
1771 
1772  int first_vertex = begin;
1773  int first_bi = blendt[first_vertex];
1774 
1775  while (first_vertex < end) {
1776  // At this point, first_vertex is the first of a series of
1777  // vertices that shares the blend index first_bi.
1778 
1779  // Scan for the end of this series of vertices--we're
1780  // looking for the next vertex with a different blend index.
1781  int next_vertex = first_vertex;
1782  int next_bi = first_bi;
1783  ++next_vertex;
1784  while (next_vertex < end) {
1785  next_bi = blendt[next_vertex];
1786  if (next_bi != first_bi) {
1787  break;
1788  }
1789  ++next_vertex;
1790  }
1791 
1792  // We've just reached the end of the vertices with a matching
1793  // blend index. Transform all those vertices as a block.
1794  LMatrix4 mat;
1795  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1796  new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex);
1797 
1798  first_vertex = next_vertex;
1799  first_bi = next_bi;
1800  }
1801  }
1802  }
1803 
1804  } else {
1805  // The blend indices are anything else. Use the
1806  // GeomVertexReader to iterate through them.
1807  GeomVertexReader blendi(this, InternalName::get_transform_blend());
1808  nassertv(blendi.has_column());
1809 
1810  int ci;
1811  for (ci = 0; ci < new_format->get_num_points(); ci++) {
1812  GeomVertexRewriter data(new_data, new_format->get_point(ci));
1813 
1814  for (int i = 0; i < num_subranges; ++i) {
1815  int begin = rows.get_subrange_begin(i);
1816  int end = rows.get_subrange_end(i);
1817  nassertv(begin < end);
1818  blendi.set_row_unsafe(begin);
1819 
1820  int first_vertex = begin;
1821  int first_bi = blendi.get_data1i();
1822 
1823  while (first_vertex < end) {
1824  // At this point, first_vertex is the first of a series of
1825  // vertices that shares the blend index first_bi.
1826 
1827  // Scan for the end of this series of vertices--we're
1828  // looking for the next vertex with a different blend index.
1829  int next_vertex = first_vertex;
1830  int next_bi = first_bi;
1831  ++next_vertex;
1832  while (next_vertex < end) {
1833  next_bi = blendi.get_data1i();
1834  if (next_bi != first_bi) {
1835  break;
1836  }
1837  ++next_vertex;
1838  }
1839 
1840  // We've just reached the end of the vertices with a matching
1841  // blend index. Transform all those vertices as a block.
1842  LMatrix4 mat;
1843  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1844  new_data->do_transform_point_column(new_format, data, mat, first_vertex, next_vertex);
1845 
1846  first_vertex = next_vertex;
1847  first_bi = next_bi;
1848  }
1849  }
1850  }
1851 
1852  for (ci = 0; ci < new_format->get_num_vectors(); ci++) {
1853  GeomVertexRewriter data(new_data, new_format->get_vector(ci));
1854 
1855  for (int i = 0; i < num_subranges; ++i) {
1856  int begin = rows.get_subrange_begin(i);
1857  int end = rows.get_subrange_end(i);
1858  nassertv(begin != end);
1859  blendi.set_row_unsafe(begin);
1860 
1861  int first_vertex = begin;
1862  int first_bi = blendi.get_data1i();
1863 
1864  while (first_vertex < end) {
1865  // At this point, first_vertex is the first of a series of
1866  // vertices that shares the blend index first_bi.
1867 
1868  // Scan for the end of this series of vertices--we're
1869  // looking for the next vertex with a different blend index.
1870  int next_vertex = first_vertex;
1871  int next_bi = first_bi;
1872  ++next_vertex;
1873  while (next_vertex < end) {
1874  next_bi = blendi.get_data1i();
1875  if (next_bi != first_bi) {
1876  break;
1877  }
1878  ++next_vertex;
1879  }
1880 
1881  // We've just reached the end of the vertices with a matching
1882  // blend index. Transform all those vertices as a block.
1883  LMatrix4 mat;
1884  tb_table->get_blend(first_bi).get_blend(mat, current_thread);
1885  new_data->do_transform_vector_column(new_format, data, mat, first_vertex, next_vertex);
1886 
1887  first_vertex = next_vertex;
1888  first_bi = next_bi;
1889  }
1890  }
1891  }
1892  }
1893  }
1894 }
1895 
1896 
1897 ////////////////////////////////////////////////////////////////////
1898 // Function: GeomVertexData::do_transform_point_column
1899 // Access: Private
1900 // Description: Transforms a range of vertices for one particular
1901 // column, as a point.
1902 ////////////////////////////////////////////////////////////////////
1903 void GeomVertexData::
1904 do_transform_point_column(const GeomVertexFormat *format, GeomVertexRewriter &data,
1905  const LMatrix4 &mat, int begin_row, int end_row) {
1906  const GeomVertexColumn *data_column = data.get_column();
1907  int num_values = data_column->get_num_values();
1908 
1909  if ((num_values == 3 || num_values == 4) &&
1910  data_column->get_numeric_type() == NT_float32) {
1911  // The table of points is a table of LPoint3f's or LPoint4f's.
1912  // Optimize this common case.
1913  GeomVertexArrayDataHandle *data_handle = data.get_array_handle();
1914 
1915  size_t stride = data.get_stride();
1916  size_t num_rows = end_row - begin_row;
1917  unsigned char *datat = data_handle->get_write_pointer();
1918  datat += data_column->get_start() + begin_row * stride;
1919  LMatrix4f matf = LCAST(float, mat);
1920 
1921  if (num_values == 3) {
1922  table_xform_point3f(datat, num_rows, stride, matf);
1923  } else {
1924  table_xform_vecbase4f(datat, num_rows, stride, matf);
1925  }
1926 
1927  } else if (num_values == 4) {
1928  // Use the GeomVertexRewriter to adjust the 4-component
1929  // points.
1930 
1931  data.set_row_unsafe(begin_row);
1932  for (int j = begin_row; j < end_row; ++j) {
1933  LPoint4 vertex = data.get_data4();
1934  data.set_data4(vertex * mat);
1935  }
1936 
1937  } else {
1938  // Use the GeomVertexRewriter to adjust the 3-component
1939  // points.
1940 
1941  data.set_row_unsafe(begin_row);
1942  for (int j = begin_row; j < end_row; ++j) {
1943  LPoint3 vertex = data.get_data3();
1944  data.set_data3(vertex * mat);
1945  }
1946  }
1947 }
1948 
1949 ////////////////////////////////////////////////////////////////////
1950 // Function: GeomVertexData::do_transform_vector_column
1951 // Access: Private
1952 // Description: Transforms a range of vertices for one particular
1953 // column, as a vector.
1954 ////////////////////////////////////////////////////////////////////
1955 void GeomVertexData::
1956 do_transform_vector_column(const GeomVertexFormat *format, GeomVertexRewriter &data,
1957  const LMatrix4 &mat, int begin_row, int end_row) {
1958  const GeomVertexColumn *data_column = data.get_column();
1959  int num_values = data_column->get_num_values();
1960 
1961  LMatrix4 xform;
1962  bool normalize = false;
1963  if (data_column->get_contents() == C_normal) {
1964  // This is to preserve perpendicularity to the surface.
1965  LVecBase3 scale, shear, hpr;
1966  if (decompose_matrix(mat.get_upper_3(), scale, shear, hpr) &&
1967  IS_NEARLY_EQUAL(scale[0], scale[1]) &&
1968  IS_NEARLY_EQUAL(scale[0], scale[2])) {
1969  if (scale[0] == 1) {
1970  // No scale to worry about.
1971  xform = mat;
1972  } else {
1973  // Simply take the uniform scale out of the transformation.
1974  // Not sure if it might be better to just normalize?
1975  compose_matrix(xform, LVecBase3(1, 1, 1), shear, hpr, LVecBase3::zero());
1976  }
1977  } else {
1978  // There is a non-uniform scale, so we need to do all this to
1979  // preserve orthogonality to the surface.
1980  xform.invert_from(mat);
1981  xform.transpose_in_place();
1982  normalize = true;
1983  }
1984  } else {
1985  xform = mat;
1986  }
1987 
1988  if ((num_values == 3 || num_values == 4) &&
1989  data_column->get_numeric_type() == NT_float32) {
1990  // The table of vectors is a table of LVector3f's or LVector4f's.
1991  // Optimize this common case.
1992  GeomVertexArrayDataHandle *data_handle = data.get_array_handle();
1993 
1994  size_t stride = data.get_stride();
1995  size_t num_rows = end_row - begin_row;
1996  unsigned char *datat = data_handle->get_write_pointer();
1997  datat += data_column->get_start() + begin_row * stride;
1998  LMatrix4f matf = LCAST(float, xform);
1999 
2000  if (normalize) {
2001  table_xform_normal3f(datat, num_rows, stride, matf);
2002  } else if (num_values == 3) {
2003  table_xform_vector3f(datat, num_rows, stride, matf);
2004  } else {
2005  table_xform_vecbase4f(datat, num_rows, stride, matf);
2006  }
2007 
2008  } else {
2009  // Use the GeomVertexRewriter to transform the vectors.
2010  data.set_row_unsafe(begin_row);
2011 
2012  if (normalize) {
2013  for (int j = begin_row; j < end_row; ++j) {
2014  LVector3 vector = data.get_data3();
2015  vector *= xform;
2016  vector.normalize();
2017  data.set_data3(vector);
2018  }
2019  } else {
2020  for (int j = begin_row; j < end_row; ++j) {
2021  LVector3 vector = data.get_data3();
2022  data.set_data3(vector * xform);
2023  }
2024  }
2025  }
2026 }
2027 
2028 ////////////////////////////////////////////////////////////////////
2029 // Function: GeomVertexData::table_xform_point3f
2030 // Access: Private, Static
2031 // Description: Transforms each of the LPoint3f objects in the
2032 // indicated table by the indicated matrix.
2033 ////////////////////////////////////////////////////////////////////
2034 void GeomVertexData::
2035 table_xform_point3f(unsigned char *datat, size_t num_rows, size_t stride,
2036  const LMatrix4f &matf) {
2037  // We don't bother checking for the unaligned case here, because in
2038  // practice it doesn't matter with a 3-component point.
2039  for (size_t i = 0; i < num_rows; ++i) {
2040  LPoint3f &vertex = *(LPoint3f *)(&datat[i * stride]);
2041  vertex *= matf;
2042  }
2043 }
2044 
2045 ////////////////////////////////////////////////////////////////////
2046 // Function: GeomVertexData::table_xform_normal3f
2047 // Access: Private, Static
2048 // Description: Transforms each of the LVector3f objects in the
2049 // indicated table by the indicated matrix, and also
2050 // normalizes them.
2051 ////////////////////////////////////////////////////////////////////
2052 void GeomVertexData::
2053 table_xform_normal3f(unsigned char *datat, size_t num_rows, size_t stride,
2054  const LMatrix4f &matf) {
2055  // We don't bother checking for the unaligned case here, because in
2056  // practice it doesn't matter with a 3-component vector.
2057  for (size_t i = 0; i < num_rows; ++i) {
2058  LNormalf &vertex = *(LNormalf *)(&datat[i * stride]);
2059  vertex *= matf;
2060  vertex.normalize();
2061  }
2062 }
2063 
2064 ////////////////////////////////////////////////////////////////////
2065 // Function: GeomVertexData::table_xform_vector3f
2066 // Access: Private, Static
2067 // Description: Transforms each of the LVector3f objects in the
2068 // indicated table by the indicated matrix.
2069 ////////////////////////////////////////////////////////////////////
2070 void GeomVertexData::
2071 table_xform_vector3f(unsigned char *datat, size_t num_rows, size_t stride,
2072  const LMatrix4f &matf) {
2073  // We don't bother checking for the unaligned case here, because in
2074  // practice it doesn't matter with a 3-component vector.
2075  for (size_t i = 0; i < num_rows; ++i) {
2076  LVector3f &vertex = *(LVector3f *)(&datat[i * stride]);
2077  vertex *= matf;
2078  }
2079 }
2080 
2081 ////////////////////////////////////////////////////////////////////
2082 // Function: GeomVertexData::table_xform_vecbase4f
2083 // Access: Private, Static
2084 // Description: Transforms each of the LVecBase4f objects in the
2085 // indicated table by the indicated matrix.
2086 ////////////////////////////////////////////////////////////////////
2087 void GeomVertexData::
2088 table_xform_vecbase4f(unsigned char *datat, size_t num_rows, size_t stride,
2089  const LMatrix4f &matf) {
2090 #if defined(HAVE_EIGEN) && defined(LINMATH_ALIGN)
2091  // Check if the table is unaligned. If it is, we can't use the
2092  // LVecBase4f object directly, which assumes 16-byte alignment.
2093  if (((size_t)datat & 0xf) != 0 || (stride & 0xf) != 0) {
2094  // Instead, we'll use low-level Eigen calls to multiply out the
2095  // unaligned memory.
2096  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)));
2097  for (size_t i = 0; i < num_rows; ++i) {
2098  table.row(i) *= matf._m;
2099  }
2100  return;
2101  }
2102 #endif // HAVE_EIGEN
2103 
2104  // If the table is properly aligned (or we don't require alignment),
2105  // we can directly use the high-level LVecBase4f object, which will
2106  // do the right thing.
2107  for (size_t i = 0; i < num_rows; ++i) {
2108  LVecBase4f &vertex = *(LVecBase4f *)(&datat[i * stride]);
2109  vertex *= matf;
2110  }
2111 }
2112 
2113 ////////////////////////////////////////////////////////////////////
2114 // Function: GeomVertexData::register_with_read_factory
2115 // Access: Public, Static
2116 // Description: Tells the BamReader how to create objects of type
2117 // GeomVertexData.
2118 ////////////////////////////////////////////////////////////////////
2119 void GeomVertexData::
2121  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
2122 }
2123 
2124 ////////////////////////////////////////////////////////////////////
2125 // Function: GeomVertexData::write_datagram
2126 // Access: Public, Virtual
2127 // Description: Writes the contents of this object to the datagram
2128 // for shipping out to a Bam file.
2129 ////////////////////////////////////////////////////////////////////
2130 void GeomVertexData::
2132  CopyOnWriteObject::write_datagram(manager, dg);
2133 
2134  dg.add_string(_name);
2135  manager->write_cdata(dg, _cycler);
2136 }
2137 
2138 ////////////////////////////////////////////////////////////////////
2139 // Function: GeomVertexData::make_from_bam
2140 // Access: Protected, Static
2141 // Description: This function is called by the BamReader's factory
2142 // when a new object of type GeomVertexData is encountered
2143 // in the Bam file. It should create the GeomVertexData
2144 // and extract its information from the file.
2145 ////////////////////////////////////////////////////////////////////
2146 TypedWritable *GeomVertexData::
2147 make_from_bam(const FactoryParams &params) {
2148  GeomVertexData *object = new GeomVertexData;
2149  DatagramIterator scan;
2150  BamReader *manager;
2151 
2152  parse_params(params, scan, manager);
2153  object->fillin(scan, manager);
2154  manager->register_finalize(object);
2155 
2156  return object;
2157 }
2158 
2159 ////////////////////////////////////////////////////////////////////
2160 // Function: GeomVertexData::complete_pointers
2161 // Access: Public, Virtual
2162 // Description: Receives an array of pointers, one for each time
2163 // manager->read_pointer() was called in fillin().
2164 // Returns the number of pointers processed.
2165 ////////////////////////////////////////////////////////////////////
2166 int GeomVertexData::
2168  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
2169  return pi;
2170 }
2171 
2172 ////////////////////////////////////////////////////////////////////
2173 // Function: GeomVertexData::require_fully_complete
2174 // Access: Public, Virtual
2175 // Description: Some objects require all of their nested pointers to
2176 // have been completed before the objects themselves can
2177 // be completed. If this is the case, override this
2178 // method to return true, and be careful with circular
2179 // references (which would make the object unreadable
2180 // from a bam file).
2181 ////////////////////////////////////////////////////////////////////
2182 bool GeomVertexData::
2184  return true;
2185 }
2186 
2187 ////////////////////////////////////////////////////////////////////
2188 // Function: GeomVertexData::finalize
2189 // Access: Public, Virtual
2190 // Description: Called by the BamReader to perform any final actions
2191 // needed for setting up the object after all objects
2192 // have been read and all pointers have been completed.
2193 ////////////////////////////////////////////////////////////////////
2194 void GeomVertexData::
2195 finalize(BamReader *manager) {
2196  // NOTE: This method may be called more than once, because the
2197  // Geom::finalize() will call it explicitly. We have to be prepared
2198  // to accept multiple finalize() calls.
2199 
2200  // Now we need to register the format that we have read from the bam
2201  // file (since it doesn't come out of the bam file automatically
2202  // registered). This may change the format's pointer, which we
2203  // should then update our own data to reflect. But since this may
2204  // cause the unregistered object to destruct, we have to also tell
2205  // the BamReader to return the new object from now on.
2206 
2207  // This extends to the nested array datas, as well as the transform
2208  // table and slider tables, as well.
2209 
2210  CDWriter cdata(_cycler, true);
2211 
2212  for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
2213  CPT(GeomVertexFormat) new_format =
2214  GeomVertexFormat::register_format(cdata->_format);
2215  manager->change_pointer(cdata->_format, new_format);
2216  cdata->_format = new_format;
2217 
2218  CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
2219  PT(GeomVertexArrayData) array_obj = cdata->_arrays[i].get_unsafe_pointer();
2220  nassertv(new_array_format->is_data_subset_of(*array_obj->_array_format));
2221 
2222  manager->change_pointer(array_obj->_array_format, new_array_format);
2223  array_obj->_array_format = new_array_format;
2224  }
2225 
2226  if (cdata->_transform_table != (TransformTable *)NULL) {
2227  CPT(TransformTable) new_transform_table =
2228  TransformTable::register_table(cdata->_transform_table);
2229  manager->change_pointer(cdata->_transform_table, new_transform_table);
2230  cdata->_transform_table = new_transform_table;
2231  }
2232 
2233  if (cdata->_slider_table != (SliderTable *)NULL) {
2234  CPT(SliderTable) new_slider_table =
2235  SliderTable::register_table(cdata->_slider_table);
2236  manager->change_pointer(cdata->_slider_table, new_slider_table);
2237  cdata->_slider_table = new_slider_table;
2238  }
2239 }
2240 
2241 ////////////////////////////////////////////////////////////////////
2242 // Function: GeomVertexData::fillin
2243 // Access: Protected
2244 // Description: This internal function is called by make_from_bam to
2245 // read in all of the relevant data from the BamFile for
2246 // the new GeomVertexData.
2247 ////////////////////////////////////////////////////////////////////
2248 void GeomVertexData::
2249 fillin(DatagramIterator &scan, BamReader *manager) {
2250  CopyOnWriteObject::fillin(scan, manager);
2251 
2252  set_name(scan.get_string());
2253  manager->read_cdata(scan, _cycler);
2254 }
2255 
2256 ////////////////////////////////////////////////////////////////////
2257 // Function: GeomVertexData::CDataCache::make_copy
2258 // Access: Public, Virtual
2259 // Description:
2260 ////////////////////////////////////////////////////////////////////
2261 CycleData *GeomVertexData::CDataCache::
2262 make_copy() const {
2263  return new CDataCache(*this);
2264 }
2265 
2266 ////////////////////////////////////////////////////////////////////
2267 // Function: GeomVertexData::CacheEntry::evict_callback
2268 // Access: Public, Virtual
2269 // Description: Called when the entry is evicted from the cache, this
2270 // should clean up the owning object appropriately.
2271 ////////////////////////////////////////////////////////////////////
2274  LightMutexHolder holder(_source->_cache_lock);
2275  Cache::iterator ci = _source->_cache.find(&_key);
2276  nassertv(ci != _source->_cache.end());
2277  nassertv((*ci).second == this);
2278  _source->_cache.erase(ci);
2279 }
2280 
2281 ////////////////////////////////////////////////////////////////////
2282 // Function: GeomVertexData::CacheEntry::output
2283 // Access: Public, Virtual
2284 // Description:
2285 ////////////////////////////////////////////////////////////////////
2286 void GeomVertexData::CacheEntry::
2287 output(ostream &out) const {
2288  out << "vertex data " << (void *)_source << " to "
2289  << *_key._modifier;
2290 }
2291 
2292 ////////////////////////////////////////////////////////////////////
2293 // Function: GeomVertexData::CData::make_copy
2294 // Access: Public, Virtual
2295 // Description:
2296 ////////////////////////////////////////////////////////////////////
2297 CycleData *GeomVertexData::CData::
2298 make_copy() const {
2299  return new CData(*this);
2300 }
2301 
2302 ////////////////////////////////////////////////////////////////////
2303 // Function: GeomVertexData::CData::write_datagram
2304 // Access: Public, Virtual
2305 // Description: Writes the contents of this object to the datagram
2306 // for shipping out to a Bam file.
2307 ////////////////////////////////////////////////////////////////////
2308 void GeomVertexData::CData::
2309 write_datagram(BamWriter *manager, Datagram &dg) const {
2310  manager->write_pointer(dg, _format);
2311  dg.add_uint8(_usage_hint);
2312 
2313  dg.add_uint16(_arrays.size());
2314  Arrays::const_iterator ai;
2315  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
2316  manager->write_pointer(dg, (*ai).get_read_pointer());
2317  }
2318 
2319  manager->write_pointer(dg, _transform_table);
2320  manager->write_pointer(dg, _transform_blend_table.get_read_pointer());
2321  manager->write_pointer(dg, _slider_table);
2322 }
2323 
2324 ////////////////////////////////////////////////////////////////////
2325 // Function: GeomVertexData::CData::complete_pointers
2326 // Access: Public, Virtual
2327 // Description: Receives an array of pointers, one for each time
2328 // manager->read_pointer() was called in fillin().
2329 // Returns the number of pointers processed.
2330 ////////////////////////////////////////////////////////////////////
2331 int GeomVertexData::CData::
2332 complete_pointers(TypedWritable **p_list, BamReader *manager) {
2333  int pi = CycleData::complete_pointers(p_list, manager);
2334 
2335  _format = DCAST(GeomVertexFormat, p_list[pi++]);
2336 
2337  Arrays::iterator ai;
2338  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
2339  (*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);
2340  }
2341 
2342  _transform_table = DCAST(TransformTable, p_list[pi++]);
2343  _transform_blend_table = DCAST(TransformBlendTable, p_list[pi++]);
2344  _slider_table = DCAST(SliderTable, p_list[pi++]);
2345 
2346  _modified = Geom::get_next_modified();
2347 
2348  if (!_arrays.empty() && manager->get_file_minor_ver() < 7) {
2349  // Bam files prior to 6.7 did not store a SparseArray in the
2350  // SliderTable or TransformBlendTable entries. We need to make up
2351  // a SparseArray for each of them that reflects the complete
2352  // number of rows in the data.
2353  SparseArray all_rows;
2354  CPT(GeomVertexArrayData) adata = _arrays[0].get_read_pointer();
2355  all_rows.set_range(0, adata->get_num_rows());
2356 
2357  if (_slider_table != (SliderTable *)NULL) {
2358  int num_sliders = _slider_table->get_num_sliders();
2359  for (int i = 0; i < num_sliders; ++i) {
2360  ((SliderTable *)_slider_table.p())->set_slider_rows(i, all_rows);
2361  }
2362  }
2363  if (!_transform_blend_table.is_null()) {
2364  _transform_blend_table.get_unsafe_pointer()->set_rows(all_rows);
2365  }
2366  }
2367 
2368  return pi;
2369 }
2370 
2371 ////////////////////////////////////////////////////////////////////
2372 // Function: GeomVertexData::CData::fillin
2373 // Access: Public, Virtual
2374 // Description: This internal function is called by make_from_bam to
2375 // read in all of the relevant data from the BamFile for
2376 // the new GeomVertexData.
2377 ////////////////////////////////////////////////////////////////////
2378 void GeomVertexData::CData::
2379 fillin(DatagramIterator &scan, BamReader *manager) {
2380  manager->read_pointer(scan);
2381  _usage_hint = (UsageHint)scan.get_uint8();
2382 
2383  size_t num_arrays = scan.get_uint16();
2384  _arrays.reserve(num_arrays);
2385  for (size_t i = 0; i < num_arrays; ++i) {
2386  manager->read_pointer(scan);
2387  _arrays.push_back(NULL);
2388  }
2389 
2390  manager->read_pointer(scan);
2391  manager->read_pointer(scan);
2392  manager->read_pointer(scan);
2393 }
2394 
2395 ////////////////////////////////////////////////////////////////////
2396 // Function: GeomVertexDataPipelineBase::get_num_bytes
2397 // Access: Public
2398 // Description:
2399 ////////////////////////////////////////////////////////////////////
2400 int GeomVertexDataPipelineBase::
2401 get_num_bytes() const {
2402  int num_bytes = sizeof(GeomVertexData);
2403 
2404  GeomVertexData::Arrays::const_iterator ai;
2405  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2406  num_bytes += (*ai).get_read_pointer()->get_data_size_bytes();
2407  }
2408 
2409  return num_bytes;
2410 }
2411 
2412 ////////////////////////////////////////////////////////////////////
2413 // Function: GeomVertexDataPipelineReader::get_num_rows
2414 // Access: Published
2415 // Description:
2416 ////////////////////////////////////////////////////////////////////
2417 int GeomVertexDataPipelineReader::
2418 get_num_rows() const {
2419  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), 0);
2420  nassertr(_got_array_readers, 0);
2421 
2422  if (_cdata->_format->get_num_arrays() == 0) {
2423  // No arrays means no rows. Weird but legal.
2424  return 0;
2425  }
2426 
2427  // Look up the answer on the first array (since any array will do).
2428  int stride = _cdata->_format->get_array(0)->get_stride();
2429  return _array_readers[0]->get_data_size_bytes() / stride;
2430 }
2431 
2432 ////////////////////////////////////////////////////////////////////
2433 // Function: GeomVertexDataPipelineReader::get_array_info
2434 // Access: Public
2435 // Description:
2436 ////////////////////////////////////////////////////////////////////
2437 bool GeomVertexDataPipelineReader::
2438 get_array_info(const InternalName *name,
2439  const GeomVertexArrayDataHandle *&array_reader,
2440  int &num_values,
2441  GeomVertexDataPipelineReader::NumericType &numeric_type,
2442  int &start, int &stride) const {
2443  nassertr(_got_array_readers, false);
2444  int array_index;
2445  const GeomVertexColumn *column;
2446  if (_cdata->_format->get_array_info(name, array_index, column)) {
2447  array_reader = _array_readers[array_index];
2448  num_values = column->get_num_values();
2449  numeric_type = column->get_numeric_type();
2450  start = column->get_start();
2451  stride = _cdata->_format->get_array(array_index)->get_stride();
2452  return true;
2453  }
2454  return false;
2455 }
2456 
2457 ////////////////////////////////////////////////////////////////////
2458 // Function: GeomVertexDataPipelineReader::get_array_info
2459 // Access: Public
2460 // Description:
2461 ////////////////////////////////////////////////////////////////////
2462 bool GeomVertexDataPipelineReader::
2463 get_array_info(const InternalName *name,
2464  const GeomVertexArrayDataHandle *&array_reader,
2465  int &num_values,
2466  GeomVertexDataPipelineReader::NumericType &numeric_type,
2467  int &start, int &stride, int &divisor,
2468  int &num_elements, int &element_stride) const {
2469  nassertr(_got_array_readers, false);
2470  int array_index;
2471  const GeomVertexColumn *column;
2472  if (_cdata->_format->get_array_info(name, array_index, column)) {
2473  array_reader = _array_readers[array_index];
2474  num_values = column->get_num_values();
2475  numeric_type = column->get_numeric_type();
2476  start = column->get_start();
2477  stride = _cdata->_format->get_array(array_index)->get_stride();
2478  divisor = _cdata->_format->get_array(array_index)->get_divisor();
2479  num_elements = column->get_num_elements();
2480  element_stride = column->get_element_stride();
2481  return true;
2482  }
2483  return false;
2484 }
2485 
2486 ////////////////////////////////////////////////////////////////////
2487 // Function: GeomVertexDataPipelineReader::get_vertex_info
2488 // Access: Public
2489 // Description:
2490 ////////////////////////////////////////////////////////////////////
2491 bool GeomVertexDataPipelineReader::
2492 get_vertex_info(const GeomVertexArrayDataHandle *&array_reader,
2493  int &num_values,
2494  GeomVertexDataPipelineReader::NumericType &numeric_type,
2495  int &start, int &stride) const {
2496  nassertr(_got_array_readers, false);
2497  int array_index = _cdata->_format->get_vertex_array_index();
2498  if (array_index >= 0) {
2499  const GeomVertexColumn *column = _cdata->_format->get_vertex_column();
2500 
2501  array_reader = _array_readers[array_index];
2502  num_values = column->get_num_values();
2503  numeric_type = column->get_numeric_type();
2504  start = column->get_start();
2505  stride = _cdata->_format->get_array(array_index)->get_stride();
2506  return true;
2507  }
2508  return false;
2509 }
2510 
2511 ////////////////////////////////////////////////////////////////////
2512 // Function: GeomVertexDataPipelineReader::get_normal_info
2513 // Access: Public
2514 // Description:
2515 ////////////////////////////////////////////////////////////////////
2516 bool GeomVertexDataPipelineReader::
2517 get_normal_info(const GeomVertexArrayDataHandle *&array_reader,
2518  GeomVertexDataPipelineReader::NumericType &numeric_type,
2519  int &start, int &stride) const {
2520  nassertr(_got_array_readers, false);
2521  int array_index = _cdata->_format->get_normal_array_index();
2522  if (array_index >= 0) {
2523  const GeomVertexColumn *column = _cdata->_format->get_normal_column();
2524 
2525  array_reader = _array_readers[array_index];
2526  numeric_type = column->get_numeric_type();
2527  start = column->get_start();
2528  stride = _cdata->_format->get_array(array_index)->get_stride();
2529  return true;
2530  }
2531  return false;
2532 }
2533 
2534 ////////////////////////////////////////////////////////////////////
2535 // Function: GeomVertexDataPipelineReader::get_color_info
2536 // Access: Public
2537 // Description:
2538 ////////////////////////////////////////////////////////////////////
2539 bool GeomVertexDataPipelineReader::
2540 get_color_info(const GeomVertexArrayDataHandle *&array_reader,
2541  int &num_values,
2542  GeomVertexDataPipelineReader::NumericType &numeric_type,
2543  int &start, int &stride) const {
2544  nassertr(_got_array_readers, false);
2545  int array_index = _cdata->_format->get_color_array_index();
2546  if (array_index >= 0) {
2547  const GeomVertexColumn *column = _cdata->_format->get_color_column();
2548 
2549  array_reader = _array_readers[array_index];
2550  num_values = column->get_num_values();
2551  numeric_type = column->get_numeric_type();
2552  start = column->get_start();
2553  stride = _cdata->_format->get_array(array_index)->get_stride();
2554  return true;
2555  }
2556  return false;
2557 }
2558 
2559 ////////////////////////////////////////////////////////////////////
2560 // Function: GeomVertexDataPipelineReader::make_array_readers
2561 // Access: Private
2562 // Description:
2563 ////////////////////////////////////////////////////////////////////
2564 void GeomVertexDataPipelineReader::
2565 make_array_readers() {
2566  nassertv(!_got_array_readers);
2567 
2568  _array_readers.reserve(_cdata->_arrays.size());
2569  GeomVertexData::Arrays::const_iterator ai;
2570  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2571  CPT(GeomVertexArrayData) array_obj = (*ai).get_read_pointer();
2572  _array_readers.push_back(array_obj->get_handle(_current_thread));
2573  }
2574 
2575  _got_array_readers = true;
2576 }
2577 
2578 ////////////////////////////////////////////////////////////////////
2579 // Function: GeomVertexDataPipelineReader::delete_array_readers
2580 // Access: Private
2581 // Description:
2582 ////////////////////////////////////////////////////////////////////
2583 void GeomVertexDataPipelineReader::
2584 delete_array_readers() {
2585  nassertv(_got_array_readers);
2586 
2587  _array_readers.clear();
2588  _got_array_readers = false;
2589 }
2590 
2591 ////////////////////////////////////////////////////////////////////
2592 // Function: GeomVertexDataPipelineWriter::get_num_rows
2593 // Access: Published
2594 // Description:
2595 ////////////////////////////////////////////////////////////////////
2596 int GeomVertexDataPipelineWriter::
2597 get_num_rows() const {
2598  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), 0);
2599  nassertr(_got_array_writers, 0);
2600 
2601  if (_cdata->_format->get_num_arrays() == 0) {
2602  // No arrays means no rows. Weird but legal.
2603  return 0;
2604  }
2605 
2606  // Look up the answer on the first array (since any array will do).
2607  int stride = _cdata->_format->get_array(0)->get_stride();
2608  return _array_writers[0]->get_data_size_bytes() / stride;
2609 }
2610 
2611 ////////////////////////////////////////////////////////////////////
2612 // Function: GeomVertexDataPipelineWriter::set_num_rows
2613 // Access: Public
2614 // Description:
2615 ////////////////////////////////////////////////////////////////////
2616 bool GeomVertexDataPipelineWriter::
2617 set_num_rows(int n) {
2618  nassertr(_got_array_writers, false);
2619  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), false);
2620 
2621  bool any_changed = false;
2622 
2623  int color_array = -1;
2624  int orig_color_rows = -1;
2625 
2626  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2627  if (_array_writers[i]->get_num_rows() != n) {
2628  if (_array_writers[i]->get_object()->has_column(InternalName::get_color())) {
2629  color_array = i;
2630  orig_color_rows = _array_writers[i]->get_num_rows();
2631  }
2632  _array_writers[i]->set_num_rows(n);
2633  any_changed = true;
2634  }
2635  }
2636 
2637  if (color_array >= 0 && orig_color_rows < n) {
2638  // We have just added some rows; fill the "color" column with
2639  // (1, 1, 1, 1), for the programmer's convenience.
2640  GeomVertexArrayDataHandle *array_writer = _array_writers[color_array];
2641  const GeomVertexArrayFormat *array_format = array_writer->get_array_format();
2642  const GeomVertexColumn *column =
2643  array_format->get_column(InternalName::get_color());
2644  int stride = array_format->get_stride();
2645  unsigned char *start =
2646  array_writer->get_write_pointer() + column->get_start();
2647  unsigned char *stop = start + array_writer->get_data_size_bytes();
2648  unsigned char *pointer = start + stride * orig_color_rows;
2649  int num_values = column->get_num_values();
2650 
2651  switch (column->get_numeric_type()) {
2652  case NT_packed_dcba:
2653  case NT_packed_dabc:
2654  case NT_uint8:
2655  case NT_uint16:
2656  case NT_uint32:
2657  while (pointer < stop) {
2658  memset(pointer, 0xff, column->get_total_bytes());
2659  pointer += stride;
2660  }
2661  break;
2662 
2663  case NT_float32:
2664  while (pointer < stop) {
2665  PN_float32 *pi = (PN_float32 *)pointer;
2666  for (int i = 0; i < num_values; i++) {
2667  pi[i] = 1.0f;
2668  }
2669  pointer += stride;
2670  }
2671  break;
2672 
2673  case NT_float64:
2674  while (pointer < stop) {
2675  PN_float64 *pi = (PN_float64 *)pointer;
2676  for (int i = 0; i < num_values; i++) {
2677  pi[i] = 1.0f;
2678  }
2679  pointer += stride;
2680  }
2681  break;
2682 
2683  case NT_stdfloat:
2684  // Shouldn't have this type in the format.
2685  nassertr(false, false);
2686  }
2687  }
2688 
2689  if (any_changed) {
2690  _object->clear_cache_stage();
2691  _cdata->_modified = Geom::get_next_modified();
2692  _cdata->_animated_vertices.clear();
2693  }
2694 
2695  return any_changed;
2696 }
2697 
2698 ////////////////////////////////////////////////////////////////////
2699 // Function: GeomVertexDataPipelineWriter::unclean_set_num_rows
2700 // Access: Public
2701 // Description:
2702 ////////////////////////////////////////////////////////////////////
2703 bool GeomVertexDataPipelineWriter::
2704 unclean_set_num_rows(int n) {
2705  nassertr(_got_array_writers, false);
2706  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), false);
2707 
2708  bool any_changed = false;
2709 
2710  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2711  if (_array_writers[i]->get_num_rows() != n) {
2712  if (_array_writers[i]->unclean_set_num_rows(n)) {
2713  any_changed = true;
2714  }
2715  }
2716  }
2717 
2718  if (any_changed) {
2719  _object->clear_cache_stage();
2720  _cdata->_modified = Geom::get_next_modified();
2721  _cdata->_animated_vertices.clear();
2722  }
2723 
2724  return any_changed;
2725 }
2726 
2727 ////////////////////////////////////////////////////////////////////
2728 // Function: GeomVertexDataPipelineWriter::reserve_num_rows
2729 // Access: Public
2730 // Description:
2731 ////////////////////////////////////////////////////////////////////
2732 bool GeomVertexDataPipelineWriter::
2733 reserve_num_rows(int n) {
2734  nassertr(_got_array_writers, false);
2735  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), false);
2736 
2737  bool any_changed = false;
2738 
2739  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
2740  if (_array_writers[i]->reserve_num_rows(n)) {
2741  any_changed = true;
2742  }
2743  }
2744 
2745  return any_changed;
2746 }
2747 
2748 ////////////////////////////////////////////////////////////////////
2749 // Function: GeomVertexDataPipelineWriter::modify_array
2750 // Access: Public
2751 // Description:
2752 ////////////////////////////////////////////////////////////////////
2753 PT(GeomVertexArrayData) GeomVertexDataPipelineWriter::
2754 modify_array(int i) {
2755  nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
2756 
2757  PT(GeomVertexArrayData) new_data;
2758  if (_got_array_writers) {
2759  new_data = _array_writers[i]->get_object();
2760  } else {
2761  new_data = _cdata->_arrays[i].get_write_pointer();
2762  }
2763 
2764  _object->clear_cache_stage();
2765  _cdata->_modified = Geom::get_next_modified();
2766  _cdata->_animated_vertices_modified = UpdateSeq();
2767 
2768  return new_data;
2769 }
2770 
2771 ////////////////////////////////////////////////////////////////////
2772 // Function: GeomVertexDataPipelineWriter::set_array
2773 // Access: Public
2774 // Description:
2775 ////////////////////////////////////////////////////////////////////
2776 void GeomVertexDataPipelineWriter::
2777 set_array(int i, const GeomVertexArrayData *array) {
2778  nassertv(i >= 0 && i < (int)_cdata->_arrays.size());
2779  _cdata->_arrays[i] = (GeomVertexArrayData *)array;
2780  _object->clear_cache_stage();
2781  _cdata->_modified = Geom::get_next_modified();
2782  _cdata->_animated_vertices_modified = UpdateSeq();
2783 
2784  if (_got_array_writers) {
2785  _array_writers[i] = _cdata->_arrays[i].get_write_pointer()->modify_handle(_current_thread);
2786  }
2787 }
2788 
2789 ////////////////////////////////////////////////////////////////////
2790 // Function: GeomVertexDataPipelineWriter::make_array_writers
2791 // Access: Private
2792 // Description:
2793 ////////////////////////////////////////////////////////////////////
2794 void GeomVertexDataPipelineWriter::
2795 make_array_writers() {
2796  nassertv(!_got_array_writers);
2797 
2798  _array_writers.reserve(_cdata->_arrays.size());
2799  GeomVertexData::Arrays::iterator ai;
2800  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
2801  PT(GeomVertexArrayData) array_obj = (*ai).get_write_pointer();
2802  _array_writers.push_back(array_obj->modify_handle(_current_thread));
2803  }
2804 
2805  _object->clear_cache_stage();
2806  _cdata->_modified = Geom::get_next_modified();
2807  _cdata->_animated_vertices_modified = UpdateSeq();
2808 
2809  _got_array_writers = true;
2810 }
2811 
2812 ////////////////////////////////////////////////////////////////////
2813 // Function: GeomVertexDataPipelineWriter::delete_array_writers
2814 // Access: Private
2815 // Description:
2816 ////////////////////////////////////////////////////////////////////
2817 void GeomVertexDataPipelineWriter::
2818 delete_array_writers() {
2819  nassertv(_got_array_writers);
2820 
2821  _array_writers.clear();
2822  _got_array_writers = false;
2823 }
const SliderTable * get_slider_table() const
Returns a const pointer to the SliderTable assigned to this data.
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_at_end() const
Returns true if the reader or writer is currently at the end of the list of vertices, false otherwise.
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
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
Contents get_contents() const
Returns the token representing the semantic meaning of the stored value.
void transform_vertices(const LMatrix4 &mat)
Applies the indicated transform matrix to all of the vertices in the GeomVertexData.
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.
int get_num_arrays() const
Returns the number of individual arrays stored within the data.
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:753
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. ...
NumericType get_numeric_type() const
Returns the token representing the numeric type of the data storage.
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
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.
int get_array() const
Returns the array index containing the data type that the writer is working on.
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
bool has_homogeneous_coord() const
Returns true if this Contents type is one that includes a homogeneous coordinate in the fourth compon...
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
size_t get_stride() const
Returns the per-row stride (bytes between consecutive rows) of the underlying vertex array...
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
GeomVertexArrayDataHandle * get_array_handle() const
Returns the write handle to the array object that the rewriter is currently processing.
bool is_zero() const
Returns true if the entire bitmask is zero, false otherwise.
Definition: sparseArray.I:246
LMatrix3f get_upper_3() const
Retrieves the upper 3x3 submatrix.
Definition: lmatrix.h:1169
bool is_packed_argb() const
Returns true if this column is the standard DirectX representation of 4-component color: C_color...
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...
bool get_indexed_transforms() const
This is only meaningful for animation_type AT_hardware.
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
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
static const LVecBase3f & zero()
Returns a zero-length vector.
Definition: lvecBase3.h:382
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
void get_blend(LMatrix4 &result, Thread *current_thread) const
Returns the current value of the blend, based on the current value of all of the nested transform obj...
bool request_resident() const
Returns true if the vertex data is currently resident in memory.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:3194
string get_string()
Extracts a variable-length string.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:105
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.
int get_subrange_end(int n) const
Returns the last numeric element, plus one, in the nth subrange.
Definition: sparseArray.I:545
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
int get_num_elements() const
Returns the number of times this column is repeated.
void set_transform_blend_table(const TransformBlendTable *table)
Replaces the TransformBlendTable on this vertex data with the indicated table.
A lightweight class that represents a single element that may be timed and/or counted via stats...
bool is_at_end() const
Returns true if the writer is currently at the end of the list of vertices, false otherwise...
const TransformBlend & get_blend(int n) const
Returns the nth blend in the table.
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->read_pointer() was called in fillin()...
bool is_registered() const
Returns true if this table has been registered.
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
int get_element_stride() const
This value is only relevant for matrix types.
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...
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
This is a 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.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
const VertexTransform * get_transform(int n) const
Returns the nth transform stored in the blend object.
Similar to MutexHolder, but for a light mutex.
const GeomVertexFormat * get_format() const
Returns a pointer to the GeomVertexFormat structure that defines this data.
void acquire() const
Grabs the lightMutex if it is available.
bool change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer)
Indicates that an object recently read from the bam stream should be replaced with a new object...
Definition: bamReader.cxx:503
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
UsageHint get_usage_hint() const
Returns the usage hint that was passed to the constructor, and which will be passed to each array dat...
virtual bool require_fully_complete() const
Some objects require all of their nested pointers to have been completed before the objects themselve...
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
void release() const
Releases the lightMutex.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
int get_num_values() const
Returns the number of numeric values of the column: the number of distinct numeric values that go int...
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...
AnimationType get_animation_type() const
Returns the type of animation represented by this spec.
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)...
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
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
Definition: cycleData.cxx: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:886
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.
int get_num_rows() const
Returns the number of rows stored within all the arrays.
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
int get_num_subranges() const
Returns the number of separate subranges stored in the SparseArray.
Definition: sparseArray.I:518
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.
int get_start() const
Returns the byte within the array record at which this column starts.
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...
bool is_registered() const
Returns true if this table has been registered.
Definition: sliderTable.I:26
void set_range(int low_bit, int size)
Sets the indicated range of bits on.
Definition: sparseArray.I:305
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
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
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
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
bool is_at_end() const
Returns true if the reader is currently at the end of the list of vertices, false otherwise...
int compare_to(const GeomVertexData &other) const
Returns 0 if the two objects are equivalent, even if they are not the same pointer.
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.
void set_row_unsafe(int row)
Sets the start row to the indicated value, without internal checks.
A class to retrieve the individual data elements previously stored in a Datagram. ...
const InternalName * get_name() const
Returns the name of this particular data field, e.g.
static unsigned int unpack_abcd_a(PN_uint32 data)
Returns the first packed value from a DirectX-style NT_packed_abcd.
int get_total_bytes() const
Returns the number of bytes used by each element of the column: component_bytes * num_components...
const GeomVertexColumn * get_column() const
Returns the description of the data type that the rewriter is working on.
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
bool set_num_rows(int n)
Sets the length of the array to n rows.
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
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.
const string & get_name() const
Returns the name passed to the constructor, if any.
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:493
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:658
static void register_with_read_factory()
Tells the BamReader how to create objects of type GeomVertexData.