Panda3D
Loading...
Searching...
No Matches
billboardEffect.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 billboardEffect.cxx
10 * @author drose
11 * @date 2002-03-14
12 */
13
14#include "billboardEffect.h"
15#include "cullTraverser.h"
16#include "cullTraverserData.h"
17#include "nodePath.h"
18#include "look_at.h"
19#include "bamReader.h"
20#include "bamWriter.h"
21#include "datagram.h"
22#include "datagramIterator.h"
23
24TypeHandle BillboardEffect::_type_handle;
25
26/**
27 * Constructs a new BillboardEffect object with the indicated properties.
28 */
29CPT(RenderEffect) BillboardEffect::
30make(const LVector3 &up_vector, bool eye_relative,
31 bool axial_rotate, PN_stdfloat offset, const NodePath &look_at,
32 const LPoint3 &look_at_point, bool fixed_depth) {
33 BillboardEffect *effect = new BillboardEffect;
34 effect->_up_vector = up_vector;
35 effect->_eye_relative = eye_relative;
36 effect->_axial_rotate = axial_rotate;
37 effect->_offset = offset;
38 effect->_look_at = look_at;
39 effect->_look_at_point = look_at_point;
40 effect->_fixed_depth = fixed_depth;
41 effect->_off = false;
42 return return_new(effect);
43}
44
45/**
46 * Returns true if it is generally safe to transform this particular kind of
47 * RenderEffect by calling the xform() method, false otherwise.
48 */
50safe_to_transform() const {
51 return false;
52}
53
54/**
55 * Preprocesses the accumulated transform that is about to be applied to (or
56 * through) this node due to a flatten operation. The returned value will be
57 * used instead.
58 */
59CPT(TransformState) BillboardEffect::
60prepare_flatten_transform(const TransformState *net_transform) const {
61 // We don't want any flatten operation to rotate the billboarded node, since
62 // the billboard effect should eat any rotation that comes in from above.
63 return net_transform->set_hpr(LVecBase3(0, 0, 0));
64}
65
66/**
67 *
68 */
69void BillboardEffect::
70output(std::ostream &out) const {
71 out << get_type() << ":";
72 if (is_off()) {
73 out << "(off)";
74 } else {
75 if (_axial_rotate) {
76 out << "(axis";
77 } else {
78 out << "(point";
79 }
80 if (!_up_vector.almost_equal(LVector3::up())) {
81 out << " up " << _up_vector;
82 }
83 if (_eye_relative) {
84 out << " eye";
85 }
86 if (_fixed_depth) {
87 out << " depth " << -_offset;
88 } else if (_offset != 0.0f) {
89 out << " offset " << _offset;
90 }
91 if (!_look_at.is_empty()) {
92 out << " look at " << _look_at;
93 }
94 if (!_look_at_point.almost_equal(LPoint3(0.0f, 0.0f, 0.0f))) {
95 out << " look at point " << _look_at_point;
96 }
97 out << ")";
98 }
99}
100
101/**
102 * Should be overridden by derived classes to return true if cull_callback()
103 * has been defined. Otherwise, returns false to indicate cull_callback()
104 * does not need to be called for this effect during the cull traversal.
105 */
106bool BillboardEffect::
107has_cull_callback() const {
108 return true;
109}
110
111/**
112 * If has_cull_callback() returns true, this function will be called during
113 * the cull traversal to perform any additional operations that should be
114 * performed at cull time. This may include additional manipulation of render
115 * state or additional visible/invisible decisions, or any other arbitrary
116 * operation.
117 *
118 * At the time this function is called, the current node's transform and state
119 * have not yet been applied to the net_transform and net_state. This
120 * callback may modify the node_transform and node_state to apply an effective
121 * change to the render state at this level.
122 */
123void BillboardEffect::
124cull_callback(CullTraverser *trav, CullTraverserData &data,
125 CPT(TransformState) &node_transform,
126 CPT(RenderState) &) const {
127 CPT(TransformState) modelview_transform = data.get_modelview_transform(trav);
128 if (modelview_transform->is_singular()) {
129 // If we're under a singular transform, never mind.
130 return;
131 }
132
133 // Since the "modelview" transform from the cull traverser already includes
134 // the inverse camera transform, the camera transform is identity.
135 CPT(TransformState) camera_transform = TransformState::make_identity();
136
137 // But if we're rotating to face something other than the camera, we have to
138 // compute the "camera" transform to compensate for that.
139 if (!_look_at.is_empty()) {
140 camera_transform = trav->get_camera_transform()->invert_compose(_look_at.get_net_transform());
141 }
142
143 compute_billboard(node_transform, modelview_transform, camera_transform);
144}
145
146/**
147 * Should be overridden by derived classes to return true if
148 * adjust_transform() has been defined, and therefore the RenderEffect has
149 * some effect on the node's apparent local and net transforms.
150 */
151bool BillboardEffect::
152has_adjust_transform() const {
153 // A BillboardEffect can only affect the net transform when it is to a
154 // particular node. A billboard to a camera is camera-dependent, of course,
155 // so it has no effect in the absence of any particular camera viewing it.
156 return !_look_at.is_empty();
157}
158
159/**
160 * Performs some operation on the node's apparent net and/or local transforms.
161 * This will only be called if has_adjust_transform() is redefined to return
162 * true.
163 *
164 * Both parameters are in/out. The original transforms will be passed in, and
165 * they may (or may not) be modified in-place by the RenderEffect.
166 */
167void BillboardEffect::
168adjust_transform(CPT(TransformState) &net_transform,
169 CPT(TransformState) &node_transform,
170 const PandaNode *) const {
171 // A BillboardEffect can only affect the net transform when it is to a
172 // particular node. A billboard to a camera is camera-dependent, of course,
173 // so it has no effect in the absence of any particular camera viewing it.
174 if (_look_at.is_empty()) {
175 return;
176 }
177
178 CPT(TransformState) camera_transform = _look_at.get_net_transform();
179
180 compute_billboard(node_transform, net_transform, camera_transform);
181}
182
183
184/**
185 * Intended to be overridden by derived BillboardEffect types to return a
186 * unique number indicating whether this BillboardEffect is equivalent to the
187 * other one.
188 *
189 * This should return 0 if the two BillboardEffect objects are equivalent, a
190 * number less than zero if this one should be sorted before the other one,
191 * and a number greater than zero otherwise.
192 *
193 * This will only be called with two BillboardEffect objects whose get_type()
194 * functions return the same.
195 */
196int BillboardEffect::
197compare_to_impl(const RenderEffect *other) const {
198 const BillboardEffect *ta;
199 DCAST_INTO_R(ta, other, 0);
200
201 if (_axial_rotate != ta->_axial_rotate) {
202 return (int)_axial_rotate - (int)ta->_axial_rotate;
203 }
204 if (_eye_relative != ta->_eye_relative) {
205 return (int)_eye_relative - (int)ta->_eye_relative;
206 }
207 if (_fixed_depth != ta->_fixed_depth) {
208 return (int)_fixed_depth - (int)ta->_fixed_depth;
209 }
210 if (_offset != ta->_offset) {
211 return _offset < ta->_offset ? -1 : 1;
212 }
213 int compare = _up_vector.compare_to(ta->_up_vector);
214 if (compare != 0) {
215 return compare;
216 }
217 compare = _look_at.compare_to(ta->_look_at);
218 if (compare != 0) {
219 return compare;
220 }
221 compare = _look_at_point.compare_to(ta->_look_at_point);
222 if (compare != 0) {
223 return compare;
224 }
225 return 0;
226}
227
228/**
229 * Computes the billboard operation given the parent's net transform and the
230 * camera transform.
231 *
232 * The result is applied to node_transform, which is modified in-place.
233 */
234void BillboardEffect::
235compute_billboard(CPT(TransformState) &node_transform,
236 const TransformState *net_transform,
237 const TransformState *camera_transform) const {
238 // First, extract out just the translation component of the node's local
239 // transform. This gets applied to the net transform, to compute the look-
240 // at direction properly.
241 CPT(TransformState) translate = TransformState::make_pos(node_transform->get_pos());
242
243 // And then the translation gets removed from the node, but we keep its
244 // rotation etc., which gets applied after the billboard operation.
245 node_transform = node_transform->set_pos(LPoint3(0.0f, 0.0f, 0.0f));
246
247 CPT(TransformState) rel_transform =
248 net_transform->compose(translate)->invert_compose(camera_transform);
249 if (!rel_transform->has_mat()) {
250 // Never mind.
251 return;
252 }
253
254 const LMatrix4 &rel_mat = rel_transform->get_mat();
255
256 // Determine the look_at point in the camera space.
257 LVector3 camera_pos, up;
258
259 // If this is an eye-relative Billboard, then (a) the up vector is relative
260 // to the camera, not to the world, and (b) the look direction is towards
261 // the plane that contains the camera, perpendicular to the forward
262 // direction, not directly to the camera.
263
264 if (_eye_relative) {
265 up = _up_vector * rel_mat;
266 camera_pos = LVector3::forward() * rel_mat;
267
268 } else {
269 up = _up_vector;
270 camera_pos = -(_look_at_point * rel_mat);
271 }
272
273 // Now determine the rotation matrix for the Billboard.
274 LMatrix4 rotate;
275 if (_axial_rotate) {
276 heads_up(rotate, camera_pos, up);
277 } else {
278 look_at(rotate, camera_pos, up);
279 }
280
281 // Also slide the billboard geometry towards the camera according to the
282 // offset factor.
283 if (_offset != 0.0f || _fixed_depth) {
284 LVector3 translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2));
285 LPoint3 pos;
286 if (_fixed_depth) {
287 pos = translate / rel_mat(3, 3);
288 } else {
289 pos.fill(0.0f);
290 }
291 translate.normalize();
292 translate *= _offset;
293 rotate.set_row(3, pos + translate);
294 }
295
296 node_transform = translate->compose(TransformState::make_mat(rotate))->compose(node_transform);
297}
298
299/**
300 * Tells the BamReader how to create objects of type BillboardEffect.
301 */
302void BillboardEffect::
303register_with_read_factory() {
304 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
305}
306
307/**
308 * Writes the contents of this object to the datagram for shipping out to a
309 * Bam file.
310 */
312write_datagram(BamWriter *manager, Datagram &dg) {
313 RenderEffect::write_datagram(manager, dg);
314
315 dg.add_bool(_off);
316 _up_vector.write_datagram(dg);
317 dg.add_bool(_eye_relative);
318 dg.add_bool(_axial_rotate);
319 dg.add_stdfloat(_offset);
320 _look_at_point.write_datagram(dg);
321
322 if (manager->get_file_minor_ver() >= 43) {
323 _look_at.write_datagram(manager, dg);
324 dg.add_bool(_fixed_depth);
325 }
326}
327
328/**
329 * Receives an array of pointers, one for each time manager->read_pointer()
330 * was called in fillin(). Returns the number of pointers processed.
331 */
333complete_pointers(TypedWritable **p_list, BamReader *manager) {
334 int pi = RenderEffect::complete_pointers(p_list, manager);
335
336 if (manager->get_file_minor_ver() >= 43) {
337 pi += _look_at.complete_pointers(p_list + pi, manager);
338 }
339
340 return pi;
341}
342
343/**
344 * This function is called by the BamReader's factory when a new object of
345 * type BillboardEffect is encountered in the Bam file. It should create the
346 * BillboardEffect and extract its information from the file.
347 */
348TypedWritable *BillboardEffect::
349make_from_bam(const FactoryParams &params) {
350 BillboardEffect *effect = new BillboardEffect;
351 DatagramIterator scan;
352 BamReader *manager;
353
354 parse_params(params, scan, manager);
355 effect->fillin(scan, manager);
356
357 return effect;
358}
359
360/**
361 * This internal function is called by make_from_bam to read in all of the
362 * relevant data from the BamFile for the new BillboardEffect.
363 */
364void BillboardEffect::
365fillin(DatagramIterator &scan, BamReader *manager) {
366 RenderEffect::fillin(scan, manager);
367
368 _off = scan.get_bool();
369 _up_vector.read_datagram(scan);
370 _eye_relative = scan.get_bool();
371 _axial_rotate = scan.get_bool();
372 _offset = scan.get_stdfloat();
373 _look_at_point.read_datagram(scan);
374
375 if (manager->get_file_minor_ver() >= 43) {
376 _look_at.fillin(scan, manager);
377 _fixed_depth = scan.get_bool();
378 } else {
379 _fixed_depth = false;
380 }
381}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition bamReader.I:275
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
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition bamReader.I:83
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition bamWriter.I:59
Indicates that geometry at this node should automatically rotate to face the camera,...
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
bool is_off() const
Returns true if the BillboardEffect is an 'off' BillboardEffect, indicating that it does not enable b...
virtual bool safe_to_transform() const
Returns true if it is generally safe to transform this particular kind of RenderEffect by calling the...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
const TransformState * get_camera_transform() const
Returns the position of the camera relative to the starting node.
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...
bool get_bool()
Extracts a boolean value.
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_bool(bool value)
Adds a boolean value to the datagram.
Definition datagram.I:34
An instance of this class is passed to the Factory when requesting it to do its business and construc...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition factory.I:73
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
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...
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
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 NodePath &other) const
Returns a number less than zero if this NodePath sorts before the other one, greater than zero if it ...
Definition nodePath.I:1965
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 basic node of the scene graph or data graph.
Definition pandaNode.h:65
This is the base class for a number of special render effects that may be set on scene graph nodes to...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
Indicates a coordinate-system transform on vertices.
get_mat
Returns the matrix that describes the transform.
get_pos
Returns the pos component of the transform.
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.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.