support common transforming of shared PartBundles

This commit is contained in:
David Rose 2007-10-03 21:14:33 +00:00
parent 1b2a1e605e
commit 7c9997118c
14 changed files with 407 additions and 81 deletions

View File

@ -26,6 +26,7 @@
movingPartBase.I movingPartBase.h \
movingPartMatrix.I movingPartMatrix.h movingPartScalar.I \
movingPartScalar.h partBundle.I partBundle.N partBundle.h \
partBundleHandle.I partBundleHandle.h \
partBundleNode.I partBundleNode.h \
partGroup.I partGroup.h \
partSubset.I partSubset.h \
@ -45,6 +46,7 @@
animControlCollection.cxx animGroup.cxx auto_bind.cxx \
config_chan.cxx movingPartBase.cxx movingPartMatrix.cxx \
movingPartScalar.cxx partBundle.cxx \
partBundleHandle.cxx \
partBundleNode.cxx \
partGroup.cxx \
partSubset.cxx \
@ -66,6 +68,7 @@
movingPart.I movingPart.h movingPartBase.I \
movingPartBase.h movingPartMatrix.I movingPartMatrix.h \
movingPartScalar.I movingPartScalar.h partBundle.I partBundle.h \
partBundleHandle.I partBundleHandle.h \
partBundleNode.I partBundleNode.h \
partGroup.I partGroup.h \
partSubset.I partSubset.h \

View File

@ -124,6 +124,46 @@ set_anim_blend_flag(bool anim_blend_flag) {
}
}
////////////////////////////////////////////////////////////////////
// Function: PartBundle::apply_transform
// Access: Published
// Description: Returns a PartBundle that is a duplicate of this one,
// but with the indicated transform applied. If this is
// called multiple times with the same TransformState
// pointer, it returns the same PartBundle each time.
////////////////////////////////////////////////////////////////////
PT(PartBundle) PartBundle::
apply_transform(const TransformState *transform) {
if (transform->is_identity()) {
// Trivial no-op.
return this;
}
AppliedTransforms::iterator ati = _applied_transforms.find(transform);
if (ati != _applied_transforms.end()) {
if ((*ati).first.is_valid_pointer() &&
(*ati).second.is_valid_pointer()) {
// Here's our cached result.
return (*ati).second.p();
}
}
PT(PartBundle) new_bundle = DCAST(PartBundle, copy_subgraph());
new_bundle->xform(transform->get_mat());
if (ati != _applied_transforms.end()) {
// A stale pointer to a deleted result. Update it.
(*ati).first.refresh();
(*ati).second = new_bundle;
} else {
// No such result yet. Store it.
bool inserted = _applied_transforms.insert(AppliedTransforms::value_type(transform, new_bundle)).second;
nassertr(inserted, new_bundle);
}
return new_bundle;
}
////////////////////////////////////////////////////////////////////
// Function: PartBundle::clear_control_effects
// Access: Published

View File

