// 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 //////////////////////////////////////////////////////////////////// // 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 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 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 << ")"; }