Panda3D
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 
24 TypeHandle BillboardEffect::_type_handle;
25 
26 /**
27  * Constructs a new BillboardEffect object with the indicated properties.
28  */
29 CPT(RenderEffect) BillboardEffect::
30 make(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  */
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  */
59 CPT(TransformState) BillboardEffect::
60 prepare_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  */
69 void BillboardEffect::
70 output(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  */
106 bool BillboardEffect::
107 has_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  */
123 void BillboardEffect::
124 cull_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  */
151 bool BillboardEffect::
152 has_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  */
167 void BillboardEffect::
168 adjust_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  */
196 int BillboardEffect::
197 compare_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  */
234 void BillboardEffect::
235 compute_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  */
302 void BillboardEffect::
303 register_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  */
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  */
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  */
348 TypedWritable *BillboardEffect::
349 make_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  */
364 void BillboardEffect::
365 fillin(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 }
CPT(RenderEffect) BillboardEffect
Constructs a new BillboardEffect object with the indicated properties.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
const TransformState * get_camera_transform() const
Returns the position of the camera relative to the starting node.
Definition: cullTraverser.I:61
Indicates a coordinate-system transform on vertices.
bool get_bool()
Extracts a boolean value.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
virtual bool safe_to_transform() const
Returns true if it is generally safe to transform this particular kind of RenderEffect by calling the...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
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:1955
bool is_off() const
Returns true if the BillboardEffect is an 'off' BillboardEffect, indicating that it does not enable b...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
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 collects together the pieces of data that are accumulated for each node while walking the scene ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
This is the base class for a number of special render effects that may be set on scene graph nodes to...
Definition: renderEffect.h:48
Indicates that geometry at this node should automatically rotate to face the camera,...
get_mat
Returns the matrix that describes the transform.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
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 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
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
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().
get_pos
Returns the pos component of the transform.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Definition: nodePath.cxx:6753
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
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write_datagram(BamWriter *manager, Datagram &dg) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: nodePath.cxx:6702
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...
Definition: nodePath.cxx:6813