mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
1719 lines
64 KiB
C++
1719 lines
64 KiB
C++
// Filename: geomVertexData.cxx
|
|
// Created by: drose (06Mar05)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "geomVertexData.h"
|
|
#include "geomVertexReader.h"
|
|
#include "geomVertexWriter.h"
|
|
#include "geomVertexRewriter.h"
|
|
#include "pStatTimer.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
#include "pset.h"
|
|
#include "indent.h"
|
|
|
|
TypeHandle GeomVertexData::_type_handle;
|
|
|
|
PStatCollector GeomVertexData::_convert_pcollector("*:Munge:Convert");
|
|
PStatCollector GeomVertexData::_scale_color_pcollector("*:Munge:Scale color");
|
|
PStatCollector GeomVertexData::_set_color_pcollector("*:Munge:Set color");
|
|
PStatCollector GeomVertexData::_animation_pcollector("*:Animation");
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Default Constructor
|
|
// Access: Private
|
|
// Description: Constructs an invalid object. This is only used when
|
|
// reading from the bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexData::
|
|
GeomVertexData() :
|
|
_char_pcollector(_animation_pcollector, "unnamed"),
|
|
_skinning_pcollector(_char_pcollector, "Skinning"),
|
|
_morphs_pcollector(_char_pcollector, "Morphs")
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexData::
|
|
GeomVertexData(const string &name,
|
|
const GeomVertexFormat *format,
|
|
GeomVertexData::UsageHint usage_hint) :
|
|
_name(name),
|
|
_format(format),
|
|
_char_pcollector(PStatCollector(_animation_pcollector, name)),
|
|
_skinning_pcollector(_char_pcollector, "Skinning"),
|
|
_morphs_pcollector(_char_pcollector, "Morphs")
|
|
{
|
|
nassertv(_format->is_registered());
|
|
|
|
set_usage_hint(usage_hint);
|
|
|
|
// Create some empty arrays as required by the format.
|
|
CDWriter cdata(_cycler);
|
|
|
|
int num_arrays = _format->get_num_arrays();
|
|
for (int i = 0; i < num_arrays; i++) {
|
|
PT(GeomVertexArrayData) array = new GeomVertexArrayData
|
|
(_format->get_array(i), usage_hint);
|
|
cdata->_arrays.push_back(array);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Copy Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexData::
|
|
GeomVertexData(const GeomVertexData ©) :
|
|
TypedWritableReferenceCount(copy),
|
|
_name(copy._name),
|
|
_format(copy._format),
|
|
_cycler(copy._cycler),
|
|
_char_pcollector(copy._char_pcollector),
|
|
_skinning_pcollector(copy._skinning_pcollector),
|
|
_morphs_pcollector(copy._morphs_pcollector)
|
|
{
|
|
CDWriter cdata(_cycler);
|
|
// It's important that we *not* copy the animated_vertices pointer.
|
|
cdata->_animated_vertices = NULL;
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Constructor
|
|
// Access: Published
|
|
// Description: This constructor copies all of the basic properties
|
|
// of the source VertexData, like usage_hint and
|
|
// animation tables, but does not copy the actual data,
|
|
// and it allows you to specify a different format.
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexData::
|
|
GeomVertexData(const GeomVertexData ©,
|
|
const GeomVertexFormat *format) :
|
|
TypedWritableReferenceCount(copy),
|
|
_name(copy._name),
|
|
_format(format),
|
|
_cycler(copy._cycler),
|
|
_char_pcollector(copy._char_pcollector),
|
|
_skinning_pcollector(copy._skinning_pcollector),
|
|
_morphs_pcollector(copy._morphs_pcollector)
|
|
{
|
|
nassertv(_format->is_registered());
|
|
|
|
// Create some empty arrays as required by the format.
|
|
CDWriter cdata(_cycler);
|
|
|
|
UsageHint usage_hint = cdata->_usage_hint;
|
|
cdata->_arrays.clear();
|
|
int num_arrays = _format->get_num_arrays();
|
|
for (int i = 0; i < num_arrays; i++) {
|
|
PT(GeomVertexArrayData) array = new GeomVertexArrayData
|
|
(_format->get_array(i), usage_hint);
|
|
cdata->_arrays.push_back(array);
|
|
}
|
|
|
|
// It's important that we *not* copy the animated_vertices pointer.
|
|
cdata->_animated_vertices = NULL;
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Copy Assignment Operator
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
operator = (const GeomVertexData ©) {
|
|
TypedWritableReferenceCount::operator = (copy);
|
|
_name = copy._name;
|
|
_format = copy._format;
|
|
_cycler = copy._cycler;
|
|
_char_pcollector = copy._char_pcollector;
|
|
_skinning_pcollector = copy._skinning_pcollector;
|
|
_morphs_pcollector = copy._morphs_pcollector;
|
|
|
|
CDWriter cdata(_cycler);
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::Destructor
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexData::
|
|
~GeomVertexData() {
|
|
// When we destruct, we should ensure that all of our cached
|
|
// entries, across all pipeline stages, are properly removed from
|
|
// the cache manager.
|
|
int num_stages = _cycler.get_num_stages();
|
|
for (int i = 0; i < num_stages; i++) {
|
|
if (_cycler.is_stage_unique(i)) {
|
|
CData *cdata = _cycler.write_stage(i);
|
|
for (Cache::iterator ci = cdata->_cache.begin();
|
|
ci != cdata->_cache.end();
|
|
++ci) {
|
|
CacheEntry *entry = (*ci);
|
|
entry->erase();
|
|
}
|
|
cdata->_cache.clear();
|
|
_cycler.release_write_stage(i, cdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_name
|
|
// Access: Published
|
|
// Description: Changes the name of the vertex data. This name is
|
|
// reported on the PStats graph for vertex computations.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_name(const string &name) {
|
|
_name = name;
|
|
_char_pcollector = PStatCollector(_animation_pcollector, name);
|
|
_skinning_pcollector = PStatCollector(_char_pcollector, "Skinning");
|
|
_morphs_pcollector = PStatCollector(_char_pcollector, "Morphs");
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_usage_hint
|
|
// Access: Published
|
|
// Description: Changes the UsageHint hint for this vertex data, and
|
|
// for all of the arrays that share this data. See
|
|
// get_usage_hint().
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_usage_hint(GeomVertexData::UsageHint usage_hint) {
|
|
CDWriter cdata(_cycler);
|
|
cdata->_usage_hint = usage_hint;
|
|
|
|
Arrays::iterator ai;
|
|
for (ai = cdata->_arrays.begin();
|
|
ai != cdata->_arrays.end();
|
|
++ai) {
|
|
if ((*ai)->get_ref_count() > 1) {
|
|
(*ai) = new GeomVertexArrayData(*(*ai));
|
|
}
|
|
(*ai)->set_usage_hint(usage_hint);
|
|
}
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_num_rows
|
|
// Access: Published
|
|
// Description: Returns the number of rows stored within all the
|
|
// arrays. All arrays store data for the same n
|
|
// rows.
|
|
////////////////////////////////////////////////////////////////////
|
|
int GeomVertexData::
|
|
get_num_rows() const {
|
|
CDReader cdata(_cycler);
|
|
nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), 0);
|
|
if (_format->get_num_arrays() == 0) {
|
|
// No arrays means no rows. Weird but legal.
|
|
return 0;
|
|
}
|
|
|
|
// Look up the answer on the first array (since any array will do).
|
|
int stride = _format->get_array(0)->get_stride();
|
|
return cdata->_arrays[0]->get_data_size_bytes() / stride;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::clear_rows
|
|
// Access: Published
|
|
// Description: Removes all of the rows from the arrays;
|
|
// functionally equivalent to set_num_rows(0) (but
|
|
// faster).
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
clear_rows() {
|
|
CDWriter cdata(_cycler);
|
|
nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
|
|
|
|
Arrays::iterator ai;
|
|
for (ai = cdata->_arrays.begin();
|
|
ai != cdata->_arrays.end();
|
|
++ai) {
|
|
if ((*ai)->get_ref_count() > 1) {
|
|
(*ai) = new GeomVertexArrayData(*(*ai));
|
|
}
|
|
(*ai)->clear_rows();
|
|
}
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::modify_array
|
|
// Access: Published
|
|
// Description: Returns a modifiable pointer to the indicated vertex
|
|
// array, so that application code may directly
|
|
// manipulate the data. You should avoid changing
|
|
// the length of this array, since all of the arrays
|
|
// should be kept in sync--use set_num_rows()
|
|
// instead.
|
|
////////////////////////////////////////////////////////////////////
|
|
GeomVertexArrayData *GeomVertexData::
|
|
modify_array(int i) {
|
|
// Perform copy-on-write: if the reference count on the vertex data
|
|
// is greater than 1, assume some other GeomVertexData has the same
|
|
// pointer, so make a copy of it first.
|
|
CDWriter cdata(_cycler);
|
|
nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
|
|
|
|
if (cdata->_arrays[i]->get_ref_count() > 1) {
|
|
cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
|
|
}
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
|
|
return cdata->_arrays[i];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_array
|
|
// Access: Published
|
|
// Description: Replaces the indicated vertex data array with
|
|
// a completely new array. You should be careful that
|
|
// the new array has the same length and format as the
|
|
// old one, unless you know what you are doing.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_array(int i, const GeomVertexArrayData *array) {
|
|
CDWriter cdata(_cycler);
|
|
nassertv(i >= 0 && i < (int)cdata->_arrays.size());
|
|
cdata->_arrays[i] = (GeomVertexArrayData *)array;
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_transform_table
|
|
// Access: Published
|
|
// Description: Replaces the TransformTable on this vertex
|
|
// data with the indicated table. The length of this
|
|
// table should be consistent with the maximum table
|
|
// index assigned to the vertices under the
|
|
// "transform_index" name.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_transform_table(const TransformTable *table) {
|
|
nassertv(table == (TransformTable *)NULL || table->is_registered());
|
|
|
|
CDWriter cdata(_cycler);
|
|
cdata->_transform_table = (TransformTable *)table;
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::modify_transform_blend_table
|
|
// Access: Published
|
|
// Description: Returns a modifiable pointer to the current
|
|
// TransformBlendTable on this vertex data, if any, or
|
|
// NULL if there is not a TransformBlendTable. See
|
|
// get_transform_blend_table().
|
|
////////////////////////////////////////////////////////////////////
|
|
TransformBlendTable *GeomVertexData::
|
|
modify_transform_blend_table() {
|
|
// Perform copy-on-write: if the reference count on the table is
|
|
// greater than 1, assume some other GeomVertexData has the same
|
|
// pointer, so make a copy of it first.
|
|
CDWriter cdata(_cycler);
|
|
|
|
if (cdata->_transform_blend_table->get_ref_count() > 1) {
|
|
cdata->_transform_blend_table = new TransformBlendTable(*cdata->_transform_blend_table);
|
|
}
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
|
|
return cdata->_transform_blend_table;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_transform_blend_table
|
|
// Access: Published
|
|
// Description: Replaces the TransformBlendTable on this vertex
|
|
// data with the indicated table. The length of this
|
|
// table should be consistent with the maximum table
|
|
// index assigned to the vertices under the
|
|
// "transform_blend" name.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_transform_blend_table(const TransformBlendTable *table) {
|
|
CDWriter cdata(_cycler);
|
|
cdata->_transform_blend_table = (TransformBlendTable *)table;
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_slider_table
|
|
// Access: Published
|
|
// Description: Replaces the SliderTable on this vertex
|
|
// data with the indicated table. There should be an
|
|
// entry in this table for each kind of morph offset
|
|
// defined in the vertex data.
|
|
//
|
|
// The SliderTable object must have been registered
|
|
// prior to setting it on the GeomVertexData.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
set_slider_table(const SliderTable *table) {
|
|
nassertv(table == (SliderTable *)NULL || table->is_registered());
|
|
|
|
CDWriter cdata(_cycler);
|
|
cdata->_slider_table = (SliderTable *)table;
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices_modified = UpdateSeq();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_num_bytes
|
|
// Access: Published
|
|
// Description: Returns the total number of bytes consumed by the
|
|
// different arrays of the vertex data.
|
|
////////////////////////////////////////////////////////////////////
|
|
int GeomVertexData::
|
|
get_num_bytes() const {
|
|
CDReader cdata(_cycler);
|
|
|
|
int num_bytes = sizeof(GeomVertexData);
|
|
|
|
Arrays::const_iterator ai;
|
|
for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) {
|
|
num_bytes += (*ai)->get_data_size_bytes();
|
|
}
|
|
|
|
return num_bytes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::copy_from
|
|
// Access: Published
|
|
// Description: Copies all the data from the other array into the
|
|
// corresponding data types in this array, by matching
|
|
// data types name-by-name.
|
|
//
|
|
// keep_data_objects specifies what to do when one or
|
|
// more of the arrays can be copied without the need to
|
|
// apply any conversion operation. If it is true, the
|
|
// original GeomVertexArrayData objects in this object
|
|
// are retained, but their data arrays are copied
|
|
// pointerwise from the source; if it is false, then the
|
|
// GeomVertexArrayData objects themselves are copied
|
|
// pointerwise from the source.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
copy_from(const GeomVertexData *source, bool keep_data_objects) {
|
|
const GeomVertexFormat *source_format = source->get_format();
|
|
const GeomVertexFormat *dest_format = get_format();
|
|
|
|
int num_rows = source->get_num_rows();
|
|
int num_arrays = source_format->get_num_arrays();
|
|
int source_i;
|
|
|
|
// First, check to see if any arrays can be simply appropriated for
|
|
// the new format, without changing the data.
|
|
pset<int> done_arrays;
|
|
|
|
for (source_i = 0; source_i < num_arrays; ++source_i) {
|
|
const GeomVertexArrayFormat *source_array_format =
|
|
source_format->get_array(source_i);
|
|
|
|
bool array_done = false;
|
|
|
|
int dest_num_arrays = dest_format->get_num_arrays();
|
|
for (int dest_i = 0;
|
|
dest_i < dest_num_arrays && !array_done;
|
|
++dest_i) {
|
|
const GeomVertexArrayFormat *dest_array_format =
|
|
dest_format->get_array(dest_i);
|
|
if (dest_array_format->is_data_subset_of(*source_array_format)) {
|
|
// Great! Just use the same data for this one.
|
|
if (keep_data_objects) {
|
|
// Copy the data, but keep the same GeomVertexArrayData object.
|
|
|
|
// Maybe it even has the same data pointer already. If so,
|
|
// avoid flipping the modified flag.
|
|
CPTA_uchar source_data = source->get_array(source_i)->get_data();
|
|
if (get_array(dest_i)->get_data() != source_data) {
|
|
modify_array(dest_i)->set_data(source_data);
|
|
}
|
|
} else {
|
|
// Copy the GeomVertexArrayData object.
|
|
if (get_array(dest_i) != source->get_array(source_i)) {
|
|
set_array(dest_i, source->get_array(source_i));
|
|
}
|
|
}
|
|
|
|
array_done = true;
|
|
done_arrays.insert(dest_i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now make sure the arrays we didn't share are all filled in.
|
|
set_num_rows(num_rows);
|
|
|
|
// Now go back through and copy any data that's left over.
|
|
for (source_i = 0; source_i < num_arrays; ++source_i) {
|
|
CPTA_uchar array_data = source->get_array(source_i)->get_data();
|
|
const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
|
|
int num_columns = source_array_format->get_num_columns();
|
|
for (int di = 0; di < num_columns; ++di) {
|
|
const GeomVertexColumn *source_column = source_array_format->get_column(di);
|
|
|
|
int dest_i = dest_format->get_array_with(source_column->get_name());
|
|
if (dest_i >= 0 && done_arrays.count(dest_i) == 0) {
|
|
// The data type exists in the new format; we have to copy it.
|
|
const GeomVertexArrayFormat *dest_array_format =
|
|
dest_format->get_array(dest_i);
|
|
const GeomVertexColumn *dest_column =
|
|
dest_array_format->get_column(source_column->get_name());
|
|
nassertv(dest_column != (const GeomVertexColumn *)NULL);
|
|
|
|
if (dest_column->is_bytewise_equivalent(*source_column)) {
|
|
// We can do a quick bytewise copy.
|
|
PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
|
|
|
|
bytewise_copy(dest_array_data + dest_column->get_start(),
|
|
dest_array_format->get_stride(),
|
|
array_data + source_column->get_start(), source_array_format->get_stride(),
|
|
source_column, num_rows);
|
|
|
|
} else if (dest_column->is_packed_argb() &&
|
|
source_column->is_uint8_rgba()) {
|
|
// A common special case: OpenGL color to DirectX color.
|
|
PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
|
|
|
|
uint8_rgba_to_packed_argb
|
|
(dest_array_data + dest_column->get_start(),
|
|
dest_array_format->get_stride(),
|
|
array_data + source_column->get_start(), source_array_format->get_stride(),
|
|
num_rows);
|
|
|
|
} else if (dest_column->is_uint8_rgba() &&
|
|
source_column->is_packed_argb()) {
|
|
// Another common special case: DirectX color to OpenGL
|
|
// color.
|
|
PTA_uchar dest_array_data = modify_array(dest_i)->modify_data();
|
|
|
|
packed_argb_to_uint8_rgba
|
|
(dest_array_data + dest_column->get_start(),
|
|
dest_array_format->get_stride(),
|
|
array_data + source_column->get_start(), source_array_format->get_stride(),
|
|
num_rows);
|
|
|
|
} else {
|
|
// A generic copy.
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "generic copy " << *dest_column << " from "
|
|
<< *source_column << "\n";
|
|
}
|
|
GeomVertexWriter to(this);
|
|
to.set_column(dest_i, dest_column);
|
|
GeomVertexReader from(source);
|
|
from.set_column(source_i, source_column);
|
|
|
|
while (!from.is_at_end()) {
|
|
to.set_data4f(from.get_data4f());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also convert the animation tables as necessary.
|
|
const GeomVertexAnimationSpec &source_animation = source_format->get_animation();
|
|
const GeomVertexAnimationSpec &dest_animation = dest_format->get_animation();
|
|
if (source_animation != dest_animation) {
|
|
if (dest_animation.get_animation_type() == AT_hardware) {
|
|
// Convert Panda-style animation tables to hardware-style
|
|
// animation tables.
|
|
CPT(TransformBlendTable) blend_table = source->get_transform_blend_table();
|
|
if (blend_table != (TransformBlendTable *)NULL) {
|
|
PT(TransformTable) transform_table = new TransformTable;
|
|
TransformMap already_added;
|
|
|
|
if (dest_animation.get_indexed_transforms()) {
|
|
// Build an indexed transform array. This is easier; this
|
|
// means we can put the blends in any order.
|
|
GeomVertexWriter weight(this, InternalName::get_transform_weight());
|
|
GeomVertexWriter index(this, InternalName::get_transform_index());
|
|
GeomVertexReader from(source, InternalName::get_transform_blend());
|
|
|
|
while (!from.is_at_end()) {
|
|
const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
|
|
LVecBase4f weights = LVecBase4f::zero();
|
|
int indices[4] = {0, 0, 0, 0};
|
|
nassertv(blend.get_num_transforms() <= 4);
|
|
|
|
for (int i = 0; i < blend.get_num_transforms(); i++) {
|
|
weights[i] = blend.get_weight(i);
|
|
indices[i] = add_transform(transform_table, blend.get_transform(i),
|
|
already_added);
|
|
}
|
|
if (weight.has_column()) {
|
|
weight.set_data4f(weights);
|
|
}
|
|
index.set_data4i(indices);
|
|
}
|
|
} else {
|
|
// Build a nonindexed transform array. This means we have to
|
|
// use the same n transforms, in the same order, for each vertex.
|
|
GeomVertexWriter weight(this, InternalName::get_transform_weight());
|
|
GeomVertexReader from(source, InternalName::get_transform_blend());
|
|
|
|
while (!from.is_at_end()) {
|
|
const TransformBlend &blend = blend_table->get_blend(from.get_data1i());
|
|
LVecBase4f weights = LVecBase4f::zero();
|
|
|
|
for (int i = 0; i < blend.get_num_transforms(); i++) {
|
|
int index = add_transform(transform_table, blend.get_transform(i),
|
|
already_added);
|
|
nassertv(index <= 4);
|
|
weights[index] = blend.get_weight(i);
|
|
}
|
|
if (weight.has_column()) {
|
|
weight.set_data4f(weights);
|
|
}
|
|
}
|
|
}
|
|
|
|
clear_transform_blend_table();
|
|
set_transform_table(TransformTable::register_table(transform_table));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::copy_row_from
|
|
// Access: Published
|
|
// Description: Copies a single row of the data from the other array
|
|
// into the indicated row of this array. In this case,
|
|
// the source format must exactly match the destination
|
|
// format.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
copy_row_from(int dest_row, const GeomVertexData *source,
|
|
int source_row) {
|
|
const GeomVertexFormat *source_format = source->get_format();
|
|
const GeomVertexFormat *dest_format = get_format();
|
|
nassertv(source_format == dest_format);
|
|
nassertv(source_row >= 0 && source_row < source->get_num_rows());
|
|
|
|
if (dest_row >= get_num_rows()) {
|
|
// Implicitly add enough rows to get to the indicated row.
|
|
set_num_rows(dest_row + 1);
|
|
}
|
|
|
|
int num_arrays = source_format->get_num_arrays();
|
|
|
|
for (int i = 0; i < num_arrays; ++i) {
|
|
PTA_uchar dest_array_data = modify_array(i)->modify_data();
|
|
CPTA_uchar source_array_data = source->get_array(i)->get_data();
|
|
const GeomVertexArrayFormat *array_format = source_format->get_array(i);
|
|
int stride = array_format->get_stride();
|
|
|
|
memcpy(dest_array_data + stride * dest_row,
|
|
source_array_data + stride * source_row,
|
|
stride);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::convert_to
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData that represents the same
|
|
// contents as this one, with all data types matched up
|
|
// name-by-name to the indicated new format.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
convert_to(const GeomVertexFormat *new_format) const {
|
|
if (new_format == _format) {
|
|
// Trivial case: no change is needed.
|
|
return this;
|
|
}
|
|
|
|
// Look up the new format in our cache--maybe we've recently applied
|
|
// it.
|
|
{
|
|
CDReader cdata(_cycler);
|
|
CacheEntry temp_entry(new_format);
|
|
temp_entry.local_object();
|
|
Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
|
|
if (ci != cdata->_cache.end()) {
|
|
CacheEntry *entry = (*ci);
|
|
// Record a cache hit, so this element will stay in the cache a
|
|
// while longer.
|
|
entry->refresh();
|
|
return entry->_result;
|
|
}
|
|
}
|
|
|
|
// Okay, convert the data to the new format.
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Converting " << get_num_rows() << " rows from " << *_format
|
|
<< " to " << *new_format << "\n";
|
|
}
|
|
PStatTimer timer(_convert_pcollector);
|
|
|
|
PT(GeomVertexData) new_data =
|
|
new GeomVertexData(get_name(), new_format, get_usage_hint());
|
|
new_data->set_transform_blend_table(get_transform_blend_table());
|
|
new_data->set_slider_table(get_slider_table());
|
|
|
|
new_data->copy_from(this, false);
|
|
|
|
{
|
|
// Record the new result in the cache.
|
|
CacheEntry *entry;
|
|
{
|
|
CDWriter cdata(((GeomVertexData *)this)->_cycler);
|
|
entry = new CacheEntry((GeomVertexData *)this, new_format, new_data);
|
|
bool inserted = cdata->_cache.insert(entry).second;
|
|
nassertr(inserted, new_data);
|
|
}
|
|
|
|
// And tell the cache manager about the new entry. (It might
|
|
// immediately request a delete from the cache of the thing we
|
|
// just added.)
|
|
entry->record();
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::scale_color
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData object with the color
|
|
// table modified in-place to apply the indicated scale.
|
|
//
|
|
// If the vertex data does not include a color column, a
|
|
// new one will not be added.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
scale_color(const LVecBase4f &color_scale) const {
|
|
const GeomVertexColumn *old_column =
|
|
_format->get_column(InternalName::get_color());
|
|
if (old_column == (GeomVertexColumn *)NULL) {
|
|
return this;
|
|
}
|
|
|
|
PT(GeomVertexData) new_data = new GeomVertexData(*this);
|
|
GeomVertexRewriter data(new_data, InternalName::get_color());
|
|
while (!data.is_at_end()) {
|
|
Colorf color = data.get_data4f();
|
|
data.set_data4f(color[0] * color_scale[0],
|
|
color[1] * color_scale[1],
|
|
color[2] * color_scale[2],
|
|
color[3] * color_scale[3]);
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::scale_color
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData object with the color
|
|
// table replaced with a new color table that has been
|
|
// scaled by the indicated value. The new color table
|
|
// will be added as a new array; if the old color table
|
|
// was interleaved with a previous array, the previous
|
|
// array will not be repacked.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
scale_color(const LVecBase4f &color_scale, int num_components,
|
|
GeomVertexData::NumericType numeric_type,
|
|
GeomVertexData::Contents contents) const {
|
|
int old_color_array = _format->get_array_with(InternalName::get_color());
|
|
if (old_color_array == -1) {
|
|
// Oops, no color anyway.
|
|
return set_color(color_scale, num_components, numeric_type, contents);
|
|
}
|
|
|
|
int num_rows = get_num_rows();
|
|
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Scaling color for " << num_rows << " vertices by "
|
|
<< color_scale << ".\n";
|
|
}
|
|
PStatTimer timer(_scale_color_pcollector);
|
|
|
|
PT(GeomVertexData) new_data = replace_column
|
|
(InternalName::get_color(), num_components, numeric_type, contents);
|
|
|
|
// Now go through and apply the scale, copying it to the new data.
|
|
GeomVertexWriter to(new_data, InternalName::get_color());
|
|
GeomVertexReader from(this, InternalName::get_color());
|
|
|
|
for (int i = 0; i < num_rows; i++) {
|
|
Colorf color = from.get_data4f();
|
|
to.set_data4f(color[0] * color_scale[0],
|
|
color[1] * color_scale[1],
|
|
color[2] * color_scale[2],
|
|
color[3] * color_scale[3]);
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_color
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData object with the color
|
|
// data modified in-place with the new value.
|
|
//
|
|
// If the vertex data does not include a color column, a
|
|
// new one will not be added.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
set_color(const Colorf &color) const {
|
|
const GeomVertexColumn *old_column =
|
|
_format->get_column(InternalName::get_color());
|
|
if (old_column == (GeomVertexColumn *)NULL) {
|
|
return this;
|
|
}
|
|
|
|
PT(GeomVertexData) new_data = new GeomVertexData(*this);
|
|
GeomVertexWriter to(new_data, InternalName::get_color());
|
|
while (!to.is_at_end()) {
|
|
to.set_data4f(color);
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::set_color
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData object with the color
|
|
// table replaced with a new color table for which each
|
|
// vertex has the indicated value. The new color table
|
|
// will be added as a new array; if the old color table
|
|
// was interleaved with a previous array, the previous
|
|
// array will not be repacked.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
set_color(const Colorf &color, int num_components,
|
|
GeomVertexData::NumericType numeric_type,
|
|
GeomVertexData::Contents contents) const {
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Setting color for " << get_num_rows() << " vertices to "
|
|
<< color << ".\n";
|
|
}
|
|
PStatTimer timer(_set_color_pcollector);
|
|
|
|
PT(GeomVertexData) new_data = replace_column
|
|
(InternalName::get_color(), num_components, numeric_type, contents);
|
|
|
|
// Now go through and set the new color value.
|
|
GeomVertexWriter to(new_data, InternalName::get_color());
|
|
while (!to.is_at_end()) {
|
|
to.set_data4f(color);
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::animate_vertices
|
|
// Access: Published
|
|
// Description: Returns a GeomVertexData that represents the results
|
|
// of computing the vertex animation on the CPU for this
|
|
// GeomVertexData.
|
|
//
|
|
// If there is no CPU-defined vertex animation on this
|
|
// object, this just returns the original object.
|
|
//
|
|
// If there is vertex animation, but the VertexTransform
|
|
// values have not changed since last time, this may
|
|
// return the same pointer it returned previously. Even
|
|
// if the VertexTransform values have changed, it may
|
|
// still return the same pointer, but with its contents
|
|
// modified (this is preferred, since it allows the
|
|
// graphics backend to update vertex buffers optimally).
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(GeomVertexData) GeomVertexData::
|
|
animate_vertices() const {
|
|
CDReader cdata(_cycler);
|
|
|
|
if (_format->get_animation().get_animation_type() != AT_panda) {
|
|
return this;
|
|
}
|
|
|
|
UpdateSeq modified;
|
|
if (cdata->_transform_blend_table != (TransformBlendTable *)NULL) {
|
|
if (cdata->_slider_table != (SliderTable *)NULL) {
|
|
modified =
|
|
max(cdata->_transform_blend_table->get_modified(),
|
|
cdata->_slider_table->get_modified());
|
|
} else {
|
|
modified = cdata->_transform_blend_table->get_modified();
|
|
}
|
|
|
|
} else if (cdata->_slider_table != (SliderTable *)NULL) {
|
|
modified = cdata->_slider_table->get_modified();
|
|
|
|
} else {
|
|
// No transform blend table or slider table--ergo, no vertex
|
|
// animation.
|
|
return this;
|
|
}
|
|
|
|
if (cdata->_animated_vertices_modified == modified &&
|
|
cdata->_animated_vertices != (GeomVertexData *)NULL) {
|
|
// No changes.
|
|
return cdata->_animated_vertices;
|
|
}
|
|
CDWriter cdataw(((GeomVertexData *)this)->_cycler, cdata);
|
|
cdataw->_animated_vertices_modified = modified;
|
|
((GeomVertexData *)this)->update_animated_vertices(cdataw);
|
|
return cdataw->_animated_vertices;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::bytewise_copy
|
|
// Access: Private, Static
|
|
// Description: Quickly copies data without the need to convert it.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
bytewise_copy(unsigned char *to, int to_stride,
|
|
const unsigned char *from, int from_stride,
|
|
const GeomVertexColumn *from_type,
|
|
int num_records) {
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "bytewise_copy(" << (void *)to << ", " << to_stride
|
|
<< ", " << (const void *)from << ", " << from_stride
|
|
<< ", " << *from_type << ", " << num_records << ")\n";
|
|
}
|
|
if (to_stride == from_type->get_total_bytes() &&
|
|
from_stride == from_type->get_total_bytes()) {
|
|
// Fantastic! It's just a linear array of this one data type.
|
|
// Copy the whole thing all at once.
|
|
memcpy(to, from, num_records * from_type->get_total_bytes());
|
|
|
|
} else {
|
|
// Ok, it's interleaved in with other data. Copy them one record
|
|
// at a time.
|
|
while (num_records > 0) {
|
|
memcpy(to, from, from_type->get_total_bytes());
|
|
to += to_stride;
|
|
from += from_stride;
|
|
num_records--;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::replace_column
|
|
// Access: Published
|
|
// Description: Returns a new GeomVertexData object, suitable for
|
|
// modification, with the indicated data type replaced
|
|
// with a new table filled with undefined values. The
|
|
// new table will be added as a new array; if the old
|
|
// table was interleaved with a previous array, the
|
|
// previous array will not be repacked.
|
|
//
|
|
// If num_components is 0, the indicated name is simply
|
|
// removed from the type, without replacing it with
|
|
// anything else.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(GeomVertexData) GeomVertexData::
|
|
replace_column(InternalName *name, int num_components,
|
|
GeomVertexData::NumericType numeric_type,
|
|
GeomVertexData::Contents contents) const {
|
|
PT(GeomVertexFormat) new_format = new GeomVertexFormat(*_format);
|
|
|
|
// Remove the old description of the type from the format.
|
|
bool removed_type_array = false;
|
|
int old_type_array = _format->get_array_with(name);
|
|
if (old_type_array != -1) {
|
|
GeomVertexArrayFormat *array_format = new_format->modify_array(old_type_array);
|
|
if (array_format->get_num_columns() == 1) {
|
|
// Actually, this array didn't have any other data types, so
|
|
// just drop the whole array.
|
|
new_format->remove_array(old_type_array);
|
|
removed_type_array = true;
|
|
|
|
} else {
|
|
// Remove the description for the type, but don't bother to
|
|
// repack the array.
|
|
array_format->remove_column(name);
|
|
}
|
|
}
|
|
|
|
// Now define a new array to contain just the type.
|
|
int new_type_array = -1;
|
|
if (num_components != 0) {
|
|
PT(GeomVertexArrayFormat) type_array_format =
|
|
new GeomVertexArrayFormat(name, num_components, numeric_type, contents);
|
|
new_type_array = new_format->add_array(type_array_format);
|
|
}
|
|
|
|
CPT(GeomVertexFormat) format =
|
|
GeomVertexFormat::register_format(new_format);
|
|
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Replacing data type " << *name << "; converting "
|
|
<< get_num_rows() << " rows from "
|
|
<< *_format << " to " << *format << "\n";
|
|
}
|
|
|
|
PT(GeomVertexData) new_data = new GeomVertexData(*this, format);
|
|
|
|
int j = 0;
|
|
int num_arrays = get_num_arrays();
|
|
for (int i = 0; i < num_arrays; ++i) {
|
|
if (i == old_type_array) {
|
|
if (!removed_type_array) {
|
|
// Pointer-copy the original array that includes the type
|
|
// (since it also includes other data).
|
|
new_data->set_array(j, get_array(i));
|
|
++j;
|
|
}
|
|
|
|
} else {
|
|
// Just pointer-copy any arrays other than type.
|
|
new_data->set_array(j, get_array(i));
|
|
++j;
|
|
}
|
|
}
|
|
|
|
if (new_type_array != -1) {
|
|
nassertr(j == new_type_array, new_data);
|
|
|
|
// For the new type array, we set up a temporary array that has
|
|
// room for the right number of rows.
|
|
PT(GeomVertexArrayData) new_array = new GeomVertexArrayData
|
|
(format->get_array(j), get_usage_hint());
|
|
new_array->set_num_rows(get_num_rows());
|
|
new_data->set_array(j, new_array);
|
|
}
|
|
|
|
return new_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::output
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
output(ostream &out) const {
|
|
if (!get_name().empty()) {
|
|
out << get_name() << " ";
|
|
}
|
|
out << get_num_rows() << " rows: " << *get_format();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::write
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
write(ostream &out, int indent_level) const {
|
|
if (!get_name().empty()) {
|
|
indent(out, indent_level) << get_name() << "\n";
|
|
}
|
|
_format->write_with_data(out, indent_level + 2, this);
|
|
if (get_transform_blend_table() != (TransformBlendTable *)NULL) {
|
|
indent(out, indent_level)
|
|
<< "Transform blend table:\n";
|
|
get_transform_blend_table()->write(out, indent_level + 2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_array_info
|
|
// Access: Public
|
|
// Description: A convenience function to collect together the
|
|
// important parts of the array data for rendering.
|
|
// Given the name of a data type, fills in the start of
|
|
// the array, the number of numeric values for each
|
|
// vertex, the starting bytes number, and the number of
|
|
// bytes to increment for each consecutive vertex.
|
|
//
|
|
// The return value is true if the named array data
|
|
// exists in this record, or false if it does not (in
|
|
// which case none of the output parameters are valid).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GeomVertexData::
|
|
get_array_info(const InternalName *name,
|
|
const GeomVertexArrayData *&array_data,
|
|
int &num_values,
|
|
GeomVertexData::NumericType &numeric_type,
|
|
int &start, int &stride) const {
|
|
int array_index;
|
|
const GeomVertexColumn *column;
|
|
if (_format->get_array_info(name, array_index, column)) {
|
|
CDReader cdata(_cycler);
|
|
array_data = cdata->_arrays[array_index];
|
|
num_values = column->get_num_values();
|
|
numeric_type = column->get_numeric_type();
|
|
start = column->get_start();
|
|
stride = _format->get_array(array_index)->get_stride();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_vertex_info
|
|
// Access: Public
|
|
// Description: A shortcut to get_array_info() for the "vertex"
|
|
// column.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GeomVertexData::
|
|
get_vertex_info(const GeomVertexArrayData *&array_data,
|
|
int &num_values,
|
|
GeomVertexData::NumericType &numeric_type,
|
|
int &start, int &stride) const {
|
|
int array_index = _format->get_vertex_array_index();
|
|
if (array_index >= 0) {
|
|
const GeomVertexColumn *column = _format->get_vertex_column();
|
|
|
|
CDReader cdata(_cycler);
|
|
array_data = cdata->_arrays[array_index];
|
|
num_values = column->get_num_values();
|
|
numeric_type = column->get_numeric_type();
|
|
start = column->get_start();
|
|
stride = _format->get_array(array_index)->get_stride();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_normal_info
|
|
// Access: Public
|
|
// Description: A shortcut to get_array_info() for the "normal"
|
|
// column. Note that there is no num_values return,
|
|
// since normals should always have three values.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GeomVertexData::
|
|
get_normal_info(const GeomVertexArrayData *&array_data,
|
|
GeomVertexData::NumericType &numeric_type,
|
|
int &start, int &stride) const {
|
|
int array_index = _format->get_normal_array_index();
|
|
if (array_index >= 0) {
|
|
const GeomVertexColumn *column = _format->get_normal_column();
|
|
nassertr(column->get_num_values() == 3, false);
|
|
|
|
CDReader cdata(_cycler);
|
|
array_data = cdata->_arrays[array_index];
|
|
numeric_type = column->get_numeric_type();
|
|
start = column->get_start();
|
|
stride = _format->get_array(array_index)->get_stride();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::get_color_info
|
|
// Access: Public
|
|
// Description: A shortcut to get_array_info() for the "color"
|
|
// column.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GeomVertexData::
|
|
get_color_info(const GeomVertexArrayData *&array_data,
|
|
int &num_values,
|
|
GeomVertexData::NumericType &numeric_type,
|
|
int &start, int &stride) const {
|
|
int array_index = _format->get_color_array_index();
|
|
if (array_index >= 0) {
|
|
const GeomVertexColumn *column = _format->get_color_column();
|
|
|
|
CDReader cdata(_cycler);
|
|
array_data = cdata->_arrays[array_index];
|
|
num_values = column->get_num_values();
|
|
numeric_type = column->get_numeric_type();
|
|
start = column->get_start();
|
|
stride = _format->get_array(array_index)->get_stride();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::packed_argb_to_uint8_rgba
|
|
// Access: Private, Static
|
|
// Description: Quickly converts DirectX-style color to OpenGL-style
|
|
// color.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
packed_argb_to_uint8_rgba(unsigned char *to, int to_stride,
|
|
const unsigned char *from, int from_stride,
|
|
int num_records) {
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "packed_argb_to_uint8_rgba(" << (void *)to << ", " << to_stride
|
|
<< ", " << (const void *)from << ", " << from_stride
|
|
<< ", " << num_records << ")\n";
|
|
}
|
|
|
|
while (num_records > 0) {
|
|
PN_uint32 dword = *(const PN_uint32 *)from;
|
|
to[0] = unpack_abcd_b(dword);
|
|
to[1] = unpack_abcd_c(dword);
|
|
to[2] = unpack_abcd_d(dword);
|
|
to[3] = unpack_abcd_a(dword);
|
|
|
|
to += to_stride;
|
|
from += from_stride;
|
|
num_records--;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::uint8_rgba_to_packed_argb
|
|
// Access: Private, Static
|
|
// Description: Quickly converts OpenGL-style color to DirectX-style
|
|
// color.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
|
|
const unsigned char *from, int from_stride,
|
|
int num_records) {
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "uint8_rgba_to_packed_argb(" << (void *)to << ", " << to_stride
|
|
<< ", " << (const void *)from << ", " << from_stride
|
|
<< ", " << num_records << ")\n";
|
|
}
|
|
|
|
while (num_records > 0) {
|
|
*(PN_uint32 *)to = pack_abcd(from[3], from[0], from[1], from[2]);
|
|
|
|
to += to_stride;
|
|
from += from_stride;
|
|
num_records--;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::do_set_num_rows
|
|
// Access: Private
|
|
// Description: The private implementation of set_num_rows().
|
|
////////////////////////////////////////////////////////////////////
|
|
bool GeomVertexData::
|
|
do_set_num_rows(int n, GeomVertexData::CData *cdata) {
|
|
nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false);
|
|
|
|
bool any_changed = false;
|
|
|
|
int color_array = -1;
|
|
int orig_color_rows = -1;
|
|
|
|
for (size_t i = 0; i < cdata->_arrays.size(); i++) {
|
|
if (cdata->_arrays[i]->get_num_rows() != n) {
|
|
// Copy-on-write.
|
|
if (cdata->_arrays[i]->get_ref_count() > 1) {
|
|
cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
|
|
}
|
|
if (cdata->_arrays[i]->has_column(InternalName::get_color())) {
|
|
color_array = i;
|
|
orig_color_rows = cdata->_arrays[i]->get_num_rows();
|
|
}
|
|
cdata->_arrays[i]->set_num_rows(n);
|
|
any_changed = true;
|
|
}
|
|
}
|
|
|
|
if (color_array >= 0 && orig_color_rows < n) {
|
|
// We have just added some rows, fill the "color" column with
|
|
// (1, 1, 1, 1), for the programmer's convenience.
|
|
GeomVertexArrayData *array_data = cdata->_arrays[color_array];
|
|
const GeomVertexColumn *column =
|
|
array_data->get_array_format()->get_column(InternalName::get_color());
|
|
int stride = array_data->get_array_format()->get_stride();
|
|
unsigned char *start =
|
|
array_data->modify_data() + column->get_start();
|
|
unsigned char *stop = start + array_data->get_data_size_bytes();
|
|
unsigned char *pointer = start + stride * orig_color_rows;
|
|
int num_values = column->get_num_values();
|
|
|
|
switch (column->get_numeric_type()) {
|
|
case NT_packed_dcba:
|
|
case NT_packed_dabc:
|
|
case NT_uint8:
|
|
case NT_uint16:
|
|
case NT_uint32:
|
|
while (pointer < stop) {
|
|
memset(pointer, 0xff, column->get_total_bytes());
|
|
pointer += stride;
|
|
}
|
|
break;
|
|
|
|
case NT_float32:
|
|
while (pointer < stop) {
|
|
PN_float32 *pi = (PN_float32 *)pointer;
|
|
for (int i = 0; i < num_values; i++) {
|
|
pi[i] = 1.0f;
|
|
}
|
|
pointer += stride;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (any_changed) {
|
|
do_clear_cache(cdata);
|
|
cdata->_modified = Geom::get_next_modified();
|
|
cdata->_animated_vertices.clear();
|
|
}
|
|
|
|
return any_changed;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::update_animated_vertices
|
|
// Access: Private
|
|
// Description: Recomputes the results of computing the vertex
|
|
// animation on the CPU, and applies them to the
|
|
// existing animated_vertices object.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
update_animated_vertices(GeomVertexData::CData *cdata) {
|
|
int num_rows = get_num_rows();
|
|
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Animating " << num_rows << " vertices for " << get_name()
|
|
<< "\n";
|
|
}
|
|
|
|
PStatTimer timer(_char_pcollector);
|
|
|
|
if (cdata->_animated_vertices == (GeomVertexData *)NULL) {
|
|
CPT(GeomVertexFormat) new_format = _format->get_post_animated_format();
|
|
cdata->_animated_vertices =
|
|
new GeomVertexData(get_name(), new_format,
|
|
min(get_usage_hint(), UH_dynamic));
|
|
}
|
|
PT(GeomVertexData) new_data = cdata->_animated_vertices;
|
|
|
|
// We have to make a complete copy of the data first so we can
|
|
// modify it. If we were clever, we could maybe just figure out the
|
|
// subset of the data that might have changed since last frame, but
|
|
// that's too much trouble (and isn't obviously faster than just
|
|
// copying the whole thing).
|
|
new_data->copy_from(this, true);
|
|
|
|
// First, apply all of the morphs.
|
|
CPT(SliderTable) slider_table = cdata->_slider_table;
|
|
if (slider_table != (SliderTable *)NULL) {
|
|
PStatTimer timer2(_morphs_pcollector);
|
|
int num_morphs = _format->get_num_morphs();
|
|
for (int mi = 0; mi < num_morphs; mi++) {
|
|
CPT(InternalName) slider_name = _format->get_morph_slider(mi);
|
|
const VertexSlider *slider = slider_table->find_slider(slider_name);
|
|
if (slider != (VertexSlider *)NULL) {
|
|
float slider_value = slider->get_slider();
|
|
if (slider_value != 0.0f) {
|
|
CPT(InternalName) base_name = _format->get_morph_base(mi);
|
|
CPT(InternalName) delta_name = _format->get_morph_delta(mi);
|
|
|
|
GeomVertexRewriter data(new_data, base_name);
|
|
GeomVertexReader delta(this, delta_name);
|
|
|
|
if (data.get_column()->get_num_values() == 4) {
|
|
if (data.get_column()->has_homogeneous_coord()) {
|
|
for (int i = 0; i < num_rows; i++) {
|
|
// Scale the delta by the homogeneous coordinate.
|
|
LPoint4f vertex = data.get_data4f();
|
|
LPoint3f d = delta.get_data3f();
|
|
d *= slider_value * vertex[3];
|
|
data.set_data4f(vertex[0] + d[0],
|
|
vertex[1] + d[1],
|
|
vertex[2] + d[2],
|
|
vertex[3]);
|
|
}
|
|
} else {
|
|
// Just apply the four-component delta.
|
|
for (int i = 0; i < num_rows; i++) {
|
|
const LPoint4f &vertex = data.get_data4f();
|
|
LPoint4f d = delta.get_data4f();
|
|
data.set_data4f(vertex + d * slider_value);
|
|
}
|
|
}
|
|
} else {
|
|
// 3-component or smaller values; don't worry about a
|
|
// homogeneous coordinate.
|
|
for (int i = 0; i < num_rows; i++) {
|
|
const LPoint3f &vertex = data.get_data3f();
|
|
LPoint3f d = delta.get_data3f();
|
|
data.set_data3f(vertex + d * slider_value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then apply the transforms.
|
|
CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table;
|
|
if (tb_table != (TransformBlendTable *)NULL) {
|
|
PStatTimer timer3(_skinning_pcollector);
|
|
|
|
// Recompute all the blends up front, so we don't have to test
|
|
// each one for staleness at each vertex.
|
|
int num_blends = tb_table->get_num_blends();
|
|
int bi;
|
|
for (bi = 0; bi < num_blends; bi++) {
|
|
tb_table->get_blend(bi).update_blend();
|
|
}
|
|
|
|
// Now go through and apply the transforms.
|
|
GeomVertexReader blendi(this, InternalName::get_transform_blend());
|
|
if (!blendi.has_column()) {
|
|
gobj_cat.warning()
|
|
<< "Vertex data " << get_name()
|
|
<< " has a transform_blend_table, but no transform_blend data.\n";
|
|
return;
|
|
}
|
|
|
|
int ci;
|
|
for (ci = 0; ci < _format->get_num_points(); ci++) {
|
|
GeomVertexRewriter data(new_data, _format->get_point(ci));
|
|
blendi.set_row(0);
|
|
|
|
if (data.get_column()->get_num_values() == 4) {
|
|
for (int i = 0; i < num_rows; i++) {
|
|
LPoint4f vertex = data.get_data4f();
|
|
int bi = blendi.get_data1i();
|
|
tb_table->get_blend(bi).transform_point(vertex);
|
|
data.set_data4f(vertex);
|
|
}
|
|
} else {
|
|
for (int i = 0; i < num_rows; i++) {
|
|
LPoint3f vertex = data.get_data3f();
|
|
int bi = blendi.get_data1i();
|
|
tb_table->get_blend(bi).transform_point(vertex);
|
|
data.set_data3f(vertex);
|
|
}
|
|
}
|
|
}
|
|
for (ci = 0; ci < _format->get_num_vectors(); ci++) {
|
|
GeomVertexRewriter data(new_data, _format->get_vector(ci));
|
|
blendi.set_row(0);
|
|
|
|
for (int i = 0; i < num_rows; i++) {
|
|
LVector3f vertex = data.get_data3f();
|
|
int bi = blendi.get_data1i();
|
|
tb_table->get_blend(bi).transform_vector(vertex);
|
|
data.set_data3f(vertex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::do_clear_cache
|
|
// Access: Private
|
|
// Description: The private implementation of clear_cache().
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
do_clear_cache(GeomVertexData::CData *cdata) {
|
|
// Probably we shouldn't do anything at all here unless we are
|
|
// running in pipeline stage 0.
|
|
for (Cache::iterator ci = cdata->_cache.begin();
|
|
ci != cdata->_cache.end();
|
|
++ci) {
|
|
CacheEntry *entry = (*ci);
|
|
entry->erase();
|
|
}
|
|
cdata->_cache.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::register_with_read_factory
|
|
// Access: Public, Static
|
|
// Description: Tells the BamReader how to create objects of type
|
|
// GeomVertexData.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritableReferenceCount::write_datagram(manager, dg);
|
|
|
|
dg.add_string(_name);
|
|
manager->write_pointer(dg, _format);
|
|
|
|
manager->write_cdata(dg, _cycler);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::make_from_bam
|
|
// Access: Protected, Static
|
|
// Description: This function is called by the BamReader's factory
|
|
// when a new object of type GeomVertexData is encountered
|
|
// in the Bam file. It should create the GeomVertexData
|
|
// and extract its information from the file.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *GeomVertexData::
|
|
make_from_bam(const FactoryParams ¶ms) {
|
|
GeomVertexData *object = new GeomVertexData;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
object->fillin(scan, manager);
|
|
manager->register_finalize(object);
|
|
|
|
return object;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::complete_pointers
|
|
// Access: Public, Virtual
|
|
// Description: Receives an array of pointers, one for each time
|
|
// manager->read_pointer() was called in fillin().
|
|
// Returns the number of pointers processed.
|
|
////////////////////////////////////////////////////////////////////
|
|
int GeomVertexData::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
|
|
|
|
_format = DCAST(GeomVertexFormat, p_list[pi++]);
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::finalize
|
|
// Access: Public, Virtual
|
|
// Description: Called by the BamReader to perform any final actions
|
|
// needed for setting up the object after all objects
|
|
// have been read and all pointers have been completed.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
finalize(BamReader *manager) {
|
|
// NOTE: This method may be called more than once, because the
|
|
// Geom::finalize() will call it explicitly. We have to be prepared
|
|
// to accept multiple finalize() calls.
|
|
|
|
// Now we need to register the format that we have read from the bam
|
|
// file (since it doesn't come out of the bam file automatically
|
|
// registered). This may change the format's pointer, which we
|
|
// should then update our own data to reflect. But since this may
|
|
// cause the unregistered object to destruct, we have to also tell
|
|
// the BamReader to return the new object from now on.
|
|
|
|
// This extends to the nested array datas, as well as the transform
|
|
// table and slider tables, as well.
|
|
|
|
CDWriter cdata(_cycler);
|
|
|
|
CPT(GeomVertexFormat) new_format =
|
|
GeomVertexFormat::register_format(_format);
|
|
|
|
for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
|
|
CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
|
|
nassertv(cdata->_arrays[i]->_array_format->compare_to(*new_array_format) == 0);
|
|
|
|
manager->change_pointer(cdata->_arrays[i]->_array_format, new_array_format);
|
|
cdata->_arrays[i]->_array_format = new_array_format;
|
|
}
|
|
|
|
manager->change_pointer(_format, new_format);
|
|
_format = new_format;
|
|
|
|
if (cdata->_transform_table != (TransformTable *)NULL) {
|
|
CPT(TransformTable) new_transform_table =
|
|
TransformTable::register_table(cdata->_transform_table);
|
|
manager->change_pointer(cdata->_transform_table, new_transform_table);
|
|
cdata->_transform_table = new_transform_table;
|
|
}
|
|
|
|
if (cdata->_slider_table != (SliderTable *)NULL) {
|
|
CPT(SliderTable) new_slider_table =
|
|
SliderTable::register_table(cdata->_slider_table);
|
|
manager->change_pointer(cdata->_slider_table, new_slider_table);
|
|
cdata->_slider_table = new_slider_table;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::fillin
|
|
// Access: Protected
|
|
// Description: This internal function is called by make_from_bam to
|
|
// read in all of the relevant data from the BamFile for
|
|
// the new GeomVertexData.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritableReferenceCount::fillin(scan, manager);
|
|
|
|
set_name(scan.get_string());
|
|
manager->read_pointer(scan);
|
|
|
|
manager->read_cdata(scan, _cycler);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CacheEntry::evict_callback
|
|
// Access: Public, Virtual
|
|
// Description: Called when the entry is evicted from the cache, this
|
|
// should clean up the owning object appropriately.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::CacheEntry::
|
|
evict_callback() {
|
|
// We have to operate on stage 0 of the pipeline, since that's where
|
|
// the cache really counts. Because of the multistage pipeline, we
|
|
// might not actually have a cache entry there (it might have been
|
|
// added to stage 1 instead). No big deal if we don't.
|
|
CData *cdata = _source->_cycler.write_stage(0);
|
|
Cache::iterator ci = cdata->_cache.find(this);
|
|
if (ci != cdata->_cache.end()) {
|
|
cdata->_cache.erase(ci);
|
|
}
|
|
_source->_cycler.release_write_stage(0, cdata);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CacheEntry::output
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::CacheEntry::
|
|
output(ostream &out) const {
|
|
out << "vertex data " << (void *)_source << " to "
|
|
<< *_modifier;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CData::make_copy
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CycleData *GeomVertexData::CData::
|
|
make_copy() const {
|
|
return new CData(*this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CData::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::CData::
|
|
write_datagram(BamWriter *manager, Datagram &dg) const {
|
|
dg.add_uint8(_usage_hint);
|
|
|
|
dg.add_uint16(_arrays.size());
|
|
Arrays::const_iterator ai;
|
|
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
|
|
manager->write_pointer(dg, *ai);
|
|
}
|
|
|
|
manager->write_pointer(dg, _transform_table);
|
|
manager->write_pointer(dg, _transform_blend_table);
|
|
manager->write_pointer(dg, _slider_table);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CData::complete_pointers
|
|
// Access: Public, Virtual
|
|
// Description: Receives an array of pointers, one for each time
|
|
// manager->read_pointer() was called in fillin().
|
|
// Returns the number of pointers processed.
|
|
////////////////////////////////////////////////////////////////////
|
|
int GeomVertexData::CData::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = CycleData::complete_pointers(p_list, manager);
|
|
|
|
Arrays::iterator ai;
|
|
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
|
|
(*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);
|
|
}
|
|
|
|
_transform_table = DCAST(TransformTable, p_list[pi++]);
|
|
_transform_blend_table = DCAST(TransformBlendTable, p_list[pi++]);
|
|
_slider_table = DCAST(SliderTable, p_list[pi++]);
|
|
|
|
_modified = Geom::get_next_modified();
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: GeomVertexData::CData::fillin
|
|
// Access: Public, Virtual
|
|
// Description: This internal function is called by make_from_bam to
|
|
// read in all of the relevant data from the BamFile for
|
|
// the new GeomVertexData.
|
|
////////////////////////////////////////////////////////////////////
|
|
void GeomVertexData::CData::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
_usage_hint = (UsageHint)scan.get_uint8();
|
|
|
|
size_t num_arrays = scan.get_uint16();
|
|
_arrays.reserve(num_arrays);
|
|
for (size_t i = 0; i < num_arrays; ++i) {
|
|
manager->read_pointer(scan);
|
|
_arrays.push_back(NULL);
|
|
}
|
|
|
|
manager->read_pointer(scan);
|
|
manager->read_pointer(scan);
|
|
manager->read_pointer(scan);
|
|
}
|