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  */
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  */
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  */
268 complete_pointers(TypedWritable **p_list, BamReader *manager) {
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  */
287 fillin(DatagramIterator &scan, BamReader *manager) {
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
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
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
A thread; that is, a lightweight process.
Definition: thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
This defines a single entry in a TransformBlendTable.
void limit_transforms(int max_transforms)
If the total number of transforms in the blend exceeds max_transforms, removes the n least-important ...
void update_blend(Thread *current_thread) const
Recomputes the internal representation of the blend value, if necessary.
has_transform
Returns true if the blend has the indicated transform, false otherwise.
get_weight
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
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...
int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
int compare_to(const TransformBlend &other) const
Defines an arbitrary ordering for TransformBlend objects.
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...
remove_transform
Removes the indicated transform from the blend.
void write_datagram(BamWriter *manager, Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
void normalize_weights()
Rescales all of the weights on the various transforms so that they sum to 1.0.
void add_transform(const VertexTransform *transform, PN_stdfloat weight)
Adds a new transform to the blend.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
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.
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...
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
size_type_0 size() const
Returns the number of elements in the ordered vector.
bool empty() const
Returns true if the ordered vector is empty, false otherwise.
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
void clear()
Removes all elements from the ordered vector.
void sort()
Maps to sort_unique().
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.