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