Started experimenting with Bullet

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@46 ea6a568a-9f4f-0410-981a-c910a81bb256
pull/7/head
nkorslund 16 years ago
parent 4d37d36d1e
commit 1cfaeeb8fc

@ -8,14 +8,18 @@ DMD=gdmd -version=Posix
# Some extra flags for niftool and bsatool
NIFFLAGS=-debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose
# Compiler settings for Ogre + OIS. Change as needed.
# Compiler settings for Ogre + OIS.
CF_OIS=$(shell pkg-config --cflags OGRE OIS)
OGCC=$(CXX) $(CXXFLAGS) $(CF_OIS)
# Compiler settings for ffmpeg. Change as needed.
# Compiler settings for ffmpeg.
CF_FFMPEG=$(shell pkg-config --cflags libavcodec libavformat)
AVGCC=$(CXX) $(CXXFLAGS) $(CF_FFMPEG)
# Settings for Bullet
CF_BULLET=-Iinclude/bullet
BGCC=$(CXX) $(CXXFLAGS) $(CF_BULLET)
# Ogre C++ files, on the form ogre/cpp_X.cpp. Only the first file is
# passed to the compiler, the rest are dependencies.
ogre_cpp=ogre framelistener interface overlay bsaarchive
@ -23,10 +27,14 @@ ogre_cpp=ogre framelistener interface overlay bsaarchive
# FFmpeg files, in the form sound/cpp_X.cpp.
avcodec_cpp=avcodec
## No modifications should be required below this line. ##
# Bullet cpp files
bullet_cpp=bullet
#### No modifications should be required below this line. ####
ogre_cpp_files=$(ogre_cpp:%=ogre/cpp_%.cpp)
avcodec_cpp_files=$(avcodec_cpp:%=sound/cpp_%.cpp)
bullet_cpp_files=$(bullet_cpp:%=bullet/cpp_%.cpp)
# All object files needed by openmw and esmtool
src := $(wildcard */*.d)
@ -47,7 +55,7 @@ obj_nif := $(src_nif:%.d=nifobjs/%.o)
all: openmw esmtool niftool bsatool bored
# Only build C++ sources. Used when building from DSSS.
cpp: cpp_ogre.o cpp_avcodec.o
cpp: cpp_ogre.o cpp_avcodec.o cpp_bullet.o
cpp_ogre.o: $(ogre_cpp_files)
$(OGCC) -c $<
@ -55,6 +63,9 @@ cpp_ogre.o: $(ogre_cpp_files)
cpp_avcodec.o: $(avcodec_cpp_files)
$(AVGCC) -c $<
cpp_bullet.o: $(bullet_cpp_files)
$(BGCC) -c $<
objs/%.o: %.d
dirname $@ | xargs mkdir -p
$(DMD) -c $< -of$@
@ -63,8 +74,8 @@ nifobjs/%.o: %.d
dirname $@ | xargs mkdir -p
$(DMD) $(NIFFLAGS) -c $< -of$@
openmw: openmw.d cpp_ogre.o cpp_avcodec.o $(obj)
$(DMD) $^ -of$@ -L-lopenal -L-lOgreMain -L-lOIS -L-lavcodec -L-lavformat
openmw: openmw.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj)
$(DMD) $^ -of$@ -L-lopenal -L-lOgreMain -L-lOIS -L-lavcodec -L-lavformat bullet/libBulletDynamics.a bullet/libBulletCollision.a bullet/libLinearMath.a
esmtool: esmtool.d cpp_ogre.o cpp_avcodec.o $(obj)
$(DMD) $^ -of$@ -L-lopenal -L-lOgreMain -L-lOIS -L-lavcodec -L-lavformat

@ -0,0 +1,43 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (bindings.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
module bullet.bindings;
/*
* This module is the interface between D and the C++ code that
* handles Bullet.
*/
extern(C):
// Initialize the dynamic world. Returns non-zero if an error occurs.
int cpp_initBullet();
void cpp_timeStep(float delta);
void cpp_cleanupBullet();
// Insert a debug collision object
void cpp_insertBox(float x, float y, float z);
// Move the player's collision shape
int cpp_movePlayerCollision(float x, float y, float z,
float dx, float dy, float dz);

@ -0,0 +1,11 @@
module bullet.bullet;
import bullet.bindings;
void initBullet()
{
if(cpp_initBullet())
throw new Exception("Bullet setup failed");
}
void cleanupBullet() { cpp_cleanupBullet(); }

@ -0,0 +1,196 @@
#include "btBulletCollisionCommon.h"
#include <iostream>
using namespace std;
btDefaultCollisionConfiguration* m_collisionConfiguration;
btCollisionDispatcher *m_dispatcher;
btBroadphaseInterface *m_broadphase;
//btSequentialImpulseConstraintSolver* m_solver;
//btDynamicsWorld *m_dynamicsWorld;
btCollisionWorld *m_collisionWorld;
//btCollisionObject* m_playerObject;
btConvexShape *playerShape;
extern "C" int32_t cpp_initBullet()
{
// Set up basic objects
m_collisionConfiguration = new btDefaultCollisionConfiguration();
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
m_broadphase = new btDbvtBroadphase();
m_collisionWorld =
new btCollisionWorld(m_dispatcher, m_broadphase,
m_collisionConfiguration);
/*m_solver = new btSequentialImpulseConstraintSolver;
m_dynamicsWorld =
new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase,
m_solver, m_collisionConfiguration);
m_dynamicsWorld->setGravity(btVector3(0,-10,0));
*/
// Create player collision object
//playerShape = new btCylinderShape(btVector3(50, 50, 50));
playerShape = new btSphereShape(50);
/*
m_playerObject = new btCollisionObject ();
m_playerObject->setCollisionShape (m_shape);
m_playerObject->setCollisionFlags (btCollisionObject::CF_NO_CONTACT_RESPONSE);
*/
/*
// Dynamic shapes:
// Re-using the same collision is better for memory usage and
// performance.
btCollisionShape* colShape = new btBoxShape(btVector3(1,1,1));
//btCollisionShape* colShape = new btSphereShape(btScalar(1.));
//m_collisionShapes.push_back(colShape);
// Create Dynamic Objects
btTransform startTransform;
startTransform.setIdentity();
mass = 1.f;
colShape->calculateLocalInertia(mass,localInertia);
///create 125 (5x5x5) dynamic objects
#define ARRAY_SIZE_X 5
#define ARRAY_SIZE_Y 5
#define ARRAY_SIZE_Z 5
#define START_POS_X -5
#define START_POS_Y -5
#define START_POS_Z -3
float start_x = START_POS_X - ARRAY_SIZE_X/2;
float start_y = START_POS_Y;
float start_z = START_POS_Z - ARRAY_SIZE_Z/2;
for (int k=0;k<ARRAY_SIZE_Y;k++)
for (int i=0;i<ARRAY_SIZE_X;i++)
for(int j = 0;j<ARRAY_SIZE_Z;j++)
{
startTransform.setOrigin(btVector3(2.0*i + start_x,
10+2.0*k + start_y,
2.0*j + start_z));
btDefaultMotionState* myMotionState =
new btDefaultMotionState(startTransform);
btRigidBody::btRigidBodyConstructionInfo
rbInfo(mass,myMotionState,colShape,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
body->setActivationState(ISLAND_SLEEPING);
m_dynamicsWorld->addRigidBody(body);
body->setActivationState(ISLAND_SLEEPING);
}
*/
return 0;
}
extern "C" int32_t cpp_movePlayerCollision(float x, float y, float z,
float dx, float dy, float dz)
{
btTransform start, end;
start.setIdentity();
end.setIdentity();
// The sweep test moves the shape from the old position to the
// new. The 0.1 offset in one of the coordinates is to make sure a
// sweep is performed even when the player does not move.
start.setOrigin(btVector3(x, y, z));
end.setOrigin(btVector3(x+dx,y+dy,z+dz));
btCollisionWorld::ClosestConvexResultCallback cb(btVector3(0,0,0),
btVector3(0,0,0));
m_collisionWorld->convexSweepTest(playerShape, start, end, cb);
if(cb.hasHit()) return 1;
else return 0;
}
extern "C" void cpp_insertBox(float x, float y, float z)
{
btCollisionShape* groundShape =
new btSphereShape(50);
//new btBoxShape(btVector3(100,100,100));
btTransform groundTransform;
groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(x,y,z));
btCollisionObject *obj = new btCollisionObject;
obj->setCollisionShape(groundShape);
obj->setWorldTransform(groundTransform);
m_collisionWorld->addCollisionObject(obj);
/*
m_collisionWorld->addCollisionObject(obj,
btBroadphaseProxy::DebrisFilter, // ??
btBroadphaseProxy::StaticFilter); // Only static objects
/*
btDefaultMotionState* myMotionState =
new btDefaultMotionState(groundTransform);
btRigidBody::btRigidBodyConstructionInfo
rbInfo(0, myMotionState, groundShape, btVector3(0,0,0));
btRigidBody* body = new btRigidBody(rbInfo);
// Add the body to the dynamics world
m_dynamicsWorld->addRigidBody(body);
*/
}
/*
extern "C" void cpp_timeStep(float delta)
{
// TODO: Find what unit Bullet uses here
m_dynamicsWorld->stepSimulation(delta / 1000.f);
}
*/
// Cleanup in the reverse order of creation/initialization
extern "C" void cpp_cleanupBullet()
{
// Remove the rigidbodies from the dynamics world and delete them
for (int i=m_collisionWorld->getNumCollisionObjects()-1; i>=0 ;i--)
{
btCollisionObject* obj = m_collisionWorld->getCollisionObjectArray()[i];
/*
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
delete body->getMotionState();
*/
m_collisionWorld->removeCollisionObject( obj );
delete obj;
}
// Delete collision shapes
/*
for (int j=0;j<m_collisionShapes.size();j++)
{
btCollisionShape* shape = m_collisionShapes[j];
delete shape;
}
*/
delete m_collisionWorld;
//delete m_solver;
delete m_broadphase;
delete m_dispatcher;
delete m_collisionConfiguration;
}

