555 lines
16 KiB
C++
555 lines
16 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file characterJoint.cxx
|
|
* @author drose
|
|
* @date 1999-02-23
|
|
*/
|
|
|
|
#include "characterJoint.h"
|
|
#include "config_char.h"
|
|
#include "jointVertexTransform.h"
|
|
#include "characterJointEffect.h"
|
|
#include "datagram.h"
|
|
#include "datagramIterator.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
|
|
TypeHandle CharacterJoint::_type_handle;
|
|
|
|
/**
|
|
* For internal use only.
|
|
*/
|
|
CharacterJoint::
|
|
CharacterJoint() :
|
|
_character(NULL)
|
|
{
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
CharacterJoint::
|
|
CharacterJoint(const CharacterJoint ©) :
|
|
MovingPartMatrix(copy),
|
|
_character(NULL),
|
|
_net_transform(copy._net_transform),
|
|
_initial_net_transform_inverse(copy._initial_net_transform_inverse),
|
|
_skinning_matrix(copy._skinning_matrix)
|
|
{
|
|
// We don't copy the sets of transform nodes.
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
CharacterJoint::
|
|
CharacterJoint(Character *character,
|
|
PartBundle *root, PartGroup *parent, const string &name,
|
|
const LMatrix4 &default_value) :
|
|
MovingPartMatrix(parent, name, default_value),
|
|
_character(character)
|
|
{
|
|
Thread *current_thread = Thread::get_current_thread();
|
|
|
|
// Now that we've constructed and we're in the tree, let's call
|
|
// update_internals() to get our _net_transform set properly.
|
|
update_internals(root, parent, true, false, current_thread);
|
|
|
|
// And then compute its inverse. This is needed to track changes in
|
|
// _net_transform as the joint moves, so we can recompute _skinning_matrix,
|
|
// which maps vertices from their initial positions to their animated
|
|
// positions.
|
|
_initial_net_transform_inverse = invert(_net_transform);
|
|
_skinning_matrix = LMatrix4::ident_mat();
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
CharacterJoint::
|
|
~CharacterJoint() {
|
|
nassertv(_vertex_transforms.empty());
|
|
nassertv(_character == (Character *)NULL);
|
|
}
|
|
|
|
/**
|
|
* Returns true if this part is a CharacterJoint, false otherwise. This is a
|
|
* tiny optimization over is_of_type(CharacterType::get_class_type()).
|
|
*/
|
|
bool CharacterJoint::
|
|
is_character_joint() const {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Allocates and returns a new copy of the node. Children are not copied, but
|
|
* see copy_subgraph().
|
|
*/
|
|
PartGroup *CharacterJoint::
|
|
make_copy() const {
|
|
return new CharacterJoint(*this);
|
|
}
|
|
|
|
/**
|
|
* This is called by do_update() whenever the part or some ancestor has
|
|
* changed values. It is a hook for derived classes to update whatever cache
|
|
* they may have that depends on these.
|
|
*
|
|
* The return value is true if the part has changed as a result of the update,
|
|
* or false otherwise.
|
|
*
|
|
* In the case of a CharacterJoint, of course, it means to recompute the joint
|
|
* angles and associated transforms for this particular joint.
|
|
*/
|
|
bool CharacterJoint::
|
|
update_internals(PartBundle *root, PartGroup *parent, bool self_changed,
|
|
bool parent_changed, Thread *current_thread) {
|
|
nassertr(parent != (PartGroup *)NULL, false);
|
|
|
|
bool net_changed = false;
|
|
if (parent->is_character_joint()) {
|
|
// The joint is not a toplevel joint; its parent therefore affects its net
|
|
// transform.
|
|
if (parent_changed || self_changed) {
|
|
CharacterJoint *parent_joint = DCAST(CharacterJoint, parent);
|
|
|
|
_net_transform = _value * parent_joint->_net_transform;
|
|
net_changed = true;
|
|
}
|
|
|
|
} else {
|
|
// The joint is a toplevel joint, so therefore it gets its root transform
|
|
// from the bundle.
|
|
if (self_changed) {
|
|
_net_transform = _value * root->get_root_xform();
|
|
net_changed = true;
|
|
}
|
|
}
|
|
|
|
if (net_changed) {
|
|
if (!_net_transform_nodes.empty()) {
|
|
CPT(TransformState) t = TransformState::make_mat(_net_transform);
|
|
|
|
NodeList::iterator ai;
|
|
for (ai = _net_transform_nodes.begin();
|
|
ai != _net_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
node->set_transform(t, current_thread);
|
|
}
|
|
}
|
|
|
|
// Recompute the transform used by any vertices animated by this joint.
|
|
_skinning_matrix = _initial_net_transform_inverse * _net_transform;
|
|
|
|
// Also tell our related JointVertexTransforms that we've changed their
|
|
// underlying matrix.
|
|
VertexTransforms::iterator vti;
|
|
for (vti = _vertex_transforms.begin(); vti != _vertex_transforms.end(); ++vti) {
|
|
(*vti)->mark_modified(current_thread);
|
|
}
|
|
}
|
|
|
|
if (self_changed && !_local_transform_nodes.empty()) {
|
|
CPT(TransformState) t = TransformState::make_mat(_value);
|
|
|
|
NodeList::iterator ai;
|
|
for (ai = _local_transform_nodes.begin();
|
|
ai != _local_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
node->set_transform(t, current_thread);
|
|
}
|
|
}
|
|
|
|
return self_changed || net_changed;
|
|
}
|
|
|
|
/**
|
|
* Called by PartBundle::xform(), this indicates the indicated transform is
|
|
* being applied to the root joint.
|
|
*/
|
|
void CharacterJoint::
|
|
do_xform(const LMatrix4 &mat, const LMatrix4 &inv_mat) {
|
|
_initial_net_transform_inverse = inv_mat * _initial_net_transform_inverse;
|
|
|
|
MovingPartMatrix::do_xform(mat, inv_mat);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Adds the indicated node to the list of nodes that will be updated each
|
|
* frame with the joint's net transform from the root. Returns true if the
|
|
* node is successfully added, false if it had already been added.
|
|
*
|
|
* A CharacterJointEffect for this joint's Character will automatically be
|
|
* added to the specified node.
|
|
*/
|
|
bool CharacterJoint::
|
|
add_net_transform(PandaNode *node) {
|
|
if (_character != (Character *)NULL) {
|
|
node->set_effect(CharacterJointEffect::make(_character));
|
|
}
|
|
CPT(TransformState) t = TransformState::make_mat(_net_transform);
|
|
node->set_transform(t, Thread::get_current_thread());
|
|
return _net_transform_nodes.insert(node).second;
|
|
}
|
|
|
|
/**
|
|
* Removes the indicated node from the list of nodes that will be updated each
|
|
* frame with the joint's net transform from the root. Returns true if the
|
|
* node is successfully removed, false if it was not on the list.
|
|
*
|
|
* If the node has a CharacterJointEffect that matches this joint's Character,
|
|
* it will be cleared.
|
|
*/
|
|
bool CharacterJoint::
|
|
remove_net_transform(PandaNode *node) {
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
|
|
return (_net_transform_nodes.erase(node) > 0);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the node is on the list of nodes that will be updated each
|
|
* frame with the joint's net transform from the root, false otherwise.
|
|
*/
|
|
bool CharacterJoint::
|
|
has_net_transform(PandaNode *node) const {
|
|
return (_net_transform_nodes.count(node) > 0);
|
|
}
|
|
|
|
/**
|
|
* Removes all nodes from the list of nodes that will be updated each frame
|
|
* with the joint's net transform from the root.
|
|
*/
|
|
void CharacterJoint::
|
|
clear_net_transforms() {
|
|
NodeList::iterator ai;
|
|
for (ai = _net_transform_nodes.begin();
|
|
ai != _net_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
}
|
|
|
|
_net_transform_nodes.clear();
|
|
}
|
|
|
|
/**
|
|
* Returns a list of the net transforms set for this node. Note that this
|
|
* returns a list of NodePaths, even though the net transforms are actually a
|
|
* list of PandaNodes.
|
|
*/
|
|
NodePathCollection CharacterJoint::
|
|
get_net_transforms() {
|
|
NodePathCollection npc;
|
|
|
|
NodeList::iterator ai;
|
|
for (ai = _net_transform_nodes.begin();
|
|
ai != _net_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
npc.add_path(NodePath::any_path(node));
|
|
}
|
|
|
|
return npc;
|
|
}
|
|
|
|
/**
|
|
* Adds the indicated node to the list of nodes that will be updated each
|
|
* frame with the joint's local transform from its parent. Returns true if
|
|
* the node is successfully added, false if it had already been added.
|
|
*
|
|
* The Character pointer should be the Character object that owns this joint;
|
|
* this will be used to create a CharacterJointEffect for this node. If it is
|
|
* NULL, no such effect will be created.
|
|
*
|
|
* A CharacterJointEffect for this joint's Character will automatically be
|
|
* added to the specified node.
|
|
*/
|
|
bool CharacterJoint::
|
|
add_local_transform(PandaNode *node) {
|
|
if (_character != (Character *)NULL) {
|
|
node->set_effect(CharacterJointEffect::make(_character));
|
|
}
|
|
CPT(TransformState) t = TransformState::make_mat(_value);
|
|
node->set_transform(t, Thread::get_current_thread());
|
|
return _local_transform_nodes.insert(node).second;
|
|
}
|
|
|
|
/**
|
|
* Removes the indicated node from the list of nodes that will be updated each
|
|
* frame with the joint's local transform from its parent. Returns true if
|
|
* the node is successfully removed, false if it was not on the list.
|
|
*
|
|
* If the node has a CharacterJointEffect that matches this joint's Character,
|
|
* it will be cleared.
|
|
*/
|
|
bool CharacterJoint::
|
|
remove_local_transform(PandaNode *node) {
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
|
|
return (_local_transform_nodes.erase(node) > 0);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the node is on the list of nodes that will be updated each
|
|
* frame with the joint's local transform from its parent, false otherwise.
|
|
*/
|
|
bool CharacterJoint::
|
|
has_local_transform(PandaNode *node) const {
|
|
return (_local_transform_nodes.count(node) > 0);
|
|
}
|
|
|
|
/**
|
|
* Removes all nodes from the list of nodes that will be updated each frame
|
|
* with the joint's local transform from its parent.
|
|
*/
|
|
void CharacterJoint::
|
|
clear_local_transforms() {
|
|
NodeList::iterator ai;
|
|
for (ai = _local_transform_nodes.begin();
|
|
ai != _local_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
}
|
|
|
|
_local_transform_nodes.clear();
|
|
}
|
|
|
|
/**
|
|
* Returns a list of the local transforms set for this node. Note that this
|
|
* returns a list of NodePaths, even though the local transforms are actually
|
|
* a list of PandaNodes.
|
|
*/
|
|
NodePathCollection CharacterJoint::
|
|
get_local_transforms() {
|
|
NodePathCollection npc;
|
|
|
|
NodeList::iterator ai;
|
|
for (ai = _local_transform_nodes.begin();
|
|
ai != _local_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
npc.add_path(NodePath::any_path(node));
|
|
}
|
|
|
|
return npc;
|
|
}
|
|
|
|
/**
|
|
* Copies the joint's current transform into the indicated matrix.
|
|
*/
|
|
void CharacterJoint::
|
|
get_transform(LMatrix4 &transform) const {
|
|
transform = _value;
|
|
}
|
|
|
|
CPT(TransformState) CharacterJoint::
|
|
get_transform_state() const {
|
|
return TransformState::make_mat( _value );
|
|
}
|
|
|
|
/**
|
|
* Copies the joint's current net transform (composed from the root of the
|
|
* character joint hierarchy) into the indicated matrix.
|
|
*/
|
|
void CharacterJoint::
|
|
get_net_transform(LMatrix4 &transform) const {
|
|
transform = _net_transform;
|
|
}
|
|
|
|
/**
|
|
* Returns the Character that owns this joint.
|
|
*/
|
|
Character *CharacterJoint::
|
|
get_character() const {
|
|
return _character;
|
|
}
|
|
|
|
/**
|
|
* Changes the Character that owns this joint.
|
|
*/
|
|
void CharacterJoint::
|
|
set_character(Character *character) {
|
|
if (character != _character) {
|
|
|
|
if (character != (Character *)NULL) {
|
|
// Change or set a _character pointer on each joint's exposed node.
|
|
NodeList::iterator ai;
|
|
for (ai = _net_transform_nodes.begin();
|
|
ai != _net_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
node->set_effect(CharacterJointEffect::make(character));
|
|
}
|
|
for (ai = _local_transform_nodes.begin();
|
|
ai != _local_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
node->set_effect(CharacterJointEffect::make(character));
|
|
}
|
|
|
|
} else { // (character == (Character *)NULL)
|
|
// Clear the _character pointer on each joint's exposed node.
|
|
NodeList::iterator ai;
|
|
for (ai = _net_transform_nodes.begin();
|
|
ai != _net_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
}
|
|
for (ai = _local_transform_nodes.begin();
|
|
ai != _local_transform_nodes.end();
|
|
++ai) {
|
|
PandaNode *node = *ai;
|
|
|
|
CPT(RenderEffect) effect = node->get_effect(CharacterJointEffect::get_class_type());
|
|
if (effect != (RenderEffect *)NULL &&
|
|
DCAST(CharacterJointEffect, effect)->matches_character(_character)) {
|
|
node->clear_effect(CharacterJointEffect::get_class_type());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_character = character;
|
|
}
|
|
|
|
/**
|
|
* Function to write the important information in the particular object to a
|
|
* Datagram
|
|
*/
|
|
void CharacterJoint::
|
|
write_datagram(BamWriter *manager, Datagram &me) {
|
|
NodeList::iterator ni;
|
|
MovingPartMatrix::write_datagram(manager, me);
|
|
|
|
manager->write_pointer(me, _character);
|
|
|
|
me.add_uint16(_net_transform_nodes.size());
|
|
for (ni = _net_transform_nodes.begin();
|
|
ni != _net_transform_nodes.end();
|
|
ni++) {
|
|
manager->write_pointer(me, (*ni));
|
|
}
|
|
|
|
me.add_uint16(_local_transform_nodes.size());
|
|
for (ni = _local_transform_nodes.begin();
|
|
ni != _local_transform_nodes.end();
|
|
ni++) {
|
|
manager->write_pointer(me, (*ni));
|
|
}
|
|
|
|
_initial_net_transform_inverse.write_datagram(me);
|
|
}
|
|
|
|
/**
|
|
* Function that reads out of the datagram (or asks manager to read) all of
|
|
* the data that is needed to re-create this object and stores it in the
|
|
* appropiate place
|
|
*/
|
|
void CharacterJoint::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
int i;
|
|
MovingPartMatrix::fillin(scan, manager);
|
|
|
|
if (manager->get_file_minor_ver() >= 4) {
|
|
manager->read_pointer(scan);
|
|
}
|
|
|
|
_num_net_nodes = scan.get_uint16();
|
|
for(i = 0; i < _num_net_nodes; i++) {
|
|
manager->read_pointer(scan);
|
|
}
|
|
|
|
_num_local_nodes = scan.get_uint16();
|
|
for(i = 0; i < _num_local_nodes; i++) {
|
|
manager->read_pointer(scan);
|
|
}
|
|
|
|
_initial_net_transform_inverse.read_datagram(scan);
|
|
}
|
|
|
|
/**
|
|
* Takes in a vector of pointers to TypedWritable objects that correspond to
|
|
* all the requests for pointers that this object made to BamReader.
|
|
*/
|
|
int CharacterJoint::
|
|
complete_pointers(TypedWritable **p_list, BamReader* manager) {
|
|
int pi = MovingPartMatrix::complete_pointers(p_list, manager);
|
|
|
|
if (manager->get_file_minor_ver() >= 4) {
|
|
_character = DCAST(Character, p_list[pi++]);
|
|
} else {
|
|
_character = NULL;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < _num_net_nodes; i++) {
|
|
PandaNode *node = DCAST(PandaNode, p_list[pi++]);
|
|
_net_transform_nodes.insert(node);
|
|
}
|
|
|
|
for (i = 0; i < _num_local_nodes; i++) {
|
|
PandaNode *node = DCAST(PandaNode, p_list[pi++]);
|
|
_local_transform_nodes.insert(node);
|
|
}
|
|
|
|
return pi;
|
|
}
|
|
|
|
/**
|
|
* Factory method to generate a CharacterJoint object
|
|
*/
|
|
TypedWritable* CharacterJoint::
|
|
make_CharacterJoint(const FactoryParams ¶ms) {
|
|
CharacterJoint *me = new CharacterJoint;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
me->fillin(scan, manager);
|
|
return me;
|
|
}
|
|
|
|
/**
|
|
* Factory method to generate a CharacterJoint object
|
|
*/
|
|
void CharacterJoint::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_CharacterJoint);
|
|
}
|