Panda3D
Loading...
Searching...
No Matches
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
19TypeHandle TransformBlend::_type_handle;
20
21/**
22 * Defines an arbitrary ordering for TransformBlend objects.
23 */
25compare_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 */
52add_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 */
77remove_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 */
95limit_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 */
143has_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 */
155PN_stdfloat TransformBlend::
156get_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 */
170void TransformBlend::
171output(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 */
188void TransformBlend::
189write(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 */
211void TransformBlend::
212recompute_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 */
238void TransformBlend::
239clear_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 */
253write_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 */
268complete_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 */
287fillin(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 */
301CycleData *TransformBlend::CData::
302make_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.
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.
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.
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.