panda3d/panda/src/gobj/transformBlend.cxx
2005-04-08 23:27:55 +00:00

314 lines
10 KiB
C++

// Filename: transformBlend.cxx
// Created by: drose (24Mar05)
//
////////////////////////////////////////////////////////////////////
//
// 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 "transformBlend.h"
#include "indent.h"
#include "bamReader.h"
#include "bamWriter.h"
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::compare_to
// Access: Published
// Description: Defines an arbitrary ordering for TransformBlend
// objects.
////////////////////////////////////////////////////////////////////
int TransformBlend::
compare_to(const TransformBlend &other) const {
if (_entries.size() != other._entries.size()) {
return (int)_entries.size() - (int)other._entries.size();
}
Entries::const_iterator ai, bi;
ai = _entries.begin();
bi = other._entries.begin();
while (ai != _entries.end() && bi != other._entries.end()) {
if ((*ai)._transform != (*bi)._transform) {
return (*ai)._transform < (*bi)._transform ? -1 : 1;
}
if (!IS_NEARLY_EQUAL((*ai)._weight, (*bi)._weight)) {
return (*ai)._weight < (*bi)._weight ? -1 : 1;
}
++ai;
++bi;
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::add_transform
// Access: Published
// Description: Adds a new transform to the blend. If the transform
// already existed, increases its weight factor.
////////////////////////////////////////////////////////////////////
void TransformBlend::
add_transform(const VertexTransform *transform, float weight) {
if (!IS_NEARLY_ZERO(weight)) {
TransformEntry entry;
entry._transform = transform;
entry._weight = weight;
pair<Entries::iterator, bool> result = _entries.insert(entry);
if (!result.second) {
// If the new value was not inserted, it was already there;
// increment the existing weight factor.
Entries::iterator ei = result.first;
(*ei)._weight += weight;
if (IS_NEARLY_ZERO((*ei)._weight)) {
// If we have just zeroed out the weight, remove it.
_entries.erase(ei);
}
}
clear_result();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::remove_transform
// Access: Published
// Description: Removes the indicated transform to the blend.
////////////////////////////////////////////////////////////////////
void TransformBlend::
remove_transform(const VertexTransform *transform) {
TransformEntry entry;
entry._transform = transform;
entry._weight = 0.0f;
Entries::iterator ei = _entries.find(entry);
if (ei != _entries.end()) {
_entries.erase(ei);
}
clear_result();
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::normalize_weights
// Access: Published
// Description: Rescales all of the weights on the various transforms
// so that they sum to 1.0. It is generally a good idea
// to call this after adding or removing transforms from
// the blend.
////////////////////////////////////////////////////////////////////
void TransformBlend::
normalize_weights() {
float net_weight = 0.0f;
Entries::iterator ei;
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
net_weight += (*ei)._weight;
}
if (net_weight != 0.0f) {
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
(*ei)._weight /= net_weight;
}
}
clear_result();
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::has_transform
// Access: Published
// Description: Returns true if the blend has the indicated
// transform, false otherwise.
////////////////////////////////////////////////////////////////////
bool TransformBlend::
has_transform(const VertexTransform *transform) const {
TransformEntry entry;
entry._transform = transform;
entry._weight = 0.0f;
Entries::const_iterator ei = _entries.find(entry);
return (ei != _entries.end());
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::get_weight
// Access: Published
// Description: Returns the weight associated with the indicated
// transform, or 0 if there is no entry for the
// transform.
////////////////////////////////////////////////////////////////////
float TransformBlend::
get_weight(const VertexTransform *transform) const {
TransformEntry entry;
entry._transform = transform;
entry._weight = 0.0f;
Entries::const_iterator ei = _entries.find(entry);
if (ei != _entries.end()) {
return (*ei)._weight;
}
return 0.0f;
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void TransformBlend::
output(ostream &out) const {
if (_entries.empty()) {
out << "empty";
} else {
Entries::const_iterator ei = _entries.begin();
out << *(*ei)._transform << ":" << (*ei)._weight;
++ei;
while (ei != _entries.end()) {
out << " " << *(*ei)._transform << ":" << (*ei)._weight;
++ei;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::write
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void TransformBlend::
write(ostream &out, int indent_level) const {
Entries::const_iterator ei;
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
indent(out, indent_level)
<< *(*ei)._transform << ":" << (*ei)._weight << "\n";
LMatrix4f mat;
(*ei)._transform->get_matrix(mat);
mat.write(out, indent_level + 4);
}
LMatrix4f blend;
get_blend(blend);
indent(out, indent_level)
<< "Blended result =\n";
blend.write(out, indent_level + 2);
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::recompute_result
// Access: Private
// Description: Recomputes the blend result from the various
// VertexTransform objects, if necessary.
////////////////////////////////////////////////////////////////////
void TransformBlend::
recompute_result(TransformBlend::CDWriter &cdata) {
// Update the global_modified sequence number first, to prevent race
// conditions.
cdata->_global_modified = VertexTransform::get_global_modified();
// Now see if we really need to recompute.
UpdateSeq seq;
Entries::const_iterator ei;
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
seq = max(seq, (*ei)._transform->get_modified());
}
if (cdata->_modified != seq) {
// We do need to recompute.
cdata->_modified = seq;
cdata->_result.set(0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f);
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
(*ei)._transform->accumulate_matrix(cdata->_result, (*ei)._weight);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::clear_result
// Access: Private
// Description: Removes the computed result to force it to be
// recomputed.
////////////////////////////////////////////////////////////////////
void TransformBlend::
clear_result() {
CDWriter cdata(_cycler);
cdata->_global_modified = UpdateSeq();
if (cdata->_modified != UpdateSeq()) {
cdata->_modified = UpdateSeq();
cdata->_result = LMatrix4f::ident_mat();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::write_datagram
// Access: Public
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void TransformBlend::
write_datagram(BamWriter *manager, Datagram &dg) const {
dg.add_uint16(_entries.size());
Entries::const_iterator ei;
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
manager->write_pointer(dg, (*ei)._transform);
dg.add_float32((*ei)._weight);
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::complete_pointers
// Access: Public
// Description: Receives an array of pointers, one for each time
// manager->read_pointer() was called in fillin().
// Returns the number of pointers processed.
////////////////////////////////////////////////////////////////////
int TransformBlend::
complete_pointers(TypedWritable **p_list, BamReader *manager) {
int pi = 0;
Entries::iterator ei;
for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
(*ei)._transform = DCAST(VertexTransform, p_list[pi++]);
}
// Now that we have actual pointers, we can sort the list of
// entries.
_entries.sort();
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::fillin
// Access: Public
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new PandaNode.
////////////////////////////////////////////////////////////////////
void TransformBlend::
fillin(DatagramIterator &scan, BamReader *manager) {
size_t num_entries = scan.get_uint16();
_entries.reserve(num_entries);
for (size_t i = 0; i < num_entries; ++i) {
TransformEntry entry;
manager->read_pointer(scan);
entry._weight = scan.get_float32();
_entries.push_back(entry);
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformBlend::CData::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *TransformBlend::CData::
make_copy() const {
return new CData(*this);
}