open_toontown_panda3d/panda/src/pgraph/billboardEffect.cxx

394 lines
15 KiB
C++

// Filename: billboardEffect.cxx
// Created by: drose (14Mar02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "billboardEffect.h"
#include "cullTraverser.h"
#include "cullTraverserData.h"
#include "nodePath.h"
#include "look_at.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagram.h"
#include "datagramIterator.h"
TypeHandle BillboardEffect::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::make
// Access: Published, Static
// Description: Constructs a new BillboardEffect object with the
// indicated properties.
////////////////////////////////////////////////////////////////////
CPT(RenderEffect) BillboardEffect::
make(const LVector3f &up_vector, bool eye_relative,
bool axial_rotate, float offset, const NodePath &look_at,
const LPoint3f &look_at_point) {
BillboardEffect *effect = new BillboardEffect;
effect->_up_vector = up_vector;
effect->_eye_relative = eye_relative;
effect->_axial_rotate = axial_rotate;
effect->_offset = offset;
effect->_look_at = look_at;
effect->_look_at_point = look_at_point;
effect->_off = false;
return return_new(effect);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::safe_to_transform
// Access: Public, Virtual
// Description: Returns true if it is generally safe to transform
// this particular kind of RenderEffect by calling the
// xform() method, false otherwise.
////////////////////////////////////////////////////////////////////
bool BillboardEffect::
safe_to_transform() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::prepare_flatten_transform
// Access: Public, Virtual
// Description: Preprocesses the accumulated transform that is about
// to be applied to (or through) this node due to a
// flatten operation. The returned value will be used
// instead.
////////////////////////////////////////////////////////////////////
CPT(TransformState) BillboardEffect::
prepare_flatten_transform(const TransformState *net_transform) const {
// We don't want any flatten operation to rotate the billboarded
// node, since the billboard effect should eat any rotation that
// comes in from above.
return net_transform->set_hpr(LVecBase3f(0, 0, 0));
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void BillboardEffect::
output(ostream &out) const {
out << get_type() << ":";
if (is_off()) {
out << "(off)";
} else {
if (_axial_rotate) {
out << "(axis";
} else {
out << "(point";
}
if (!_up_vector.almost_equal(LVector3f::up())) {
out << " up " << _up_vector;
}
if (_eye_relative) {
out << " eye";
}
if (_offset != 0.0f) {
out << " offset " << _offset;
}
if (!_look_at.is_empty()) {
out << " look at " << _look_at;
}
if (!_look_at_point.almost_equal(LPoint3f(0.0f, 0.0f, 0.0f))) {
out << " look at point " << _look_at_point;
}
out << ")";
}
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::has_cull_callback
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if cull_callback() has been defined. Otherwise,
// returns false to indicate cull_callback() does not
// need to be called for this effect during the cull
// traversal.
////////////////////////////////////////////////////////////////////
bool BillboardEffect::
has_cull_callback() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::cull_callback
// Access: Public, Virtual
// Description: If has_cull_callback() returns true, this function
// will be called during the cull traversal to perform
// any additional operations that should be performed at
// cull time. This may include additional manipulation
// of render state or additional visible/invisible
// decisions, or any other arbitrary operation.
//
// At the time this function is called, the current
// node's transform and state have not yet been applied
// to the net_transform and net_state. This callback
// may modify the node_transform and node_state to apply
// an effective change to the render state at this
// level.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
cull_callback(CullTraverser *trav, CullTraverserData &data,
CPT(TransformState) &node_transform,
CPT(RenderState) &) const {
CPT(TransformState) net_transform = data._modelview_transform;
if (net_transform->is_singular()) {
// If we're under a singular transform, never mind.
return;
}
// Since the "net" transform from the cull traverser already
// includes the inverse camera transform, the camera transform is
// identity.
CPT(TransformState) camera_transform = TransformState::make_identity();
// But if we're rotating to face something other than the camera, we
// have to compute the "camera" transform to compensate for that.
if (!_look_at.is_empty()) {
camera_transform = trav->get_camera_transform()->invert_compose(_look_at.get_net_transform());
}
compute_billboard(node_transform, net_transform, camera_transform);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::has_adjust_transform
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if adjust_transform() has been defined, and
// therefore the RenderEffect has some effect on the
// node's apparent local and net transforms.
////////////////////////////////////////////////////////////////////
bool BillboardEffect::
has_adjust_transform() const {
// A BillboardEffect can only affect the net transform when it is to
// a particular node. A billboard to a camera is camera-dependent,
// of course, so it has no effect in the absence of any particular
// camera viewing it.
return !_look_at.is_empty();
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::adjust_transform
// Access: Public, Virtual
// Description: Performs some operation on the node's apparent net
// and/or local transforms. This will only be called if
// has_adjust_transform() is redefined to return true.
//
// Both parameters are in/out. The original transforms
// will be passed in, and they may (or may not) be
// modified in-place by the RenderEffect.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
adjust_transform(CPT(TransformState) &net_transform,
CPT(TransformState) &node_transform) const {
// A BillboardEffect can only affect the net transform when it is to
// a particular node. A billboard to a camera is camera-dependent,
// of course, so it has no effect in the absence of any particular
// camera viewing it.
if (_look_at.is_empty()) {
return;
}
CPT(TransformState) camera_transform = _look_at.get_net_transform();
compute_billboard(node_transform, net_transform, camera_transform);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::compare_to_impl
// Access: Protected, Virtual
// Description: Intended to be overridden by derived BillboardEffect
// types to return a unique number indicating whether
// this BillboardEffect is equivalent to the other one.
//
// This should return 0 if the two BillboardEffect objects
// are equivalent, a number less than zero if this one
// should be sorted before the other one, and a number
// greater than zero otherwise.
//
// This will only be called with two BillboardEffect
// objects whose get_type() functions return the same.
////////////////////////////////////////////////////////////////////
int BillboardEffect::
compare_to_impl(const RenderEffect *other) const {
const BillboardEffect *ta;
DCAST_INTO_R(ta, other, 0);
if (_axial_rotate != ta->_axial_rotate) {
return _axial_rotate - ta->_axial_rotate;
}
if (_eye_relative != ta->_eye_relative) {
return _eye_relative - ta->_eye_relative;
}
if (_offset != ta->_offset) {
return _offset < ta->_offset ? -1 : 1;
}
int compare = _up_vector.compare_to(ta->_up_vector);
if (compare != 0) {
return compare;
}
compare = _look_at.compare_to(ta->_look_at);
if (compare != 0) {
return compare;
}
compare = _look_at_point.compare_to(ta->_look_at_point);
if (compare != 0) {
return compare;
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::compute_billboard
// Access: Private
// Description: Computes the billboard operation given the parent's
// net transform and the camera transform.
//
// The result is applied to node_transform, which is
// modified in-place.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
compute_billboard(CPT(TransformState) &node_transform,
const TransformState *net_transform,
const TransformState *camera_transform) const {
// First, extract out just the translation component of the node's
// local transform. This gets applied to the net transform, to
// compute the look-at direction properly.
CPT(TransformState) translate = TransformState::make_pos(node_transform->get_pos());
// And then the translation gets removed from the node, but we keep
// its rotation etc., which gets applied after the billboard
// operation.
node_transform = node_transform->set_pos(LPoint3f(0.0f, 0.0f, 0.0f));
CPT(TransformState) rel_transform =
net_transform->compose(translate)->invert_compose(camera_transform);
const LMatrix4f &rel_mat = rel_transform->get_mat();
// Determine the look_at point in the camera space.
LVector3f camera_pos, up;
// If this is an eye-relative Billboard, then (a) the up vector is
// relative to the camera, not to the world, and (b) the look
// direction is towards the plane that contains the camera,
// perpendicular to the forward direction, not directly to the
// camera.
if (_eye_relative) {
up = _up_vector * rel_mat;
camera_pos = LVector3f::forward() * rel_mat;
} else {
up = _up_vector;
camera_pos = -(_look_at_point * rel_mat);
}
// Now determine the rotation matrix for the Billboard.
LMatrix4f rotate;
if (_axial_rotate) {
heads_up(rotate, camera_pos, up);
} else {
look_at(rotate, camera_pos, up);
}
// Also slide the billboard geometry towards the camera according to
// the offset factor.
if (_offset != 0.0f) {
LVector3f translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2));
translate.normalize();
translate *= _offset;
rotate.set_row(3, translate);
}
node_transform = translate->compose(TransformState::make_mat(rotate))->compose(node_transform);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// BillboardEffect.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
write_datagram(BamWriter *manager, Datagram &dg) {
RenderEffect::write_datagram(manager, dg);
dg.add_bool(_off);
_up_vector.write_datagram(dg);
dg.add_bool(_eye_relative);
dg.add_bool(_axial_rotate);
dg.add_float32(_offset);
_look_at_point.write_datagram(dg);
// *** We don't write out the _look_at NodePath right now. Maybe
// we should.
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type BillboardEffect is encountered
// in the Bam file. It should create the BillboardEffect
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *BillboardEffect::
make_from_bam(const FactoryParams &params) {
BillboardEffect *effect = new BillboardEffect;
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
effect->fillin(scan, manager);
return effect;
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::fillin
// Access: Protected
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new BillboardEffect.
////////////////////////////////////////////////////////////////////
void BillboardEffect::
fillin(DatagramIterator &scan, BamReader *manager) {
RenderEffect::fillin(scan, manager);
_off = scan.get_bool();
_up_vector.read_datagram(scan);
_eye_relative = scan.get_bool();
_axial_rotate = scan.get_bool();
_offset = scan.get_float32();
_look_at_point.read_datagram(scan);
}