CompassEffects and node-relative BillboardEffects now work on cameras

This commit is contained in:
David Rose 2005-03-05 14:28:58 +00:00
parent 3b8c754898
commit d46a88a76e
11 changed files with 308 additions and 67 deletions

View File

@ -31,6 +31,7 @@
#include "lens.h"
#include "perspectiveLens.h"
#include "pointerTo.h"
#include "compassEffect.h"
TypeHandle GraphicsOutput::_type_handle;
@ -627,6 +628,13 @@ make_cube_map(const string &name, int size, bool to_ram,
}
}
// Usually, we want the whole camera_rig to keep itself unrotated
// with respect to the world coordinate space, so the user can apply
// TexGenAttrib::M_world_cube_map to the objects on which the cube
// map texture is applied. If for some reason the user doesn't want
// this behavior, he can take this effect off again.
camera_rig.node()->set_effect(CompassEffect::make(NodePath()));
PT(Texture) tex = new Texture(name);
tex->setup_cube_map();
tex->set_wrap_u(Texture::WM_clamp);

View File

@ -131,8 +131,13 @@ void BillboardEffect::
cull_callback(CullTraverser *trav, CullTraverserData &data,
CPT(TransformState) &node_transform,
CPT(RenderState) &) const {
CPT(TransformState) net_transform = data._net_transform->compose(node_transform);
const TransformState *camera_transform = trav->get_camera_transform();
CPT(TransformState) net_transform = data._net_transform;
if (net_transform->is_singular()) {
// If we're under a singular transform, never mind.
return;
}
CPT(TransformState) camera_transform = trav->get_camera_transform();
// Determine the relative transform to our camera (or other look_at
// coordinate space).
@ -140,53 +145,58 @@ cull_callback(CullTraverser *trav, CullTraverserData &data,
camera_transform = _look_at.get_net_transform();
}
if (net_transform->is_singular()) {
// If we're under a singular transform, never mind.
return;
}
CPT(TransformState) billboard_transform =
compute_billboard(net_transform, camera_transform);
CPT(TransformState) rel_transform =
net_transform->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 = node_transform->compose(TransformState::make_mat(rotate));
node_transform = billboard_transform->compose(node_transform);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::has_net_transform
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if net_transform() has been defined, and
// therefore the RenderEffect has some effect on the
// node's apparent net transform.
////////////////////////////////////////////////////////////////////
bool BillboardEffect::
has_net_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::net_transform
// Access: Public, Virtual
// Description: Given the node's parent's net transform, compute its
// parent's new net transform after application of the
// RenderEffect. Presumably this interposes some
// special transform derived from the RenderEffect.
// This may only be called if has_net_transform(),
// above, has been defined to return true.
////////////////////////////////////////////////////////////////////
CPT(TransformState) BillboardEffect::
net_transform(const TransformState *orig_net_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 orig_net_transform;
}
CPT(TransformState) camera_transform = _look_at.get_net_transform();
CPT(TransformState) billboard_transform =
compute_billboard(orig_net_transform, camera_transform);
return orig_net_transform->compose(billboard_transform);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::compare_to_impl
// Access: Protected, Virtual
@ -231,6 +241,57 @@ compare_to_impl(const RenderEffect *other) const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::compute_billboard
// Access: Private
// Description: Computes the billboard operation given the parent's
// net transform and the camera transform.
////////////////////////////////////////////////////////////////////
CPT(TransformState) BillboardEffect::
compute_billboard(const TransformState *net_transform,
const TransformState *camera_transform) const {
CPT(TransformState) rel_transform =
net_transform->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);
}
return TransformState::make_mat(rotate);
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::register_with_read_factory
// Access: Public, Static

View File

