Panda3D

billboardEffect.cxx

00001 // Filename: billboardEffect.cxx
00002 // Created by:  drose (14Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "billboardEffect.h"
00016 #include "cullTraverser.h"
00017 #include "cullTraverserData.h"
00018 #include "nodePath.h"
00019 #include "look_at.h"
00020 #include "bamReader.h"
00021 #include "bamWriter.h"
00022 #include "datagram.h"
00023 #include "datagramIterator.h"
00024 
00025 TypeHandle BillboardEffect::_type_handle;
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: BillboardEffect::make
00029 //       Access: Published, Static
00030 //  Description: Constructs a new BillboardEffect object with the
00031 //               indicated properties.
00032 ////////////////////////////////////////////////////////////////////
00033 CPT(RenderEffect) BillboardEffect::
00034 make(const LVector3 &up_vector, bool eye_relative,
00035      bool axial_rotate, PN_stdfloat offset, const NodePath &look_at,
00036      const LPoint3 &look_at_point) {
00037   BillboardEffect *effect = new BillboardEffect;
00038   effect->_up_vector = up_vector;
00039   effect->_eye_relative = eye_relative;
00040   effect->_axial_rotate = axial_rotate;
00041   effect->_offset = offset;
00042   effect->_look_at = look_at;
00043   effect->_look_at_point = look_at_point;
00044   effect->_off = false;
00045   return return_new(effect);
00046 }
00047 
00048 ////////////////////////////////////////////////////////////////////
00049 //     Function: BillboardEffect::safe_to_transform
00050 //       Access: Public, Virtual
00051 //  Description: Returns true if it is generally safe to transform
00052 //               this particular kind of RenderEffect by calling the
00053 //               xform() method, false otherwise.
00054 ////////////////////////////////////////////////////////////////////
00055 bool BillboardEffect::
00056 safe_to_transform() const {
00057   return false;
00058 }
00059 
00060 ////////////////////////////////////////////////////////////////////
00061 //     Function: BillboardEffect::prepare_flatten_transform
00062 //       Access: Public, Virtual
00063 //  Description: Preprocesses the accumulated transform that is about
00064 //               to be applied to (or through) this node due to a
00065 //               flatten operation.  The returned value will be used
00066 //               instead.
00067 ////////////////////////////////////////////////////////////////////
00068 CPT(TransformState) BillboardEffect::
00069 prepare_flatten_transform(const TransformState *net_transform) const {
00070   // We don't want any flatten operation to rotate the billboarded
00071   // node, since the billboard effect should eat any rotation that
00072   // comes in from above.
00073   return net_transform->set_hpr(LVecBase3(0, 0, 0));
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: BillboardEffect::output
00078 //       Access: Public, Virtual
00079 //  Description: 
00080 ////////////////////////////////////////////////////////////////////
00081 void BillboardEffect::
00082 output(ostream &out) const {
00083   out << get_type() << ":";
00084   if (is_off()) {
00085     out << "(off)";
00086   } else {
00087     if (_axial_rotate) {
00088       out << "(axis";
00089     } else {
00090       out << "(point";
00091     }
00092     if (!_up_vector.almost_equal(LVector3::up())) {
00093       out << " up " << _up_vector;
00094     }
00095     if (_eye_relative) {
00096       out << " eye";
00097     }
00098     if (_offset != 0.0f) {
00099       out << " offset " << _offset;
00100     }
00101     if (!_look_at.is_empty()) {
00102       out << " look at " << _look_at;
00103     }
00104     if (!_look_at_point.almost_equal(LPoint3(0.0f, 0.0f, 0.0f))) {
00105       out << " look at point " << _look_at_point;
00106     }
00107     out << ")";
00108   }
00109 }
00110 
00111 ////////////////////////////////////////////////////////////////////
00112 //     Function: BillboardEffect::has_cull_callback
00113 //       Access: Public, Virtual
00114 //  Description: Should be overridden by derived classes to return
00115 //               true if cull_callback() has been defined.  Otherwise,
00116 //               returns false to indicate cull_callback() does not
00117 //               need to be called for this effect during the cull
00118 //               traversal.
00119 ////////////////////////////////////////////////////////////////////
00120 bool BillboardEffect::
00121 has_cull_callback() const {
00122   return true;
00123 }
00124 
00125 ////////////////////////////////////////////////////////////////////
00126 //     Function: BillboardEffect::cull_callback
00127 //       Access: Public, Virtual
00128 //  Description: If has_cull_callback() returns true, this function
00129 //               will be called during the cull traversal to perform
00130 //               any additional operations that should be performed at
00131 //               cull time.  This may include additional manipulation
00132 //               of render state or additional visible/invisible
00133 //               decisions, or any other arbitrary operation.
00134 //
00135 //               At the time this function is called, the current
00136 //               node's transform and state have not yet been applied
00137 //               to the net_transform and net_state.  This callback
00138 //               may modify the node_transform and node_state to apply
00139 //               an effective change to the render state at this
00140 //               level.
00141 ////////////////////////////////////////////////////////////////////
00142 void BillboardEffect::
00143 cull_callback(CullTraverser *trav, CullTraverserData &data,
00144               CPT(TransformState) &node_transform,
00145               CPT(RenderState) &) const {
00146   CPT(TransformState) modelview_transform = data.get_modelview_transform(trav);
00147   if (modelview_transform->is_singular()) {
00148     // If we're under a singular transform, never mind.
00149     return;
00150   }
00151 
00152   // Since the "modelview" transform from the cull traverser already
00153   // includes the inverse camera transform, the camera transform is
00154   // identity.
00155   CPT(TransformState) camera_transform = TransformState::make_identity();
00156 
00157   // But if we're rotating to face something other than the camera, we
00158   // have to compute the "camera" transform to compensate for that.
00159   if (!_look_at.is_empty()) {
00160     camera_transform = trav->get_camera_transform()->invert_compose(_look_at.get_net_transform());
00161   }
00162 
00163   compute_billboard(node_transform, modelview_transform, camera_transform);
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: BillboardEffect::has_adjust_transform
00168 //       Access: Public, Virtual
00169 //  Description: Should be overridden by derived classes to return
00170 //               true if adjust_transform() has been defined, and
00171 //               therefore the RenderEffect has some effect on the
00172 //               node's apparent local and net transforms.
00173 ////////////////////////////////////////////////////////////////////
00174 bool BillboardEffect::
00175 has_adjust_transform() const {
00176   // A BillboardEffect can only affect the net transform when it is to
00177   // a particular node.  A billboard to a camera is camera-dependent,
00178   // of course, so it has no effect in the absence of any particular
00179   // camera viewing it.
00180   return !_look_at.is_empty();
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: BillboardEffect::adjust_transform
00185 //       Access: Public, Virtual
00186 //  Description: Performs some operation on the node's apparent net
00187 //               and/or local transforms.  This will only be called if
00188 //               has_adjust_transform() is redefined to return true.
00189 //
00190 //               Both parameters are in/out.  The original transforms
00191 //               will be passed in, and they may (or may not) be
00192 //               modified in-place by the RenderEffect.
00193 ////////////////////////////////////////////////////////////////////
00194 void BillboardEffect::
00195 adjust_transform(CPT(TransformState) &net_transform,
00196                  CPT(TransformState) &node_transform,
00197                  PandaNode *) const {
00198   // A BillboardEffect can only affect the net transform when it is to
00199   // a particular node.  A billboard to a camera is camera-dependent,
00200   // of course, so it has no effect in the absence of any particular
00201   // camera viewing it.
00202   if (_look_at.is_empty()) {
00203     return;
00204   }
00205 
00206   CPT(TransformState) camera_transform = _look_at.get_net_transform();
00207 
00208   compute_billboard(node_transform, net_transform, camera_transform);
00209 }
00210 
00211 
00212 ////////////////////////////////////////////////////////////////////
00213 //     Function: BillboardEffect::compare_to_impl
00214 //       Access: Protected, Virtual
00215 //  Description: Intended to be overridden by derived BillboardEffect
00216 //               types to return a unique number indicating whether
00217 //               this BillboardEffect is equivalent to the other one.
00218 //
00219 //               This should return 0 if the two BillboardEffect objects
00220 //               are equivalent, a number less than zero if this one
00221 //               should be sorted before the other one, and a number
00222 //               greater than zero otherwise.
00223 //
00224 //               This will only be called with two BillboardEffect
00225 //               objects whose get_type() functions return the same.
00226 ////////////////////////////////////////////////////////////////////
00227 int BillboardEffect::
00228 compare_to_impl(const RenderEffect *other) const {
00229   const BillboardEffect *ta;
00230   DCAST_INTO_R(ta, other, 0);
00231 
00232   if (_axial_rotate != ta->_axial_rotate) {
00233     return (int)_axial_rotate - (int)ta->_axial_rotate;
00234   }
00235   if (_eye_relative != ta->_eye_relative) {
00236     return (int)_eye_relative - (int)ta->_eye_relative;
00237   }
00238   if (_offset != ta->_offset) {
00239     return _offset < ta->_offset ? -1 : 1;
00240   }
00241   int compare = _up_vector.compare_to(ta->_up_vector);
00242   if (compare != 0) {
00243     return compare;
00244   }
00245   compare = _look_at.compare_to(ta->_look_at);
00246   if (compare != 0) {
00247     return compare;
00248   }
00249   compare = _look_at_point.compare_to(ta->_look_at_point);
00250   if (compare != 0) {
00251     return compare;
00252   }
00253   return 0;
00254 }
00255 
00256 ////////////////////////////////////////////////////////////////////
00257 //     Function: BillboardEffect::compute_billboard
00258 //       Access: Private
00259 //  Description: Computes the billboard operation given the parent's
00260 //               net transform and the camera transform.
00261 //
00262 //               The result is applied to node_transform, which is
00263 //               modified in-place.
00264 ////////////////////////////////////////////////////////////////////
00265 void BillboardEffect::
00266 compute_billboard(CPT(TransformState) &node_transform, 
00267                   const TransformState *net_transform, 
00268                   const TransformState *camera_transform) const {
00269   // First, extract out just the translation component of the node's
00270   // local transform.  This gets applied to the net transform, to
00271   // compute the look-at direction properly.
00272   CPT(TransformState) translate = TransformState::make_pos(node_transform->get_pos());
00273 
00274   // And then the translation gets removed from the node, but we keep
00275   // its rotation etc., which gets applied after the billboard
00276   // operation.
00277   node_transform = node_transform->set_pos(LPoint3(0.0f, 0.0f, 0.0f));
00278 
00279   CPT(TransformState) rel_transform =
00280     net_transform->compose(translate)->invert_compose(camera_transform);
00281   if (!rel_transform->has_mat()) {
00282     // Never mind.
00283     return;
00284   }
00285 
00286   const LMatrix4 &rel_mat = rel_transform->get_mat();
00287 
00288   // Determine the look_at point in the camera space.
00289   LVector3 camera_pos, up;
00290 
00291   // If this is an eye-relative Billboard, then (a) the up vector is
00292   // relative to the camera, not to the world, and (b) the look
00293   // direction is towards the plane that contains the camera,
00294   // perpendicular to the forward direction, not directly to the
00295   // camera.
00296 
00297   if (_eye_relative) {
00298     up = _up_vector * rel_mat;
00299     camera_pos = LVector3::forward() * rel_mat;
00300 
00301   } else {
00302     up = _up_vector;
00303     camera_pos = -(_look_at_point * rel_mat);
00304   }
00305 
00306   // Now determine the rotation matrix for the Billboard.
00307   LMatrix4 rotate;
00308   if (_axial_rotate) {
00309     heads_up(rotate, camera_pos, up);
00310   } else {
00311     look_at(rotate, camera_pos, up);
00312   }
00313 
00314   // Also slide the billboard geometry towards the camera according to
00315   // the offset factor.
00316   if (_offset != 0.0f) {
00317     LVector3 translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2));
00318     translate.normalize();
00319     translate *= _offset;
00320     rotate.set_row(3, translate);
00321   }
00322 
00323   node_transform = translate->compose(TransformState::make_mat(rotate))->compose(node_transform);
00324 }
00325 
00326 ////////////////////////////////////////////////////////////////////
00327 //     Function: BillboardEffect::register_with_read_factory
00328 //       Access: Public, Static
00329 //  Description: Tells the BamReader how to create objects of type
00330 //               BillboardEffect.
00331 ////////////////////////////////////////////////////////////////////
00332 void BillboardEffect::
00333 register_with_read_factory() {
00334   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00335 }
00336 
00337 ////////////////////////////////////////////////////////////////////
00338 //     Function: BillboardEffect::write_datagram
00339 //       Access: Public, Virtual
00340 //  Description: Writes the contents of this object to the datagram
00341 //               for shipping out to a Bam file.
00342 ////////////////////////////////////////////////////////////////////
00343 void BillboardEffect::
00344 write_datagram(BamWriter *manager, Datagram &dg) {
00345   RenderEffect::write_datagram(manager, dg);
00346 
00347   dg.add_bool(_off);
00348   _up_vector.write_datagram(dg);
00349   dg.add_bool(_eye_relative);
00350   dg.add_bool(_axial_rotate);
00351   dg.add_stdfloat(_offset);
00352   _look_at_point.write_datagram(dg);
00353 
00354   // *** We don't write out the _look_at NodePath right now.  Maybe
00355   // we should.
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: BillboardEffect::make_from_bam
00360 //       Access: Protected, Static
00361 //  Description: This function is called by the BamReader's factory
00362 //               when a new object of type BillboardEffect is encountered
00363 //               in the Bam file.  It should create the BillboardEffect
00364 //               and extract its information from the file.
00365 ////////////////////////////////////////////////////////////////////
00366 TypedWritable *BillboardEffect::
00367 make_from_bam(const FactoryParams &params) {
00368   BillboardEffect *effect = new BillboardEffect;
00369   DatagramIterator scan;
00370   BamReader *manager;
00371 
00372   parse_params(params, scan, manager);
00373   effect->fillin(scan, manager);
00374 
00375   return effect;
00376 }
00377 
00378 ////////////////////////////////////////////////////////////////////
00379 //     Function: BillboardEffect::fillin
00380 //       Access: Protected
00381 //  Description: This internal function is called by make_from_bam to
00382 //               read in all of the relevant data from the BamFile for
00383 //               the new BillboardEffect.
00384 ////////////////////////////////////////////////////////////////////
00385 void BillboardEffect::
00386 fillin(DatagramIterator &scan, BamReader *manager) {
00387   RenderEffect::fillin(scan, manager);
00388 
00389   _off = scan.get_bool();
00390   _up_vector.read_datagram(scan);
00391   _eye_relative = scan.get_bool();
00392   _axial_rotate = scan.get_bool();
00393   _offset = scan.get_stdfloat();
00394   _look_at_point.read_datagram(scan);
00395 }
 All Classes Functions Variables Enumerations