@ -334,8 +334,6 @@ struct ResourceManager
assert(ti.bsaFile == -1);
}
ti.ml = null;
textureLookup[ti.name] = ti;
return ti;
@ -447,7 +445,6 @@ struct TextureResource
int bsaFile; // If set to -1, the file is in the file system
int bsaIndex;
char[] type; // Texture format, eg "tga" or "dds";
ManualLoader ml;
public:
@ -460,11 +457,13 @@ struct TextureResource
return bsaIndex == -1;
}
/*KILLME
// Returns true if resource is loaded
bool isLoaded()
{
return ml != null;
}
*/
}
// OLD STUFF
@ -495,11 +494,5 @@ struct TextureResource
writefln(" Done\n");
}
void killBSAs()
{
foreach(BSAFile f; archives)
delete f;
}
}
+/

@ -4,7 +4,7 @@
[openmw.d]
# Add libraries and the two C++ object files
buildflags = -llOgreMain -llopenal -llOIS -llavcodec -llavformat cpp_ogre.o cpp_avcodec.o
buildflags = -llOgreMain -llopenal -llOIS -llavcodec -llavformat cpp_ogre.o cpp_avcodec.o cpp_bullet.o bullet/libBulletDynamics.a bullet/libBulletCollision.a bullet/libLinearMath.a
# Make sure the C++ object files are built first
version(Windows) {
@ -34,7 +34,7 @@ prebuild += dsss clean niftool
[esmtool.d]
# Because of interdepencies between the ESM code and the resource
# manager, we have to include all the C++ stuff.
buildflags = -llOgreMain -llopenal -llOIS -llavcodec -llavformat cpp_ogre.o cpp_avcodec.o
buildflags = -llOgreMain -llopenal -llOIS -llavcodec -llavformat cpp_ogre.o cpp_avcodec.o cpp_bullet.o bullet/libBulletDynamics.a bullet/libBulletCollision.a bullet/libLinearMath.a

@ -557,6 +557,13 @@ struct TES3File
file.seekCur(leftSub);
}
// Check the name and size before skipping
void skipHNSub(char[] name, uint size)
{
getSubNameIs(name);
skipHSubSize(size);
}
// These read an entire sub-record, including the header. They also
// adjust and check leftSub and leftRecord variables through calling
// getSubHeader().

