diff --git a/panda/src/egg2pg/eggLoaderBase.h b/panda/src/egg2pg/eggLoaderBase.h new file mode 100644 index 0000000000..87577fb8ca --- /dev/null +++ b/panda/src/egg2pg/eggLoaderBase.h @@ -0,0 +1,56 @@ +// Filename: eggLoaderBase.h +// Created by: drose (06Mar02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGLOADERBASE_H +#define EGGLOADERBASE_H + +#include "pandabase.h" +#include "luse.h" + +class EggPrimitive; +class PandaNode; +class NamedNode; +class ComputedVerticesMaker; + +/////////////////////////////////////////////////////////////////// +// Class : EggLoaderBase +// Description : QP: A temporary hack around having to have two kinds +// of EggLoaders and one kind of CharacterMaker, this +// presents the interface to both kinds of EggLoaders. +//////////////////////////////////////////////////////////////////// +class EggLoaderBase { +public: + virtual ~EggLoaderBase() { } + + virtual void make_nonindexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform = NULL) { } + + virtual void make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker) { } + + virtual void make_nonindexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform = NULL) { } + + virtual void make_indexed_primitive(EggPrimitive *egg_prim, NamedNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker) { } +}; + +#endif + diff --git a/panda/src/egg2pg/qpcharacterMaker.cxx b/panda/src/egg2pg/qpcharacterMaker.cxx new file mode 100644 index 0000000000..be050c113d --- /dev/null +++ b/panda/src/egg2pg/qpcharacterMaker.cxx @@ -0,0 +1,390 @@ +// Filename: qpcharacterMaker.cxx +// Created by: drose (06Mar02) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "qpcharacterMaker.h" +#include "qpeggLoader.h" +#include "config_egg2pg.h" + +#include "computedVertices.h" +#include "eggGroup.h" +#include "eggPrimitive.h" +#include "partGroup.h" +#include "characterJoint.h" +#include "characterJointBundle.h" +#include "characterSlider.h" +#include "qpcharacter.h" +#include "transformState.h" + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::Construtor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpCharacterMaker:: +qpCharacterMaker(EggGroup *root, EggLoaderBase &loader) + : _loader(loader), _egg_root(root) { + + _character_node = new qpCharacter(_egg_root->get_name()); + _bundle = _character_node->get_bundle(); + + _morph_root = (PartGroup *)NULL; + _skeleton_root = new PartGroup(_bundle, ""); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::make_node +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpCharacter *qpCharacterMaker:: +make_node() { + make_bundle(); + _character_node->_parts = _parts; + return _character_node; +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::egg_to_part +// Access: Public +// Description: Returns the PartGroup node associated with the given +// egg node. If the egg node is not a node in the +// character's hierarchy, returns the top of the +// character's hierarchy. +//////////////////////////////////////////////////////////////////// +PartGroup *qpCharacterMaker:: +egg_to_part(EggNode *egg_node) const { + int index = egg_to_index(egg_node); + if (index < 0) { + // If there's a reference to the geometry outside of the + // character, just return the root of the character. + return _bundle; + } + nassertr(index < (int)_parts.size(), NULL); + return _parts[index]; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::egg_to_index +// Access: Public +// Description: Returns the index number associated with the +// PartGroup node for the given egg node, or -1. +//////////////////////////////////////////////////////////////////// +int qpCharacterMaker:: +egg_to_index(EggNode *egg_node) const { + NodeMap::const_iterator nmi = _node_map.find(egg_node); + if (nmi == _node_map.end()) { + return -1; + } + return (*nmi).second; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::part_to_node +// Access: Public +// Description: Returns the scene graph node associated with the +// given PartGroup node, if there is one. If the +// PartGroup does not have an associated node, returns +// the character's top node. +//////////////////////////////////////////////////////////////////// +PandaNode *qpCharacterMaker:: +part_to_node(PartGroup *part) const { + if (part->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *joint = DCAST(CharacterJoint, part); + if (joint->_qpgeom_node != (PandaNode *)NULL) { + return joint->_qpgeom_node; + } + } + + return _character_node; +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::create_slider +// Access: Public +// Description: Creates a new morph slider of the given name, and +// returns its index. This is actually called by +// ComputedVerticesMaker, which is responsible for +// identifying all the unique morph target names. +//////////////////////////////////////////////////////////////////// +int qpCharacterMaker:: +create_slider(const string &name) { + if (_morph_root == (PartGroup *)NULL) { + _morph_root = new PartGroup(_bundle, "morph"); + } + CharacterSlider *slider = new CharacterSlider(_morph_root, name); + int index = _parts.size(); + _parts.push_back(slider); + return index; +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::make_bundle +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +CharacterJointBundle *qpCharacterMaker:: +make_bundle() { + build_joint_hierarchy(_egg_root, _skeleton_root); + _bundle->sort_descendants(); + + parent_joint_nodes(_skeleton_root); + make_geometry(_egg_root); + + _character_node->_computed_vertices = + _comp_verts_maker.make_computed_vertices(_character_node, *this); + + return _bundle; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::build_hierarchy +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpCharacterMaker:: +build_joint_hierarchy(EggNode *egg_node, PartGroup *part) { + int index = -1; + + if (egg_node->is_of_type(EggGroup::get_class_type())) { + EggGroup *egg_group = DCAST(EggGroup, egg_node); + + // Each joint we come across is significant, and gets added to the + // hierarchy. Non-joints we encounter are ignored. + if (egg_group->get_group_type() == EggGroup::GT_joint) { + // We need to get the transform of the joint, and then convert + // it to single-precision. + LMatrix4d matd; + if (egg_group->has_transform()) { + matd = egg_group->get_transform(); + } else { + matd = LMatrix4d::ident_mat(); + } + + LMatrix4f matf = LCAST(float, matd); + + CharacterJoint *joint = + new CharacterJoint(part, egg_group->get_name(), matf); + index = _parts.size(); + _parts.push_back(joint); + + if (egg_group->get_dcs_flag()) { + // If the joint requested an explicit DCS, create a node for + // it. + joint->_qpgeom_node = new PandaNode(egg_group->get_name()); + } + + part = joint; + } + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + build_joint_hierarchy((*ci), part); + } + } + + _node_map[egg_node] = index; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::parent_joint_nodes +// Access: Private +// Description: Walks the joint hierarchy, and parents any explicit +// nodes created for the joints under the character +// node. +//////////////////////////////////////////////////////////////////// +void qpCharacterMaker:: +parent_joint_nodes(PartGroup *part) { + if (part->is_of_type(CharacterJoint::get_class_type())) { + CharacterJoint *joint = DCAST(CharacterJoint, part); + PandaNode *joint_node = joint->_qpgeom_node; + if (joint_node != NULL) { + _character_node->add_child(joint_node); + // joint->add_net_transform(joint_node); + joint_node->set_transform(TransformState::make_mat(joint->_net_transform)); + } + } + + for (int i = 0; i < part->get_num_children(); i++) { + parent_joint_nodes(part->get_child(i)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::make_geometry +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpCharacterMaker:: +make_geometry(EggNode *egg_node) { + if (egg_node->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *egg_primitive = DCAST(EggPrimitive, egg_node); + if (!egg_primitive->empty()) { + EggGroupNode *prim_home = determine_primitive_home(egg_primitive); + + if (prim_home == NULL) { + // This is a totally dynamic primitive that lives under the + // character's node. + make_dynamic_primitive(egg_primitive, _egg_root); + + } else { + // This is a static primitive that lives under a particular + // node. + make_static_primitive(egg_primitive, prim_home); + } + } + } + + if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_geometry(*ci); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::make_static_primitive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpCharacterMaker:: +make_static_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { + PandaNode *node = part_to_node(egg_to_part(prim_home)); + + // We need this funny transform to convert from the coordinate + // space of the original vertices to that of the new joint node. + LMatrix4d transform = + egg_primitive->get_vertex_frame() * + prim_home->get_node_frame_inv(); + + _loader.make_nonindexed_primitive(egg_primitive, node, &transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::make_dynamic_primitive +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpCharacterMaker:: +make_dynamic_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { + PandaNode *node = part_to_node(egg_to_part(prim_home)); + + LMatrix4d transform = + egg_primitive->get_vertex_frame() * + prim_home->get_node_frame_inv(); + + _loader.make_indexed_primitive(egg_primitive, node, &transform, + _comp_verts_maker); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpCharacterMaker::determine_primitive_home +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +EggGroupNode *qpCharacterMaker:: +determine_primitive_home(EggPrimitive *egg_primitive) { + // A primitive's vertices may be referenced by any joint in the + // character. Or, the primitive itself may be explicitly placed + // under a joint. + + // If any of the vertices are referenced by multiple joints, or if + // any two vertices are referenced by different joints, then the + // entire primitive must be considered dynamic. (We'll indicate a + // dynamic primitive by returning NULL.) + + // We need to keep track of the one joint we've encountered so far, + // to see if all the vertices are referenced by the same joint. + EggGroupNode *home = NULL; + + EggPrimitive::const_iterator vi; + for (vi = egg_primitive->begin(); + vi != egg_primitive->end(); + ++vi) { + EggVertex *vertex = (*vi); + if (vertex->gref_size() > 1) { + // This vertex is referenced by multiple joints; the primitive + // is dynamic. + return NULL; + } + + EggGroupNode *vertex_home; + + if (vertex->gref_size() == 0) { + // This vertex is not referenced at all, which means it belongs + // right where it is. + vertex_home = egg_primitive->get_parent(); + } else { + nassertr(vertex->gref_size() == 1, NULL); + // This vertex is referenced exactly once. + vertex_home = *vertex->gref_begin(); + } + + if (home != NULL && home != vertex_home) { + // Oops, two vertices are referenced by different joints! The + // primitive is dynamic. + return NULL; + } + + home = vertex_home; + } + + // This shouldn't be possible, unless there are no vertices--but we + // check for that before calling this function. + nassertr(home != NULL, NULL); + + // So, all the vertices are assigned to the same group. This means + // the polygon belongs entirely to one joint. + + // If the group is not, in fact, a joint then we return the first + // joint above the group. + EggGroup *egg_group = (EggGroup *)NULL; + if (home->is_of_type(EggGroup::get_class_type())) { + egg_group = DCAST(EggGroup, home); + } + while (egg_group != (EggGroup *)NULL && + egg_group->get_group_type() != EggGroup::GT_joint && + egg_group->get_dart_type() == EggGroup::DT_none) { + nassertr(egg_group->get_parent() != (EggGroupNode *)NULL, NULL); + home = egg_group->get_parent(); + egg_group = (EggGroup *)NULL; + if (home->is_of_type(EggGroup::get_class_type())) { + egg_group = DCAST(EggGroup, home); + } + } + + if (egg_group != (EggGroup *)NULL && + egg_group->get_group_type() == EggGroup::GT_joint && + !egg_group->get_dcs_flag()) { + // If the home is a joint without a flag--this is the normal + // case--we'll move the polygon under the character node and + // animate it from there explicitly. + return NULL; + } + + // Otherwise, if the joint *does* have a flag, we'll create + // static geometry that we parent directly to the joint node. + // We'll also create static geometry for polygons that have no + // explicit joint assignment. + return home; +} diff --git a/panda/src/egg2pg/qpeggLoader.cxx b/panda/src/egg2pg/qpeggLoader.cxx index 0719250e11..5a6a2672bb 100644 --- a/panda/src/egg2pg/qpeggLoader.cxx +++ b/panda/src/egg2pg/qpeggLoader.cxx @@ -20,7 +20,7 @@ #include "qpeggLoader.h" #include "config_egg2pg.h" -#include "nodeChain.h" +#include "qpnodePath.h" #include "renderState.h" #include "transformState.h" #include "textureAttrib.h" @@ -154,14 +154,14 @@ reparent_decals() { PandaNode *node = (*di); nassertv(node != (PandaNode *)NULL); - // The NodeChain interface is best for this. - NodeChain parent(node); + // The qpNodePath interface is best for this. + qpNodePath parent(node); // First, search for the GeomNode. - NodeChain geom_parent; + qpNodePath geom_parent; int num_children = parent.get_num_children(); for (int i = 0; i < num_children; i++) { - NodeChain child = parent.get_child(i); + qpNodePath child = parent.get_child(i); if (child.node()->is_of_type(qpGeomNode::get_class_type())) { if (!geom_parent.is_empty()) { @@ -187,7 +187,7 @@ reparent_decals() { // list. int i = 0; while (i < num_children) { - NodeChain child = parent.get_child(i); + qpNodePath child = parent.get_child(i); if (child.node()->is_of_type(qpGeomNode::get_class_type())) { i++;