open_toontown_panda3d/panda/src/gobj/geomVertexFormat.cxx

1054 lines
32 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file geomVertexFormat.cxx
* @author drose
* @date 2005-03-07
*/
#include "geomVertexFormat.h"
#include "geomVertexData.h"
#include "geomMunger.h"
#include "lightReMutexHolder.h"
#include "indent.h"
#include "bamReader.h"
#include "bamWriter.h"
GeomVertexFormat::Registry *GeomVertexFormat::_registry = NULL;
TypeHandle GeomVertexFormat::_type_handle;
/**
*
*/
GeomVertexFormat::
GeomVertexFormat() :
_is_registered(false),
_post_animated_format(NULL)
{
}
/**
*
*/
GeomVertexFormat::
GeomVertexFormat(const GeomVertexArrayFormat *array_format) :
_is_registered(false),
_post_animated_format(NULL)
{
add_array(array_format);
}
/**
*
*/
GeomVertexFormat::
GeomVertexFormat(const GeomVertexFormat &copy) :
_is_registered(false),
_animation(copy._animation),
_arrays(copy._arrays),
_post_animated_format(NULL)
{
}
/**
*
*/
void GeomVertexFormat::
operator = (const GeomVertexFormat &copy) {
nassertv(!is_registered());
_animation = copy._animation;
_arrays = copy._arrays;
}
/**
*
*/
GeomVertexFormat::
~GeomVertexFormat() {
// unref() should have unregistered us.
nassertv(!is_registered());
}
/**
* This method overrides ReferenceCount::unref() to unregister the object when
* its reference count goes to zero.
*/
bool GeomVertexFormat::
unref() const {
Registry *registry = get_registry();
LightReMutexHolder holder(registry->_lock);
if (ReferenceCount::unref()) {
return true;
}
if (is_registered()) {
registry->unregister_format((GeomVertexFormat *)this);
}
return false;
}
/**
* Returns a suitable vertex format for sending the animated vertices to the
* graphics backend. This is the same format as the source format, with the
* CPU-animation data elements removed.
*
* This may only be called after the format has been registered. The return
* value will have been already registered.
*/
CPT(GeomVertexFormat) GeomVertexFormat::
get_post_animated_format() const {
nassertr(is_registered(), NULL);
if (_post_animated_format == (GeomVertexFormat *)NULL) {
PT(GeomVertexFormat) new_format = new GeomVertexFormat(*this);
new_format->remove_column(InternalName::get_transform_blend());
int num_morphs = get_num_morphs();
for (int mi = 0; mi < num_morphs; mi++) {
CPT(InternalName) delta_name = get_morph_delta(mi);
new_format->remove_column(delta_name);
}
new_format->_animation.set_none();
CPT(GeomVertexFormat) registered =
GeomVertexFormat::register_format(new_format);
((GeomVertexFormat *)this)->_post_animated_format = registered;
if (_post_animated_format != this) {
// We only keep the reference count if the new pointer is not the same
// as this, to avoid a circular dependency.
_post_animated_format->ref();
}
}
_post_animated_format->test_ref_count_integrity();
return _post_animated_format;
}
/**
* Returns a new GeomVertexFormat that includes all of the columns defined in
* either this GeomVertexFormat or the other one. If any column is defined in
* both formats with different sizes (for instance, texcoord2 vs. texcoord3),
* the new format will include the larger of the two definitions.
*
* This may only be called after both source formats have been registered.
* The return value will also have been already registered.
*/
CPT(GeomVertexFormat) GeomVertexFormat::
get_union_format(const GeomVertexFormat *other) const {
nassertr(is_registered() && other->is_registered(), NULL);
PT(GeomVertexFormat) new_format = new GeomVertexFormat;
// Preserve whichever animation type is not AT_None. (If both animation
// types are not AT_None, but they are different, this whole operation is
// questionable.)
if (_animation.get_animation_type() != AT_none) {
new_format->set_animation(_animation);
} else {
new_format->set_animation(other->get_animation());
}
// Keep track of the columns we have already added.
typedef pset< CPT(InternalName) > ColumnNames;
ColumnNames column_names;
// We go through all the (0)-level arrays first, then all the (1)-level
// arrays, and so on. We do this to ensure that the new format gets written
// out with all the (0)-level columns appearing before all the (1)-level
// columns, which might lead to a small optimization at render time.
// We also try to keep the structure as similar as possible. If both source
// formats have columns (A, B) in array 0, and columns (C, D, E) in array 1,
// then the resulting union format will also have (A, B) in array 0 and (C,
// D, E) in array 1. In general, a column will appear in the result in the
// first array it appears in either of the inputs.
size_t num_arrays = max(_arrays.size(), other->_arrays.size());
for (size_t ai = 0; ai < num_arrays; ++ai) {
PT(GeomVertexArrayFormat) new_array = new GeomVertexArrayFormat;
// Add the columns from the first format.
if (ai < _arrays.size()) {
GeomVertexArrayFormat *array_format = _arrays[ai];
size_t num_columns = array_format->get_num_columns();
for (size_t i = 0; i < num_columns; ++i) {
const GeomVertexColumn *column_a = array_format->get_column(i);
bool inserted = column_names.insert(column_a->get_name()).second;
if (inserted) {
const GeomVertexColumn *column_b = other->get_column(column_a->get_name());
if (column_b != (GeomVertexColumn *)NULL &&
column_b->get_total_bytes() > column_a->get_total_bytes()) {
// Column b is larger. Keep it.
new_array->add_column(column_b->get_name(),
column_b->get_num_components(),
column_b->get_numeric_type(),
column_b->get_contents());
} else {
// Column a is larger. Keep it.
new_array->add_column(column_a->get_name(),
column_a->get_num_components(),
column_a->get_numeric_type(),
column_a->get_contents());
}
}
}
}
// Add the columns from the second format.
if (ai < other->_arrays.size()) {
GeomVertexArrayFormat *array_format = other->_arrays[ai];
size_t num_columns = array_format->get_num_columns();
for (size_t i = 0; i < num_columns; ++i) {
const GeomVertexColumn *column_a = array_format->get_column(i);
bool inserted = column_names.insert(column_a->get_name()).second;
if (inserted) {
const GeomVertexColumn *column_b = get_column(column_a->get_name());
if (column_b != (GeomVertexColumn *)NULL &&
column_b->get_total_bytes() > column_a->get_total_bytes()) {
// Column b is larger. Keep it.
new_array->add_column(column_b->get_name(),
column_b->get_num_components(),
column_b->get_numeric_type(),
column_b->get_contents());
} else {
// Column a is larger. Keep it.
new_array->add_column(column_a->get_name(),
column_a->get_num_components(),
column_a->get_numeric_type(),
column_a->get_contents());
}
}
}
}
if (new_array->get_num_columns() != 0) {
new_format->add_array(new_array);
}
}
// Finally, register the format for the thing.
return GeomVertexFormat::register_format(new_format);
}
/**
* Returns a modifiable pointer to the indicated array. This means
* duplicating it if it is shared or registered.
*
* This may not be called once the format has been registered.
*/
GeomVertexArrayFormat *GeomVertexFormat::
modify_array(size_t array) {
nassertr(!is_registered(), NULL);
nassertr(array < _arrays.size(), NULL);
if (_arrays[array]->is_registered() ||
_arrays[array]->get_ref_count() > 1) {
_arrays[array] = new GeomVertexArrayFormat(*_arrays[array]);
}
return _arrays[array];
}
/**
* Replaces the definition of the indicated array.
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
set_array(size_t array, const GeomVertexArrayFormat *format) {
nassertv(!is_registered());
nassertv(array < _arrays.size());
_arrays[array] = (GeomVertexArrayFormat *)format;
}
/**
* Removes the nth array from the format.
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
remove_array(size_t array) {
nassertv(!is_registered());
nassertv(array < _arrays.size());
_arrays.erase(_arrays.begin() + array);
}
/**
* Adds the indicated array definition to the list of arrays included within
* this vertex format definition. The return value is the index number of the
* new array.
*
* This may not be called once the format has been registered.
*/
size_t GeomVertexFormat::
add_array(const GeomVertexArrayFormat *array_format) {
nassertr(!is_registered(), 0);
size_t new_array = _arrays.size();
_arrays.push_back((GeomVertexArrayFormat *)array_format);
return new_array;
}
/**
* Adds the indicated array definition to the list of arrays at the indicated
* position. This works just like add_array(), except that you can specify
* which array index the new array should have.
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
insert_array(size_t array, const GeomVertexArrayFormat *array_format) {
nassertv(!is_registered());
if (array > _arrays.size()) {
array = _arrays.size();
}
_arrays.insert(_arrays.begin() + array, (GeomVertexArrayFormat *)array_format);
}
/**
* Removes all of the array definitions from the format and starts over.
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
clear_arrays() {
nassertv(!is_registered());
_arrays.clear();
}
/**
* Removes the arrays that define no columns.
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
remove_empty_arrays() {
nassertv(!is_registered());
Arrays orig_arrays;
orig_arrays.swap(_arrays);
Arrays::const_iterator ai;
for (ai = orig_arrays.begin(); ai != orig_arrays.end(); ++ai) {
GeomVertexArrayFormat *array_format = (*ai);
if (array_format->get_num_columns() != 0) {
_arrays.push_back(array_format);
}
}
}
/**
* Returns the total number of different columns in the specification, across
* all arrays.
*/
size_t GeomVertexFormat::
get_num_columns() const {
size_t num_columns = 0;
Arrays::const_iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
num_columns += (*ai)->get_num_columns();
}
return num_columns;
}
/**
* Returns the ith column of the specification, across all arrays.
*/
const GeomVertexColumn *GeomVertexFormat::
get_column(size_t i) const {
Arrays::const_iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
if (i < (size_t)(*ai)->get_num_columns()) {
return (*ai)->get_column(i);
}
i -= (*ai)->get_num_columns();
}
return NULL;
}
/**
* Returns the name of the ith column, across all arrays.
*/
const InternalName *GeomVertexFormat::
get_column_name(size_t i) const {
Arrays::const_iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
if (i < (size_t)(*ai)->get_num_columns()) {
return (*ai)->get_column(i)->get_name();
}
i -= (*ai)->get_num_columns();
}
return nullptr;
}
/**
* Returns the index number of the array with the ith column.
*
* The return value can be passed to get_array_format() to get the format of
* the array. It may also be passed to GeomVertexData::get_array_data() or
* get_data() or set_data() to manipulate the actual array data.
*/
int GeomVertexFormat::
get_array_with(size_t i) const {
int array_index = 0;
for (array_index = 0; array_index < (int)_arrays.size(); array_index++) {
if (i < (size_t)_arrays[array_index]->get_num_columns()) {
return array_index;
}
i -= _arrays[array_index]->get_num_columns();
}
return -1;
}
/**
* Returns the index number of the array with the indicated column, or -1 if
* no arrays contained that name.
*
* The return value can be passed to get_array_format() to get the format of
* the array. It may also be passed to GeomVertexData::get_array_data() or
* get_data() or set_data() to manipulate the actual array data.
*
* This may only be called after the format has been registered.
*/
int GeomVertexFormat::
get_array_with(const InternalName *name) const {
nassertr(_is_registered, -1);
DataTypesByName::const_iterator ai;
ai = _columns_by_name.find(name);
if (ai != _columns_by_name.end()) {
return (*ai).second._array_index;
}
return -1;
}
/**
* Returns the specification with the indicated name, or NULL if the name is
* not used. Use get_array_with() to determine which array this column is
* associated with.
*/
const GeomVertexColumn *GeomVertexFormat::
get_column(const InternalName *name) const {
if (!_is_registered) {
// If the format hasn't yet been registered, we have to search for the
// column the hard way.
Arrays::const_iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
const GeomVertexColumn *column = (*ai)->get_column(name);
if (column != (GeomVertexColumn *)NULL) {
return column;
}
}
return NULL;
} else {
// If the format has been registered, we can just check the toplevel
// index.
DataTypesByName::const_iterator ai;
ai = _columns_by_name.find(name);
if (ai != _columns_by_name.end()) {
int array_index = (*ai).second._array_index;
int column_index = (*ai).second._column_index;
nassertr(array_index >= 0 && array_index < (int)_arrays.size(), NULL);
return _arrays[array_index]->get_column(column_index);
}
return NULL;
}
}
/**
* Removes the named column from the format, from whichever array it exists
* in. If there are other columns remaining in the array, the array is left
* with a gap where the column used to be; if this was the only column in the
* array, the array is removed (unless keep_empty_array is true).
*
* This may not be called once the format has been registered.
*/
void GeomVertexFormat::
remove_column(const InternalName *name, bool keep_empty_array) {
nassertv(!_is_registered);
// Since the format's not registered, it doesn't yet have an index of
// columns--so we have to search all of the arrays, one at a time, until we
// find it.
for (int array = 0; array < (int)_arrays.size(); ++array) {
GeomVertexArrayFormat *array_format = _arrays[array];
if (array_format->get_column(name) != (GeomVertexColumn *)NULL) {
// Here's the array with the named column!
if (array_format->is_registered() ||
array_format->get_ref_count() > 1) {
// Get a safe-to-modify copy of the array format.
_arrays[array] = new GeomVertexArrayFormat(*array_format);
array_format = _arrays[array];
}
array_format->remove_column(name);
// Are there any columns remaining in the array?
if (!keep_empty_array && array_format->get_num_columns() == 0) {
// Remove the whole array.
remove_array(array);
}
return;
}
}
// It appears that column wasn't part of the format anyway. No problem;
// quietly return.
}
/**
* Removes wasted space between columns.
*/
void GeomVertexFormat::
pack_columns() {
nassertv(!_is_registered);
Arrays::iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
if ((*ai)->is_registered()) {
(*ai) = new GeomVertexArrayFormat(*(*ai));
}
(*ai)->pack_columns();
}
}
/**
* Reprocesses the columns in the format to align the C_point and C_vector
* columns to 16-byte boundaries to allow for the more efficient SSE2
* operations (assuming SSE2 is enabled in the build).
*
* Also see maybe_align_columns_for_animation().
*/
void GeomVertexFormat::
align_columns_for_animation() {
nassertv(!_is_registered);
Arrays::iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
if ((*ai)->is_registered()) {
(*ai) = new GeomVertexArrayFormat(*(*ai));
}
(*ai)->align_columns_for_animation();
}
}
/**
* Calls align_columns_for_animation() if this format's AnimationSpec
* indicates that it contains animated vertices, and if vertex-animation-
* align-16 is true.
*/
void GeomVertexFormat::
maybe_align_columns_for_animation() {
if (_animation.get_animation_type() == AT_panda && vertex_animation_align_16) {
align_columns_for_animation();
}
}
/**
*
*/
void GeomVertexFormat::
output(ostream &out) const {
if (_arrays.empty()) {
out << "(empty)";
} else {
Arrays::const_iterator ai;
ai = _arrays.begin();
out << *(*ai);
++ai;
while (ai != _arrays.end()) {
out << ", " << *(*ai);
++ai;
}
}
if (_animation.get_animation_type() != AT_none) {
out << ", anim " << _animation;
}
}
/**
*
*/
void GeomVertexFormat::
write(ostream &out, int indent_level) const {
for (size_t i = 0; i < _arrays.size(); i++) {
indent(out, indent_level)
<< "Array " << i << ":\n";
_arrays[i]->write(out, indent_level + 2);
}
if (_animation.get_animation_type() != AT_none) {
indent(out, indent_level)
<< "anim " << _animation << "\n";
}
}
/**
*
*/
void GeomVertexFormat::
write_with_data(ostream &out, int indent_level,
const GeomVertexData *data) const {
indent(out, indent_level)
<< data->get_num_rows() << " rows.\n";
for (size_t i = 0; i < _arrays.size(); i++) {
CPT(GeomVertexArrayDataHandle) handle = data->get_array(i)->get_handle();
const unsigned char *array_data = handle->get_read_pointer(true);
indent(out, indent_level)
<< "Array " << i << " (" << (void *)array_data << ", "
<< *_arrays[i] << "):\n";
_arrays[i]->write_with_data(out, indent_level + 2, data->get_array(i));
}
}
/**
* Quickly looks up the indicated column within all of the nested arrays and
* sets array_index and column appropriately. Returns true if the data type
* exists in this format, false if it does not. If it returns false,
* array_index is set to -1, and column is set to NULL.
*
* This may only be called after the format has been registered.
*/
bool GeomVertexFormat::
get_array_info(const InternalName *name, int &array_index,
const GeomVertexColumn *&column) const {
nassertr(_is_registered, false);
DataTypesByName::const_iterator ai;
ai = _columns_by_name.find(name);
if (ai != _columns_by_name.end()) {
array_index = (*ai).second._array_index;
column = _arrays[array_index]->get_column((*ai).second._column_index);
return true;
}
array_index = -1;
column = NULL;
return false;
}
/**
*
*/
int GeomVertexFormat::
compare_to(const GeomVertexFormat &other) const {
int compare = _animation.compare_to(other._animation);
if (compare != 0) {
return compare;
}
if (_arrays.size() != other._arrays.size()) {
return (int)_arrays.size() - (int)other._arrays.size();
}
for (size_t i = 0; i < _arrays.size(); i++) {
int compare = _arrays[i]->compare_to(*other._arrays[i]);
if (compare != 0) {
return compare;
}
}
return 0;
}
/**
* Returns the global registry object.
*/
void GeomVertexFormat::
make_registry() {
if (_registry == (Registry *)NULL) {
_registry = new Registry;
_registry->make_standard_formats();
}
}
/**
* Called internally when the format is registered.
*/
void GeomVertexFormat::
do_register() {
nassertv(!is_registered());
nassertv(_columns_by_name.empty());
Arrays orig_arrays;
orig_arrays.swap(_arrays);
Arrays::const_iterator ai;
for (ai = orig_arrays.begin(); ai != orig_arrays.end(); ++ai) {
CPT(GeomVertexArrayFormat) array_format = (*ai);
if (!array_format->is_registered()) {
array_format = GeomVertexArrayFormat::register_format(array_format);
}
// Let's keep arrays with nonzero stride but no used columns; they're
// needed to preserve the isomorphic nature of matching formats. But
// we'll toss arrays with 0 stride, which add nothing of value and only
// cause problems later.
if (array_format->get_stride() == 0) {
gobj_cat.warning()
<< "Dropping empty array from GeomVertexFormat.\n";
continue;
}
int array = (int)_arrays.size();
_arrays.push_back((GeomVertexArrayFormat *)array_format.p());
// Now add the names to the index.
int num_columns = array_format->get_num_columns();
for (int i = 0; i < num_columns; i++) {
const GeomVertexColumn *column = array_format->get_column(i);
pair<DataTypesByName::iterator, bool> result;
result = _columns_by_name.insert(DataTypesByName::value_type(column->get_name(), DataTypeRecord()));
if (!result.second) {
gobj_cat.warning()
<< "Column " << *column->get_name() << " repeated in format.\n";
} else {
DataTypeRecord &record = (*result.first).second;
record._array_index = array;
record._column_index = i;
}
}
}
// Go back through the index now and identify the points, vectors, and morph
// descriptions, so we can quickly look these up later.
DataTypesByName::iterator ni;
for (ni = _columns_by_name.begin();
ni != _columns_by_name.end();
++ni) {
const DataTypeRecord &record = (*ni).second;
const GeomVertexColumn *column = _arrays[record._array_index]->get_column(record._column_index);
switch (column->get_contents()) {
case C_point:
// It's a point.
_points.push_back(column->get_name());
break;
case C_vector:
case C_normal:
// It's a vector.
_vectors.push_back(column->get_name());
break;
case C_texcoord:
// It's a texcoord.
_texcoords.push_back(column->get_name());
break;
case C_morph_delta:
{
// It's a morph description.
MorphRecord morph;
morph._delta = column->get_name();
// The delta name must be of the form "basename.morph.slidername".
int n = morph._delta->find_ancestor("morph");
if (n < 0) {
gobj_cat.warning()
<< "vertex format defines " << *column->get_name()
<< ", which is stored as a C_morph_delta, but its name does not include \"morph\".\n";
} else {
morph._slider = InternalName::make(morph._delta->get_net_basename(n - 1));
morph._base = morph._delta->get_ancestor(n + 1);
if (_columns_by_name.find(morph._base) == _columns_by_name.end()) {
gobj_cat.warning()
<< "vertex format defines "
<< *column->get_name() << " but does not define "
<< *morph._base << "\n";
} else {
_morphs.push_back(morph);
}
}
}
break;
default:
// Some other type of value we don't care about caching a pointer to.
break;
}
}
_is_registered = true;
get_array_info(InternalName::get_vertex(), _vertex_array_index,
_vertex_column);
get_array_info(InternalName::get_normal(), _normal_array_index,
_normal_column);
get_array_info(InternalName::get_color(), _color_array_index,
_color_column);
}
/**
* Called internally when the format is unregistered.
*/
void GeomVertexFormat::
do_unregister() {
nassertv(_is_registered);
_is_registered = false;
_columns_by_name.clear();
_points.clear();
_vectors.clear();
_texcoords.clear();
_morphs.clear();
if (_post_animated_format != (GeomVertexFormat *)NULL &&
_post_animated_format != this) {
unref_delete(_post_animated_format);
}
_post_animated_format = NULL;
}
/**
* Tells the BamReader how to create objects of type GeomVertexFormat.
*/
void GeomVertexFormat::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
/**
* Writes the contents of this object to the datagram for shipping out to a
* Bam file.
*/
void GeomVertexFormat::
write_datagram(BamWriter *manager, Datagram &dg) {
TypedWritableReferenceCount::write_datagram(manager, dg);
_animation.write_datagram(manager, dg);
dg.add_uint16(_arrays.size());
Arrays::const_iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
manager->write_pointer(dg, *ai);
}
}
/**
* Receives an array of pointers, one for each time manager->read_pointer()
* was called in fillin(). Returns the number of pointers processed.
*/
int GeomVertexFormat::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
Arrays::iterator ai;
for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
(*ai) = DCAST(GeomVertexArrayFormat, p_list[pi++]);
}
return pi;
}
/**
* This function is called by the BamReader's factory when a new object of
* type GeomVertexFormat is encountered in the Bam file. It should create the
* GeomVertexFormat and extract its information from the file.
*/
TypedWritable *GeomVertexFormat::
make_from_bam(const FactoryParams &params) {
GeomVertexFormat *object = new GeomVertexFormat;
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
object->fillin(scan, manager);
return object;
}
/**
* This internal function is called by make_from_bam to read in all of the
* relevant data from the BamFile for the new GeomVertexFormat.
*/
void GeomVertexFormat::
fillin(DatagramIterator &scan, BamReader *manager) {
TypedWritableReferenceCount::fillin(scan, manager);
_animation.fillin(scan, manager);
int num_arrays = scan.get_uint16();
_arrays.reserve(num_arrays);
for (int i = 0; i < num_arrays; i++) {
manager->read_pointer(scan);
_arrays.push_back(NULL);
}
}
/**
*
*/
GeomVertexFormat::Registry::
Registry() {
}
/**
*
*/
void GeomVertexFormat::Registry::
make_standard_formats() {
_empty = register_format(new GeomVertexFormat);
_v3 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point));
_v3n3 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal));
_v3t2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
_v3n3t2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
// Define the DirectX-style packed color formats
_v3cp = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_color(), 1,
NT_packed_dabc, C_color));
_v3n3cp = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal,
InternalName::get_color(), 1,
NT_packed_dabc, C_color));
_v3cpt2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_color(), 1,
NT_packed_dabc, C_color,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
_v3n3cpt2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal,
InternalName::get_color(), 1,
NT_packed_dabc, C_color,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
// Define the OpenGL-style per-byte color formats. This is not the same as
// a packed format, above, because the resulting byte order is endian-
// independent.
_v3c4 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_color(), 4,
NT_uint8, C_color));
_v3n3c4 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal,
InternalName::get_color(), 4,
NT_uint8, C_color));
_v3c4t2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_color(), 4,
NT_uint8, C_color,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
_v3n3c4t2 = register_format(new GeomVertexArrayFormat
(InternalName::get_vertex(), 3,
NT_stdfloat, C_point,
InternalName::get_normal(), 3,
NT_stdfloat, C_normal,
InternalName::get_color(), 4,
NT_uint8, C_color,
InternalName::get_texcoord(), 2,
NT_stdfloat, C_texcoord));
}
/**
* Adds the indicated format to the registry, if there is not an equivalent
* format already there; in either case, returns the pointer to the equivalent
* format now in the registry.
*
* This must be called before a format may be used in a Geom. After this
* call, you should discard the original pointer you passed in (which may or
* may not now be invalid) and let its reference count decrement normally; you
* should use only the returned value from this point on.
*/
CPT(GeomVertexFormat) GeomVertexFormat::Registry::
register_format(GeomVertexFormat *format) {
if (format->is_registered()) {
return format;
}
// Save the incoming pointer in a local PointerTo, so that if it has a zero
// reference count and is not added into the map below, it will be
// automatically deleted when this function returns.
PT(GeomVertexFormat) pt_format = format;
GeomVertexFormat *new_format;
{
LightReMutexHolder holder(_lock);
Formats::iterator fi = _formats.insert(format).first;
new_format = (*fi);
if (!new_format->is_registered()) {
new_format->do_register();
}
}
return new_format;
}
/**
* Removes the indicated format from the registry. Normally this should not
* be done until the format is destructing.
*
* The lock should be held prior to calling this method.
*/
void GeomVertexFormat::Registry::
unregister_format(GeomVertexFormat *format) {
nassertv(format->is_registered());
Formats::iterator fi = _formats.find(format);
nassertv(fi != _formats.end());
_formats.erase(fi);
format->do_unregister();
}