From c094324ef2bcbd542bd5ce58226d14a9f382fe98 Mon Sep 17 00:00:00 2001 From: nkorslund Date: Tue, 30 Sep 2008 18:14:39 +0000 Subject: [PATCH] First alpha release of REAL collision detection. git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@48 ea6a568a-9f4f-0410-981a-c910a81bb256 --- Makefile | 2 +- README.txt | 19 +++- bullet/bindings.d | 37 ++++++-- bullet/bullet.d | 27 +++++- bullet/cpp_bullet.cpp | 185 +++++++++++++++++++++++++++++++++---- bullet/cpp_player.cpp | 74 +++++++++------ core/resource.d | 7 +- input/events.d | 42 ++++----- ogre/bindings.d | 86 +++++++++-------- ogre/cpp_bsaarchive.cpp | 10 +- ogre/cpp_framelistener.cpp | 16 ++-- ogre/cpp_interface.cpp | 118 +++++++++++++++-------- ogre/cpp_ogre.cpp | 20 ---- ogre/cpp_overlay.cpp | 9 +- ogre/meshloader.d | 51 +++++++--- ogre/ogre.d | 42 ++++++--- openmw.d | 19 ++-- sound/al.d | 23 +++++ sound/alc.d | 23 +++++ sound/avcodec.d | 35 +++++-- sound/cpp_avcodec.cpp | 16 ++-- sound/music.d | 16 ++-- sound/sfx.d | 10 +- 23 files changed, 618 insertions(+), 269 deletions(-) diff --git a/Makefile b/Makefile index b4b799799..a681562f1 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ ogre_cpp=ogre framelistener interface overlay bsaarchive avcodec_cpp=avcodec # Bullet cpp files -bullet_cpp=bullet +bullet_cpp=bullet player #### No modifications should be required below this line. #### diff --git a/README.txt b/README.txt index fa75f045f..cc4989bd5 100644 --- a/README.txt +++ b/README.txt @@ -49,11 +49,11 @@ On the near-future TODO list: - full support for localized versions (with character recoding) - support for Mac -- collision detection + walking & fall physics +- collision detection + walk & fall physics - displaying creatures correcty, animation - rendering NPCs - rendering outdoor scenes (exterior cells) -- choosing a GUI/HUD system that playes well with OGRE +- choosing a GUI/HUD system that plays well with OGRE @@ -62,7 +62,7 @@ Installation ============ Currently supported platforms are Windows, Linux and FreeBSD. Most -testing has been on Windows XP and Ubuntu 8.04. +testing is on Ubuntu 8.04 and Windows XP Professional (in that order). For instructions, see one of the following: @@ -70,7 +70,9 @@ README-win32.txt - instructions for binary Windows release COMPILE-win32.txt - instructions for building source on Windows COMPILE-linux.tx - instructions for building source on Linux / Unix -Linux 64 bit is known NOT to work, because of compiler deficiencies. +Linux 64 bit is known NOT to work, because of current compiler +deficiencies. This will hopefully be sorted out at some point, but +it's not a bug in the OpenMW code. @@ -115,6 +117,15 @@ Thanks goes out to: Changelog: ========== +0.5 (WIP) + +- working on collision detection with Bullet +- working on fixing sound issues for windows (running out of sound + resources, music playback doesn't good) +- added build files for CMake (with CMakeD) and Code::Blocks (neither + are tested yet) +- various minor changes and updates + 0.4 (2008 aug. 30) - latest release - switched from Audiere to OpenAL (BIG thanks to Chris Robinson) diff --git a/bullet/bindings.d b/bullet/bindings.d index e5fc07390..5bb45f7db 100644 --- a/bullet/bindings.d +++ b/bullet/bindings.d @@ -28,27 +28,46 @@ module bullet.bindings; * handles Bullet. */ +typedef void* BulletShape; + extern(C): // Initialize the dynamic world. Returns non-zero if an error occurs. -int cpp_initBullet(); +int bullet_init(); // Warp the player to a specific location. -void cpp_movePlayer(float x, float y, float z); +void bullet_movePlayer(float x, float y, float z); // Request that the player moves in this direction -void cpp_setPlayerDir(float x, float y, float z); +void bullet_setPlayerDir(float x, float y, float z); // Get the current player position, after physics and collision have // been applied. -void cpp_getPlayerPos(float *x, float *y, float *z); - -// Insert a debug collision object -void cpp_insertBox(float x, float y, float z); +void bullet_getPlayerPos(float *x, float *y, float *z); + +// 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 +// them all in one shape, we must transform the vertices manually. +void bullet_createTriShape(int numFaces, + void *triArray, + int numVerts, + void *vertArray, + float *trans,float *matrix); + +// "Flushes" the meshes created with createTriShape, returning the +// pointer to the final shape object. +BulletShape bullet_getFinalShape(); + +// Insert a static mesh with the given translation, quaternion +// rotation and scale. The quaternion is assumed to be in Ogre format, +// ie. with the W first. +void bullet_insertStatic(BulletShape shp, float *pos, + float *quat, float scale); // Move the physics simulation 'delta' seconds forward in time -void cpp_timeStep(float delta); +void bullet_timeStep(float delta); // Deallocate objects -void cpp_cleanupBullet(); +void bullet_cleanup(); diff --git a/bullet/bullet.d b/bullet/bullet.d index dff995c57..d33a22b7c 100644 --- a/bullet/bullet.d +++ b/bullet/bullet.d @@ -1,11 +1,34 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (bullet.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.bullet; import bullet.bindings; void initBullet() { - if(cpp_initBullet()) + if(bullet_init()) throw new Exception("Bullet setup failed"); } -void cleanupBullet() { cpp_cleanupBullet(); } +void cleanupBullet() { bullet_cleanup(); } diff --git a/bullet/cpp_bullet.cpp b/bullet/cpp_bullet.cpp index 61a377ba0..26ecc440b 100644 --- a/bullet/cpp_bullet.cpp +++ b/bullet/cpp_bullet.cpp @@ -1,3 +1,26 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (cpp_bullet.cpp) 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/ . + + */ + #include "btBulletDynamicsCommon.h" #include @@ -20,20 +43,24 @@ btConvexShape *g_playerShape; // Player position. This is updated automatically by the physics // system based on g_walkDirection and collisions. It is read by D -// code through cpp_getPlayerPos(). +// code through bullet_getPlayerPos(). btVector3 g_playerPosition; // Walking vector - defines direction and speed that the player // intends to move right now. This is updated from D code each frame -// through cpp_setPlayerDir(), based on player input (and later, AI +// through bullet_setPlayerDir(), based on player input (and later, AI // decisions.) The units of the vector are points per second. btVector3 g_walkDirection; +// The current trimesh shape being built. All new inserted meshes are +// added into this, until bullet_getFinalShape() is called. +btTriangleIndexVertexArray *g_currentMesh; + // 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 // player (in g_pairCache). This list is used in the function called -// recoverFromPenetration() below. +// recoverFromPenetration(). btHashedOverlappingPairCache* g_pairCache; CustomOverlappingPairCallback *g_customPairCallback; @@ -77,7 +104,7 @@ public: */ }; -extern "C" int32_t cpp_initBullet() +extern "C" int32_t bullet_init() { // ------- SET UP THE WORLD ------- @@ -89,9 +116,14 @@ extern "C" int32_t cpp_initBullet() // TODO: Figure out what to do with this. We need the user callback // function used below (I think), but this is only offered by this - // broadphase implementation (as far as I can see.) - btVector3 worldMin(-100000,-100000,-100000); - btVector3 worldMax(100000,100000,100000); + // broadphase implementation (as far as I can see.) Maybe we can + // scan through the cell first and find good values that covers all + // the objects before we set up the dynamic world. Another option is + // 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); g_broadphase = new btAxisSweep3(worldMin,worldMax); g_dynamicsWorld = @@ -107,18 +139,18 @@ extern "C" int32_t cpp_initBullet() // Create the player collision shape. float width = 50; - //float height = 50; /* // One possible shape is the convex hull around two spheres + float height = 100; 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); - m_shape = new btMultiSphereShape(btVector3(width/2.0, height/2.0, width/2.0), - &spherePositions[0], &sphereRadii[0], 2); - */ + 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)); g_playerShape = new btSphereShape(width); @@ -146,13 +178,16 @@ extern "C" int32_t cpp_initBullet() //,btBroadphaseProxy::StaticFilter ); + // Make sure this is zero at startup + g_currentMesh = NULL; + // Success! return 0; } // Warp the player to a specific location. We do not bother setting // rotation, since it's completely irrelevant for collision detection. -extern "C" void cpp_movePlayer(float x, float y, float z) +extern "C" void bullet_movePlayer(float x, float y, float z) { btTransform tr; tr.setIdentity(); @@ -161,20 +196,120 @@ extern "C" void cpp_movePlayer(float x, float y, float z) } // Request that the player moves in this direction -extern "C" void cpp_setPlayerDir(float x, float y, float z) +extern "C" void bullet_setPlayerDir(float x, float y, float z) { g_walkDirection.setValue(x,y,z); } // Get the current player position, after physics and collision have // been applied. -extern "C" void cpp_getPlayerPos(float *x, float *y, float *z) +extern "C" void bullet_getPlayerPos(float *x, float *y, float *z) { *x = g_playerPosition.getX(); *y = g_playerPosition.getY(); *z = g_playerPosition.getZ(); } +unsigned char* copyBuffer(void *buf, int elemSize, int len) +{ + int size = elemSize * len; + void *res = malloc(size); + memcpy(res, buf, size); + + return (unsigned char*)res; +} + +// Create a triangle shape and insert it into the current index/vertex +// array. If no array is active, create one. +extern "C" void bullet_createTriShape(int32_t numFaces, + void *triArray, + int32_t numVerts, + void *vertArray, + float *trans, + float *matrix) +{ + // This struct holds the index and vertex buffers of a single + // trimesh. + btIndexedMesh im; + + // Set up the triangles + int numTriangles = numFaces / 3; + im.m_numTriangles = numTriangles; + im.m_triangleIndexStride = 6; // 3 indices * 2 bytes per short + im.m_triangleIndexBase = copyBuffer(triArray, 6, numTriangles); + + // Set up the vertices + im.m_numVertices = numVerts; + im.m_vertexStride = 12; // 4 bytes per float * 3 floats per vertex + im.m_vertexBase = copyBuffer(vertArray, 12, numVerts); + + // Transform all the vertex values according to 'trans' and 'matrix' + float *vb = (float*) im.m_vertexBase; + for(int i=0; iaddIndexedMesh(im, PHY_SHORT); +} + +// Get the shape built up so far, if any. This clears g_currentMesh, +// 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; + + // Create the shape from the completed mesh + btBvhTriangleMeshShape *shape = + new btBvhTriangleMeshShape(g_currentMesh, false); + + g_currentMesh = NULL; + return shape; +} + +// Insert a static mesh +extern "C" void bullet_insertStatic(btCollisionShape *shape, + float *pos, + float *quat, + float scale) +{ + if(scale != 1.0) + { + cout << "WARNING: Cannot scale collision meshes yet (wanted " + << scale << ")\n"; + return; + } + + btTransform trafo; + trafo.setIdentity(); + trafo.setOrigin(btVector3(pos[0], pos[1], pos[2])); + + // Ogre uses WXYZ quaternions, Bullet uses XYZW. + trafo.setRotation(btQuaternion(quat[1], quat[2], quat[3], quat[0])); + + // Create and insert the collision object + btCollisionObject *obj = new btCollisionObject(); + obj->setCollisionShape(shape); + obj->setWorldTransform(trafo); + g_dynamicsWorld->addCollisionObject(obj); +} + + +/* // Insert a debug collision shape -extern "C" void cpp_insertBox(float x, float y, float z) +extern "C" void bullet_insertBox(float x, float y, float z) { btCollisionShape* groundShape = new btSphereShape(50); @@ -184,21 +319,33 @@ extern "C" void cpp_insertBox(float x, float y, float z) btTransform groundTransform; groundTransform.setIdentity(); groundTransform.setOrigin(btVector3(x,y,z)); + + // Use a simple collision object for static objects + btCollisionObject *obj = new btCollisionObject(); + obj->setCollisionShape(groundShape); + obj->setWorldTransform(groundTransform); + + g_dynamicsWorld->addCollisionObject(obj); + + // You can also use a rigid body with a motion state, but this is + // overkill for statics. + /* btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform); // Create a rigid body from the motion state. Give it zero mass and - // inertia. + // zero inertia. btRigidBody::btRigidBodyConstructionInfo rbInfo(0, myMotionState, groundShape, btVector3(0,0,0)); btRigidBody* body = new btRigidBody(rbInfo); // Add the body to the world g_dynamicsWorld->addRigidBody(body); -} + */ +//} // Move the physics simulation 'delta' seconds forward in time -extern "C" void cpp_timeStep(float delta) +extern "C" void bullet_timeStep(float delta) { // TODO: We might experiment with the number of time steps. Remember // that the function also returns the number of steps performed. @@ -206,7 +353,7 @@ extern "C" void cpp_timeStep(float delta) } // Cleanup in the reverse order of creation/initialization -extern "C" void cpp_cleanupBullet() +extern "C" void bullet_cleanup() { // Remove the rigidbodies from the dynamics world and delete them for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) diff --git a/bullet/cpp_player.cpp b/bullet/cpp_player.cpp index 1e38fa9c0..1b9a04c68 100644 --- a/bullet/cpp_player.cpp +++ b/bullet/cpp_player.cpp @@ -1,3 +1,26 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (cpp_player.cpp) 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/ . + + */ + // This file handles player-specific physics and collision detection // TODO: Later we might handle various physics modes, eg. dynamic @@ -10,6 +33,8 @@ // player collision, these will be member variables. bool g_touchingContact; btVector3 g_touchingNormal; +btScalar g_currentStepOffset; +float g_stepHeight = 20; // Returns the reflection direction of a ray going 'direction' hitting // a surface with normal 'normal' @@ -49,11 +74,12 @@ public: } }; -/* Used to step up small steps and slopes. Not done. -void KinematicCharacterController::stepUp (const btCollisionWorld* world) +// Used to step up small steps and slopes. +void stepUp() { // phase 1: up - btVector3 targetPosition = g_playerPosition + btVector3 (btScalar(0.0), m_stepHeight, btScalar(0.0)); + btVector3 targetPosition = g_playerPosition + + btVector3(0.0, 0.0, g_stepHeight); btTransform start, end; start.setIdentity (); @@ -63,23 +89,22 @@ void KinematicCharacterController::stepUp (const btCollisionWorld* world) start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0)); end.setOrigin (targetPosition); - ClosestNotMeConvexResultCallback callback (g_playerObject); - world->convexSweepTest (g_playerShape, start, end, callback); + ClosestNotMeConvexResultCallback callback; + g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); if (callback.hasHit()) { // we moved up only a fraction of the step height - m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction; + g_currentStepOffset = g_stepHeight * callback.m_closestHitFraction; g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, callback.m_closestHitFraction); } else { - m_currentStepOffset = m_stepHeight; + g_currentStepOffset = g_stepHeight; g_playerPosition = targetPosition; } } -*/ void updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btVector3 &targetPosition) @@ -168,15 +193,15 @@ void stepForward(btVector3& walkMove) } } -/* Not done. Will handle gravity, falling, sliding, etc. -void KinematicCharacterController::stepDown (const btCollisionWorld* g_dynamicsWorld, btScalar dt) +void stepDown (btScalar dt) { btTransform start, end; // phase 3: down - btVector3 step_drop = btVector3(btScalar(0.0), m_currentStepOffset, btScalar(0.0)); - btVector3 gravity_drop = btVector3(btScalar(0.0), m_stepHeight, btScalar(0.0)); - targetPosition -= (step_drop + gravity_drop); + btVector3 step_drop = btVector3(0,0,g_currentStepOffset); + btVector3 gravity_drop = btVector3(0,0,g_stepHeight); + + btVector3 targetPosition = g_playerPosition - step_drop - gravity_drop; start.setIdentity (); end.setIdentity (); @@ -184,20 +209,17 @@ void KinematicCharacterController::stepDown (const btCollisionWorld* g_dynamicsW start.setOrigin (g_playerPosition); end.setOrigin (targetPosition); - ClosestNotMeConvexResultCallback callback (g_playerObject); - g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); + ClosestNotMeConvexResultCallback callback; + g_dynamicsWorld->convexSweepTest(g_playerShape, start, end, callback); if (callback.hasHit()) - { - // we dropped a fraction of the height -> hit floor - g_playerPosition.setInterpolate3 (g_playerPosition, targetPosition, callback.m_closestHitFraction); - } else { - // we dropped the full height - - g_playerPosition = targetPosition; - } + // we dropped a fraction of the height -> hit floor + g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, + callback.m_closestHitFraction); + else + // we dropped the full height + g_playerPosition = targetPosition; } -*/ // Check if the player currently collides with anything, and adjust // its position accordingly. Returns true if collisions were found. @@ -292,9 +314,9 @@ void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep) btVector3 walkStep = g_walkDirection * timeStep; - //stepUp(); + stepUp(); stepForward(walkStep); - //stepDown(dt); + stepDown(timeStep); // Move the player (but keep rotation) xform = g_playerObject->getWorldTransform (); diff --git a/core/resource.d b/core/resource.d index 5c8d54410..b9ceaab90 100644 --- a/core/resource.d +++ b/core/resource.d @@ -35,6 +35,8 @@ import util.random; import bsa.bsafile; +import bullet.bindings; + import core.memory; import core.config; @@ -361,7 +363,7 @@ struct ResourceManager // Load and insert nif // TODO: Might add BSA name to the handle name, for clarity - mi.node = meshLoader.loadMesh(mi.name); + meshLoader.loadMesh(mi.name, mi.node, mi.shape); // TODO: We could clear the BSA memory mapping here to free some // mem @@ -409,6 +411,9 @@ struct MeshResource public: + // Bullet collision shape. Can be null. + BulletShape shape; + NodePtr getNode() in { diff --git a/input/events.d b/input/events.d index 7c5a33cd6..23e227558 100644 --- a/input/events.d +++ b/input/events.d @@ -94,7 +94,7 @@ void toggleBattle() void toggleFullscreen() { - cpp_toggleFullscreen(); + ogre_toggleFullscreen(); } const float volDiff = 0.05; @@ -126,7 +126,7 @@ void mainVolume(bool increase) void takeScreenShot() { char[] file = format("screenshot_%06d.png", config.screenShotNum++); - cpp_screenshot(toStringz(file)); + ogre_screenshot(toStringz(file)); writefln("Wrote '%s'", file); } @@ -161,8 +161,8 @@ extern(C) void d_handleMouseMove(MouseState *state) state.X.abs, state.Y.abs, state.Z.abs, state.X.rel, state.Y.rel, state.Z.rel); - cpp_rotateCamera( state.X.rel * effMX, - state.Y.rel * effMY ); + ogre_rotateCamera( state.X.rel * effMX, + state.Y.rel * effMY ); } extern(C) void d_handleMouseButton(MouseState *state, int button) @@ -221,7 +221,7 @@ extern(C) void d_handleKey(KC keycode, dchar text = 0) case Keys.Mute: toggleMute(); break; case Keys.Fullscreen: toggleFullscreen(); break; - case Keys.Debug: cpp_debug(0); break; + case Keys.Debug: ogre_debug(0); break; case Keys.ScreenShot: takeScreenShot(); break; case Keys.Pause: togglePause(); break; case Keys.Exit: exitProgram(); break; @@ -247,16 +247,10 @@ void initializeInput() // at all, and should be moved. with(playerData.position) { - // TODO: Think about renaming these functions to ogre_moveCamera - // and bullet_movePlayer etc. - cpp_moveCamera(position[0], position[1], position[2]); - cpp_setCameraRotation(rotation[0], rotation[1], rotation[2]); + ogre_moveCamera(position[0], position[1], position[2]); + ogre_setCameraRotation(rotation[0], rotation[1], rotation[2]); - cpp_movePlayer(position[0], position[1], position[2]); - - // Insert a collision shape close to the player - cpp_insertBox(position[0], position[1]+500, position[2]); - cpp_drawBox(position[0], position[1]+500, position[2]); + bullet_movePlayer(position[0], position[1], position[2]); } // TODO/FIXME: This should have been in config, but DMD's module @@ -269,14 +263,14 @@ void initializeInput() float tmpTime = 0; int cnt; -extern(C) int cpp_isPressed(int keysym); +extern(C) int ois_isPressed(int keysym); // Check if a key is currently down bool isPressed(Keys key) { KeyBind *b = &keyBindings.bindings[key]; foreach(i; b.syms) - if(i != 0 && cpp_isPressed(i)) return true; + if(i != 0 && ois_isPressed(i)) return true; return false; } @@ -321,16 +315,16 @@ extern(C) int d_frameStarted(float time) // This isn't very elegant, but it's simple and it works. // Get the current coordinates - cpp_getCameraPos(&ox, &oy, &oz); + ogre_getCameraPos(&ox, &oy, &oz); // Move camera using relative coordinates. TODO: We won't really // need to move the camera here (since it's moved below anyway), we // only want the transformation from camera space to world // space. This can likely be done more efficiently. - cpp_moveCameraRel(moveX, moveY, moveZ); + ogre_moveCameraRel(moveX, moveY, moveZ); // Get the result - cpp_getCameraPos(&x, &y, &z); + ogre_getCameraPos(&x, &y, &z); // The result is the real movement direction, in world coordinates moveX = x-ox; @@ -338,14 +332,14 @@ extern(C) int d_frameStarted(float time) moveZ = z-oz; // Tell Bullet that this is where we want to go - cpp_setPlayerDir(moveX, moveY, moveZ); + bullet_setPlayerDir(moveX, moveY, moveZ); // Perform a Bullet time step - cpp_timeStep(time); + bullet_timeStep(time); // Get the final (actual) player position and update the camera - cpp_getPlayerPos(&x, &y, &z); - cpp_moveCamera(x,y,z); + bullet_getPlayerPos(&x, &y, &z); + ogre_moveCamera(x,y,z); sndCumTime += time; if(sndCumTime > sndRefresh) @@ -353,7 +347,7 @@ extern(C) int d_frameStarted(float time) float fx, fy, fz; float ux, uy, uz; - cpp_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz); + ogre_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz); soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz); sndCumTime -= sndRefresh; diff --git a/ogre/bindings.d b/ogre/bindings.d index fb585492d..2b0c0f187 100644 --- a/ogre/bindings.d +++ b/ogre/bindings.d @@ -49,49 +49,58 @@ extern(C): // Do engine configuration. Returns 0 if we should continue, 1 if // not. -int cpp_configure(int showConfig, // Do we show the config dialogue? - char *plugincfg // Name of 'plugin.cfg' file - ); +int ogre_configure(int showConfig, // Do we show the config dialogue? + char *plugincfg // Name of 'plugin.cfg' file + ); // Sets up the window -void cpp_initWindow(); +void ogre_initWindow(); // Set up an empty scene. -void cpp_makeScene(); +void ogre_makeScene(); // Set the ambient light and "sunlight" -void cpp_setAmbient(float r, float g, float b, +void ogre_setAmbient(float r, float g, float b, float rs, float gs, float bs); // Set fog color and view distance -void cpp_setFog(float rf, float gf, float bf, +void ogre_setFog(float rf, float gf, float bf, float flow, float fhigh); // Create a simple sky dome -int cpp_makeSky(); +int ogre_makeSky(); // Enter main rendering loop -void cpp_startRendering(); +void ogre_startRendering(); // Cleans up after ogre -void cpp_cleanup(); +void ogre_cleanup(); // Gets a child SceneNode from the root node, then detatches it to // hide it from view. Used for creating the "template" node associated // with a NIF mesh. -NodePtr cpp_getDetachedNode(); +NodePtr ogre_getDetachedNode(); + +// Convert a Morrowind rotation (3 floats) to a quaternion (4 floats) +void ogre_mwToQuaternion(float *mw, float *quat); // Create a copy of the given scene node, with the given coordinates -// and rotation. -NodePtr cpp_insertNode(NodePtr base, char* name, - Placement *pos, float scale); +// and rotation (as a quaternion.) +NodePtr ogre_insertNode(NodePtr base, char* name, + float *pos, float *quat, float scale); + +// Get the world transformation of a node, returned as a translation +// and a matrix. The matrix includes both rotation and scaling. The +// buffers given must be large enough to store the result (3 and 9 +// floats respectively.) +void ogre_getWorldTransform(NodePtr node, float *trans, float *matrix); // Create a (very crappy looking) plane to simulate the water level -void cpp_createWater(float level); +void ogre_createWater(float level); // Creates a scene node as a child of 'parent', then translates and // rotates it according to the data in 'trafo'. -NodePtr cpp_createNode( +NodePtr ogre_createNode( char *name, // Name to give the node Transformation *trafo, // Transformation NodePtr parent, // Parent node @@ -99,23 +108,23 @@ NodePtr cpp_createNode( // Create a light with the given diffuse color. Attach it to SceneNode // 'parent'. -NodePtr cpp_attachLight(char* name, NodePtr parent, - float r, float g, float b, - float radius); +NodePtr ogre_attachLight(char* name, NodePtr parent, + float r, float g, float b, + float radius); // Create the specified material -void cpp_createMaterial(char *name, // Name to give resource - float *ambient, // Ambient RBG value - float *diffuse, - float *specular, - float *emissive,// Self illumination - float glossiness,// Same as shininess? - float alpha, // Use this in all alpha values? - char *texture); // Texture name +void ogre_createMaterial(char *name, // Name to give resource + float *ambient, // Ambient RBG value + float *diffuse, + float *specular, + float *emissive,// Self illumination + float glossiness,// Same as shininess? + float alpha, // Use this in all alpha values? + char *texture); // Texture name // Creates a mesh and gives it a bounding box. Also creates an entity // and attached it to the given SceneNode 'owner'. -void cpp_createMesh( +void ogre_createMesh( char* name, // Name of the mesh int numVerts, // Number of vertices float* vertices, // Vertex list @@ -135,21 +144,18 @@ void cpp_createMesh( ); // Toggle fullscreen mode -void cpp_toggleFullscreen(); +void ogre_toggleFullscreen(); // Save a screen shot to the given file name -void cpp_screenshot(char *filename); +void ogre_screenshot(char *filename); // Camera control and information -void cpp_rotateCamera(float x, float y); -void cpp_moveCamera(float x, float y, float z); -void cpp_setCameraRotation(float r1, float r2, float r3); -void cpp_getCameraPos(float *x, float *y, float *z); -void cpp_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz); -void cpp_moveCameraRel(float x, float y, float z); +void ogre_rotateCamera(float x, float y); +void ogre_moveCamera(float x, float y, float z); +void ogre_setCameraRotation(float r1, float r2, float r3); +void ogre_getCameraPos(float *x, float *y, float *z); +void ogre_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz); +void ogre_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); +void ogre_debug(int i); diff --git a/ogre/cpp_bsaarchive.cpp b/ogre/cpp_bsaarchive.cpp index e2e903a46..1eaabe20c 100644 --- a/ogre/cpp_bsaarchive.cpp +++ b/ogre/cpp_bsaarchive.cpp @@ -23,7 +23,7 @@ // This file inserts an archive manager for .bsa files into the ogre // resource loading system. It uses callbacks to D to interact with -// the d-based bsa handling code. The D bindings are in +// the D-based bsa handling code. The D bindings are in // core/resource.d. // Callbacks to D code @@ -49,17 +49,19 @@ public: bool isCaseSensitive(void) const { return false; } // The archives are already loaded in D code, and they are never - // unloaded. + // unloaded. Just ignore these. void load() {} void unload() {} + // Open a file in the archive. We delegate the function to D-code. DataStreamPtr open(const String& filename) const { //std::cout << "open(" << filename << ")\n"; void *ptr; uint32_t size; - // Open the file + // Open the file. The BSA archives are memory mapped, D code + // returns a pointer and a file size. d_bsaOpenFile(filename.c_str(), &ptr, &size); return DataStreamPtr(new MemoryDataStream(ptr, size)); @@ -93,7 +95,7 @@ public: } // Gets called once for each of the ogre formats, *.program, - // *.material etc. We can ignore that. + // *.material etc. We can ignore this. FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true, bool dirs = false) { diff --git a/ogre/cpp_framelistener.cpp b/ogre/cpp_framelistener.cpp index 1063ad9da..37feeb9a5 100644 --- a/ogre/cpp_framelistener.cpp +++ b/ogre/cpp_framelistener.cpp @@ -101,13 +101,13 @@ InputListener mInput; // Functions called from D during event handling -extern "C" int32_t cpp_isPressed(int32_t keysym) +extern "C" int32_t ois_isPressed(int32_t keysym) { return mKeyboard->isKeyDown((OIS::KeyCode)keysym); } // Dump screen contents to file -extern "C" void cpp_screenshot(char* filename) +extern "C" void ogre_screenshot(char* filename) { mWindow->writeContentsToFile(filename); @@ -117,14 +117,14 @@ extern "C" void cpp_screenshot(char* filename) } // Rotate camera as result of mouse movement -extern "C" void cpp_rotateCamera(float x, float y) +extern "C" void ogre_rotateCamera(float x, float y) { mCamera->yaw(Degree(-x)); mCamera->pitch(Degree(-y)); } // Get current camera position -extern "C" void cpp_getCameraPos(float *x, float *y, float *z) +extern "C" void ogre_getCameraPos(float *x, float *y, float *z) { Vector3 pos = mCamera->getPosition(); *x = pos[0]; @@ -134,7 +134,7 @@ extern "C" void cpp_getCameraPos(float *x, float *y, float *z) // Get current camera orientation, in the form of 'front' and 'up' // vectors. -extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz, +extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz) { Vector3 front = mCamera->getDirection(); @@ -148,7 +148,7 @@ extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz, } // Move camera -extern "C" void cpp_moveCamera(float x, float y, float z) +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 @@ -157,7 +157,7 @@ extern "C" void cpp_moveCamera(float x, float y, float z) } // Rotate camera using Morrowind rotation specifiers -extern "C" void cpp_setCameraRotation(float r1, float r2, float r3) +extern "C" void ogre_setCameraRotation(float r1, float r2, float r3) { // TODO: This translation is probably not correct, but for now I // have no reference point. Fix it later when we teleport from one @@ -175,7 +175,7 @@ extern "C" void cpp_setCameraRotation(float r1, float r2, float r3) } // Move camera relative to its own axis set. -extern "C" void cpp_moveCameraRel(float x, float y, float z) +extern "C" void ogre_moveCameraRel(float x, float y, float z) { mCamera->moveRelative(Vector3(x,y,z)); } diff --git a/ogre/cpp_interface.cpp b/ogre/cpp_interface.cpp index 80446c568..fb796c012 100644 --- a/ogre/cpp_interface.cpp +++ b/ogre/cpp_interface.cpp @@ -25,7 +25,7 @@ // E X P O R T E D F U N C T I O N S //----------------------------------------------------------------------- -extern "C" void cpp_cleanup() +extern "C" void ogre_cleanup() { // Kill the input systems. This will reset input options such as key // repetition. @@ -41,7 +41,7 @@ extern "C" void cpp_cleanup() } } -extern "C" int32_t cpp_configure( +extern "C" int32_t ogre_configure( int32_t showConfig, // Do we show the config dialogue? char *plugincfg // Name of 'plugin.cfg' file ) @@ -93,9 +93,9 @@ extern "C" int32_t cpp_configure( } // Initialize window. This will create and show the actual window. -extern "C" void cpp_initWindow() +extern "C" void ogre_initWindow() { - std::cout << "cpp_initWindow()\n"; + std::cout << "ogre_initWindow()\n"; // Initialize OGRE. mWindow = mRoot->initialise(true); @@ -160,11 +160,11 @@ extern "C" void cpp_initWindow() mKeyboard -> setEventCallback( &mInput ); mMouse -> setEventCallback( &mInput ); - std::cout << "cpp_initWindow finished\n"; + std::cout << "ogre_initWindow finished\n"; } // Make a scene, set the given ambient light -extern "C" void cpp_makeScene() +extern "C" void ogre_makeScene() { // Get the SceneManager, in this case a generic one mSceneMgr = mRoot->createSceneManager(ST_GENERIC); @@ -202,12 +202,12 @@ extern "C" void cpp_makeScene() // Create a sky dome. Currently disabled since we aren't including the // Ogre example data (which has the sky material.) -extern "C" void cpp_makeSky() +extern "C" void ogre_makeSky() { //mSceneMgr->setSkyDome( true, "Examples/CloudySky", 5, 8 ); } -extern "C" Light* cpp_attachLight(char *name, SceneNode* base, +extern "C" Light* ogre_attachLight(char *name, SceneNode* base, float r, float g, float b, float radius) { @@ -225,12 +225,12 @@ extern "C" Light* cpp_attachLight(char *name, SceneNode* base, } // Toggle between fullscreen and windowed mode. -extern "C" void cpp_toggleFullscreen() +extern "C" void ogre_toggleFullscreen() { std::cout << "Not implemented yet\n"; } -extern "C" void cpp_setAmbient(float r, float g, float b, // Ambient light +extern "C" void ogre_setAmbient(float r, float g, float b, // Ambient light float rs, float gs, float bs) // "Sunlight" { ColourValue c = ColourValue(r, g, b); @@ -244,7 +244,7 @@ extern "C" void cpp_setAmbient(float r, float g, float b, // Ambient light l->setDirection(0,-1,0); } -extern "C" void cpp_setFog(float rf, float gf, float bf, // Fog color +extern "C" void ogre_setFog(float rf, float gf, float bf, // Fog color float flow, float fhigh) // Fog distance { ColourValue fogColor( rf, gf, bf ); @@ -257,7 +257,7 @@ extern "C" void cpp_setFog(float rf, float gf, float bf, // Fog color //vp->setBackgroundColour(fogColor); } -extern "C" void cpp_startRendering() +extern "C" void ogre_startRendering() { mRoot->startRendering(); } @@ -292,43 +292,81 @@ void cloneNode(SceneNode *from, SceneNode *to, char* name) } } +// Convert a Morrowind rotation (3 floats) to a quaternion (4 floats) +extern "C" void ogre_mwToQuaternion(float *mw, float *quat) +{ + // Rotate around X axis + Quaternion xr(Radian(-mw[0]), Vector3::UNIT_X); + + // Rotate around Y axis + Quaternion yr(Radian(-mw[1]), Vector3::UNIT_Y); + + // Rotate around Z axis + Quaternion zr(Radian(-mw[2]), Vector3::UNIT_Z); + + // Rotates first around z, then y, then x + Quaternion res = xr*yr*zr; + + // Copy result back to caller + for(int i=0; i<4; i++) + quat[i] = res[i]; +} + // Supposed to insert a copy of the node, for now it just inserts the // actual node. -extern "C" SceneNode *cpp_insertNode(SceneNode *base, char* name, - float *pos, float scale) +extern "C" SceneNode *ogre_insertNode(SceneNode *base, char* name, + float *pos, float *quat, + float scale) { - //std::cout << "cpp_insertNode(" << name << ")\n"; + //std::cout << "ogre_insertNode(" << name << ")\n"; SceneNode *node = root->createChildSceneNode(name); // Make a copy of the node cloneNode(base, node, name); - // pos points to a Placement struct, which has the format - // float x, y, z; // position - // float r1, r2, r3; // rotation - + // Apply transformations node->setPosition(pos[0], pos[1], pos[2]); - - // Rotate around X axis - Quaternion xr(Radian(-pos[3]), Vector3::UNIT_X); - - // Rotate around Y axis - Quaternion yr(Radian(-pos[4]), Vector3::UNIT_Y); - - // Rotate around Z axis - Quaternion zr(Radian(-pos[5]), Vector3::UNIT_Z); - - // Rotates first around z, then y, then x - node->setOrientation(xr*yr*zr); + node->setOrientation(quat[0], quat[1], quat[2], quat[3]); node->setScale(scale, scale, scale); return node; } +// Get the world transformation of a node (the total transformation of +// this node and all parent nodes). Return it as a translation +// (3-vector) and a rotation / scaling part (3x3 matrix) +extern "C" void ogre_getWorldTransform(SceneNode *node, + float *trans, // Storage for translation + float *matrix)// For 3x3 matrix +{ + // Get the world transformation first + Matrix4 trafo; + node->getWorldTransforms(&trafo); + + // Extract the translation part and pass it to the caller + Vector3 tr = trafo.getTrans(); + trans[0] = tr[0]; + trans[1] = tr[1]; + trans[2] = tr[2]; + + // Next extract the matrix + Matrix3 mat; + trafo.extract3x3Matrix(mat); + matrix[0] = mat[0][0]; + matrix[1] = mat[0][1]; + matrix[2] = mat[0][2]; + matrix[3] = mat[1][0]; + matrix[4] = mat[1][1]; + matrix[5] = mat[1][2]; + matrix[6] = mat[2][0]; + matrix[7] = mat[2][1]; + matrix[8] = mat[2][2]; +} + // Create the water plane. It doesn't really resemble "water" yet // though. -extern "C" void cpp_createWater(float level) +extern "C" void ogre_createWater(float level) { // Create a plane aligned with the xy-plane. MeshManager::getSingleton().createPlane("water", @@ -362,7 +400,7 @@ public: String LASTNAME; // Load the contents of a mesh -extern "C" void cpp_createMesh( +extern "C" void ogre_createMesh( char* name, // Name of the mesh int32_t numVerts, // Number of vertices float* vertices, // Vertex list @@ -522,7 +560,7 @@ extern "C" void cpp_createMesh( msh->_setBoundingSphereRadius(radius); } -extern "C" void cpp_createMaterial(char *name, // Name to give +extern "C" void ogre_createMaterial(char *name, // Name to give // resource float *ambient, // Ambient RBG @@ -568,20 +606,20 @@ extern "C" void cpp_createMaterial(char *name, // Name to give LASTNAME = material->getName(); } -extern "C" SceneNode *cpp_getDetachedNode() +extern "C" SceneNode *ogre_getDetachedNode() { SceneNode *node = root->createChildSceneNode(); root->removeChild(node); return node; } -extern "C" SceneNode* cpp_createNode( +extern "C" SceneNode* ogre_createNode( char *name, float *trafo, SceneNode *parent, int32_t noRot) { - //std::cout << "cpp_createNode(" << name << ")"; + //std::cout << "ogre_createNode(" << name << ")"; SceneNode *node = parent->createChildSceneNode(name); //std::cout << " ... done\n"; @@ -613,7 +651,7 @@ extern "C" SceneNode* cpp_createNode( /* Code currently not in use // We need this later for animated meshes. Boy will this be a mess. -extern "C" void* cpp_setupSkeleton(char* name) +extern "C" void* ogre_setupSkeleton(char* name) { SkeletonPtr skel = SkeletonManager::getSingleton().create( name, "Closet", true); @@ -627,7 +665,7 @@ extern "C" void* cpp_setupSkeleton(char* name) } // Use this later when loading textures directly from NIF files -extern "C" void cpp_createTexture(char* name, uint32_t width, uint32_t height) +extern "C" void ogre_createTexture(char* name, uint32_t width, uint32_t height) { TexturePtr texture = TextureManager::getSingleton().createManual( name, // name @@ -671,7 +709,7 @@ extern "C" void cpp_createTexture(char* name, uint32_t width, uint32_t height) material->getTechnique(0)->getPass(0)->setSceneBlending(SBT_TRANSPARENT_ALPHA); } -extern "C" void *cpp_insertBone(char* name, void* rootBone, int32_t index) +extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index) { return (void*) ( ((Bone*)rootBone)->createChild(index) ); } diff --git a/ogre/cpp_ogre.cpp b/ogre/cpp_ogre.cpp index 309f59356..ac786d786 100644 --- a/ogre/cpp_ogre.cpp +++ b/ogre/cpp_ogre.cpp @@ -56,23 +56,3 @@ 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/ogre/cpp_overlay.cpp b/ogre/cpp_overlay.cpp index f5eccb77d..0ea80a846 100644 --- a/ogre/cpp_overlay.cpp +++ b/ogre/cpp_overlay.cpp @@ -27,14 +27,9 @@ PanelOverlayElement *cont; int visible; -void crap(char *p, ColourValue v) +extern "C" void ogre_debug(int32_t i) { - std::cerr << p << ": " << v.getAsRGBA() << "\n"; -} - -extern "C" void cpp_debug(int32_t i) -{ - std::cerr << "Running cpp_debug(" << i << ")\n"; + std::cerr << "Running ogre_debug(" << i << ")\n"; if(om) { diff --git a/ogre/meshloader.d b/ogre/meshloader.d index 18ec7b2f8..e86984aa4 100644 --- a/ogre/meshloader.d +++ b/ogre/meshloader.d @@ -32,6 +32,8 @@ import nif.record; import core.resource; import ogre.bindings; +import bullet.bindings; + import util.uniquename; /* @@ -52,15 +54,15 @@ struct MeshLoader // Not sure how to handle the bounding box, just ignore it for now. char[] baseName; // NIF file name. Used in scene node names etc. so - // that we can easier identify where they came - // from. + // that we can identify where they came from in + // case of error messages. // Load a NIF mesh. Assumes nifMesh is already opened. This creates // a "template" scene node containing this mesh, and removes it from // the main scene. This node can later be "cloned" so that multiple // instances of the object can be inserted into the world without // inserting the mesh more than once. - NodePtr loadMesh(char[] name) + void loadMesh(char[] name, out NodePtr base, out BulletShape shape) { baseName = name; @@ -72,16 +74,19 @@ struct MeshLoader // TODO: Figure out what to do in this case, we should // probably throw. writefln("NIF '%s' IS NOT A MESH", name); - return null; + return; } // Get a fresh SceneNode and detatch it from the root. We use this // as the base for our mesh. - NodePtr base = cpp_getDetachedNode(); + base = ogre_getDetachedNode(); // Recursively insert nodes (don't rotate the first node) insertNode(n, base, true); + // Get the final shape, if any + shape = bullet_getFinalShape(); + return base; } @@ -99,7 +104,7 @@ struct MeshLoader // problem, I don't know when to do this and when not to. Neither // is always right. Update: It looks like we should always set // noRot to false in exterior cells. - NodePtr node = cpp_createNode(UniqueName(data.name).ptr, &data.trafo, + NodePtr node = ogre_createNode(UniqueName(data.name).ptr, &data.trafo, parent, cast(int)noRot); // Handle any general properties here @@ -206,7 +211,7 @@ struct MeshLoader // Create the material if(material.length) - cpp_createMaterial(material.ptr, mp.ambient.array.ptr, mp.diffuse.array.ptr, + ogre_createMaterial(material.ptr, mp.ambient.array.ptr, mp.diffuse.array.ptr, mp.specular.array.ptr, mp.emissive.array.ptr, mp.glossiness, mp.alpha, texturePtr); else if(texturePtr) @@ -218,7 +223,7 @@ struct MeshLoader zero[] = 0.0; one[] = 1.0; - cpp_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr, 0.0, 1.0, + ogre_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr, 0.0, 1.0, texturePtr); } @@ -258,7 +263,23 @@ struct MeshLoader if( vertices[i+2] > maxZ) maxZ = vertices[i+2]; } - cpp_createMesh(newName.ptr, vertices.length, vertices.ptr, + // TODO: Get the node world transformation, needed to set up + // the collision shape properly. + float[3] trans; + 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, + 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); @@ -267,19 +288,19 @@ struct MeshLoader } /* // Create a skeleton and get the root bone (index 0) - BonePtr bone = cpp_setupSkeleton(name); + BonePtr bone = ogre_setupSkeleton(name); // Reset the bone index. The next bone to be created has index 1. boneIndex = 1; // Create a mesh and assign the skeleton to it - MeshPtr mesh = cpp_setupMesh(name); + MeshPtr mesh = ogre_setupMesh(name); // Loop through the nodes, creating submeshes, materials and // skeleton bones in the process. handleNode(node, bone, mesh); // Create the "template" entity - EntityPtr entity = cpp_createEntity(name); + EntityPtr entity = ogre_createEntity(name); // Loop through once again, this time to set the right // transformations on the entity's SkeletonInstance. The order of @@ -291,20 +312,20 @@ struct MeshLoader if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match"); if(!hasBBox) - cpp_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); + ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); return entity; } void handleNode(Node node, BonePtr root, MeshPtr mesh) { // Insert a new bone for this node - BonePtr bone = cpp_insertBone(node.name, root, boneIndex++); + BonePtr bone = ogre_insertBone(node.name, root, boneIndex++); } void transformBones(Node node, EntityPtr entity) { - cpp_transformBone(entity, &node.trafo, boneIndex++); + ogre_transformBone(entity, &node.trafo, boneIndex++); NiNode n = cast(NiNode)node; if(n !is null) diff --git a/ogre/ogre.d b/ogre/ogre.d index 0c4015bd2..14dcaf411 100644 --- a/ogre/ogre.d +++ b/ogre/ogre.d @@ -27,6 +27,7 @@ import core.resource; import core.config; import ogre.bindings; +import bullet.bindings; import util.uniquename; import std.stdio; @@ -44,7 +45,8 @@ class OgreException : Exception // Place a mesh in the 3D scene graph, at the given // location/scale. Returns a node pointer to the inserted object. -NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale) +NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale, + bool collide) { // Get a scene node for this object. mesh.getNode() will either load // it from file or BSA archive, or give us a handle if it is already @@ -52,22 +54,35 @@ NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale) // This must be called BEFORE UniqueName below, because it might // possibly use UniqueName itself and overwrite the data - // there. (That was a fun bug to track down...) + // there. (That was a fun bug to track down...) Calling getNode() + // will load the mesh if it is not already loaded. NodePtr node = mesh.getNode(); - // Let us insert a copy + // First, convert the Morrowind rotation to a quaternion + float[4] quat; + ogre_mwToQuaternion(pos.rotation.ptr, quat.ptr); + + // Insert a mesh copy into Ogre. char[] name = UniqueName(mesh.getName); - return cpp_insertNode(node, name.ptr, pos, scale); + node = ogre_insertNode(node, name.ptr, pos.position.ptr, + quat.ptr, scale); + + // Insert a collision shape too, if the mesh has one. + if(collide && mesh.shape !is null) + bullet_insertStatic(mesh.shape, pos.position.ptr, + quat.ptr, scale); + + return node; } NodePtr attachLight(NodePtr parent, Color c, float radius) { - return cpp_attachLight(UniqueName("_light").ptr, parent, + return ogre_attachLight(UniqueName("_light").ptr, parent, c.red/255.0, c.green/255.0, c.blue/255.0, radius); } -// If 'true' then we must call cpp_cleanup() on exit. +// If 'true' then we must call ogre_cleanup() on exit. bool ogreSetup = false; // Make sure we clean up @@ -91,22 +106,22 @@ void setupOgre() // Later we will send more config info from core.config along with // this function - if(cpp_configure(config.finalOgreConfig, toStringz(plugincfg))) + if(ogre_configure(config.finalOgreConfig, toStringz(plugincfg))) OgreException("Configuration abort"); - cpp_initWindow(); + ogre_initWindow(); // We set up the scene manager in a separate function, since we // might have to do that for every new cell later on, and handle // exterior cells differently, etc. - cpp_makeScene(); + ogre_makeScene(); ogreSetup = true; } void setAmbient(Color amb, Color sun, Color fog, float density) { - cpp_setAmbient(amb.red/255.0, amb.green/255.0, amb.blue/255.0, + ogre_setAmbient(amb.red/255.0, amb.green/255.0, amb.blue/255.0, sun.red/255.0, sun.green/255.0, sun.blue/255.0); // Calculate fog distance @@ -114,7 +129,7 @@ void setAmbient(Color amb, Color sun, Color fog, float density) float fhigh = 4500 + 9000*(1-density); float flow = 200 + 2000*(1-density); - cpp_setFog(fog.red/255.0, fog.green/255.0, fog.blue/255.0, 200, fhigh); + ogre_setFog(fog.red/255.0, fog.green/255.0, fog.blue/255.0, 200, fhigh); } // Jump into the OGRE rendering loop. Everything should be loaded and @@ -122,7 +137,7 @@ void setAmbient(Color amb, Color sun, Color fog, float density) void startRendering() { // Kick OGRE into gear - cpp_startRendering(); + ogre_startRendering(); } // Cleans up after OGRE. Resets things like screen resolution and @@ -130,7 +145,7 @@ void startRendering() void cleanupOgre() { if(ogreSetup) - cpp_cleanup(); + ogre_cleanup(); ogreSetup = false; } @@ -143,3 +158,4 @@ align(1) struct Placement float[3] position; float[3] rotation; } +static assert(Placement.sizeof == 4*6); diff --git a/openmw.d b/openmw.d index 5cbcd952c..195ba8d60 100644 --- a/openmw.d +++ b/openmw.d @@ -207,13 +207,14 @@ void main(char[][] args) } // Simple safety hack - NodePtr putObject(MeshIndex m, Placement *pos, float scale) + NodePtr putObject(MeshIndex m, Placement *pos, float scale, + bool collide=false) { if(m == null) writefln("WARNING: CANNOT PUT NULL OBJECT"); else if(m.isEmpty) writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName); - else return placeObject(m, pos, scale); + else return placeObject(m, pos, scale, collide); return null; } @@ -235,7 +236,7 @@ void main(char[][] args) // Not all interior cells have water if(cd.inCell.flags & CellFlags.HasWater) - cpp_createWater(cd.water); + ogre_createWater(cd.water); } else { @@ -246,15 +247,15 @@ void main(char[][] args) setAmbient(c, c, c, 0); // Put in the water - cpp_createWater(cd.water); + ogre_createWater(cd.water); // Create an ugly sky - cpp_makeSky(); + ogre_makeSky(); } // Insert the meshes of statics into the scene foreach(ref LiveStatic ls; cd.statics) - putObject(ls.m.model, &ls.base.pos, ls.base.scale); + putObject(ls.m.model, &ls.base.pos, ls.base.scale, true); // Inventory lights foreach(ref LiveLight ls; cd.lights) { @@ -277,7 +278,7 @@ void main(char[][] args) // Static lights foreach(ref LiveLight ls; cd.statLights) { - NodePtr n = putObject(ls.m.model, &ls.base.pos, ls.base.scale); + NodePtr n = putObject(ls.m.model, &ls.base.pos, ls.base.scale, true); ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); if(!noSound) { @@ -303,7 +304,7 @@ void main(char[][] args) */ // Containers foreach(ref LiveContainer ls; cd.containers) - putObject(ls.m.model, &ls.base.pos, ls.base.scale); + putObject(ls.m.model, &ls.base.pos, ls.base.scale, true); // Doors foreach(ref LiveDoor ls; cd.doors) putObject(ls.m.model, &ls.base.pos, ls.base.scale); @@ -334,7 +335,7 @@ void main(char[][] args) // Tools foreach(ref LiveTool ls; cd.tools) putObject(ls.m.model, &ls.base.pos, ls.base.scale); - // Creatures (these often look like shit + // Creatures (not displayed very well yet) foreach(ref LiveCreature ls; cd.creatures) putObject(ls.m.model, &ls.base.pos, ls.base.scale); diff --git a/sound/al.d b/sound/al.d index 91da3f6cc..07c931eb8 100644 --- a/sound/al.d +++ b/sound/al.d @@ -1,3 +1,26 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (al.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 sound.al; extern(C): diff --git a/sound/alc.d b/sound/alc.d index 06d3b3d01..a1a8d1c66 100644 --- a/sound/alc.d +++ b/sound/alc.d @@ -1,3 +1,26 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (alc.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 sound.alc; extern(C): diff --git a/sound/avcodec.d b/sound/avcodec.d index e83d50b2f..71607f3fc 100644 --- a/sound/avcodec.d +++ b/sound/avcodec.d @@ -1,3 +1,26 @@ +/* + OpenMW - The completely unofficial reimplementation of Morrowind + Copyright (C) 2008 Nicolay Korslund + Email: < korslund@gmail.com > + WWW: http://openmw.snaptoad.com/ + + This file (avcodec.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 sound.avcodec; extern (C): @@ -13,19 +36,19 @@ typedef void* AVAudio; // Open the named file, and return a unique handle representing it. // Returns NULL on error -AVFile cpp_openAVFile(char *fname); +AVFile avc_openAVFile(char *fname); // Close the file handle, invalidating all streams taken from it -void cpp_closeAVFile(AVFile file); +void avc_closeAVFile(AVFile file); // Get a unique handle to an audio stream in the file. The given number // is for files that can contain multiple audio streams (generally you // would pass 0, for the first audio stream) -AVAudio cpp_getAVAudioStream(AVFile file, int streamnum); +AVAudio avc_getAVAudioStream(AVFile file, int streamnum); // Get audio info representing the current stream. Returns 0 for success // (not likely to fail) -int cpp_getAVAudioInfo(AVAudio stream, int *rate, int *channels, int *bits); +int avc_getAVAudioInfo(AVAudio stream, int *rate, int *channels, int *bits); // Decode the next bit of data for the given audio stream. The function // must provide no less than the requested number of bytes, except for @@ -34,9 +57,9 @@ int cpp_getAVAudioInfo(AVAudio stream, int *rate, int *channels, int *bits); // any stream that has had a stream handle returned. // eg. if a file has one video stream and 2 audio streams and the app // gets a handle to the video stream and one audio stream, it must -// not destroy video data for subsequent calls to cpp_getAVVideoData if +// not destroy video data for subsequent calls to avc_getAVVideoData if // it has to read over it while decoding the audio stream. The other // audio stream's data, however, may be discarded. // Returns the number of bytes written to the buffer, which will be no // more than the provided length. -int cpp_getAVAudioData(AVAudio stream, void *data, int length); +int avc_getAVAudioData(AVAudio stream, void *data, int length); diff --git a/sound/cpp_avcodec.cpp b/sound/cpp_avcodec.cpp index e1cd2e83d..a0e6d2911 100644 --- a/sound/cpp_avcodec.cpp +++ b/sound/cpp_avcodec.cpp @@ -53,11 +53,11 @@ struct MyFile { }; // TODO: -// extern "C" MyFile::MyStream *cpp_getAVVideoStream(MyFile *file, int streamnum); -// extern "C" int cpp_getAVVideoInfo(MyFile::MyStream *stream, float *fps, int *width, int * height); -// extern "C" int cpp_getAVVideoData(MyFile::MyStream *stream, char *data, int length); +// extern "C" MyFile::MyStream *avc_getAVVideoStream(MyFile *file, int streamnum); +// extern "C" int avc_getAVVideoInfo(MyFile::MyStream *stream, float *fps, int *width, int * height); +// extern "C" int avc_getAVVideoData(MyFile::MyStream *stream, char *data, int length); -extern "C" MyFile *cpp_openAVFile(char *fname) +extern "C" MyFile *avc_openAVFile(char *fname) { static bool done = false; if(!done) { av_register_all(); @@ -75,7 +75,7 @@ extern "C" MyFile *cpp_openAVFile(char *fname) return NULL; } -extern "C" void cpp_closeAVFile(MyFile *file) +extern "C" void avc_closeAVFile(MyFile *file) { if(!file) return; @@ -92,7 +92,7 @@ extern "C" void cpp_closeAVFile(MyFile *file) delete file; } -extern "C" MyFile::MyStream *cpp_getAVAudioStream(MyFile *file, int streamnum) +extern "C" MyFile::MyStream *avc_getAVAudioStream(MyFile *file, int streamnum) { if(!file) return NULL; for(unsigned int i = 0;i < file->FmtCtx->nb_streams;i++) @@ -122,7 +122,7 @@ extern "C" MyFile::MyStream *cpp_getAVAudioStream(MyFile *file, int streamnum) return NULL; } -extern "C" int cpp_getAVAudioInfo(MyFile::MyStream *stream, +extern "C" int avc_getAVAudioInfo(MyFile::MyStream *stream, int *rate, int *channels, int *bits) { if(!stream) return 1; @@ -159,7 +159,7 @@ static void getNextPacket(MyFile *file, int streamidx) } } -extern "C" int cpp_getAVAudioData(MyFile::MyStream *stream, char *data, int length) +extern "C" int avc_getAVAudioData(MyFile::MyStream *stream, char *data, int length) { if(!stream) return 0; diff --git a/sound/music.d b/sound/music.d index 806a2286d..0975b4000 100644 --- a/sound/music.d +++ b/sound/music.d @@ -167,7 +167,7 @@ struct MusicManager checkALError("killing current track"); } - if(fileHandle) cpp_closeAVFile(fileHandle); + if(fileHandle) avc_closeAVFile(fileHandle); fileHandle = null; audioHandle = null; @@ -185,7 +185,7 @@ struct MusicManager { // This block is only executed if an exception is thrown. - if(fileHandle) cpp_closeAVFile(fileHandle); + if(fileHandle) avc_closeAVFile(fileHandle); fileHandle = null; audioHandle = null; @@ -208,16 +208,16 @@ struct MusicManager if(checkALError()) fail("Couldn't generate buffers"); - fileHandle = cpp_openAVFile(toStringz(playlist[index])); + fileHandle = avc_openAVFile(toStringz(playlist[index])); if(!fileHandle) fail("Unable to open " ~ playlist[index]); - audioHandle = cpp_getAVAudioStream(fileHandle, 0); + audioHandle = avc_getAVAudioStream(fileHandle, 0); if(!audioHandle) fail("Unable to load music track " ~ playlist[index]); int ch, bits, rate; - if(cpp_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) + if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) fail("Unable to get info for music track " ~ playlist[index]); bufRate = rate; @@ -248,7 +248,7 @@ struct MusicManager foreach(int i, ref b; bIDs) { - int length = cpp_getAVAudioData(audioHandle, outData.ptr, outData.length); + int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length); if(length) alBufferData(b, bufFormat, outData.ptr, length, bufRate); if(length == 0 || checkALError()) { @@ -284,7 +284,7 @@ struct MusicManager // Disable music void disableMusic() { - if(fileHandle) cpp_closeAVFile(fileHandle); + if(fileHandle) avc_closeAVFile(fileHandle); fileHandle = null; audioHandle = null; @@ -333,7 +333,7 @@ struct MusicManager for(int i = 0;i < count;i++) { - int length = cpp_getAVAudioData(audioHandle, outData.ptr, outData.length); + int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length); if(length <= 0) { if(i == 0) diff --git a/sound/sfx.d b/sound/sfx.d index af93cd89e..c227c8540 100644 --- a/sound/sfx.d +++ b/sound/sfx.d @@ -62,8 +62,8 @@ struct SoundFile bID = 0; ubyte[] outData; - AVFile fileHandle = cpp_openAVFile(toStringz(file)); - AVAudio audioHandle = cpp_getAVAudioStream(fileHandle, 0); + AVFile fileHandle = avc_openAVFile(toStringz(file)); + AVAudio audioHandle = avc_getAVAudioStream(fileHandle, 0); if(!fileHandle) { @@ -77,7 +77,7 @@ struct SoundFile } int ch, bits, rate; - if(cpp_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) + if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) { writefln("Unable to get info for sound %s", file); goto errclose; @@ -110,7 +110,7 @@ struct SoundFile // whole sound in one or two iterations, but not allocate too much // memory in case its short outData.length = outData.length+8192; - int length = cpp_getAVAudioData(audioHandle, outData.ptr+total, outData.length-total); + int length = avc_getAVAudioData(audioHandle, outData.ptr+total, outData.length-total); total += length; } while(total == outData.length); @@ -129,7 +129,7 @@ struct SoundFile } errclose: - if(fileHandle) cpp_closeAVFile(fileHandle); + if(fileHandle) avc_closeAVFile(fileHandle); fileHandle = null; audioHandle = null; }