Replaced raycast used in detecting the terrain is on: instead of raycast

against the dynamics world, it's now using a raycast against the triangle
mesh only (i.e. a single collision body instead of the whole world).
Besides being faster, this will allow raycasts with material detection
against the 'gfx' (surface) mesh.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@7694 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2011-02-13 00:23:23 +00:00
parent 4eae628cab
commit 78eb5a09eb
7 changed files with 124 additions and 83 deletions

View File

@ -29,6 +29,7 @@
#include "modes/world.hpp"
#include "network/network_manager.hpp"
#include "network/race_state.hpp"
#include "physics/triangle_mesh.hpp"
#include "tracks/track.hpp"
#include "utils/string_utils.hpp"
@ -235,9 +236,10 @@ void Powerup::use()
Vec3 hit_point;
Vec3 normal;
const Material* material_hit;
btVector3 pos = m_owner->getXYZ();
world->getTrack()->getTerrainInfo(pos, &hit_point, &normal,
&material_hit);
Vec3 pos = m_owner->getXYZ();
Vec3 to=pos+Vec3(0, -10000, 0);
world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point,
&material_hit, &normal);
// This can happen if the kart is 'over nothing' when dropping
// the bubble gum
if(!material_hit)

View File

@ -41,6 +41,7 @@
#include "network/network_manager.hpp"
#include "network/race_state.hpp"
#include "physics/btKart.hpp"
#include "physics/triangle_mesh.hpp"
#include "race/highscore_manager.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
@ -451,8 +452,10 @@ void World::resetAllKarts()
(*i)->getBody()->getMotionState()->getWorldTransform(t);
// This test can not be done only once before the loop, since
// it can happen that the kart falls through the track later!
m_track->getTerrainInfo(t.getOrigin(), &hit_point, &normal,
&material);
Vec3 to = t.getOrigin()+Vec3(0, -10000, 0);
m_track->getTriangleMesh().castRay(t.getOrigin(), to,
&hit_point, &material,
&normal);
if(!material)
{
fprintf(stderr, "ERROR: no valid starting position for kart %d on track %s.\n",

View File

@ -19,6 +19,8 @@
#include "physics/triangle_mesh.hpp"
#include "btBulletDynamicsCommon.h"
#include "modes/world.hpp"
// -----------------------------------------------------------------------------
@ -26,14 +28,15 @@
*/
TriangleMesh::TriangleMesh() : m_mesh()
{
m_body = NULL;
m_motion_state = NULL;
m_body = NULL;
m_motion_state = NULL;
// FIXME: on VS in release mode this statement actually overwrites
// part of the data of m_mesh, causing a crash later. Debugging
// shows that apparently m_collision_shape is at the same address
// as m_mesh->m_use32bitIndices and m_use4componentVertices
// (and m_mesh->m_weldingThreshold at m_normals
m_collision_shape = NULL;
m_collision_shape = NULL;
m_collision_object = NULL;
m_user_pointer.set(this);
} // TriangleMesh
@ -49,6 +52,8 @@ TriangleMesh::~TriangleMesh()
delete m_motion_state;
delete m_collision_shape;
}
if(m_collision_object)
delete m_collision_object;
} // ~TriangleMesh
// -----------------------------------------------------------------------------
@ -75,18 +80,26 @@ void TriangleMesh::addTriangle(const btVector3 &t1, const btVector3 &t2,
/** Creates a collision body only, which can be used for raycasting, but
* has no physical properties.
*/
void TriangleMesh::createCollisionShape()
void TriangleMesh::createCollisionShape(bool create_collision_object)
{
if(m_triangleIndex2Material.size()==0)
{
m_collision_shape = NULL;
m_motion_state = NULL;
m_body = NULL;
m_collision_shape = NULL;
m_motion_state = NULL;
m_body = NULL;
m_collision_object = NULL;
return;
}
// Now convert the triangle mesh into a static rigid body
m_collision_shape = new btBvhTriangleMeshShape(&m_mesh, true);
m_collision_shape->setUserPointer(&m_user_pointer);
if(create_collision_object)
{
m_collision_object = new btCollisionObject();
btTransform bt;
bt.setIdentity();
m_collision_object->setWorldTransform(bt);
}
} // createCollisionShape
// -----------------------------------------------------------------------------
@ -101,14 +114,15 @@ void TriangleMesh::createCollisionShape()
*/
void TriangleMesh::createPhysicalBody(btCollisionObject::CollisionFlags flags)
{
createCollisionShape();
// We need the collision shape, but not the collision object (since
// this will be creates when the dynamics body is anyway).
createCollisionShape(/*create_collision_object*/false);
btTransform startTransform;
startTransform.setIdentity();
m_motion_state = new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo info(0.0f, m_motion_state,
m_collision_shape);
m_body=new btRigidBody(info);
World::getWorld()->getPhysics()->addBody(m_body);
m_body->setUserPointer(&m_user_pointer);
@ -183,3 +197,81 @@ btVector3 TriangleMesh::getInterpolatedNormal(unsigned int index,
return s*n1 + t*n2 + w*n3;
} // getInterpolatedNormal
// ----------------------------------------------------------------------------
/** Casts a ray from 'from' to 'to'. If a triangle of this mesh was hit,
* xyz and material will be set.
* \param from/to The from and to position for the raycast/
* \param xyz The position in world where the ray hit.
* \param material The material of the mesh that was hit.
* \param normal The intrapolated normal at that position.
* \return True if a triangle was hit, false otherwise (and no output
* variable will be set.
*/
bool TriangleMesh::castRay(const btVector3 &from, const btVector3 &to,
btVector3 *xyz, const Material **material,
btVector3 *normal) const
{
btTransform trans_from;
trans_from.setIdentity();
trans_from.setOrigin(from);
btTransform trans_to;
trans_to.setIdentity();
trans_to.setOrigin(to);
btTransform world_trans;
world_trans.setIdentity();
btCollisionWorld::ClosestRayResultCallback result(from, to);
class MaterialRayResult : public btCollisionWorld::ClosestRayResultCallback
{
public:
const Material* m_material;
const TriangleMesh *m_this;
// --------------------------------------------------------------------
MaterialRayResult(const btVector3 &p1, const btVector3 &p2,
const TriangleMesh *me)
: btCollisionWorld::ClosestRayResultCallback(p1,p2)
{
m_material = NULL;
m_this = me;
} // MaterialRayResult
// --------------------------------------------------------------------
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,
bool normalInWorldSpace)
{
m_material =
m_this->getMaterial(rayResult.m_localShapeInfo->m_triangleIndex);
return btCollisionWorld::ClosestRayResultCallback
::addSingleResult(rayResult, normalInWorldSpace);
} // AddSingleResult
// --------------------------------------------------------------------
}; // myCollision
MaterialRayResult ray_callback(from, to, this);
// If this is a rigid body, m_collision_object is NULL, and the
// rigid body is the actual collision object.
btCollisionWorld::rayTestSingle(trans_from, trans_to,
m_collision_object ? m_collision_object
: m_body,
m_collision_shape, world_trans,
ray_callback);
if(ray_callback.hasHit())
{
*xyz = ray_callback.m_hitPointWorld;
*material = ray_callback.m_material;
*normal = ray_callback.m_hitNormalWorld;
normal->normalize();
}
else
{
*material = NULL;
normal->setValue(0, 1, 0);
}
return ray_callback.hasHit();
} // castRay

View File

@ -38,6 +38,7 @@ private:
UserPointer m_user_pointer;
std::vector<const Material*> m_triangleIndex2Material;
btRigidBody *m_body;
btCollisionObject *m_collision_object;
btTriangleMesh m_mesh;
btVector3 dummy1, dummy2;
btDefaultMotionState *m_motion_state;
@ -51,7 +52,7 @@ public:
const btVector3 &t3, const btVector3 &n1,
const btVector3 &n2, const btVector3 &n3,
const Material* m);
void createCollisionShape();
void createCollisionShape(bool create_collision_object=true);
void createPhysicalBody(btCollisionObject::CollisionFlags flags=
(btCollisionObject::CollisionFlags)0);
void removeBody();
@ -64,6 +65,10 @@ public:
const btCollisionShape &getCollisionShape() const
{return *m_collision_shape;}
// ------------------------------------------------------------------------
bool castRay(const btVector3 &from, const btVector3 &to,
btVector3 *xyz, const Material **material,
btVector3 *normal=NULL) const;
// ------------------------------------------------------------------------
/** Returns the points of the 'indx' triangle.
* \param indx Index of the triangle to get.
* \param p1,p2,p3 On return the three points of the triangle. */