@ -32,6 +32,8 @@
#include "cycleDataWriter.h"
#include "luse.h"
#include "pvector.h"
#include "transformState.h"
#include "weakPointerTo.h"
class AnimBundle;
class PartBundleNode;
@ -106,6 +108,7 @@ PUBLISHED:
INLINE void set_root_xform(const LMatrix4f &root_xform);
INLINE void xform(const LMatrix4f &mat);
INLINE const LMatrix4f &get_root_xform() const;
PT(PartBundle) apply_transform(const TransformState *transform);
INLINE int get_num_nodes() const;
INLINE PartBundleNode *get_node(int n) const;
@ -149,6 +152,9 @@ private:
typedef pvector<PartBundleNode *> Nodes;
Nodes _nodes;
typedef pmap<WCPT(TransformState), WPT(PartBundle) > AppliedTransforms;
AppliedTransforms _applied_transforms;
// This is the data that must be cycled between pipeline stages.
class CData : public CycleData {
public:

View File

@ -0,0 +1,60 @@
// Filename: partBundleHandle.I
// Created by: drose (01Oct07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: PartBundleHandle::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE PartBundleHandle::
PartBundleHandle(PartBundle *bundle) :
_bundle(bundle)
{
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleHandle::Destructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE PartBundleHandle::
~PartBundleHandle() {
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleHandle::get_bundle
// Access: Published
// Description: Returns the actual PartBundle embedded within the
// handle.
////////////////////////////////////////////////////////////////////
INLINE PartBundle *PartBundleHandle::
get_bundle() {
return _bundle;
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleHandle::set_bundle
// Access: Published
// Description: Changes the actual PartBundle embedded within the
// handle.
////////////////////////////////////////////////////////////////////
INLINE void PartBundleHandle::
set_bundle(PartBundle *bundle) {
_bundle = bundle;
}

View File

@ -0,0 +1,19 @@
// Filename: partBundleHandle.cxx
// Created by: drose (01Oct07)
//
////////////////////////////////////////////////////////////////////
//
// 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 "partBundleHandle.h"

View File

@ -0,0 +1,57 @@
// Filename: partBundleHandle.h
// Created by: drose (01Oct07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
#ifndef PARTBUNDLEHANDLE_H
#define PARTBUNDLEHANDLE_H
#include "pandabase.h"
#include "partBundle.h"
#include "pointerTo.h"
////////////////////////////////////////////////////////////////////
// Class : PartBundleHandle
// Description : This is a trivial class returned by
// PartBundleNode::get_bundle(). Its purpose is to hold
// the actual PartBundle pointer contained within the
// PartBundleNode, so that scene graph flatten
// operations can safely combine or duplicate
// PartBundles as necessary without affecting high-level
// bundle operations.
//
// The high-level Actor class defined in
// direct/src/actor, for instance, will store a list of
// PartBundleHandles instead of on actual PartBundles,
// so that it will be immune to changes from these
// flatten operations.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_CHAN PartBundleHandle : public ReferenceCount {
PUBLISHED:
INLINE PartBundleHandle(PartBundle *bundle);
INLINE ~PartBundleHandle();
INLINE PartBundle *get_bundle();
INLINE void set_bundle(PartBundle *bundle);
private:
PT(PartBundle) _bundle;
};
#include "partBundleHandle.I"
#endif

View File

@ -73,6 +73,20 @@ get_num_bundles() const {
////////////////////////////////////////////////////////////////////
INLINE PartBundle *PartBundleNode::
get_bundle(int n) const {
nassertr(n >= 0 && n < (int)_bundles.size(), NULL);
return _bundles[n]->get_bundle();
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleNode::get_bundle_handle
// Access: Public
// Description: Returns the PartBundleHandle that wraps around the
// actual nth PartBundle. While the PartBundle pointer
// might later change due to a future flatten operation,
// the PartBundleHandle will not.
////////////////////////////////////////////////////////////////////
INLINE PartBundleHandle *PartBundleNode::
get_bundle_handle(int n) const {
nassertr(n >= 0 && n < (int)_bundles.size(), NULL);
return _bundles[n];
}

View File

@ -21,6 +21,7 @@
#include "datagramIterator.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "sceneGraphReducer.h"
TypeHandle PartBundleNode::_type_handle;
@ -33,29 +34,34 @@ PartBundleNode::
~PartBundleNode() {
Bundles::iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
(*bi)->remove_node(this);
(*bi)->get_bundle()->remove_node(this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleNode::safe_to_transform
// Function: PartBundleNode::apply_attribs_to_vertices
// Access: Public, Virtual
// Description: Returns true if it is generally safe to transform
// this particular kind of PandaNode by calling the
// xform() method, false otherwise.
// Description: Applies whatever attributes are specified in the
// AccumulatedAttribs object (and by the attrib_types
// bitmask) to the vertices on this node, if
// appropriate. If this node uses geom arrays like a
// GeomNode, the supplied GeomTransformer may be used to
// unify shared arrays across multiple different nodes.
//
// This is a generalization of xform().
////////////////////////////////////////////////////////////////////
bool PartBundleNode::
safe_to_transform() const {
// If any of our bundles appear on multiple nodes, we can't
// transform any of them without transforming all of them at once.
Bundles::const_iterator bi;
void PartBundleNode::
apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
GeomTransformer &transformer) {
if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
Bundles::iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
if ((*bi)->get_num_nodes() > 1) {
return false;
PT(PartBundleHandle) handle = (*bi);
PartBundle *bundle = handle->get_bundle();
PT(PartBundle) new_bundle = bundle->apply_transform(attribs._transform);
update_bundle(handle, new_bundle);
}
}
return true;
}
////////////////////////////////////////////////////////////////////
@ -67,6 +73,10 @@ safe_to_transform() const {
////////////////////////////////////////////////////////////////////
void PartBundleNode::
xform(const LMatrix4f &mat) {
// With plain xform(), we can't attempt to share bundles across
// different nodes. Better to use apply_attribs_to_vertices(),
// instead.
if (mat.almost_equal(LMatrix4f::ident_mat())) {
// Don't bother.
return;
@ -74,7 +84,14 @@ xform(const LMatrix4f &mat) {
Bundles::iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
(*bi)->xform(mat);
PT(PartBundleHandle) handle = (*bi);
PartBundle *bundle = handle->get_bundle();
if (bundle->get_num_nodes() > 1) {
PT(PartBundle) new_bundle = DCAST(PartBundle, bundle->copy_subgraph());
update_bundle(handle, new_bundle);
bundle = new_bundle;
}
bundle->xform(mat);
}
}
@ -85,8 +102,25 @@ xform(const LMatrix4f &mat) {
////////////////////////////////////////////////////////////////////
void PartBundleNode::
add_bundle(PartBundle *bundle) {
_bundles.push_back(bundle);
bundle->add_node(this);
PT(PartBundleHandle) handle = new PartBundleHandle(bundle);
add_bundle_handle(handle);
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleNode::add_bundle_handle
// Access: Protected
// Description:
////////////////////////////////////////////////////////////////////
void PartBundleNode::
add_bundle_handle(PartBundleHandle *handle) {
Bundles::iterator bi = find(_bundles.begin(), _bundles.end(), handle);
if (bi != _bundles.end()) {
// This handle is already within the node.
return;
}
_bundles.push_back(handle);
handle->get_bundle()->add_node(this);
}
////////////////////////////////////////////////////////////////////
@ -97,16 +131,34 @@ add_bundle(PartBundle *bundle) {
////////////////////////////////////////////////////////////////////
void PartBundleNode::
steal_bundles(PartBundleNode *other) {
if (other == this) {
return;
}
Bundles::iterator bi;
for (bi = other->_bundles.begin(); bi != other->_bundles.end(); ++bi) {
PartBundle *bundle = (*bi);
_bundles.push_back(bundle);
bundle->remove_node(other);
bundle->add_node(this);
PartBundleHandle *handle = (*bi);
handle->get_bundle()->remove_node(other);
add_bundle_handle(handle);
}
other->_bundles.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleNode::update_bundle
// Access: Protected, Virtual
// Description: Replaces the contents of the indicated
// PartBundleHandle (presumably stored within this node)
// with new_bundle.
////////////////////////////////////////////////////////////////////
void PartBundleNode::
update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
PartBundle *old_bundle = old_bundle_handle->get_bundle();
old_bundle->remove_node(this);
old_bundle_handle->set_bundle(new_bundle);
new_bundle->add_node(this);
}
////////////////////////////////////////////////////////////////////
// Function: PartBundleNode::write_datagram
// Access: Public, Virtual
@ -120,7 +172,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
dg.add_uint16(_bundles.size());
Bundles::iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
manager->write_pointer(dg, (*bi));
manager->write_pointer(dg, (*bi)->get_bundle());
}
}
@ -137,8 +189,9 @@ complete_pointers(TypedWritable **p_list, BamReader* manager) {
Bundles::iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
(*bi) = DCAST(PartBundle, p_list[pi++]);
(*bi)->add_node(this);
PT(PartBundle) bundle = DCAST(PartBundle, p_list[pi++]);
bundle->add_node(this);
(*bi) = new PartBundleHandle(bundle);
}
return pi;

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "partBundle.h"
#include "partBundleHandle.h"
#include "pandaNode.h"
#include "dcast.h"
@ -43,19 +44,25 @@ protected:
public:
virtual ~PartBundleNode();
virtual bool safe_to_transform() const;
virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs,
int attrib_types,
GeomTransformer &transformer);
virtual void xform(const LMatrix4f &mat);
PUBLISHED:
INLINE int get_num_bundles() const;
INLINE PartBundle *get_bundle(int n) const;
INLINE PartBundleHandle *get_bundle_handle(int n) const;
protected:
void add_bundle(PartBundle *bundle);
void add_bundle_handle(PartBundleHandle *handle);
void steal_bundles(PartBundleNode *other);
virtual void update_bundle(PartBundleHandle *old_bundle_handle,
PartBundle *new_bundle);
protected:
typedef pvector< PT(PartBundle) > Bundles;
typedef pvector< PT(PartBundleHandle) > Bundles;
Bundles _bundles;
public:

View File

@ -230,6 +230,43 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
// pointer, and all geometry within this node will be
// updated to reference new_bundle.
//
// This method is deprecated. Use the newer version of
// this method, below.
////////////////////////////////////////////////////////////////////
void Character::
merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) {
if (old_bundle == new_bundle) {
// Trivially return.
return;
}
// Find the PartBundleHandle of old_bundle.
PT(PartBundleHandle) old_bundle_handle;
Bundles::const_iterator bi;
for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
if ((*bi)->get_bundle() == old_bundle) {
old_bundle_handle = (*bi);
break;
}
}
nassertv(!old_bundle_handle.is_null());
PT(PartBundleHandle) new_bundle_handle = new PartBundleHandle(new_bundle);
merge_bundles(old_bundle_handle, new_bundle_handle);
}
////////////////////////////////////////////////////////////////////
// Function: Character::merge_bundles
// Access: Published
// Description: Merges old_bundle_handle->get_bundle() with
// new_bundle. old_bundle_handle must be one of the
// PartBundleHandle within this node. At the end of
// this call, the bundle pointer within the
// old_bundle_handle will be replaced with that within
// the new_bundle_handle pointer, and all geometry
// within this node will be updated to reference
// new_bundle.
//
// Normally, this is called when the two bundles have
// the same, or nearly the same, hierarchies. In this
// case, new_bundle will simply be assigned over the
@ -246,34 +283,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
// LOD's of the same model.
////////////////////////////////////////////////////////////////////
void Character::
merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) {
// Find the index number of old_bundle.
size_t index = 0;
while (index < _bundles.size()) {
if (_bundles[index] == old_bundle) {
break;
}
++index;
}
nassertv_always(index < _bundles.size());
if (old_bundle == new_bundle) {
// Trivially return.
return;
}
// First, merge the bundles themselves.
JointMap joint_map;
r_merge_bundles(joint_map, old_bundle, new_bundle);
old_bundle->remove_node(this);
_bundles[index] = new_bundle;
new_bundle->add_node(this);
// Now convert the geometry to use the new bundle.
GeomVertexMap gvmap;
GeomJointMap gjmap;
GeomSliderMap gsmap;
r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
merge_bundles(PartBundleHandle *old_bundle_handle,
PartBundleHandle *new_bundle_handle) {
update_bundle(old_bundle_handle, new_bundle_handle->get_bundle());
}
////////////////////////////////////////////////////////////////////
@ -405,28 +417,6 @@ force_update() {
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::do_update
// Access: Private
// Description: The actual implementation of update(). Assumes the
// appropriate PStatCollector has already been started.
////////////////////////////////////////////////////////////////////
void Character::
do_update() {
// Update all the joints and sliders.
if (even_animation) {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
get_bundle(i)->force_update();
}
} else {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
get_bundle(i)->update();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::r_copy_children
// Access: Protected, Virtual
@ -475,6 +465,56 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::update_bundle
// Access: Protected, Virtual
// Description: Replaces the contents of the indicated
// PartBundleHandle (presumably stored within this node)
// with new_bundle.
////////////////////////////////////////////////////////////////////
void Character::
update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
if (old_bundle_handle->get_bundle() == new_bundle) {
// Trivially return.
return;
}
// First, merge the bundles, to ensure we have the same set of
// joints in the new bundle.
JointMap joint_map;
r_merge_bundles(joint_map, old_bundle_handle->get_bundle(), new_bundle);
PartBundleNode::update_bundle(old_bundle_handle, new_bundle);
// Now convert the geometry to use the new bundle.
GeomVertexMap gvmap;
GeomJointMap gjmap;
GeomSliderMap gsmap;
r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
}
////////////////////////////////////////////////////////////////////
// Function: Character::do_update
// Access: Private
// Description: The actual implementation of update(). Assumes the
// appropriate PStatCollector has already been started.
////////////////////////////////////////////////////////////////////
void Character::
do_update() {
// Update all the joints and sliders.
if (even_animation) {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
get_bundle(i)->force_update();
}
} else {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
get_bundle(i)->update();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: Character::fill_joint_map
// Access: Private

View File

@ -65,6 +65,8 @@ public:
PUBLISHED:
INLINE CharacterJointBundle *get_bundle(int i) const;
void merge_bundles(PartBundle *old_bundle, PartBundle *other_bundle);
void merge_bundles(PartBundleHandle *old_bundle_handle,
PartBundleHandle *other_bundle_handle);
CharacterJoint *find_joint(const string &name) const;
CharacterSlider *find_slider(const string &name) const;
@ -76,6 +78,12 @@ PUBLISHED:
void update();
void force_update();
protected:
virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map,
Thread *current_thread);
virtual void update_bundle(PartBundleHandle *old_bundle_handle,
PartBundle *new_bundle);
private:
void do_update();
@ -85,8 +93,6 @@ private:
typedef pmap<const VertexTransform *, PT(JointVertexTransform) > GeomJointMap;
typedef pmap<const VertexSlider *, PT(CharacterVertexSlider) > GeomSliderMap;
virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map,
Thread *current_thread);
void fill_joint_map(JointMap &joint_map, PartGroup *copy, PartGroup *orig);
void r_merge_bundles(Character::JointMap &joint_map,
PartGroup *old_group, PartGroup *new_group);

View File

@ -399,6 +399,24 @@ clear() {
reassign((To *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: WeakPointerToBase::refresh
// Access: Published
// Description: Informs the WeakPointerTo object that its pointer is
// no longer deleted. This may be used after a
// WeakPointerTo has deleted a deleted pointer, and then
// a new pointer has been reallocated. It's equivalent
// to simply reassigning the pointer to its new
// (i.e. original) value, but has the advantage that it
// is const, so can be used for WeakPointers used as
// keys in STL maps and sets.
////////////////////////////////////////////////////////////////////
template<class T>
INLINE void WeakPointerToBase<T>::
refresh() const {
((WeakPointerToBase<T> *)this)->reassign((To *)_void_ptr);
}
////////////////////////////////////////////////////////////////////
// Function: WeakPointerToBase::output
// Access: Published

View File

@ -83,6 +83,7 @@ public:
PUBLISHED:
INLINE void clear();
INLINE void refresh() const;
void output(ostream &out) const;
};

View File

@ -116,7 +116,9 @@ flatten(PandaNode *root, int combine_siblings_bits) {
num_pass_nodes += r_flatten(root, child_node, combine_siblings_bits);
}
if (combine_siblings_bits != 0 && root->get_num_children() >= 2) {
if (combine_siblings_bits != 0 &&
root->get_num_children() >= 2 &&
root->safe_to_combine_children()) {
num_pass_nodes += flatten_siblings(root, combine_siblings_bits);
}
@ -397,7 +399,7 @@ r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
// Finally, if any of our remaining children are plain PandaNodes
// with no children, just remove them.
{
if (parent_node->safe_to_combine_children()) {
for (int i = parent_node->get_num_children() - 1; i >= 0; --i) {
PandaNode *child_node = parent_node->get_child(i);
if (child_node->is_exact_type(PandaNode::get_class_type()) &&