Various collision changes:

- no collision with NCO objects
- better camera control (no turning the camera upside down)
- up/down buttons (shift/ctrl) does not move the player when walking
- eye level adjusted
- various updates


git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@55 ea6a568a-9f4f-0410-981a-c910a81bb256
pull/7/head
nkorslund 16 years ago
parent e927c9a70e
commit 08ba0278a1

@ -28,7 +28,7 @@ ogre_cpp=ogre framelistener interface overlay bsaarchive
avcodec_cpp=avcodec
# Bullet cpp files
bullet_cpp=bullet player
bullet_cpp=bullet player scale
#### No modifications should be required below this line. ####

@ -120,6 +120,7 @@ Changelog:
0.5 (WIP)
- working on collision detection with Bullet
- working on walk & fall character physics
- working on fixing sound issues for windows (running out of sound
resources, music playback doesn't good)
- new key bindings:

@ -48,6 +48,13 @@ void bullet_setPlayerDir(float x, float y, float z);
// been applied.
void bullet_getPlayerPos(float *x, float *y, float *z);
// Create a box shape.
/*
void bullet_createBoxShape(float minX, float minY, float minZ,
float maxX, float maxY, float maxZ,
float *trans,float *matrix);
*/
// Create a triangle shape. This is cumulative, all meshes created
// with this function are added to the same shape. Since the various
// parts of a mesh can be differently transformed and we are putting

@ -29,6 +29,12 @@ using namespace std;
class CustomOverlappingPairCallback;
enum
{
MASK_PLAYER = 1,
MASK_STATIC = 2
};
// System variables
btDefaultCollisionConfiguration* g_collisionConfiguration;
btCollisionDispatcher *g_dispatcher;
@ -56,6 +62,11 @@ btVector3 g_walkDirection;
// added into this, until bullet_getFinalShape() is called.
btTriangleIndexVertexArray *g_currentMesh;
// Current compound shape being built. g_compoundShape and
// g_currentMesh are returned together (the mesh inserted into the
// compound) if both are present.
btCompoundShape *g_compoundShape;
// These variables and the class below are used in player collision
// detection. The callback is injected into the broadphase and keeps a
// continuously updated list of what objects are colliding with the
@ -77,6 +88,9 @@ int g_physMode;
// Include the player physics
#include "cpp_player.cpp"
// Include the uniform shape scaler
//#include "cpp_scale.cpp"
class CustomOverlappingPairCallback : public btOverlappingPairCallback
{
public:
@ -105,13 +119,6 @@ public:
{ if (proxy0->m_clientObject==g_playerObject)
g_pairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher);
}
/* KILLME
btBroadphasePairArray& getOverlappingPairArray()
{ return g_pairCache->getOverlappingPairArray(); }
btOverlappingPairCache* getOverlappingPairCache()
{ return g_pairCache; }
*/
};
extern "C" int32_t bullet_init()
@ -132,8 +139,8 @@ extern "C" int32_t bullet_init()
// to create a custom broadphase designed for our purpose. (We
// should probably use different ones for interior and exterior
// cells in any case.)
btVector3 worldMin(-40000,-40000,-40000);
btVector3 worldMax(40000,40000,40000);
btVector3 worldMin(-20000,-20000,-20000);
btVector3 worldMax(20000,20000,20000);
g_broadphase = new btAxisSweep3(worldMin,worldMax);
g_dynamicsWorld =
@ -149,20 +156,25 @@ extern "C" int32_t bullet_init()
// Create the player collision shape.
float width = 50;
/*
// One possible shape is the convex hull around two spheres
float height = 100;
float height = 50;
btVector3 spherePositions[2];
btScalar sphereRadii[2];
sphereRadii[0] = width;
sphereRadii[1] = width;
spherePositions[0] = btVector3 (0.0, height/2.0, 0.0);
spherePositions[1] = btVector3 (0.0, -height/2.0, 0.0);
spherePositions[0] = btVector3 (0,0,0);
spherePositions[1] = btVector3 (0,0,-height);
// One possible shape is the convex hull around two spheres
g_playerShape = new btMultiSphereShape(btVector3(width/2.0, height/2.0,
width/2.0), &spherePositions[0], &sphereRadii[0], 2);
//*/
//g_playerShape = new btCylinderShape(btVector3(50, 50, 50));
*/
// Other posibilities - most are too slow, except the sphere
//g_playerShape = new btCylinderShapeZ(btVector3(width, width, height));
g_playerShape = new btSphereShape(width);
//g_playerShape = new btCapsuleShapeZ(width, height);
// Create the collision object
g_playerObject = new btCollisionObject ();
@ -183,13 +195,13 @@ extern "C" int32_t bullet_init()
g_dynamicsWorld->setInternalTickCallback(playerStepCallback);
// Add the character collision object to the world.
g_dynamicsWorld->addCollisionObject(g_playerObject
,btBroadphaseProxy::DebrisFilter
//,btBroadphaseProxy::StaticFilter
);
g_dynamicsWorld->addCollisionObject(g_playerObject,
MASK_PLAYER,
MASK_STATIC);
// Make sure this is zero at startup
// Make sure these is zero at startup
g_currentMesh = NULL;
g_compoundShape = NULL;
// Start out walking
g_physMode = PHYS_WALK;
@ -242,6 +254,41 @@ extern "C" void bullet_getPlayerPos(float *x, float *y, float *z)
*z = g_playerPosition.getZ();
}
// Create a box shape and add it to the cumulative shape of the
// current object
/*
extern "C" void bullet_createBoxShape(float minX, float minY, float minZ,
float maxX, float maxY, float maxZ,
float *trans,float *matrix)
{
if(g_compoundShape == NULL)
g_compoundShape = new btCompoundShape;
// Build a box from the given data.
int x = (maxX-minX)/2;
int y = (maxY-minY)/2;
int z = (maxZ-minZ)/2;
btBoxShape *box = new btBoxShape(btVector3(x,y,z));
// Next, create the transformations
btMatrix3x3 mat(matrix[0], matrix[1], matrix[2],
matrix[3], matrix[4], matrix[5],
matrix[6], matrix[7], matrix[8]);
// In addition to the mesh's world translation, we have to add the
// local translation of the box origin to fit with the given min/max
// values.
x += minX + trans[0];
y += minY + trans[1];
z += minZ + trans[2];
btVector3 trns(x,y,z);
// Finally, add the shape to the compound
btTransform tr(mat, trns);
g_compoundShape->addChildShape(tr, box);
}
*/
void* copyBuffer(void *buf, int elemSize, int len)
{
int size = elemSize * len;
@ -251,41 +298,6 @@ void* copyBuffer(void *buf, int elemSize, int len)
return res;
}
// Define a cube with coordinates 0,0,0 - 1,1,1.
const float cube_verts[] =
{
0,0,0, 1,0,0, 0,1,0,
1,1,0, 0,0,1, 1,0,1,
0,1,1, 1,1,1
};
// Triangles of the cube. The orientation of each triange doesn't
// matter.
const short cube_tris[] =
{
// bottom side
0, 1, 2,
1, 2, 3,
// top side
4, 5, 6,
5, 6, 7,
// front side
0, 4, 5,
0, 1, 5,
// back side
2, 3, 7,
2, 6, 7,
// left side
0, 2, 4,
2, 4, 6,
// right side
1, 3, 5,
3, 5, 7
};
const int cube_num_verts = 8;
const int cube_num_tris = 12;
// Internal version that does not copy buffers
void createTriShape(int32_t numFaces, void *triArray,
int32_t numVerts, void *vertArray,
@ -329,6 +341,41 @@ void createTriShape(int32_t numFaces, void *triArray,
g_currentMesh->addIndexedMesh(im, PHY_SHORT);
}
// Define a cube with coordinates 0,0,0 - 1,1,1.
const float cube_verts[] =
{
0,0,0, 1,0,0, 0,1,0,
1,1,0, 0,0,1, 1,0,1,
0,1,1, 1,1,1
};
// Triangles of the cube. The orientation of each triange doesn't
// matter.
const short cube_tris[] =
{
// bottom side
0, 1, 2,
1, 2, 3,
// top side
4, 5, 6,
5, 6, 7,
// front side
0, 4, 5,
0, 1, 5,
// back side
2, 3, 7,
2, 6, 7,
// left side
0, 2, 4,
2, 4, 6,
// right side
1, 3, 5,
3, 5, 7
};
const int cube_num_verts = 8;
const int cube_num_tris = 12;
// Create a (trimesh) box with the given dimensions. Used for bounding
// boxes. TODO: I guess we have to use the NIF-specified bounding box
// for this, not our automatically calculated one.
@ -379,14 +426,33 @@ extern "C" void bullet_createTriShape(int32_t numFaces,
// so the next call to createTriShape will start a new shape.
extern "C" btCollisionShape *bullet_getFinalShape()
{
// Return null if no meshes have been inserted
if(g_currentMesh == NULL) return NULL;
btCollisionShape *shape;
// Create the shape from the completed mesh
btBvhTriangleMeshShape *shape =
new btBvhTriangleMeshShape(g_currentMesh, false);
// Create a shape from all the inserted completed meshes
shape = NULL;
if(g_currentMesh != NULL)
shape = new btBvhTriangleMeshShape(g_currentMesh, false);
// Is there a compound shape as well? (usually contains bounding
// boxes)
if(g_compoundShape != NULL)
{
// If both shape types are present, insert the mesh shape into
// the compound.
if(shape != NULL)
{
btTransform tr;
tr.setIdentity();
g_compoundShape->addChildShape(tr, shape);
}
// The compound is the final shape
shape = g_compoundShape;
}
// Clear these for the next NIF
g_currentMesh = NULL;
g_compoundShape = NULL;
return shape;
}
@ -396,14 +462,12 @@ extern "C" void bullet_insertStatic(btCollisionShape *shape,
float *quat,
float scale)
{
// TODO: Good test case for scaled meshes: Aharunartus, some of the
// stairs inside the cavern currently don't collide
// Are we scaled?
if(scale != 1.0)
{
cout << "WARNING: Cannot scale collision meshes yet (wanted "
<< scale << ")\n";
return;
}
return;
// Wrap the shape in a class that scales it. TODO: Try this on
// ALL shapes, just to test the slowdown.
//shape = new ScaleShape(shape, scale);
btTransform trafo;
trafo.setIdentity();
@ -416,7 +480,7 @@ extern "C" void bullet_insertStatic(btCollisionShape *shape,
btCollisionObject *obj = new btCollisionObject();
obj->setCollisionShape(shape);
obj->setWorldTransform(trafo);
g_dynamicsWorld->addCollisionObject(obj);
g_dynamicsWorld->addCollisionObject(obj, MASK_STATIC, MASK_PLAYER);
}
// Move the physics simulation 'delta' seconds forward in time

@ -34,7 +34,7 @@
bool g_touchingContact;
btVector3 g_touchingNormal;
btScalar g_currentStepOffset;
float g_stepHeight = 20;
float g_stepHeight = 5;
// Returns the reflection direction of a ray going 'direction' hitting
// a surface with normal 'normal'
@ -293,6 +293,18 @@ void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
// simulation.
btVector3 walkStep = g_walkDirection * timeStep;
float len = walkStep.length();
// In walk mode, it shouldn't matter whether or not we look up or
// down. Rotate the vector back to the horizontal plane.
if(g_physMode == PHYS_WALK)
{
walkStep.setZ(0);
float len2 = walkStep.length();
if(len2 > 0)
walkStep *= len/len2;
}
// Get the player position
g_playerPosition = g_playerObject->getWorldTransform().getOrigin();

@ -312,6 +312,9 @@ extern(C) int d_frameStarted(float time)
if(isPressed(Keys.MoveRight)) moveX += speed;
if(isPressed(Keys.MoveForward)) moveZ -= speed;
if(isPressed(Keys.MoveBackward)) moveZ += speed;
// TODO: These should be enabled for floating modes (like swimming
// and levitation) only.
if(isPressed(Keys.MoveUp)) moveY += speed;
if(isPressed(Keys.MoveDown)) moveY -= speed;

@ -120,9 +120,15 @@ extern "C" void ogre_screenshot(char* filename)
extern "C" void ogre_rotateCamera(float x, float y)
{
mCamera->yaw(Degree(-x));
Quaternion nopitch = mCamera->getOrientation();
mCamera->pitch(Degree(-y));
//g_light->setDirection(mCamera->getDirection());
// Is the camera close to being upside down?
if(mCamera->getUp()[1] <= 0.1)
// If so, undo the last pitch
mCamera->setOrientation(nopitch);
}
// Get current camera position
@ -155,7 +161,7 @@ extern "C" void ogre_moveCamera(float x, float y, float z)
// Transforms Morrowind coordinates to OGRE coordinates. The camera
// is not affected by the rotation of the root node, so we must
// transform this manually.
mCamera->setPosition(Vector3(x,z,-y));
mCamera->setPosition(Vector3(x,z+90,-y));
//g_light->setPosition(mCamera->getPosition());
}
@ -181,5 +187,8 @@ extern "C" void ogre_setCameraRotation(float r1, float r2, float r3)
// Move camera relative to its own axis set.
extern "C" void ogre_moveCameraRel(float x, float y, float z)
{
mCamera->moveRelative(Vector3(x,y,z));
mCamera->moveRelative(Vector3(x,0,z));
// up/down movement is always done relative the world axis
mCamera->move(Vector3(0,y,0));
}

@ -182,6 +182,8 @@ extern "C" void ogre_makeScene()
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
mCamera->setFOVy(Degree(55));
// Set default mipmap level (NB some APIs ignore this)
TextureManager::getSingleton().setDefaultNumMipmaps(5);

@ -82,7 +82,7 @@ struct MeshLoader
base = ogre_getDetachedNode();
// Recursively insert nodes (don't rotate the first node)
insertNode(n, base, true);
insertNode(n, base, 0, true);
// Get the final shape, if any
shape = bullet_getFinalShape();
@ -92,10 +92,12 @@ struct MeshLoader
private:
void insertNode(Node data, NodePtr parent, bool noRot = false)
void insertNode(Node data, NodePtr parent,
int flags,
bool noRot = false)
{
// Skip hidden nodes for now.
if(data.flags & 0x01) return;
// Add the flags to the previous node's flags
flags = data.flags | flags;
// Create a scene node, move and rotate it into place. The name
// must be unique, however we might have to recognize some special
@ -115,23 +117,29 @@ struct MeshLoader
NiNode n = cast(NiNode)data;
if(n !is null)
// Handle the NiNode, and any children it might have
handleNiNode(n, node);
handleNiNode(n, node, flags);
}
{
NiTriShape n = cast(NiTriShape)data;
if(n !is null)
// Trishape, with a mesh
handleNiTriShape(n, node);
handleNiTriShape(n, node, flags);
}
}
void handleNiNode(NiNode data, NodePtr node)
void handleNiNode(NiNode data, NodePtr node, int flags)
{
// Ignore sound activators and similar objects.
NiStringExtraData d = cast(NiStringExtraData) data.extra;
if(d !is null && d.string == "MRK")
return;
if(d !is null)
{
if(d.string == "MRK")
return;
if(d.string == "NCO")
flags |= 0x800; // Temporary internal marker
}
// Handle any effects here
@ -144,16 +152,31 @@ struct MeshLoader
// Loop through children
foreach(Node n; data.children)
insertNode(n, node);
insertNode(n, node, flags);
}
void handleNiTriShape(NiTriShape shape, NodePtr node)
void handleNiTriShape(NiTriShape shape, NodePtr node, int flags)
{
char[] texture;
char[] material;
char[] newName = UniqueName(baseName);
NiMaterialProperty mp;
bool hidden = (flags & 0x01) != 0; // Not displayed
bool collide = (flags & 0x02) != 0; // Use this mesh for collision
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for
// collision
// Always use mesh collision for now
if(bbcollide) collide = true;
bbcollide = false;
// Things marked "NCO" should not collide with anything.
if(flags & 0x800)
collide = false;
// Skip the entire material phase for hidden nodes
if(hidden) goto nomaterial;
// Scan the property list for textures
foreach(Property p; shape.properties)
{
@ -227,6 +250,8 @@ struct MeshLoader
texturePtr);
}
nomaterial:
with(shape.data)
{
//writefln("Number of vertices: ", vertices.length);
@ -251,7 +276,9 @@ struct MeshLoader
maxY = -float.infinity,
maxZ = -float.infinity;
// Calculate the bounding box. TODO: This is really a hack.
// Calculate the bounding box. TODO: This is really a
// hack. IIRC the bounding box supplied by the NIF could not
// be trusted, but I can't remember why :/
for( int i; i < vertices.length; i+=3 )
{
if( vertices[i] < minX ) minX = vertices[i];
@ -269,20 +296,34 @@ struct MeshLoader
float[9] matrix;
ogre_getWorldTransform(node, trans.ptr, matrix.ptr);
// Create a bullet collision shape from the trimesh, if there
// are any triangles present. Pass along the world
// transformation as well, since we must transform the trimesh
// data manually.
if(facesPtr != null)
bullet_createTriShape(triangles.length, facesPtr,
vertices.length, vertices.ptr,
// Next we must create the actual OGRE mesh and the collision
// objects, based on the flags we have been given.
assert(!bbcollide);
/* // Bounding box collision currently disabled
if(bbcollide)
// Insert the bounding box into the collision system
bullet_createBoxShape(minX, minY, minZ, maxX, maxY, maxZ,
trans.ptr, matrix.ptr);
// Create the ogre mesh, associate it with the node
ogre_createMesh(newName.ptr, vertices.length, vertices.ptr,
normalsPtr, colorsPtr, uvsPtr, triangles.length, facesPtr,
radius, material.ptr, minX, minY, minZ, maxX, maxY, maxZ,
node);
// Create a bullet collision shape from the trimesh. Pass
// along the world transformation as well, since we must
// transform the trimesh data manually.
else*/if(collide)
{
assert(facesPtr !is null,
"cannot create collision shape without a mesh");
bullet_createTriShape(triangles.length, facesPtr,
vertices.length, vertices.ptr,
trans.ptr, matrix.ptr);
}
// Create the ogre mesh, associate it with the node. Skip for
// hidden nodes.
if(!hidden)
ogre_createMesh(newName.ptr, vertices.length, vertices.ptr,
normalsPtr, colorsPtr, uvsPtr, triangles.length,
facesPtr, radius, material.ptr, minX, minY, minZ,
maxX, maxY, maxZ, node);
}
}
}

Loading…
Cancel
Save