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