open_toontown_panda3d/panda/src/egg2pg/computedVerticesMaker.cxx

699 lines
25 KiB
C++

// Filename: computedVerticesMaker.cxx
// Created by: drose (01Mar99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "computedVerticesMaker.h"
#include "characterMaker.h"
#include "characterJoint.h"
#include "character.h"
#include "computedVertices.h"
#include "eggNode.h"
#include "eggGroup.h"
#include "eggVertex.h"
#include <algorithm>
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
ComputedVerticesMaker::
ComputedVerticesMaker()
{
_coords= PTA_Vertexf::empty_array(0);
_norms= PTA_Normalf::empty_array(0);
_colors= PTA_Colorf::empty_array(0);
_texcoords= PTA_TexCoordf::empty_array(0);
_current_vc = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::begin_new_space
// Access: Public
// Description: Should be called before beginning the definition for
// a new transform space.
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::
begin_new_space() {
_current_jw.clear();
_current_vc = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_joint
// Access: Public
// Description: Adds the joint with its associated membership amount
// to the current transform space definition.
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::
add_joint(EggNode *joint, double membership) {
// This must be called between a call to begin_new_space() and
// mark_space().
assert(_current_vc == NULL);
if (membership == 0.0) {
return;
}
assert(membership > 0.0);
JointWeights::iterator jwi = _current_jw.find(joint);
if (jwi != _current_jw.end()) {
// We'd already added this joint previously. Increment its total
// membership.
(*jwi).second += membership;
} else {
// This is the first time we've added this joint.
_current_jw[joint] = membership;
}
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_vertex_joints
// Access: Public
// Description: Adds the joints the vertex belongs to, along with
// their respective memberships, to the current
// transform space definition.
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::
add_vertex_joints(EggVertex *vertex, EggNode *object) {
if (vertex->gref_size() == 0) {
// This vertex belongs in the same group as the primitive that
// contains it.
EggGroupNode *egg_joint = object->get_parent();
// We actually walk up to find the first group above that that's a
// joint, or the character root itself, so we won't (a) be fooled
// by meaningless transforms on non-joints within a character
// hierarchy, or (b) consider meaninglessly different groups to be
// significant.
EggGroup *egg_group = (EggGroup *)NULL;
if (egg_joint->is_of_type(EggGroup::get_class_type())) {
egg_group = DCAST(EggGroup, egg_joint);
}
while (egg_group != (EggGroup *)NULL &&
egg_group->get_group_type() != EggGroup::GT_joint &&
egg_group->get_dart_type() == EggGroup::DT_none) {
nassertv(egg_group->get_parent() != (EggGroupNode *)NULL);
egg_joint = egg_group->get_parent();
egg_group = (EggGroup *)NULL;
if (egg_joint->is_of_type(EggGroup::get_class_type())) {
egg_group = DCAST(EggGroup, egg_joint);
}
}
add_joint(egg_joint, 1.0);
} else {
// This vertex belongs in the joint or joints that reference it.
EggVertex::GroupRef::const_iterator gri;
for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) {
EggGroup *egg_joint = (*gri);
double membership = egg_joint->get_vertex_membership(vertex);
add_joint(egg_joint, membership);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::mark_space
// Access: Public
// Description: Completes the definition of a transform space as a
// set of joints and memberships. From this point until
// the next call to begin_new_space(), vertices may be
// added to the transform space via calls to
// add_vertex(), add_normal(), etc.
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::
mark_space() {
// This must be called after a call to begin_new_space().
assert(_current_vc == NULL);
_current_jw.normalize_weights();
// This will look up a previously-defined VertexCollection, if we've
// used this transform space before, or it will implicitly create a
// new one if we haven't.
_current_vc = &_transforms[_current_jw];
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_vertex
// Access: Public
// Description: Adds a vertex value to the currently-defined
// transform space, and returns its index number within
// the array.
////////////////////////////////////////////////////////////////////
int ComputedVerticesMaker::
add_vertex(const Vertexd &vertex, const EggMorphVertexList &morphs,
const LMatrix4d &transform) {
// This must be called after a call to mark_space(), and before a
// call to begin_new_space().
assert(_current_vc != NULL);
Vertexf tv = LCAST(float, vertex * transform);
int index = _current_vc->_vmap.add_value(tv, morphs, _coords);
_current_vc->_vindex.insert(index);
// Now create any morph sliders.
EggMorphVertexList::const_iterator mli;
for (mli = morphs.begin(); mli != morphs.end(); ++mli) {
const EggMorphVertex &morph = (*mli);
LVector3d offset = morph.get_offset() * transform;
if (!offset.almost_equal(LVector3d(0.0, 0.0, 0.0), 0.0001)) {
MorphList &mlist = _morphs[morph.get_name()];
// Have we already morphed this vertex?
VertexMorphList::iterator vmi = mlist._vmorphs.find(index);
if (vmi != mlist._vmorphs.end()) {
// Yes, we have.
assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001));
} else {
// No, we haven't yet; morph it now.
mlist._vmorphs[index] = LCAST(float, offset);
}
}
}
return index;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_normal
// Access: Public
// Description: Adds a normal value to the currently-defined
// transform space, and returns its index number within
// the array.
////////////////////////////////////////////////////////////////////
int ComputedVerticesMaker::
add_normal(const Normald &normal, const EggMorphNormalList &morphs,
const LMatrix4d &transform) {
// This must be called after a call to mark_space(), and before a
// call to begin_new_space().
assert(_current_vc != NULL);
Normald norm = normal * transform;
norm.normalize();
int index = _current_vc->_nmap.add_value(LCAST(float, norm), morphs, _norms);
_current_vc->_nindex.insert(index);
// Now create any morph sliders.
EggMorphNormalList::const_iterator mli;
for (mli = morphs.begin(); mli != morphs.end(); ++mli) {
const EggMorphNormal &morph = (*mli);
LVector3d offset = morph.get_offset() * transform;
if (!offset.almost_equal(LVector3d(0.0, 0.0, 0.0), 0.0001)) {
MorphList &mlist = _morphs[morph.get_name()];
// Have we already morphed this normal?
NormalMorphList::iterator vmi = mlist._nmorphs.find(index);
if (vmi != mlist._nmorphs.end()) {
// Yes, we have.
assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001));
} else {
// No, we haven't yet; morph it now.
mlist._nmorphs[index] = LCAST(float, offset);
}
}
}
return index;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_texcoord
// Access: Public
// Description: Adds a texcoord value to the array (texture
// coordinates are unrelated to the current transform
// space), and returns its index number within the
// array.
////////////////////////////////////////////////////////////////////
int ComputedVerticesMaker::
add_texcoord(const TexCoordd &texcoord, const EggMorphTexCoordList &morphs,
const LMatrix3d &transform) {
TexCoordf ttc = LCAST(float, texcoord * transform);
int index = _tmap.add_value(ttc, morphs, _texcoords);
_tindex.insert(index);
// Now create any morph sliders.
EggMorphTexCoordList::const_iterator mli;
for (mli = morphs.begin(); mli != morphs.end(); ++mli) {
const EggMorphTexCoord &morph = (*mli);
LVector2d offset = morph.get_offset() * transform;
if (!offset.almost_equal(LVector2d(0.0, 0.0), 0.0001)) {
MorphList &mlist = _morphs[morph.get_name()];
// Have we already morphed this texcoord?
TexCoordMorphList::iterator vmi = mlist._tmorphs.find(index);
if (vmi != mlist._tmorphs.end()) {
// Yes, we have.
assert(offset.almost_equal(LCAST(double, (*vmi).second), 0.0001));
} else {
// No, we haven't yet; morph it now.
mlist._tmorphs[index] = LCAST(float, offset);
}
}
}
return index;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::add_color
// Access: Public
// Description: Adds a color value to the array (color values
// are unrelated to the current transform space), and
// returns its index number within the array.
////////////////////////////////////////////////////////////////////
int ComputedVerticesMaker::
add_color(const Colorf &color, const EggMorphColorList &morphs) {
int index = _cmap.add_value(color, morphs, _colors);
_cindex.insert(index);
// Now create any morph sliders.
EggMorphColorList::const_iterator mli;
for (mli = morphs.begin(); mli != morphs.end(); ++mli) {
const EggMorphColor &morph = (*mli);
LVector4f offset = morph.get_offset();
if (!offset.almost_equal(LVector4f(0.0, 0.0, 0.0, 0.0), 0.0001)) {
MorphList &mlist = _morphs[morph.get_name()];
// Have we already morphed this color?
ColorMorphList::iterator vmi = mlist._cmorphs.find(index);
if (vmi != mlist._cmorphs.end()) {
// Yes, we have.
assert(offset.almost_equal((*vmi).second, 0.0001));
} else {
// No, we haven't yet; morph it now.
mlist._cmorphs[index] = offset;
}
}
}
return index;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::make_computed_vertices
// Access: Public
// Description: After all spaces have been defined and all vertices
// added, creates a new ComputedVertices object and
// returns it.
////////////////////////////////////////////////////////////////////
ComputedVertices *ComputedVerticesMaker::
make_computed_vertices(Character *character, CharacterMaker &char_maker) {
// We must first build up a set of all the unique kinds of vertex
// transforms.
typedef pset<ComputedVertices::VertexTransform> VertexTransforms;
VertexTransforms transforms;
TransformSpaces::const_iterator tsi;
for (tsi = _transforms.begin();
tsi != _transforms.end();
++tsi) {
const JointWeights &jw = (*tsi).first;
const VertexCollection &vc = (*tsi).second;
JointWeights::const_iterator jwi;
for (jwi = jw.begin(); jwi != jw.end(); ++jwi) {
double weight = (*jwi).second;
EggNode *egg_joint = (*jwi).first;
int joint_index = char_maker.egg_to_index(egg_joint);
// Look for a VertexTransform that matches this template.
ComputedVertices::VertexTransform new_vt;
new_vt._joint_index = joint_index;
new_vt._effect = (float)weight;
// This will either insert the VertexTransform into the set and
// return its newly-created iterator, or it will return the
// iterator referring to the previously-inserted VertexTransform
// like this.
VertexTransforms::iterator vti = transforms.insert(new_vt).first;
// We can discard the const-ness of the set's iterator, because
// we will only be changing a part of the VertexTransform that
// doesn't affect its sort order within the set.
ComputedVertices::VertexTransform &insert_vt =
(ComputedVertices::VertexTransform &)*vti;
// Now add in all the vertices and normals.
copy(vc._vindex.begin(), vc._vindex.end(),
back_inserter(insert_vt._vindex));
copy(vc._nindex.begin(), vc._nindex.end(),
back_inserter(insert_vt._nindex));
}
}
// Ok, now we have the set of all VertexTransforms. Create a
// ComputedVertices object that reflects this.
ComputedVertices *comp_verts = new ComputedVertices;
copy(transforms.begin(), transforms.end(),
back_inserter(comp_verts->_transforms));
character->_cv._coords = _coords;
character->_cv._norms = _norms;
character->_cv._colors = _colors;
character->_cv._texcoords = _texcoords;
// Finally, add in all the morph definitions.
Morphs::const_iterator mi;
for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) {
const string &name = (*mi).first;
const MorphList &mlist = (*mi).second;
int slider_index = char_maker.create_slider(name);
if (!mlist._vmorphs.empty()) {
// We push an empty MorphVertex object and then modify it,
// rather than filling it first and then pushing it, just to
// avoid unnecessary copying of data.
comp_verts->_vertex_morphs.push_back(ComputedVerticesMorphVertex());
ComputedVerticesMorphVertex &mv = comp_verts->_vertex_morphs.back();
mv._slider_index = slider_index;
VertexMorphList::const_iterator vmi;
for (vmi = mlist._vmorphs.begin();
vmi != mlist._vmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
(*vmi).second));
}
}
if (!mlist._nmorphs.empty()) {
comp_verts->_normal_morphs.push_back(ComputedVerticesMorphNormal());
ComputedVerticesMorphNormal &mv = comp_verts->_normal_morphs.back();
mv._slider_index = slider_index;
NormalMorphList::const_iterator vmi;
for (vmi = mlist._nmorphs.begin();
vmi != mlist._nmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
(*vmi).second));
}
}
if (!mlist._tmorphs.empty()) {
comp_verts->_texcoord_morphs.push_back(ComputedVerticesMorphTexCoord());
ComputedVerticesMorphTexCoord &mv = comp_verts->_texcoord_morphs.back();
mv._slider_index = slider_index;
TexCoordMorphList::const_iterator vmi;
for (vmi = mlist._tmorphs.begin();
vmi != mlist._tmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue2((*vmi).first,
(*vmi).second));
}
}
if (!mlist._cmorphs.empty()) {
comp_verts->_color_morphs.push_back(ComputedVerticesMorphColor());
ComputedVerticesMorphColor &mv = comp_verts->_color_morphs.back();
mv._slider_index = slider_index;
ColorMorphList::const_iterator vmi;
for (vmi = mlist._cmorphs.begin();
vmi != mlist._cmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue4((*vmi).first,
(*vmi).second));
}
}
}
comp_verts->make_orig(character);
return comp_verts;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::make_computed_vertices
// Access: Public
// Description: After all spaces have been defined and all vertices
// added, creates a new ComputedVertices object and
// returns it.
////////////////////////////////////////////////////////////////////
ComputedVertices *ComputedVerticesMaker::
make_computed_vertices(qpCharacter *character, qpCharacterMaker &char_maker) {
// We must first build up a set of all the unique kinds of vertex
// transforms.
typedef pset<ComputedVertices::VertexTransform> VertexTransforms;
VertexTransforms transforms;
TransformSpaces::const_iterator tsi;
for (tsi = _transforms.begin();
tsi != _transforms.end();
++tsi) {
const JointWeights &jw = (*tsi).first;
const VertexCollection &vc = (*tsi).second;
JointWeights::const_iterator jwi;
for (jwi = jw.begin(); jwi != jw.end(); ++jwi) {
double weight = (*jwi).second;
EggNode *egg_joint = (*jwi).first;
int joint_index = char_maker.egg_to_index(egg_joint);
// Look for a VertexTransform that matches this template.
ComputedVertices::VertexTransform new_vt;
new_vt._joint_index = joint_index;
new_vt._effect = (float)weight;
// This will either insert the VertexTransform into the set and
// return its newly-created iterator, or it will return the
// iterator referring to the previously-inserted VertexTransform
// like this.
VertexTransforms::iterator vti = transforms.insert(new_vt).first;
// We can discard the const-ness of the set's iterator, because
// we will only be changing a part of the VertexTransform that
// doesn't affect its sort order within the set.
ComputedVertices::VertexTransform &insert_vt =
(ComputedVertices::VertexTransform &)*vti;
// Now add in all the vertices and normals.
copy(vc._vindex.begin(), vc._vindex.end(),
back_inserter(insert_vt._vindex));
copy(vc._nindex.begin(), vc._nindex.end(),
back_inserter(insert_vt._nindex));
}
}
// Ok, now we have the set of all VertexTransforms. Create a
// ComputedVertices object that reflects this.
ComputedVertices *comp_verts = new ComputedVertices;
copy(transforms.begin(), transforms.end(),
back_inserter(comp_verts->_transforms));
character->_cv._coords = _coords;
character->_cv._norms = _norms;
character->_cv._colors = _colors;
character->_cv._texcoords = _texcoords;
// Finally, add in all the morph definitions.
Morphs::const_iterator mi;
for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) {
const string &name = (*mi).first;
const MorphList &mlist = (*mi).second;
int slider_index = char_maker.create_slider(name);
if (!mlist._vmorphs.empty()) {
// We push an empty MorphVertex object and then modify it,
// rather than filling it first and then pushing it, just to
// avoid unnecessary copying of data.
comp_verts->_vertex_morphs.push_back(ComputedVerticesMorphVertex());
ComputedVerticesMorphVertex &mv = comp_verts->_vertex_morphs.back();
mv._slider_index = slider_index;
VertexMorphList::const_iterator vmi;
for (vmi = mlist._vmorphs.begin();
vmi != mlist._vmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
(*vmi).second));
}
}
if (!mlist._nmorphs.empty()) {
comp_verts->_normal_morphs.push_back(ComputedVerticesMorphNormal());
ComputedVerticesMorphNormal &mv = comp_verts->_normal_morphs.back();
mv._slider_index = slider_index;
NormalMorphList::const_iterator vmi;
for (vmi = mlist._nmorphs.begin();
vmi != mlist._nmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue3((*vmi).first,
(*vmi).second));
}
}
if (!mlist._tmorphs.empty()) {
comp_verts->_texcoord_morphs.push_back(ComputedVerticesMorphTexCoord());
ComputedVerticesMorphTexCoord &mv = comp_verts->_texcoord_morphs.back();
mv._slider_index = slider_index;
TexCoordMorphList::const_iterator vmi;
for (vmi = mlist._tmorphs.begin();
vmi != mlist._tmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue2((*vmi).first,
(*vmi).second));
}
}
if (!mlist._cmorphs.empty()) {
comp_verts->_color_morphs.push_back(ComputedVerticesMorphColor());
ComputedVerticesMorphColor &mv = comp_verts->_color_morphs.back();
mv._slider_index = slider_index;
ColorMorphList::const_iterator vmi;
for (vmi = mlist._cmorphs.begin();
vmi != mlist._cmorphs.end();
++vmi) {
mv._morphs.push_back(ComputedVerticesMorphValue4((*vmi).first,
(*vmi).second));
}
}
}
comp_verts->make_orig(character);
return comp_verts;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::
write(ostream &out) const {
out << "ComputedVerticesMaker, "
<< _transforms.size() << " transform spaces, "
<< _coords.size() << " vertices, "
<< _norms.size() << " normals, "
<< _texcoords.size() << " uvs, "
<< _colors.size() << " colors.\n";
TransformSpaces::const_iterator tsi;
for (tsi = _transforms.begin(); tsi != _transforms.end(); ++tsi) {
const JointWeights &jw = (*tsi).first;
const VertexCollection &vc = (*tsi).second;
out << " " << jw << " has "
<< vc._vindex.size() << " vertices and "
<< vc._nindex.size() << " normals\n";
}
Morphs::const_iterator mi;
for (mi = _morphs.begin(); mi != _morphs.end(); ++mi) {
const string &name = (*mi).first;
const MorphList &mlist = (*mi).second;
out << name << " morphs "
<< mlist._vmorphs.size() << " vertices, "
<< mlist._nmorphs.size() << " normals, "
<< mlist._tmorphs.size() << " uvs, and "
<< mlist._cmorphs.size() << " colors.\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::JointWeights::Ordering operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
bool ComputedVerticesMaker::JointWeights::
operator < (const JointWeights &other) const {
const_iterator i = begin();
const_iterator j = other.begin();
while (i != end() && j != other.end()) {
if ((*i).first != (*j).first) {
return (*i).first < (*j).first;
}
if ((*i).second != (*j).second) {
return (*i).second < (*j).second;
}
++i;
++j;
}
if (i == end() && j != other.end()) {
// The first i.size() items are equivalent, but list j is longer.
return true;
}
if (i != end() && j == other.end()) {
// The first j.size() items are equivalent, but list i is longer.
return false;
}
// The lists are equivalent.
return false;
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::JointWeights::normalize_weights
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::JointWeights::
normalize_weights() {
if (!empty()) {
double net_weight = 0.0;
iterator i;
for (i = begin(); i != end(); ++i) {
double weight = (*i).second;
assert(weight > 0.0);
net_weight += weight;
}
assert(net_weight != 0.0);
for (i = begin(); i != end(); ++i) {
(*i).second /= net_weight;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: ComputedVerticesMaker::JointWeights::output
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void ComputedVerticesMaker::JointWeights::
output(ostream &out) const {
out << "jw(";
if (!empty()) {
const_iterator i = begin();
out << (*i).first->get_name() << ":" << (*i).second;
for (++i; i != end(); ++i) {
out << " " << (*i).first->get_name() << ":" << (*i).second;
}
}
out << ")";
}