|
|
@@ -0,0 +1,258 @@
|
|
|
+// 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
|
|
|
+// [email protected] .
|
|
|
+//
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+#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();
|
|
|
+
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// 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) {
|
|
|
+ // TODO: unify this operation for unique transform/data combinations.
|
|
|
+
|
|
|
+ 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.
|
|
|
+ }
|
|
|
+
|
|
|
+ return new_data;
|
|
|
+}
|