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