@ -123,9 +123,9 @@ struct ExteriorCell
struct ExtCellHash
{
// This is a pretty good hash, gives no collisions at all for
// Morrowind.esm and a table size of 2048, and very few collisions
// overall. Not that it matters of course.
// This is a pretty good hash, gives no collisions for all of
// Morrowind.esm when the table size is 2048, and it gives very few
// collisions overall. Not that it matters that much.
static uint hash(uint val)
{
uint res = cast(ushort)val;
@ -304,8 +304,7 @@ struct Land
// Save file position
getContext(context);
// TODO: We don't decode these yet. Se ESLand.h from
// Morrowindremake for a lot of hints.
// Skip these here. Load the actual data when the cell is loaded.
if(isNextSub("VNML")) skipHSubSize(12675);
if(isNextSub("VHGT")) skipHSubSize(4232);
if(isNextSub("WNAM")) skipHSubSize(81);
@ -342,7 +341,8 @@ struct PathGrid
assert(state == LoadState.Unloaded);
readHNExact(&data, data.sizeof, "DATA");
getHNString("NAME"); // Cell name, we don't really need it.
getHNString("NAME"); // Cell name, we don't really need it so just
// ignore it.
// Remember this file position
getContext(context);
@ -352,7 +352,7 @@ struct PathGrid
{
int size = skipHSub();
if(size != 16*data.s2)
fail("Path grid table size");
fail("Path grid table size mismatch");
}
// Size varies. Path grid chances? Connections? Multiples of 4

@ -33,6 +33,8 @@ import core.config;
import scene.soundlist;
import scene.player;
import bullet.bindings;
import ogre.bindings;
import input.keys;
@ -246,6 +248,10 @@ void initializeInput()
{
cpp_moveCamera(position[0], position[1], position[2],
rotation[0], rotation[1], rotation[2]);
// Insert a collision box close to the player
cpp_insertBox(position[0], position[1]+500, position[2]);
cpp_drawBox(position[0], position[1]+500, position[2]);
}
// TODO/FIXME: This should have been in config, but DMD's module
@ -294,29 +300,41 @@ extern(C) int d_frameStarted(float time)
if(pause) return 1;
const float moveSpeed = 900;
float speed = moveSpeed * time;
// Check if the movement keys are pressed
float moveX = 0, moveY = 0, moveZ = 0;
if(isPressed(Keys.MoveLeft)) moveX -= moveSpeed;
if(isPressed(Keys.MoveRight)) moveX += moveSpeed;
if(isPressed(Keys.MoveForward)) moveZ -= moveSpeed;
if(isPressed(Keys.MoveBackward)) moveZ += moveSpeed;
if(isPressed(Keys.MoveUp)) moveY += moveSpeed;
if(isPressed(Keys.MoveDown)) moveY -= moveSpeed;
// Move camera. We only support "ghost-mode" at the moment, so we
// move without physics or collision detection.
cpp_moveCameraRel(moveX*time, moveY*time, moveZ*time);
float x, y, z;
if(isPressed(Keys.MoveLeft)) moveX -= speed;
if(isPressed(Keys.MoveRight)) moveX += speed;
if(isPressed(Keys.MoveForward)) moveZ -= speed;
if(isPressed(Keys.MoveBackward)) moveZ += speed;
if(isPressed(Keys.MoveUp)) moveY += speed;
if(isPressed(Keys.MoveDown)) moveY -= speed;
// Move the player. This is a temporary hack, we should do this more
// efficiently in C++.
if(moveX != 0 || moveY !=0 || moveZ != 0)
{
cpp_moveCameraRel(moveX, moveY, moveZ);
cpp_getCameraPos(&x, &y, &z);
bool nw = cpp_movePlayerCollision(x, y, z, moveX, moveY, moveZ) != 0;
if(nw != collides)
{
if(nw) writefln("Entered shape");
else writefln("Left shape");
collides = nw;
}
}
sndCumTime += time;
if(sndCumTime > sndRefresh)
{
float x, y, z;
float fx, fy, fz;
float ux, uy, uz;
cpp_getCameraPos(&x, &y, &z);
cpp_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz);
soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz);
@ -326,3 +344,4 @@ extern(C) int d_frameStarted(float time)
return 1;
}
bool collides = false;

