support velocity for collisions

This commit is contained in:
David Rose 2002-06-11 23:47:25 +00:00
parent 2f57e39d93
commit b39fa23b8e
17 changed files with 348 additions and 23 deletions

View File

@ -156,6 +156,43 @@ get_inv_wrt_space() const {
return _inv_wrt_space;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionEntry::set_from_velocity
// Access: Public
// Description: Indicates the velocity associated with the "from"
// object, in the object's coordinate space.
////////////////////////////////////////////////////////////////////
INLINE void CollisionEntry::
set_from_velocity(const LVector3f &vel) {
_from_velocity = vel;
_flags |= F_has_from_velocity;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionEntry::has_from_velocity
// Access: Public
// Description: Returns true if the "from" object had an indicated
// velocity, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool CollisionEntry::
has_from_velocity() const {
return (_flags & F_has_from_velocity) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionEntry::get_from_velocity
// Access: Public
// Description: Returns the instantaneous velocity of the "from"
// object, in its own coordinate space. This represents
// the delta between its current position and its
// position last frame.
////////////////////////////////////////////////////////////////////
INLINE const LVector3f &CollisionEntry::
get_from_velocity() const {
nassertr(has_from_velocity(), _from_velocity);
return _from_velocity;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionEntry::set_into_intersection_point
// Access: Public

View File

@ -63,6 +63,10 @@ PUBLISHED:
INLINE const LMatrix4f &get_wrt_space() const;
INLINE const LMatrix4f &get_inv_wrt_space() const;
INLINE void set_from_velocity(const LVector3f &vel);
INLINE bool has_from_velocity() const;
INLINE const LVector3f &get_from_velocity() const;
INLINE void set_into_intersection_point(const LPoint3f &point);
INLINE bool has_into_intersection_point() const;
INLINE const LPoint3f &get_into_intersection_point() const;
@ -94,10 +98,12 @@ private:
F_has_into_intersection_point = 0x0001,
F_has_into_surface_normal = 0x0002,
F_has_into_depth = 0x0004,
F_has_from_velocity = 0x0008,
};
int _flags;
LVector3f _from_velocity;
LPoint3f _into_intersection_point;
LVector3f _into_surface_normal;
float _into_depth;

View File

@ -132,7 +132,7 @@ handle_entries() {
normal[2] = 0.0f;
}
// Just to be on the safe size, we normalize the normal
// vector, even though it really out to be unit-length
// vector, even though it really ought to be unit-length
// already (unless we just forced it horizontal, above).
normal.normalize();

View File

@ -63,6 +63,20 @@ prepare_collider(const ColliderDef &def) {
} else {
GeometricBoundingVolume *gbv;
DCAST_INTO_V(gbv, bv.make_copy());
if (def._node->has_velocity()) {
// If the node has a velocity, we have to include the starting
// point in the volume as well.
// Strictly speaking, we should actually transform gbv backward
// by get_velocity(), and extend by *that* volume, instead of
// just extending by the single point at -get_velocity().
// However, assuming the solids within a moving CollisionNode
// tend to be near the origin, this will generally produce the
// same results, and is much easier to compute.
gbv->extend_by(LPoint3f(-def._node->get_velocity()));
}
gbv->xform(def._space);
_local_bounds.push_back(gbv);
}

View File

@ -19,7 +19,7 @@
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::set_collide_mask
// Access: Public
// Access: Published
// Description: Simultaneously sets both the "from" and "into"
// CollideMask values to the same thing.
////////////////////////////////////////////////////////////////////
@ -31,7 +31,7 @@ set_collide_mask(CollideMask mask) {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::set_from_collide_mask
// Access: Public
// Access: Published
// Description: Sets the "from" CollideMask. In order for a
// collision to be detected from this object into
// another object, the intersection of this object's
@ -45,7 +45,7 @@ set_from_collide_mask(CollideMask mask) {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::set_into_collide_mask
// Access: Public
// Access: Published
// Description: Sets the "into" CollideMask. In order for a
// collision to be detected from another object into
// this object, the intersection of the other object's
@ -64,7 +64,7 @@ set_into_collide_mask(CollideMask mask) {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_from_collide_mask
// Access: Public
// Access: Published
// Description: Returns the current "from" CollideMask. In order for
// a collision to be detected from this object into
// another object, the intersection of this object's
@ -78,7 +78,7 @@ get_from_collide_mask() const {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_into_collide_mask
// Access: Public
// Access: Published
// Description: Returns the current "into" CollideMask. In order for
// a collision to be detected from another object into
// this object, the intersection of the other object's
@ -92,7 +92,7 @@ get_into_collide_mask() const {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::set_collide_geom
// Access: Public
// Access: Published
// Description: Sets the state of the "collide geom" flag for this
// CollisionNode. Normally, this is false; when this is
// set true, the CollisionSolids in this node will test
@ -107,23 +107,27 @@ get_into_collide_mask() const {
////////////////////////////////////////////////////////////////////
INLINE void CollisionNode::
set_collide_geom(bool flag) {
_collide_geom = flag;
if (flag) {
_flags |= F_collide_geom;
} else {
_flags &= ~F_collide_geom;
}
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_collide_geom
// Access: Public
// Access: Published
// Description: Returns the current state of the collide_geom flag.
// See set_collide_geom().
////////////////////////////////////////////////////////////////////
INLINE bool CollisionNode::
get_collide_geom() const {
return _collide_geom;
return (_flags & F_collide_geom) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_num_solids
// Access: Public
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE int CollisionNode::
@ -133,7 +137,7 @@ get_num_solids() const {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_solid
// Access: Public
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE CollisionSolid *CollisionNode::
@ -144,7 +148,7 @@ get_solid(int n) const {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::remove_solid
// Access: Public
// Access: Published
// Description: Removes the solid with the indicated index. This
// will shift all subsequent indices down by one.
////////////////////////////////////////////////////////////////////
@ -157,7 +161,7 @@ remove_solid(int n) {
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::add_solid
// Access: Public
// Access: Published
// Description: Adds the indicated solid to the node. Returns the
// index of the new solid within the node's list of
// solids.
@ -168,3 +172,39 @@ add_solid(CollisionSolid *solid) {
mark_bound_stale();
return _solids.size() - 1;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::clear_velocity
// Access: Published
// Description: Removes the velocity information associated with the
// node. See set_velocity().
////////////////////////////////////////////////////////////////////
INLINE void CollisionNode::
clear_velocity() {
_flags &= ~F_has_velocity;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::has_velocity
// Access: Published
// Description: Returns true if the node has an associated velocity,
// false otherwise. See set_velocity().
////////////////////////////////////////////////////////////////////
INLINE bool CollisionNode::
has_velocity() const {
return (_flags & F_has_velocity) != 0;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_velocity
// Access: Published
// Description: Returns the instantaneous velocity of the node, in
// its own coordinate space. This represents the delta
// between its current position and its position last
// frame. See set_velocity().
////////////////////////////////////////////////////////////////////
INLINE const LVector3f &CollisionNode::
get_velocity() const {
nassertr(has_velocity(), _velocity);
return _velocity;
}

View File

@ -22,6 +22,10 @@
#include "geomNode.h"
#include "cullTraverserData.h"
#include "cullTraverser.h"
#include "renderState.h"
#include "transformState.h"
#include "colorScaleAttrib.h"
#include "transparencyAttrib.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "bamReader.h"
@ -40,7 +44,8 @@ CollisionNode(const string &name) :
PandaNode(name),
_from_collide_mask(CollideMask::all_on()),
_into_collide_mask(CollideMask::all_on()),
_collide_geom(false)
_velocity(0.0f, 0.0f, 0.0f),
_flags(0)
{
// CollisionNodes are hidden by default.
set_draw_mask(DrawMask::all_off());
@ -56,7 +61,8 @@ CollisionNode(const CollisionNode &copy) :
PandaNode(copy),
_from_collide_mask(copy._from_collide_mask),
_into_collide_mask(copy._into_collide_mask),
_collide_geom(copy._collide_geom),
_velocity(copy._velocity),
_flags(copy._flags),
_solids(copy._solids)
{
}
@ -212,6 +218,22 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
trav->traverse(next_data);
}
if (has_velocity()) {
// If we have a velocity, also draw the previous frame's position,
// ghosted.
CPT(TransformState) transform = TransformState::make_pos(-get_velocity());
for (si = _solids.begin(); si != _solids.end(); ++si) {
CollisionSolid *solid = (*si);
PandaNode *node = solid->get_viz();
CullTraverserData next_data(data, node);
next_data._render_transform =
next_data._render_transform->compose(transform);
next_data._state = get_last_pos_state();
trav->traverse(next_data);
}
}
// Now carry on to render our child nodes.
return true;
}
@ -232,6 +254,31 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::set_velocity
// Access: Published, Virtual
// Description: Indicates the instantaneous velocity of the node.
// This is only meaningful for nodes that represent
// "colliders", that is, nodes added to a
// CollisionTraverser to be tested for collision into
// other objects.
//
// The velocity vector represents the delta from this
// node's position last frame to its current position.
// The collision traverser automatically clears it after
// it has performed the traversal.
//
// This velocity information is optional and, if
// available, is used by the collision traverser to help
// determine which walls the collider is actually
// intersecting with, and which it is simply passing by.
////////////////////////////////////////////////////////////////////
void CollisionNode::
set_velocity(const LVector3f &vel) {
_velocity = vel;
_flags |= F_has_velocity;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::recompute_bound
// Access: Protected, Virtual
@ -293,6 +340,29 @@ recompute_internal_bound() {
return bound;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::get_last_pos_state
// Access: Protected
// Description: Returns a RenderState for rendering the ghosted
// collision solid that represents the previous frame's
// position, for those collision nodes that indicate a
// velocity.
////////////////////////////////////////////////////////////////////
CPT(RenderState) CollisionNode::
get_last_pos_state() {
// Once someone asks for this pointer, we hold its reference count
// and never free it.
static CPT(RenderState) state = (const RenderState *)NULL;
if (state == (const RenderState *)NULL) {
state = RenderState::make
(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, 0.5f)),
TransparencyAttrib::make(TransparencyAttrib::M_alpha));
}
return state;
}
////////////////////////////////////////////////////////////////////
// Function: CollisionNode::register_with_read_factory
// Access: Public, Static
@ -322,7 +392,7 @@ write_datagram(BamWriter *manager, Datagram &dg) {
dg.add_uint32(_from_collide_mask.get_word());
dg.add_uint32(_into_collide_mask.get_word());
dg.add_bool(_collide_geom);
dg.add_uint8(_flags);
}
////////////////////////////////////////////////////////////////////
@ -387,5 +457,5 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_from_collide_mask.set_word(scan.get_uint32());
_into_collide_mask.set_word(scan.get_uint32());
_collide_geom = scan.get_bool();
_flags = scan.get_uint8();
}

View File

@ -68,17 +68,31 @@ PUBLISHED:
INLINE void remove_solid(int n);
INLINE int add_solid(CollisionSolid *solid);
virtual void set_velocity(const LVector3f &vel);
INLINE void clear_velocity();
INLINE bool has_velocity() const;
INLINE const LVector3f &get_velocity() const;
protected:
virtual BoundingVolume *recompute_bound();
virtual BoundingVolume *recompute_internal_bound();
private:
CPT(RenderState) get_last_pos_state();
// This data is not cycled, for now. We assume the collision
// traversal will take place in App only. Perhaps we will revisit
// this later.
CollideMask _from_collide_mask;
CollideMask _into_collide_mask;
bool _collide_geom;
LVector3f _velocity;
enum Flags {
F_collide_geom = 0x0001,
F_has_velocity = 0x0002,
// Presently only 8 bits are written to the bam file.
};
int _flags;
typedef pvector< PT(CollisionSolid) > Solids;
Solids _solids;

View File

@ -240,7 +240,48 @@ test_intersection_from_sphere(CollisionHandler *record,
const CollisionSphere *sphere;
DCAST_INTO_R(sphere, entry.get_from(), 0);
LPoint3f from_center = sphere->get_center() * entry.get_wrt_space();
LPoint3f orig_center = sphere->get_center() * entry.get_wrt_space();
LPoint3f from_center = orig_center;
bool moved_from_center = false;
if (entry.has_from_velocity()) {
// If we have a velocity indication, we use that to determine some
// more properties of the collision.
LPoint3f b = from_center;
LPoint3f a = (sphere->get_center() - entry.get_from_velocity()) * entry.get_wrt_space();
LVector3f delta = b - a;
// First, there is no collision if the "from" object is moving in
// the same direction as the plane's normal.
float dot = delta.dot(get_normal());
if (dot > 0.0f) {
return 0;
}
if (IS_NEARLY_ZERO(dot)) {
// If we're moving parallel to the plane, the sphere is tested
// at its final point. Leave it as it is.
} else {
// Otherwise, we're moving into the plane; the sphere is tested
// at the point along its path that is closest to intersecting
// the plane. This may be the actual intersection point, or it
// may be the starting point or the final point.
float t = -(dist_to_plane(a) / dot);
if (t >= 1.0f) {
// Leave it where it is.
} else if (t < 0.0f) {
from_center = a;
moved_from_center = true;
} else {
from_center = a + t * delta;
moved_from_center = true;
}
}
}
LVector3f from_radius_v =
LVector3f(sphere->get_radius(), 0.0f, 0.0f) * entry.get_wrt_space();
float from_radius = length(from_radius_v);
@ -325,9 +366,16 @@ test_intersection_from_sphere(CollisionHandler *record,
LVector3f into_normal = get_normal() * entry.get_inv_wrt_space();
float into_depth = from_radius - dist;
if (moved_from_center) {
// We have to base the depth of intersection on the sphere's final
// resting point, not the point from which we tested the
// intersection.
into_depth = from_radius - dist_to_plane(orig_center);
}
new_entry->set_into_surface_normal(into_normal);
new_entry->set_into_depth(into_depth);
new_entry->set_into_intersection_point(from_center);
record->add_entry(new_entry);
return 1;

View File

@ -251,6 +251,17 @@ traverse(const NodePath &root) {
++hi;
}
}
if (auto_clear_velocity) {
// Clear all the velocities for next time.
OrderedColliders::iterator ci;
for (ci = _ordered_colliders.begin();
ci != _ordered_colliders.end();
++ci) {
CollisionNode *node = (*ci);
node->clear_velocity();
}
}
}
////////////////////////////////////////////////////////////////////
@ -373,6 +384,10 @@ r_traverse(CollisionLevelState &level_state) {
entry._from = level_state.get_collider(c);
entry._from_space = level_state.get_space(c);
if (entry._from_node->has_velocity()) {
entry.set_from_velocity(entry._from_node->get_velocity());
}
NodePath root;
const LMatrix4f &into_space_inv =
root.get_mat(entry._into_node_path);

View File

@ -40,6 +40,11 @@ ConfigureFn(config_collide) {
init_libcollide();
}
// Set this false to stop the CollisionTraverser from automatically
// clearing the velocity associated with its CollisionNodes after it
// has traversed them. Mainly useful for visualizing the velocities.
const bool auto_clear_velocity = config_collide.GetBool("auto-clear-velocity", true);
////////////////////////////////////////////////////////////////////
// Function: init_libcollide
// Description: Initializes the library. This must be called at

View File

@ -24,6 +24,8 @@
NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA);
extern const bool auto_clear_velocity;
extern EXPCL_PANDA void init_libcollide();
#endif

View File

@ -1167,6 +1167,25 @@ as_light() {
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::set_velocity
// Access: Public, Virtual
// Description: Indicates the instantaneous velocity of this node.
// This function is meaningless to most kinds of nodes;
// it is implemented only for CollisionNodes and is
// intended to inform the collision system of velocity.
//
// It is defined at this level only as an abstract
// interface to allow setting the velocity of a
// collision node without having to link with, or know
// anything about, the collision system.
//
// See CollisionNode::set_velocity().
////////////////////////////////////////////////////////////////////
void PandaNode::
set_velocity(const LVector3f &) {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::propagate_stale_bound
// Access: Protected, Virtual

View File

@ -166,6 +166,7 @@ PUBLISHED:
public:
virtual bool is_geom_node() const;
virtual Light *as_light();
virtual void set_velocity(const LVector3f &vel);
protected:
// Inherited from BoundedObject

View File

@ -112,8 +112,10 @@ DriveInterface(const string &name) :
_button_events_input = define_input("button_events", ButtonEventList::get_class_type());
_transform_output = define_output("transform", EventStoreMat4::get_class_type());
_velocity_output = define_output("velocity", EventStoreVec3::get_class_type());
_transform = new EventStoreMat4(LMatrix4f::ident_mat());
_velocity = new EventStoreVec3(LVector3f::zero());
_forward_speed = drive_forward_speed;
_reverse_speed = drive_reverse_speed;
@ -239,10 +241,12 @@ get_mat() const {
void DriveInterface::
force_dgraph() {
_transform->set_value(_mat);
_velocity->set_value(_vel);
DataNodeTransmit output;
output.reserve(get_num_outputs());
output.set_data(_transform_output, EventParameter(_transform));
output.set_data(_velocity_output, EventParameter(_velocity));
DataGraphTraverser dg_trav;
dg_trav.traverse_below(this, output);
@ -356,6 +360,7 @@ apply(double x, double y, bool any_button) {
}
if (_speed == 0.0f && _rot_speed == 0.0f) {
_vel.set(0.0f, 0.0f, 0.0f);
return;
}
@ -371,7 +376,8 @@ apply(double x, double y, bool any_button) {
LMatrix3f::rotate_mat_normaxis(_hpr[0], LVector3f::up(_cs), _cs);
// Take a step in the direction of our previous heading.
LVector3f step = distance * (LVector3f::forward(_cs) * rot_mat);
_vel = LVector3f::forward(_cs) * distance;
LVector3f step = (_vel * rot_mat);
// To prevent upward drift due to numerical errors, force the
// vertical component of our step to zero (it should be pretty near
@ -503,5 +509,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
apply(x, y, _mods.is_any_down());
_transform->set_value(_mat);
_velocity->set_value(_vel);
output.set_data(_transform_output, EventParameter(_transform));
output.set_data(_velocity_output, EventParameter(_velocity));
}

View File

@ -133,6 +133,7 @@ private:
LPoint3f _xyz;
LVecBase3f _hpr;
LMatrix4f _mat;
LVector3f _vel;
float _force_roll;
bool _is_force_roll;
CoordinateSystem _cs;
@ -173,8 +174,10 @@ private:
// outputs
int _transform_output;
int _velocity_output;
PT(EventStoreMat4) _transform;
PT(EventStoreVec3) _velocity;
public:
static TypeHandle get_class_type() {

View File

@ -32,14 +32,16 @@ Transform2SG(const string &name) :
DataNode(name)
{
_transform_input = define_input("transform", EventStoreMat4::get_class_type());
_velocity_input = define_input("velocity", EventStoreVec3::get_class_type());
_node = NULL;
_velocity_node = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::set_node
// Access: Public
// Description: Sets the node that this node will adjust.
// Description: Sets the node that this object will adjust.
////////////////////////////////////////////////////////////////////
void Transform2SG::
set_node(PandaNode *node) {
@ -49,7 +51,7 @@ set_node(PandaNode *node) {
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::get_node
// Access: Public
// Description: Returns the node that this node will adjust, or NULL
// Description: Returns the node that this object will adjust, or NULL
// if the node has not yet been set.
////////////////////////////////////////////////////////////////////
PandaNode *Transform2SG::
@ -57,6 +59,33 @@ get_node() const {
return _node;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::set_velocity_node
// Access: Public
// Description: Sets the node that this object will assign the
// computed velocity to. Normally this is a
// CollisionNode parented below the node indicated by
// set_node(). Setting this node allows the collision
// system to track the velocity imparted to the
// CollisionNode by the data graph object that set its
// transform, if that data is available.
////////////////////////////////////////////////////////////////////
void Transform2SG::
set_velocity_node(PandaNode *node) {
_velocity_node = node;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::get_velocity_node
// Access: Public
// Description: Returns the node that this object will assign the
// computed velocity to. See set_velocity_node().
////////////////////////////////////////////////////////////////////
PandaNode *Transform2SG::
get_velocity_node() const {
return _velocity_node;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::do_transmit_data
@ -81,4 +110,13 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &) {
_node->set_transform(TransformState::make_mat(mat));
}
}
if (input.has_data(_velocity_input)) {
const EventStoreVec3 *velocity;
DCAST_INTO_V(velocity, input.get_data(_velocity_input).get_ptr());
LVector3f vel = velocity->get_value();
if (_velocity_node != (PandaNode *)NULL) {
_velocity_node->set_velocity(vel);
}
}
}

View File

@ -38,8 +38,12 @@ PUBLISHED:
void set_node(PandaNode *node);
PandaNode *get_node() const;
void set_velocity_node(PandaNode *node);
PandaNode *get_velocity_node() const;
private:
PandaNode *_node;
PandaNode *_velocity_node;
protected:
// Inherited from DataNode
@ -49,6 +53,7 @@ protected:
private:
// inputs
int _transform_input;
int _velocity_input;
public:
static TypeHandle get_class_type() {