Panda3D
|
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 ¶ms) { 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 }