Panda3D
transformBlend.cxx
1 // Filename: transformBlend.cxx
2 // Created by: drose (24Mar05)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "transformBlend.h"
16 #include "indent.h"
17 #include "bamReader.h"
18 #include "bamWriter.h"
19 
20 TypeHandle TransformBlend::_type_handle;
21 
22 ////////////////////////////////////////////////////////////////////
23 // Function: TransformBlend::compare_to
24 // Access: Published
25 // Description: Defines an arbitrary ordering for TransformBlend
26 // objects.
27 ////////////////////////////////////////////////////////////////////
29 compare_to(const TransformBlend &other) const {
30  if (_entries.size() != other._entries.size()) {
31  return (int)_entries.size() - (int)other._entries.size();
32  }
33 
34  Entries::const_iterator ai, bi;
35  ai = _entries.begin();
36  bi = other._entries.begin();
37  while (ai != _entries.end() && bi != other._entries.end()) {
38  if ((*ai)._transform != (*bi)._transform) {
39  return (*ai)._transform < (*bi)._transform ? -1 : 1;
40  }
41  if (!IS_NEARLY_EQUAL((*ai)._weight, (*bi)._weight)) {
42  return (*ai)._weight < (*bi)._weight ? -1 : 1;
43  }
44  ++ai;
45  ++bi;
46  }
47 
48  return 0;
49 }
50 
51 ////////////////////////////////////////////////////////////////////
52 // Function: TransformBlend::add_transform
53 // Access: Published
54 // Description: Adds a new transform to the blend. If the transform
55 // already existed, increases its weight factor.
56 ////////////////////////////////////////////////////////////////////
58 add_transform(const VertexTransform *transform, PN_stdfloat weight) {
59  if (!IS_NEARLY_ZERO(weight)) {
60  TransformEntry entry;
61  entry._transform = transform;
62  entry._weight = weight;
63  pair<Entries::iterator, bool> result = _entries.insert(entry);
64  if (!result.second) {
65  // If the new value was not inserted, it was already there;
66  // increment the existing weight factor.
67  Entries::iterator ei = result.first;
68  (*ei)._weight += weight;
69  if (IS_NEARLY_ZERO((*ei)._weight)) {
70  // If we have just zeroed out the weight, remove it.
71  _entries.erase(ei);
72  }
73  }
74  Thread *current_thread = Thread::get_current_thread();
75  clear_result(current_thread);
76  }
77 }
78 
79 ////////////////////////////////////////////////////////////////////
80 // Function: TransformBlend::remove_transform
81 // Access: Published
82 // Description: Removes the indicated transform from the blend.
83 ////////////////////////////////////////////////////////////////////
85 remove_transform(const VertexTransform *transform) {
86  TransformEntry entry;
87  entry._transform = transform;
88  entry._weight = 0.0f;
89  Entries::iterator ei = _entries.find(entry);
90  if (ei != _entries.end()) {
91  _entries.erase(ei);
92  }
93  Thread *current_thread = Thread::get_current_thread();
94  clear_result(current_thread);
95 }
96 
97 ////////////////////////////////////////////////////////////////////
98 // Function: TransformBlend::limit_transforms
99 // Access: Published
100 // Description: If the total number of transforms in the blend
101 // exceeds max_transforms, removes the n least-important
102 // transforms as needed to reduce the number of
103 // transforms to max_transforms.
104 ////////////////////////////////////////////////////////////////////
105 void TransformBlend::
106 limit_transforms(int max_transforms) {
107  if (max_transforms <= 0) {
108  _entries.clear();
109  return;
110  }
111 
112  while ((int)_entries.size() > max_transforms) {
113  // Repeatedly find and remove the least-important transform.
114  nassertv(!_entries.empty());
115  Entries::iterator ei_least = _entries.begin();
116  Entries::iterator ei = ei_least;
117  ++ei;
118  while (ei != _entries.end()) {
119  if ((*ei)._weight < (*ei_least)._weight) {
120  ei_least = ei;
121  }
122  ++ei;
123  }
124 
125  _entries.erase(ei_least);
126  }
127 }
128 
129 ////////////////////////////////////////////////////////////////////
130 // Function: TransformBlend::normalize_weights
131 // Access: Published
132 // Description: Rescales all of the weights on the various transforms
133 // so that they sum to 1.0. It is generally a good idea
134 // to call this after adding or removing transforms from
135 // the blend.
136 ////////////////////////////////////////////////////////////////////
137 void TransformBlend::
139  PN_stdfloat net_weight = 0.0f;
140  Entries::iterator ei;
141  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
142  net_weight += (*ei)._weight;
143  }
144  if (net_weight != 0.0f) {
145  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
146  (*ei)._weight /= net_weight;
147  }
148  }
149  Thread *current_thread = Thread::get_current_thread();
150  clear_result(current_thread);
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: TransformBlend::has_transform
155 // Access: Published
156 // Description: Returns true if the blend has the indicated
157 // transform, false otherwise.
158 ////////////////////////////////////////////////////////////////////
159 bool TransformBlend::
160 has_transform(const VertexTransform *transform) const {
161  TransformEntry entry;
162  entry._transform = transform;
163  entry._weight = 0.0f;
164  Entries::const_iterator ei = _entries.find(entry);
165  return (ei != _entries.end());
166 }
167 
168 ////////////////////////////////////////////////////////////////////
169 // Function: TransformBlend::get_weight
170 // Access: Published
171 // Description: Returns the weight associated with the indicated
172 // transform, or 0 if there is no entry for the
173 // transform.
174 ////////////////////////////////////////////////////////////////////
175 PN_stdfloat TransformBlend::
176 get_weight(const VertexTransform *transform) const {
177  TransformEntry entry;
178  entry._transform = transform;
179  entry._weight = 0.0f;
180  Entries::const_iterator ei = _entries.find(entry);
181  if (ei != _entries.end()) {
182  return (*ei)._weight;
183  }
184  return 0.0f;
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: TransformBlend::output
189 // Access: Published
190 // Description:
191 ////////////////////////////////////////////////////////////////////
192 void TransformBlend::
193 output(ostream &out) const {
194  if (_entries.empty()) {
195  out << "empty";
196  } else {
197  Entries::const_iterator ei = _entries.begin();
198  out << *(*ei)._transform << ":" << (*ei)._weight;
199  ++ei;
200  while (ei != _entries.end()) {
201  out << " " << *(*ei)._transform << ":" << (*ei)._weight;
202  ++ei;
203  }
204  }
205 }
206 
207 ////////////////////////////////////////////////////////////////////
208 // Function: TransformBlend::write
209 // Access: Published
210 // Description:
211 ////////////////////////////////////////////////////////////////////
212 void TransformBlend::
213 write(ostream &out, int indent_level) const {
214  Thread *current_thread = Thread::get_current_thread();
215  Entries::const_iterator ei;
216  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
217  indent(out, indent_level)
218  << *(*ei)._transform << ":" << (*ei)._weight << "\n";
219  LMatrix4 mat;
220  (*ei)._transform->get_matrix(mat);
221  mat.write(out, indent_level + 4);
222  }
223  LMatrix4 blend;
224  update_blend(current_thread);
225  get_blend(blend, current_thread);
226  indent(out, indent_level)
227  << "Blended result =\n";
228  blend.write(out, indent_level + 2);
229 }
230 
231 ////////////////////////////////////////////////////////////////////
232 // Function: TransformBlend::recompute_result
233 // Access: Private
234 // Description: Recomputes the blend result from the various
235 // VertexTransform objects, if necessary.
236 ////////////////////////////////////////////////////////////////////
237 void TransformBlend::
238 recompute_result(CData *cdata, Thread *current_thread) {
239  // Update the global_modified sequence number first, to prevent race
240  // conditions.
241  cdata->_global_modified = VertexTransform::get_global_modified(current_thread);
242 
243  // Now see if we really need to recompute.
244  UpdateSeq seq;
245  Entries::const_iterator ei;
246  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
247  seq = max(seq, (*ei)._transform->get_modified(current_thread));
248  }
249 
250  if (cdata->_modified != seq) {
251  // We do need to recompute.
252  cdata->_modified = seq;
253 
254  cdata->_result = LMatrix4::zeros_mat();
255  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
256  (*ei)._transform->accumulate_matrix(cdata->_result, (*ei)._weight);
257  }
258  }
259 }
260 
261 ////////////////////////////////////////////////////////////////////
262 // Function: TransformBlend::clear_result
263 // Access: Private
264 // Description: Removes the computed result to force it to be
265 // recomputed.
266 ////////////////////////////////////////////////////////////////////
267 void TransformBlend::
268 clear_result(Thread *current_thread) {
269  CDWriter cdata(_cycler, true, current_thread);
270  cdata->_global_modified = UpdateSeq();
271  if (cdata->_modified != UpdateSeq()) {
272  cdata->_modified = UpdateSeq();
273  cdata->_result = LMatrix4::ident_mat();
274  }
275 }
276 
277 ////////////////////////////////////////////////////////////////////
278 // Function: TransformBlend::write_datagram
279 // Access: Public
280 // Description: Writes the contents of this object to the datagram
281 // for shipping out to a Bam file.
282 ////////////////////////////////////////////////////////////////////
283 void TransformBlend::
284 write_datagram(BamWriter *manager, Datagram &dg) const {
285  dg.add_uint16(_entries.size());
286 
287  Entries::const_iterator ei;
288  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
289  manager->write_pointer(dg, (*ei)._transform);
290  dg.add_stdfloat((*ei)._weight);
291  }
292 }
293 
294 ////////////////////////////////////////////////////////////////////
295 // Function: TransformBlend::complete_pointers
296 // Access: Public
297 // Description: Receives an array of pointers, one for each time
298 // manager->read_pointer() was called in fillin().
299 // Returns the number of pointers processed.
300 ////////////////////////////////////////////////////////////////////
303  int pi = 0;
304 
305  Entries::iterator ei;
306  for (ei = _entries.begin(); ei != _entries.end(); ++ei) {
307  (*ei)._transform = DCAST(VertexTransform, p_list[pi++]);
308  }
309 
310  // Now that we have actual pointers, we can sort the list of
311  // entries.
312  _entries.sort();
313 
314  return pi;
315 }
316 
317 ////////////////////////////////////////////////////////////////////
318 // Function: TransformBlend::fillin
319 // Access: Public
320 // Description: This internal function is called by make_from_bam to
321 // read in all of the relevant data from the BamFile for
322 // the new PandaNode.
323 ////////////////////////////////////////////////////////////////////
324 void TransformBlend::
326  size_t num_entries = scan.get_uint16();
327  _entries.reserve(num_entries);
328  for (size_t i = 0; i < num_entries; ++i) {
329  TransformEntry entry;
330  manager->read_pointer(scan);
331  entry._weight = scan.get_stdfloat();
332  _entries.push_back(entry);
333  }
334 }
335 
336 ////////////////////////////////////////////////////////////////////
337 // Function: TransformBlend::CData::make_copy
338 // Access: Public, Virtual
339 // Description:
340 ////////////////////////////////////////////////////////////////////
341 CycleData *TransformBlend::CData::
342 make_copy() const {
343  return new CData(*this);
344 }
static const LMatrix4f & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:903
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is called by make_from_bam to read in all of the relevant data from the BamFil...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
size_type_0 size() const
Returns the number of elements in the ordered vector.
void clear()
Removes all elements from the ordered vector.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
void update_blend(Thread *current_thread) const
Recomputes the internal representation of the blend value, if necessary.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
void remove_transform(const VertexTransform *transform)
Removes the indicated transform from the blend.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
void reserve(size_type_0 n)
Informs the vector of a planned change in size; ensures that the capacity of the vector is greater th...
void get_blend(LMatrix4 &result, Thread *current_thread) const
Returns the current value of the blend, based on the current value of all of the nested transform obj...
bool empty() const
Returns true if the ordered vector is empty, false otherwise.
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
void limit_transforms(int max_transforms)
If the total number of transforms in the blend exceeds max_transforms, removes the n least-important ...
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:240
bool has_transform(const VertexTransform *transform) const
Returns true if the blend has the indicated transform, false otherwise.
int compare_to(const TransformBlend &other) const
Defines an arbitrary ordering for TransformBlend objects.
PN_stdfloat get_weight(const VertexTransform *transform) const
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
static UpdateSeq get_global_modified(Thread *current_thread)
Returns the currently highest VertexTransform::get_modified() value in the world. ...
int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
void sort()
Maps to sort_unique().
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
This defines a single entry in a TransformBlendTable.
static const LMatrix4f & zeros_mat()
Returns an matrix filled with zeros.
Definition: lmatrix.h:923
void write_datagram(BamWriter *manager, Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
A thread; that is, a lightweight process.
Definition: thread.h:51
void normalize_weights()
Rescales all of the weights on the various transforms so that they sum to 1.0.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This is a sequence number that increments monotonically.
Definition: updateSeq.h:43
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void add_transform(const VertexTransform *transform, PN_stdfloat weight)
Adds a new transform to the blend.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279
void read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:658