// Filename: rigidBodyCombiner.cxx // Created by: drose (22Feb07) // //////////////////////////////////////////////////////////////////// // // 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 "rigidBodyCombiner.h" #include "nodePath.h" #include "geomNode.h" #include "modelNode.h" #include "geomVertexData.h" #include "geomVertexFormat.h" #include "geomVertexArrayFormat.h" #include "geomVertexAnimationSpec.h" #include "sceneGraphReducer.h" TypeHandle RigidBodyCombiner::_type_handle; //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::Constructor // Access: Published // Description: //////////////////////////////////////////////////////////////////// RigidBodyCombiner:: RigidBodyCombiner(const string &name) : PandaNode(name) { set_cull_callback(); _internal_root = new PandaNode(name); } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::Copy Constructor // Access: Published // Description: //////////////////////////////////////////////////////////////////// RigidBodyCombiner:: RigidBodyCombiner(const RigidBodyCombiner ©) : PandaNode(copy) { set_cull_callback(); _internal_root = copy._internal_root; _internal_transforms = copy._internal_transforms; } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::make_copy // Access: Public, Virtual // Description: Returns a newly-allocated PandaNode that is a shallow // copy of this one. It will be a different pointer, // but its internal data may or may not be shared with // that of the original PandaNode. No children will be // copied. //////////////////////////////////////////////////////////////////// PandaNode *RigidBodyCombiner:: make_copy() const { return new RigidBodyCombiner(*this); } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::collect // Access: Published // Description: Walks through the entire subgraph of nodes rooted at // this node, accumulates all of the RenderAttribs and // Geoms below this node, flattening them into just one // Geom (or as few as possible, if there are multiple // different states). // // Nodes that have transforms on them at the time of // collect(), or any ModelNodes with the // preserve_transform flag, will be identified as // "moving" nodes, and their transforms will be // monitored as they change in future frames and each // new transform directly applied to the vertices. // // This call must be made after adding any nodes to or // removing any nodes from the subgraph rooted at this // node. It should not be made too often, as it is a // relatively expensive call. If you need to hide // children of this node, consider scaling them to zero // (or very near zero), or moving them behind the // camera, instead. //////////////////////////////////////////////////////////////////// void RigidBodyCombiner:: collect() { _internal_root = new GeomNode(get_name()); _internal_transforms.clear(); _vd_table.clear(); Children cr = get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { r_collect(cr.get_child(i), RenderState::make_empty(), NULL); } _vd_table.clear(); SceneGraphReducer gr; gr.apply_attribs(_internal_root); gr.collect_vertex_data(_internal_root, ~(SceneGraphReducer::CVD_format | SceneGraphReducer::CVD_name | SceneGraphReducer::CVD_animation_type)); gr.unify(_internal_root, false); } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::get_internal_scene // Access: Published // Description: Returns a special NodePath that represents the // internal node of this object. This is the node that // is actually sent to the graphics card for rendering; // it contains the collection of the children of this // node into as few Geoms as possible. // // This node is filled up by the last call to collect(). //////////////////////////////////////////////////////////////////// NodePath RigidBodyCombiner:: get_internal_scene() { return NodePath(_internal_root); } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::cull_callback // Access: Protected, Virtual // Description: This function will be called during the cull // traversal to perform any additional operations that // should be performed at cull time. This may include // additional manipulation of render state or additional // visible/invisible decisions, or any other arbitrary // operation. // // Note that this function will *not* be called unless // set_cull_callback() is called in the constructor of // the derived class. It is necessary to call // set_cull_callback() to indicated that we require // cull_callback() to be called. // // By the time this function is called, the node has // already passed the bounding-volume test for the // viewing frustum, and the node's transform and state // have already been applied to the indicated // CullTraverserData object. // // The return value is true if this node should be // visible, or false if it should be culled. //////////////////////////////////////////////////////////////////// bool RigidBodyCombiner:: cull_callback(CullTraverser *trav, CullTraverserData &data) { // Pretend that all of our transforms have been modified (since we // don't really know which ones have). Thread *current_thread = Thread::get_current_thread(); Transforms::iterator ti; for (ti = _internal_transforms.begin(); ti != _internal_transforms.end(); ++ti) { (*ti)->mark_modified(current_thread); } // Render the internal scene only--this is the optimized scene. CullTraverserData next_data(data, _internal_root); trav->traverse(next_data); // Do not directly render the nodes beneath this node. return false; } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::r_collect // Access: Private // Description: Recursively visits each child or descedant of this // node, accumulating state and transform as we go. // When GeomNodes are encountered, their Geoms are // extracted and added to the _internal_root node. //////////////////////////////////////////////////////////////////// void RigidBodyCombiner:: r_collect(PandaNode *node, const RenderState *state, const VertexTransform *transform) { CPT(RenderState) next_state = state->compose(node->get_state()); CPT(VertexTransform) next_transform = transform; if (!node->get_transform()->is_identity() || node->is_of_type(ModelNode::get_class_type()) && DCAST(ModelNode, node)->get_preserve_transform() != ModelNode::PT_none) { // This node has a transform we need to keep. PT(NodeVertexTransform) new_transform = new NodeVertexTransform(node, transform); _internal_transforms.push_back(new_transform); next_transform = new_transform.p(); } if (node->is_geom_node()) { GeomNode *gnode = DCAST(GeomNode, node); GeomNode *root_gnode = DCAST(GeomNode, _internal_root); int num_geoms = gnode->get_num_geoms(); for (int i = 0; i < num_geoms; ++i) { PT(Geom) geom = gnode->get_geom(i)->make_copy(); if (transform != (const VertexTransform *)NULL) { geom->set_vertex_data(convert_vd(transform, geom->get_vertex_data())); } CPT(RenderState) gstate = next_state->compose(gnode->get_geom_state(i)); root_gnode->add_geom(geom, gstate); } } Children cr = node->get_children(); int num_children = cr.get_num_children(); for (int i = 0; i < num_children; i++) { r_collect(cr.get_child(i), next_state, next_transform); } } //////////////////////////////////////////////////////////////////// // Function: RigidBodyCombiner::convert_vd // Access: Private // Description: Converts a GeomVertexData to a new form in which all // of the vertices are transformed by the node's // transform. //////////////////////////////////////////////////////////////////// PT(GeomVertexData) RigidBodyCombiner:: convert_vd(const VertexTransform *transform, const GeomVertexData *orig) { // First, unify this operation for unique transform/data // combinations. If we encounter a given GeomVertexData more than // once under the same transform, we should return exactly the same // GeomVertexData. VDTable::iterator vdti = _vd_table.find(VDUnifier(transform, orig)); if (vdti != _vd_table.end()) { return (*vdti).second; } PT(GeomVertexFormat) format = new GeomVertexFormat(*orig->get_format()); if (!orig->get_format()->has_column(InternalName::get_transform_blend())) { PT(GeomVertexArrayFormat) af = new GeomVertexArrayFormat(InternalName::get_transform_blend(), 1, Geom::NT_uint16, Geom::C_index); format->add_array(af); } GeomVertexAnimationSpec spec; spec.set_panda(); format->set_animation(spec); CPT(GeomVertexFormat) new_format = GeomVertexFormat::register_format(format); CPT(GeomVertexData) converted = orig->convert_to(new_format); PT(GeomVertexData) new_data = new GeomVertexData(*converted); if (new_data->get_transform_blend_table() == (TransformBlendTable *)NULL) { // Create a new table that has just the one blend: all vertices // hard-assigned to the indicated transform. PT(TransformBlendTable) new_table = new TransformBlendTable; new_table->add_blend(TransformBlend(transform, 1.0f)); new_table->set_rows(SparseArray::range(0, new_data->get_num_rows())); new_data->set_transform_blend_table(new_table); } else { // The GeomVertexData already has a TransformBlendTable. In this // case, we'll have to adjust it. TODO. } // Store the result for the next time. _vd_table[VDUnifier(transform, orig)] = new_data; return new_data; }