View File

@ -50,9 +50,11 @@ TerrainInfo::TerrainInfo(const Vec3 &pos)
void TerrainInfo::update(const Vec3& pos)
{
m_last_material = m_material;
World::getWorld()->getTrack()->getTerrainInfo(pos, &m_hit_point,
&m_normal, &m_material);
m_normal.normalize();
btVector3 to(pos);
to.setY(-100000.f);
const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh();
tm.castRay(pos, to, &m_hit_point, &m_material, &m_normal);
} // update
// -----------------------------------------------------------------------------

View File

@ -1122,66 +1122,6 @@ void Track::itemCommand(const Vec3 &xyz, Item::ItemType type,
item_manager->newItem(type, loc, normal);
} // itemCommand
// ----------------------------------------------------------------------------
/** Does a ray cast straight down from the given position, and returns
* the hit point, material, and normal at the given location (or material
* ==NULL if no triangle was hit).
* \param pos Positions from which to cast the ray.
* \param hit_point On return: the position which was hit.
* \param normal On return the normal of the triangle at that point.
* \param material The material at the hit point (or NULL if no material
* was found.
*/
void Track::getTerrainInfo(const Vec3 &pos, Vec3 *hit_point, Vec3 *normal,
const Material **material) const
{
btVector3 to_pos(pos);
to_pos.setY(-100000.f);
class MaterialCollision : public btCollisionWorld::ClosestRayResultCallback
{
public:
const Material* m_material;
MaterialCollision(const btVector3 &p1, const btVector3 &p2) :
btCollisionWorld::ClosestRayResultCallback(p1,p2) {m_material=NULL;}
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,
bool normalInWorldSpace) {
if(rayResult.m_localShapeInfo && rayResult.m_localShapeInfo->m_shapePart>=0 )
{
m_material = ((TriangleMesh*)rayResult.m_collisionObject->getUserPointer())->getMaterial(rayResult.m_localShapeInfo->m_triangleIndex);
}
else
{
// This can happen if the raycast hits a kart. This should
// actually be impossible (since the kart is removed from
// the collision group), but now and again the karts don't
// have the broadphase handle set (kart::update() for
// details), and it might still happen. So in this case
// just ignore this callback and don't add it.
return 1.0f;
}
return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult,
normalInWorldSpace);
} // AddSingleResult
}; // myCollision
MaterialCollision rayCallback(pos, to_pos);
World::getWorld()->getPhysics()->getPhysicsWorld()->rayTest(pos, to_pos, rayCallback);
if(!rayCallback.hasHit())
{
*material = NULL;
return;
}
*hit_point = rayCallback.m_hitPointWorld;
*normal = rayCallback.m_hitNormalWorld;
*material = rayCallback.m_material;
// Note: material might be NULL. This happens if the ray cast does not
// hit the track, but another rigid body (kart, moving_physics) - e.g.
// assume two karts falling down, one over the other. Bullet does not
// have any triangle/material information in this case!
} // getTerrainInfo
// ----------------------------------------------------------------------------
/** Simplified version to determine only the height of the terrain.
* \param pos Position at which to determine the height (x,y coordinates
@ -1193,6 +1133,7 @@ float Track::getTerrainHeight(const Vec3 &pos) const
Vec3 hit_point;
Vec3 normal;
const Material *m;
getTerrainInfo(pos, &hit_point, &normal, &m);
Vec3 to=pos+Vec3(0,-10000,0);
m_track_mesh->castRay(pos, to, &hit_point, &m, &normal);
return hit_point.getY();
} // getTerrainHeight

View File

@ -235,10 +235,6 @@ public:
/** Starts the music for this track. */
void startMusic () const;
void getTerrainInfo(const Vec3 &pos, Vec3 *hit_point,
Vec3* normal,
const Material **material) const;
float getTerrainHeight(const Vec3 &pos) const;
void createPhysicsModel(unsigned int main_track_count);
void update(float dt);