Panda3D

transformBlend.cxx

00001 // Filename: transformBlend.cxx
00002 // Created by:  drose (24Mar05)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "transformBlend.h"
00016 #include "indent.h"
00017 #include "bamReader.h"
00018 #include "bamWriter.h"
00019 
00020 TypeHandle TransformBlend::_type_handle;
00021 
00022 ////////////////////////////////////////////////////////////////////
00023 //     Function: TransformBlend::compare_to
00024 //       Access: Published
00025 //  Description: Defines an arbitrary ordering for TransformBlend
00026 //               objects.
00027 ////////////////////////////////////////////////////////////////////
00028 int TransformBlend::
00029 compare_to(const TransformBlend &other) const {
00030   if (_entries.size() != other._entries.size()) {
00031     return (int)_entries.size() - (int)other._entries.size();
00032   }
00033 
00034   Entries::const_iterator ai, bi;
00035   ai = _entries.begin();
00036   bi = other._entries.begin();
00037   while (ai != _entries.end() && bi != other._entries.end()) {
00038     if ((*ai)._transform != (*bi)._transform) {
00039       return (*ai)._transform < (*bi)._transform ? -1 : 1;
00040     }
00041     if (!IS_NEARLY_EQUAL((*ai)._weight, (*bi)._weight)) {
00042       return (*ai)._weight < (*bi)._weight ? -1 : 1;
00043     }
00044     ++ai;
00045     ++bi;
00046   }
00047 
00048   return 0;
00049 }
00050 
00051 ////////////////////////////////////////////////////////////////////
00052 //     Function: TransformBlend::add_transform
00053 //       Access: Published
00054 //  Description: Adds a new transform to the blend.  If the transform
00055 //               already existed, increases its weight factor.
00056 ////////////////////////////////////////////////////////////////////
00057 void TransformBlend::
00058 add_transform(const VertexTransform *transform, PN_stdfloat weight) {
00059   if (!IS_NEARLY_ZERO(weight)) {
00060     TransformEntry entry;
00061     entry._transform = transform;
00062     entry._weight = weight;
00063     pair<Entries::iterator, bool> result = _entries.insert(entry);
00064     if (!result.second) {
00065       // If the new value was not inserted, it was already there;
00066       // increment the existing weight factor.
00067       Entries::iterator ei = result.first;
00068       (*ei)._weight += weight;
00069       if (IS_NEARLY_ZERO((*ei)._weight)) {
00070         // If we have just zeroed out the weight, remove it.
00071         _entries.erase(ei);
00072       }
00073     }
00074     Thread *current_thread = Thread::get_current_thread();
00075     clear_result(current_thread);
00076   }
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //     Function: TransformBlend::remove_transform
00081 //       Access: Published
00082 //  Description: Removes the indicated transform from the blend.
00083 ////////////////////////////////////////////////////////////////////
00084 void TransformBlend::
00085 remove_transform(const VertexTransform *transform) {
00086   TransformEntry entry;
00087   entry._transform = transform;
00088   entry._weight = 0.0f;
00089   Entries::iterator ei = _entries.find(entry);
00090   if (ei != _entries.end()) {
00091     _entries.erase(ei);
00092   }
00093   Thread *current_thread = Thread::get_current_thread();
00094   clear_result(current_thread);
00095 }
00096 
00097 ////////////////////////////////////////////////////////////////////
00098 //     Function: TransformBlend::limit_transforms
00099 //       Access: Published
00100 //  Description: If the total number of transforms in the blend
00101 //               exceeds max_transforms, removes the n least-important
00102 //               transforms as needed to reduce the number of
00103 //               transforms to max_transforms.
00104 ////////////////////////////////////////////////////////////////////
00105 void TransformBlend::
00106 limit_transforms(int max_transforms) {
00107   if (max_transforms <= 0) {
00108     _entries.clear();
00109     return;
00110   }
00111 
00112   while (_entries.size() > max_transforms) {
00113     // Repeatedly find and remove the least-important transform.
00114     nassertv(!_entries.empty());
00115     Entries::iterator ei_least = _entries.begin();
00116     Entries::iterator ei = ei_least;
00117     ++ei;
00118     while (ei != _entries.end()) {
00119       if ((*ei)._weight < (*ei_least)._weight) {
00120         ei_least = ei;
00121       }
00122       ++ei;
00123     }
00124 
00125     _entries.erase(ei_least);
00126   }
00127 }
00128 
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: TransformBlend::normalize_weights
00131 //       Access: Published
00132 //  Description: Rescales all of the weights on the various transforms
00133 //               so that they sum to 1.0.  It is generally a good idea
00134 //               to call this after adding or removing transforms from
00135 //               the blend.
00136 ////////////////////////////////////////////////////////////////////
00137 void TransformBlend::
00138 normalize_weights() {
00139   PN_stdfloat net_weight = 0.0f;
00140   Entries::iterator ei;
00141   for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00142     net_weight += (*ei)._weight;
00143   }
00144   if (net_weight != 0.0f) {
00145     for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00146       (*ei)._weight /= net_weight;
00147     }
00148   }
00149   Thread *current_thread = Thread::get_current_thread();
00150   clear_result(current_thread);
00151 }
00152 
00153 ////////////////////////////////////////////////////////////////////
00154 //     Function: TransformBlend::has_transform
00155 //       Access: Published
00156 //  Description: Returns true if the blend has the indicated
00157 //               transform, false otherwise.
00158 ////////////////////////////////////////////////////////////////////
00159 bool TransformBlend::
00160 has_transform(const VertexTransform *transform) const {
00161   TransformEntry entry;
00162   entry._transform = transform;
00163   entry._weight = 0.0f;
00164   Entries::const_iterator ei = _entries.find(entry);
00165   return (ei != _entries.end());
00166 }
00167 
00168 ////////////////////////////////////////////////////////////////////
00169 //     Function: TransformBlend::get_weight
00170 //       Access: Published
00171 //  Description: Returns the weight associated with the indicated
00172 //               transform, or 0 if there is no entry for the
00173 //               transform.
00174 ////////////////////////////////////////////////////////////////////
00175 PN_stdfloat TransformBlend::
00176 get_weight(const VertexTransform *transform) const {
00177   TransformEntry entry;
00178   entry._transform = transform;
00179   entry._weight = 0.0f;
00180   Entries::const_iterator ei = _entries.find(entry);
00181   if (ei != _entries.end()) {
00182     return (*ei)._weight;
00183   }
00184   return 0.0f;
00185 }
00186 
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: TransformBlend::output
00189 //       Access: Published
00190 //  Description: 
00191 ////////////////////////////////////////////////////////////////////
00192 void TransformBlend::
00193 output(ostream &out) const {
00194   if (_entries.empty()) {
00195     out << "empty";
00196   } else {
00197     Entries::const_iterator ei = _entries.begin();
00198     out << *(*ei)._transform << ":" << (*ei)._weight;
00199     ++ei;
00200     while (ei != _entries.end()) {
00201       out << " " << *(*ei)._transform << ":" << (*ei)._weight;
00202       ++ei;
00203     }
00204   }
00205 }
00206 
00207 ////////////////////////////////////////////////////////////////////
00208 //     Function: TransformBlend::write
00209 //       Access: Published
00210 //  Description: 
00211 ////////////////////////////////////////////////////////////////////
00212 void TransformBlend::
00213 write(ostream &out, int indent_level) const {
00214   Thread *current_thread = Thread::get_current_thread();
00215   Entries::const_iterator ei;
00216   for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00217     indent(out, indent_level)
00218       << *(*ei)._transform << ":" << (*ei)._weight << "\n";
00219     LMatrix4 mat;
00220     (*ei)._transform->get_matrix(mat);
00221     mat.write(out, indent_level + 4);
00222   }
00223   LMatrix4 blend;
00224   update_blend(current_thread);
00225   get_blend(blend, current_thread);
00226   indent(out, indent_level)
00227     << "Blended result =\n";
00228   blend.write(out, indent_level + 2);
00229 }
00230 
00231 ////////////////////////////////////////////////////////////////////
00232 //     Function: TransformBlend::recompute_result
00233 //       Access: Private
00234 //  Description: Recomputes the blend result from the various
00235 //               VertexTransform objects, if necessary.
00236 ////////////////////////////////////////////////////////////////////
00237 void TransformBlend::
00238 recompute_result(CData *cdata, Thread *current_thread) {
00239   // Update the global_modified sequence number first, to prevent race
00240   // conditions.
00241   cdata->_global_modified = VertexTransform::get_global_modified(current_thread);
00242 
00243   // Now see if we really need to recompute.
00244   UpdateSeq seq;
00245   Entries::const_iterator ei;
00246   for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00247     seq = max(seq, (*ei)._transform->get_modified(current_thread));
00248   }
00249 
00250   if (cdata->_modified != seq) {
00251     // We do need to recompute.
00252     cdata->_modified = seq;
00253 
00254     cdata->_result = LMatrix4::zeros_mat();
00255     for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00256       (*ei)._transform->accumulate_matrix(cdata->_result, (*ei)._weight);
00257     }
00258   }    
00259 }
00260 
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: TransformBlend::clear_result
00263 //       Access: Private
00264 //  Description: Removes the computed result to force it to be
00265 //               recomputed.
00266 ////////////////////////////////////////////////////////////////////
00267 void TransformBlend::
00268 clear_result(Thread *current_thread) {
00269   CDWriter cdata(_cycler, true, current_thread);
00270   cdata->_global_modified = UpdateSeq();
00271   if (cdata->_modified != UpdateSeq()) {
00272     cdata->_modified = UpdateSeq();
00273     cdata->_result = LMatrix4::ident_mat();
00274   }
00275 }
00276 
00277 ////////////////////////////////////////////////////////////////////
00278 //     Function: TransformBlend::write_datagram
00279 //       Access: Public
00280 //  Description: Writes the contents of this object to the datagram
00281 //               for shipping out to a Bam file.
00282 ////////////////////////////////////////////////////////////////////
00283 void TransformBlend::
00284 write_datagram(BamWriter *manager, Datagram &dg) const {
00285   dg.add_uint16(_entries.size());
00286 
00287   Entries::const_iterator ei;
00288   for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00289     manager->write_pointer(dg, (*ei)._transform);
00290     dg.add_stdfloat((*ei)._weight);
00291   }
00292 }
00293 
00294 ////////////////////////////////////////////////////////////////////
00295 //     Function: TransformBlend::complete_pointers
00296 //       Access: Public
00297 //  Description: Receives an array of pointers, one for each time
00298 //               manager->read_pointer() was called in fillin().
00299 //               Returns the number of pointers processed.
00300 ////////////////////////////////////////////////////////////////////
00301 int TransformBlend::
00302 complete_pointers(TypedWritable **p_list, BamReader *manager) {
00303   int pi = 0;
00304 
00305   Entries::iterator ei;
00306   for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
00307     (*ei)._transform = DCAST(VertexTransform, p_list[pi++]);
00308   }
00309 
00310   // Now that we have actual pointers, we can sort the list of
00311   // entries.
00312   _entries.sort();
00313 
00314   return pi;
00315 }
00316 
00317 ////////////////////////////////////////////////////////////////////
00318 //     Function: TransformBlend::fillin
00319 //       Access: Public
00320 //  Description: This internal function is called by make_from_bam to
00321 //               read in all of the relevant data from the BamFile for
00322 //               the new PandaNode.
00323 ////////////////////////////////////////////////////////////////////
00324 void TransformBlend::
00325 fillin(DatagramIterator &scan, BamReader *manager) {
00326   size_t num_entries = scan.get_uint16();
00327   _entries.reserve(num_entries);
00328   for (size_t i = 0; i < num_entries; ++i) {
00329     TransformEntry entry;
00330     manager->read_pointer(scan);
00331     entry._weight = scan.get_stdfloat();
00332     _entries.push_back(entry);
00333   }
00334 }
00335 
00336 ////////////////////////////////////////////////////////////////////
00337 //     Function: TransformBlend::CData::make_copy
00338 //       Access: Public, Virtual
00339 //  Description:
00340 ////////////////////////////////////////////////////////////////////
00341 CycleData *TransformBlend::CData::
00342 make_copy() const {
00343   return new CData(*this);
00344 }
 All Classes Functions Variables Enumerations