@ -45,9 +45,6 @@ import core.resource;
// these directly in D code, only pass them back to the C++ code.
typedef void* NodePtr;
// Pointer to a manual loader class in C++.
typedef void* ManualLoader;
extern(C):
// Do engine configuration. Returns 0 if we should continue, 1 if
@ -152,3 +149,6 @@ void cpp_moveCameraRel(float x, float y, float z);
// Do some debug action. Check the menu for today's specials!
void cpp_debug(int i);
// Insert a 100x100x100 axis-aligned cube at x,y,z
void cpp_drawBox(float x, float y, float z);

@ -146,11 +146,13 @@ extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
*uz = up[1];
}
// Move and rotate camera in place. Transforms Morrowind coordinates
// to OGRE coordinates.
// Move and rotate camera in place.
extern "C" void cpp_moveCamera(float x, float y, float z,
float r1, float r2, float r3)
{
// 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));
// Rotation is probably not correct, but for now I have no reference

@ -56,3 +56,23 @@ SceneNode *root;
#include "cpp_bsaarchive.cpp"
#include "cpp_interface.cpp"
#include "cpp_overlay.cpp"
// Testing
extern "C" void cpp_drawBox(float x, float y, float z)
{
// Create a plane aligned with the xy-plane.
/*
MeshManager::getSingleton().createPlane("box1",
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Plane(Vector3::UNIT_X, 0),
100,100);
Entity *ent = mSceneMgr->createEntity( "box", "box1" );
*/
Entity *ent = mSceneMgr->createEntity( "box", SceneManager::PT_SPHERE);
ent->setCastShadows(false);
SceneNode *nd = root->createChildSceneNode();
nd->attachObject(ent);
//nd->setScale(0.5, 0.5, 0.5);
nd->setPosition(x,y,z);
}

@ -31,6 +31,8 @@ import std.file;
import ogre.ogre;
import ogre.bindings;
import bullet.bullet;
import scene.celldata;
import scene.soundlist;
@ -219,10 +221,13 @@ void main(char[][] args)
{
// Warm up OGRE
setupOgre();
// Clean up ogre when we're finished.
scope(exit) cleanupOgre();
// Set up Bullet
initBullet();
scope(exit) cleanupBullet();
if(cd.inCell)
{
setAmbient(cd.ambi.ambient, cd.ambi.sunlight,

@ -39,7 +39,8 @@ import sound.audio;
import scene.player;
// Base properties common to all live objects. Currently extremely
// sketchy.
// sketchy. TODO: This will all be handled in Monster script at some
// point.
struct LiveObjectBase
{
// Should this stuff be in here?
@ -92,7 +93,7 @@ struct LiveObjectBase
// ??
byte unam;
// Don't even get me started on script-related issues
// TODO: Scripts
}
// Generic version of a "live" object
@ -223,6 +224,21 @@ class CellData
loadReferences();
// TODO: Set up landscape system here.
/*
with(esFile)
{
restoreContext(exCell.land.context, reg);
// TODO: Not all of these will be present at all times
readHNExact(,12675, "VNML");
readHNExact(,4232, "VHGT");
readHNExact(,81, "WNAM");
readHNExact(,12675, "VCLR");
readHNExact(,512, "VTEX");
}
*/
const float cellWidth = 8192;
// TODO/FIXME: This is temporary
@ -243,7 +259,7 @@ class CellData
esFile.restoreContext(inCell.context, reg);
// TODO: Read this crap in loadcell.d
// TODO: Read this in loadcell.d
if(esFile.isNextSub("INTV") || esFile.isNextSub("WHGT"))
water = esFile.getHInt();
//writefln("Water height: ", water);
@ -555,12 +571,6 @@ class CellData
}
}
}
// Skip this? Large chance that the same file will be used for
// the next cell load, and the rest of our system can handle
// that without closing or reopening the file.
//close(); // Close the file
}
}
}
@ -582,8 +592,9 @@ struct CellFreelist
if(next) return list[--next];
// Since these are reused, there's no waste in allocating on the
// stack here. Also, this is only semi-runtime (executed when
// loading a cell), thus a rare GC slow down is non-critical.
// heap here. Also, this is only semi-runtime (executed when
// loading a cell), thus an occational GC slow down is not
// critical.
return new CellData(new RegionManager("CELL"));
}

Loading…
Cancel
Save