diff --git a/Makefile b/Makefile index 71f5f5551..b4b799799 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/bullet/bindings.d b/bullet/bindings.d new file mode 100644 index 000000000..8c41559cc --- /dev/null +++ b/bullet/bindings.d @@ -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); diff --git a/bullet/bullet.d b/bullet/bullet.d new file mode 100644 index 000000000..dff995c57 --- /dev/null +++ b/bullet/bullet.d @@ -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(); } diff --git a/bullet/cpp_bullet.cpp b/bullet/cpp_bullet.cpp new file mode 100644 index 000000000..5e34f9a7a --- /dev/null +++ b/bullet/cpp_bullet.cpp @@ -0,0 +1,196 @@ +#include "btBulletCollisionCommon.h" + +#include + +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;ksetActivationState(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 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; diff --git a/ogre/bindings.d b/ogre/bindings.d index 9df84bab2..a53cebfc7 100644 --- a/ogre/bindings.d +++ b/ogre/bindings.d @@ -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); diff --git a/ogre/cpp_framelistener.cpp b/ogre/cpp_framelistener.cpp index 8abb88735..5e043bfc9 100644 --- a/ogre/cpp_framelistener.cpp +++ b/ogre/cpp_framelistener.cpp @@ -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 diff --git a/ogre/cpp_ogre.cpp b/ogre/cpp_ogre.cpp index ac786d786..309f59356 100644 --- a/ogre/cpp_ogre.cpp +++ b/ogre/cpp_ogre.cpp @@ -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); +} diff --git a/openmw.d b/openmw.d index 829e8f1db..5cbcd952c 100644 --- a/openmw.d +++ b/openmw.d @@ -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, diff --git a/scene/celldata.d b/scene/celldata.d index c095cec8a..ea65511df 100644 --- a/scene/celldata.d +++ b/scene/celldata.d @@ -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")); }