@ -63,9 +63,16 @@ public:
CPT(TransformState) &node_transform,
CPT(RenderState) &node_state) const;
virtual bool has_net_transform() const;
virtual CPT(TransformState) net_transform(const TransformState *orig_net_transform) const;
protected:
virtual int compare_to_impl(const RenderEffect *other) const;
private:
CPT(TransformState) compute_billboard(const TransformState *net_transform,
const TransformState *camera_transform) const;
private:
bool _off;
LVector3f _up_vector;

View File

@ -143,15 +143,59 @@ cull_callback(CullTraverser *, CullTraverserData &data,
return;
}
CPT(TransformState) net_transform = data._net_transform->compose(node_transform);
CPT(TransformState) true_net_transform = data._net_transform;
CPT(TransformState) want_transform = net_transform(data._net_transform);
// Compute the reference transform: our transform, as applied to the
// reference node.
// Now compute the transform that will convert true_net_transform to
// want_transform. This is inv(true_net_transform) * want_transform.
CPT(TransformState) compass_transform =
true_net_transform->invert_compose(want_transform);
// And modify our local node's apparent transform so that
// true_net_transform->compose(new_node_transform) produces the same
// result we would have gotten had we actually computed
// want_transform->compose(orig_node_transform).
node_transform = node_transform->compose(compass_transform);
}
////////////////////////////////////////////////////////////////////
// Function: CompassEffect::has_net_transform
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if net_transform() has been defined, and
// therefore the RenderEffect has some effect on the
// node's apparent net transform.
////////////////////////////////////////////////////////////////////
bool CompassEffect::
has_net_transform() const {
return (_properties != 0);
}
////////////////////////////////////////////////////////////////////
// Function: CompassEffect::net_transform
// Access: Public, Virtual
// Description: Given the node's parent's net transform, compute its
// parent's new net transform after application of the
// RenderEffect. Presumably this interposes some
// special transform derived from the RenderEffect.
// This may only be called if has_net_transform(),
// above, has been defined to return true.
////////////////////////////////////////////////////////////////////
CPT(TransformState) CompassEffect::
net_transform(const TransformState *orig_net_transform) const {
if (_properties == 0) {
// Nothing to do.
return orig_net_transform;
}
// The reference transform: where we are acting as if we inherit
// from. Either the root node (identity) or the specified reference
// node.
CPT(TransformState) ref_transform;
if (_reference.is_empty()) {
ref_transform = node_transform;
ref_transform = TransformState::make_identity();
} else {
ref_transform = _reference.get_net_transform()->compose(node_transform);
ref_transform = _reference.get_net_transform();
}
// Now compute the transform we actually want to achieve. This is
@ -166,7 +210,7 @@ cull_callback(CullTraverser *, CullTraverserData &data,
} else {
// How much of the pos do we want to steal? We can always
// determine a transform's pos, even if it's nondecomposable.
LVecBase3f want_pos = net_transform->get_pos();
LVecBase3f want_pos = orig_net_transform->get_pos();
const LVecBase3f &ref_pos = ref_transform->get_pos();
if ((_properties & P_x) != 0) {
want_pos[0] = ref_pos[0];
@ -180,7 +224,7 @@ cull_callback(CullTraverser *, CullTraverserData &data,
if ((_properties & ~P_pos) == 0) {
// If we only want to steal the pos, that's pretty easy.
want_transform = net_transform->set_pos(want_pos);
want_transform = orig_net_transform->set_pos(want_pos);
} else if ((_properties & (P_rot | P_scale)) == (P_rot | P_scale)) {
// If we want to steal everything *but* the pos, also easy.
@ -189,7 +233,7 @@ cull_callback(CullTraverser *, CullTraverserData &data,
} else {
// For any other combination, we have to be able to decompose both
// transforms.
if (!net_transform->has_components() ||
if (!orig_net_transform->has_components() ||
!ref_transform->has_components()) {
// If we can't decompose, just do the best we can: steal
// everything but the pos.
@ -197,12 +241,12 @@ cull_callback(CullTraverser *, CullTraverserData &data,
} else {
// If we can decompose, then take only the components we want.
LQuaternionf want_quat = net_transform->get_quat();
LQuaternionf want_quat = orig_net_transform->get_quat();
if ((_properties & P_rot) != 0) {
want_quat = ref_transform->get_quat();
}
LVecBase3f want_scale = net_transform->get_scale();
LVecBase3f want_scale = orig_net_transform->get_scale();
const LVecBase3f &ref_scale = ref_transform->get_scale();
if ((_properties & P_sx) != 0) {
want_scale[0] = ref_scale[0];
@ -220,12 +264,7 @@ cull_callback(CullTraverser *, CullTraverserData &data,
}
}
// Now compute the transform that will convert net_transform to
// want_transform. This is inv(net_transform) * want_transform.
CPT(TransformState) compass_transform =
net_transform->invert_compose(want_transform);
node_transform = node_transform->compose(compass_transform);
return want_transform;
}
////////////////////////////////////////////////////////////////////

View File

@ -88,6 +88,9 @@ public:
CPT(TransformState) &node_transform,
CPT(RenderState) &node_state) const;
virtual bool has_net_transform() const;
virtual CPT(TransformState) net_transform(const TransformState *orig_net_transform) const;
protected:
virtual int compare_to_impl(const RenderEffect *other) const;

View File

@ -658,8 +658,10 @@ get_transform(const NodePath &other) const {
int a_count, b_count;
if (find_common_ancestor(*this, other, a_count, b_count) == (NodePathComponent *)NULL) {
if (allow_unrelated_wrt) {
pgraph_cat.debug()
<< *this << " is not related to " << other << "\n";
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< *this << " is not related to " << other << "\n";
}
} else {
pgraph_cat.error()
<< *this << " is not related to " << other << "\n";
@ -667,8 +669,19 @@ get_transform(const NodePath &other) const {
}
}
CPT(TransformState) a_transform = r_get_partial_transform(_head, a_count);
CPT(TransformState) b_transform = r_get_partial_transform(other._head, b_count);
CPT(TransformState) a_transform, b_transform;
a_transform = r_get_partial_transform(_head, a_count);
if (a_transform != (TransformState *)NULL) {
b_transform = r_get_partial_transform(other._head, b_count);
}
if (b_transform == (TransformState *)NULL) {
// If either path involved a node with a net_transform
// RenderEffect applied, we have to go all the way up to the root
// to get the right answer.
a_transform = r_get_net_transform(_head);
b_transform = r_get_net_transform(other._head);
}
return b_transform->invert_compose(a_transform);
}
@ -4804,8 +4817,15 @@ r_get_net_transform(NodePathComponent *comp) const {
if (comp == (NodePathComponent *)NULL) {
return TransformState::make_identity();
} else {
CPT(TransformState) net_transform = r_get_net_transform(comp->get_next());
CPT(TransformState) transform = comp->get_node()->get_transform();
return r_get_net_transform(comp->get_next())->compose(transform);
CPT(RenderEffects) effects = comp->get_node()->get_effects();
if (effects->has_net_transform()) {
net_transform = effects->net_transform(net_transform);
}
return net_transform->compose(transform);
}
}
@ -4816,12 +4836,19 @@ r_get_net_transform(NodePathComponent *comp) const {
// indicated component node from the nth node above it.
// If n exceeds the length of the path, this returns the
// net transform from the root of the graph.
//
// If any node in the path had a net_transform effect
// applied, returns NULL--in this case the partial
// transform cannot be easily determined.
////////////////////////////////////////////////////////////////////
CPT(TransformState) NodePath::
r_get_partial_transform(NodePathComponent *comp, int n) const {
if (n == 0 || comp == (NodePathComponent *)NULL) {
return TransformState::make_identity();
} else {
if (comp->get_node()->get_effects()->has_net_transform()) {
return NULL;
}
CPT(TransformState) transform = comp->get_node()->get_transform();
return r_get_partial_transform(comp->get_next(), n - 1)->compose(transform);
}

View File

@ -161,6 +161,34 @@ cull_callback(CullTraverser *, CullTraverserData &,
CPT(TransformState) &, CPT(RenderState) &) const {
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffect::has_net_transform
// Access: Public, Virtual
// Description: Should be overridden by derived classes to return
// true if net_transform() has been defined, and
// therefore the RenderEffect has some effect on the
// node's apparent net transform.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
has_net_transform() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffect::net_transform
// Access: Public, Virtual
// Description: Given the node's parent's net transform, compute its
// parent's new net transform after application of the
// RenderEffect. Presumably this interposes some
// special transform derived from the RenderEffect.
// This may only be called if has_net_transform(),
// above, has been defined to return true.
////////////////////////////////////////////////////////////////////
CPT(TransformState) RenderEffect::
net_transform(CPT(TransformState) &orig_net_transform) const {
return orig_net_transform;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffect::output
// Access: Published, Virtual

View File

@ -75,6 +75,9 @@ public:
CPT(TransformState) &node_transform,
CPT(RenderState) &node_state) const;
virtual bool has_net_transform() const;
virtual CPT(TransformState) net_transform(CPT(TransformState) &orig_net_transform) const;
PUBLISHED:
INLINE int compare_to(const RenderEffect &other) const;

View File

@ -199,3 +199,21 @@ has_cull_callback() const {
}
return ((_flags & F_has_cull_callback) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::has_net_transform
// Access: Public
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of an effect with a compute_net_transform on this
// state.
////////////////////////////////////////////////////////////////////
INLINE bool RenderEffects::
has_net_transform() const {
if ((_flags & F_checked_net_transform) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal net_transform cache.
((RenderEffects *)this)->determine_net_transform();
}
return ((_flags & F_has_net_transform) != 0);
}

View File

@ -420,6 +420,29 @@ cull_callback(CullTraverser *trav, CullTraverserData &data,
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::net_transform
// Access: Public
// Description: Calls net_transform() on all effects. You may check
// has_net_transform() first to see if any effects
// define this method to do anything useful.
//
// The order in which the individual effects are applied
// is not defined, so if more than one effect applies a
// change to the transform on any particular node, you
// might get indeterminate results.
////////////////////////////////////////////////////////////////////
CPT(TransformState) RenderEffects::
net_transform(const TransformState *orig_net_transform) const {
CPT(TransformState) net_transform = orig_net_transform;
Effects::const_iterator ei;
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
net_transform = (*ei)._effect->net_transform(net_transform);
}
return net_transform;
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::get_num_states
// Access: Published, Static
@ -573,6 +596,24 @@ determine_cull_callback() {
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::determine_net_transform
// Access: Private
// Description: This is the private implementation of has_net_transform().
////////////////////////////////////////////////////////////////////
void RenderEffects::
determine_net_transform() {
_flags |= F_checked_net_transform;
Effects::const_iterator ei;
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
if ((*ei)._effect->has_net_transform()) {
_flags |= F_has_net_transform;
return;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: RenderEffects::register_with_read_factory
// Access: Public, Static

View File

@ -102,11 +102,15 @@ public:
CPT(TransformState) &node_transform,
CPT(RenderState) &node_state) const;
INLINE bool has_net_transform() const;
CPT(TransformState) net_transform(const TransformState *orig_net_transform) const;
private:
static CPT(RenderEffects) return_new(RenderEffects *state);
void determine_decal();
void determine_show_bounds();
void determine_cull_callback();
void determine_net_transform();
private:
typedef pset<const RenderEffects *, indirect_less<const RenderEffects *> > States;
@ -144,6 +148,8 @@ private:
F_has_show_bounds = 0x0008,
F_checked_cull_callback = 0x0010,
F_has_cull_callback = 0x0020,
F_checked_net_transform = 0x0040,
F_has_net_transform = 0x0080,
};
int _flags;