First alpha release of REAL collision detection.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@48 ea6a568a-9f4f-0410-981a-c910a81bb256
actorid
nkorslund 16 years ago
parent 5fad8ea459
commit c094324ef2

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

@ -49,11 +49,11 @@ On the near-future TODO list:
- full support for localized versions (with character recoding) - full support for localized versions (with character recoding)
- support for Mac - support for Mac
- collision detection + walking & fall physics - collision detection + walk & fall physics
- displaying creatures correcty, animation - displaying creatures correcty, animation
- rendering NPCs - rendering NPCs
- rendering outdoor scenes (exterior cells) - 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 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: 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-win32.txt - instructions for building source on Windows
COMPILE-linux.tx - instructions for building source on Linux / Unix 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: 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 0.4 (2008 aug. 30) - latest release
- switched from Audiere to OpenAL (BIG thanks to Chris Robinson) - switched from Audiere to OpenAL (BIG thanks to Chris Robinson)

@ -28,27 +28,46 @@ module bullet.bindings;
* handles Bullet. * handles Bullet.
*/ */
typedef void* BulletShape;
extern(C): extern(C):
// Initialize the dynamic world. Returns non-zero if an error occurs. // Initialize the dynamic world. Returns non-zero if an error occurs.
int cpp_initBullet(); int bullet_init();
// Warp the player to a specific location. // 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 // 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 // Get the current player position, after physics and collision have
// been applied. // been applied.
void cpp_getPlayerPos(float *x, float *y, float *z); void bullet_getPlayerPos(float *x, float *y, float *z);
// Insert a debug collision object // Create a triangle shape. This is cumulative, all meshes created
void cpp_insertBox(float x, float y, float z); // 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 // Move the physics simulation 'delta' seconds forward in time
void cpp_timeStep(float delta); void bullet_timeStep(float delta);
// Deallocate objects // Deallocate objects
void cpp_cleanupBullet(); void bullet_cleanup();

@ -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; module bullet.bullet;
import bullet.bindings; import bullet.bindings;
void initBullet() void initBullet()
{ {
if(cpp_initBullet()) if(bullet_init())
throw new Exception("Bullet setup failed"); throw new Exception("Bullet setup failed");
} }
void cleanupBullet() { cpp_cleanupBullet(); } void cleanupBullet() { bullet_cleanup(); }

@ -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 "btBulletDynamicsCommon.h"
#include <iostream> #include <iostream>
@ -20,20 +43,24 @@ btConvexShape *g_playerShape;
// Player position. This is updated automatically by the physics // Player position. This is updated automatically by the physics
// system based on g_walkDirection and collisions. It is read by D // system based on g_walkDirection and collisions. It is read by D
// code through cpp_getPlayerPos(). // code through bullet_getPlayerPos().
btVector3 g_playerPosition; btVector3 g_playerPosition;
// Walking vector - defines direction and speed that the player // Walking vector - defines direction and speed that the player
// intends to move right now. This is updated from D code each frame // 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. // decisions.) The units of the vector are points per second.
btVector3 g_walkDirection; 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 // These variables and the class below are used in player collision
// detection. The callback is injected into the broadphase and keeps a // detection. The callback is injected into the broadphase and keeps a
// continuously updated list of what objects are colliding with the // continuously updated list of what objects are colliding with the
// player (in g_pairCache). This list is used in the function called // player (in g_pairCache). This list is used in the function called
// recoverFromPenetration() below. // recoverFromPenetration().
btHashedOverlappingPairCache* g_pairCache; btHashedOverlappingPairCache* g_pairCache;
CustomOverlappingPairCallback *g_customPairCallback; CustomOverlappingPairCallback *g_customPairCallback;
@ -77,7 +104,7 @@ public:
*/ */
}; };
extern "C" int32_t cpp_initBullet() extern "C" int32_t bullet_init()
{ {
// ------- SET UP THE WORLD ------- // ------- 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 // 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 // function used below (I think), but this is only offered by this
// broadphase implementation (as far as I can see.) // broadphase implementation (as far as I can see.) Maybe we can
btVector3 worldMin(-100000,-100000,-100000); // scan through the cell first and find good values that covers all
btVector3 worldMax(100000,100000,100000); // 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_broadphase = new btAxisSweep3(worldMin,worldMax);
g_dynamicsWorld = g_dynamicsWorld =
@ -107,18 +139,18 @@ extern "C" int32_t cpp_initBullet()
// Create the player collision shape. // Create the player collision shape.
float width = 50; float width = 50;
//float height = 50;
/* /*
// One possible shape is the convex hull around two spheres // One possible shape is the convex hull around two spheres
float height = 100;
btVector3 spherePositions[2]; btVector3 spherePositions[2];
btScalar sphereRadii[2]; btScalar sphereRadii[2];
sphereRadii[0] = width; sphereRadii[0] = width;
sphereRadii[1] = width; sphereRadii[1] = width;
spherePositions[0] = btVector3 (0.0, height/2.0, 0.0); spherePositions[0] = btVector3 (0.0, height/2.0, 0.0);
spherePositions[1] = 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), g_playerShape = new btMultiSphereShape(btVector3(width/2.0, height/2.0,
&spherePositions[0], &sphereRadii[0], 2); width/2.0), &spherePositions[0], &sphereRadii[0], 2);
*/ //*/
//g_playerShape = new btCylinderShape(btVector3(50, 50, 50)); //g_playerShape = new btCylinderShape(btVector3(50, 50, 50));
g_playerShape = new btSphereShape(width); g_playerShape = new btSphereShape(width);
@ -146,13 +178,16 @@ extern "C" int32_t cpp_initBullet()
//,btBroadphaseProxy::StaticFilter //,btBroadphaseProxy::StaticFilter
); );
// Make sure this is zero at startup
g_currentMesh = NULL;
// Success! // Success!
return 0; return 0;
} }
// Warp the player to a specific location. We do not bother setting // Warp the player to a specific location. We do not bother setting
// rotation, since it's completely irrelevant for collision detection. // 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; btTransform tr;
tr.setIdentity(); 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 // 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); } { g_walkDirection.setValue(x,y,z); }
// Get the current player position, after physics and collision have // Get the current player position, after physics and collision have
// been applied. // 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(); *x = g_playerPosition.getX();
*y = g_playerPosition.getY(); *y = g_playerPosition.getY();
*z = g_playerPosition.getZ(); *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; i<numVerts; i++)
{
float x,y,z;
// Reinventing basic linear algebra for the win!
x = matrix[0]*vb[0]+matrix[1]*vb[1]+matrix[2]*vb[2] + trans[0];
y = matrix[3]*vb[0]+matrix[4]*vb[1]+matrix[5]*vb[2] + trans[1];
z = matrix[6]*vb[0]+matrix[7]*vb[1]+matrix[8]*vb[2] + trans[2];
*(vb++) = x;
*(vb++) = y;
*(vb++) = z;
}
// If no mesh is currently active, create one
if(g_currentMesh == NULL)
g_currentMesh = new btTriangleIndexVertexArray;
// Add the mesh. Nif data stores triangle indices as shorts.
g_currentMesh->addIndexedMesh(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 // 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 = btCollisionShape* groundShape =
new btSphereShape(50); new btSphereShape(50);
@ -184,21 +319,33 @@ extern "C" void cpp_insertBox(float x, float y, float z)
btTransform groundTransform; btTransform groundTransform;
groundTransform.setIdentity(); groundTransform.setIdentity();
groundTransform.setOrigin(btVector3(x,y,z)); 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 = btDefaultMotionState* myMotionState =
new btDefaultMotionState(groundTransform); new btDefaultMotionState(groundTransform);
// Create a rigid body from the motion state. Give it zero mass and // Create a rigid body from the motion state. Give it zero mass and
// inertia. // zero inertia.
btRigidBody::btRigidBodyConstructionInfo btRigidBody::btRigidBodyConstructionInfo
rbInfo(0, myMotionState, groundShape, btVector3(0,0,0)); rbInfo(0, myMotionState, groundShape, btVector3(0,0,0));
btRigidBody* body = new btRigidBody(rbInfo); btRigidBody* body = new btRigidBody(rbInfo);
// Add the body to the world // Add the body to the world
g_dynamicsWorld->addRigidBody(body); g_dynamicsWorld->addRigidBody(body);
} */
//}
// Move the physics simulation 'delta' seconds forward in time // 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 // TODO: We might experiment with the number of time steps. Remember
// that the function also returns the number of steps performed. // 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 // 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 // Remove the rigidbodies from the dynamics world and delete them
for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)

@ -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 // This file handles player-specific physics and collision detection
// TODO: Later we might handle various physics modes, eg. dynamic // TODO: Later we might handle various physics modes, eg. dynamic
@ -10,6 +33,8 @@
// player collision, these will be member variables. // player collision, these will be member variables.
bool g_touchingContact; bool g_touchingContact;
btVector3 g_touchingNormal; btVector3 g_touchingNormal;
btScalar g_currentStepOffset;
float g_stepHeight = 20;
// Returns the reflection direction of a ray going 'direction' hitting // Returns the reflection direction of a ray going 'direction' hitting
// a surface with normal 'normal' // a surface with normal 'normal'
@ -49,11 +74,12 @@ public:
} }
}; };
/* Used to step up small steps and slopes. Not done. // Used to step up small steps and slopes.
void KinematicCharacterController::stepUp (const btCollisionWorld* world) void stepUp()
{ {
// phase 1: up // 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; btTransform start, end;
start.setIdentity (); start.setIdentity ();
@ -63,23 +89,22 @@ void KinematicCharacterController::stepUp (const btCollisionWorld* world)
start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0)); start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0));
end.setOrigin (targetPosition); end.setOrigin (targetPosition);
ClosestNotMeConvexResultCallback callback (g_playerObject); ClosestNotMeConvexResultCallback callback;
world->convexSweepTest (g_playerShape, start, end, callback); g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback);
if (callback.hasHit()) if (callback.hasHit())
{ {
// we moved up only a fraction of the step height // 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, g_playerPosition.setInterpolate3(g_playerPosition, targetPosition,
callback.m_closestHitFraction); callback.m_closestHitFraction);
} }
else else
{ {
m_currentStepOffset = m_stepHeight; g_currentStepOffset = g_stepHeight;
g_playerPosition = targetPosition; g_playerPosition = targetPosition;
} }
} }
*/
void updateTargetPositionBasedOnCollision (const btVector3& hitNormal, void updateTargetPositionBasedOnCollision (const btVector3& hitNormal,
btVector3 &targetPosition) btVector3 &targetPosition)
@ -168,15 +193,15 @@ void stepForward(btVector3& walkMove)
} }
} }
/* Not done. Will handle gravity, falling, sliding, etc. void stepDown (btScalar dt)
void KinematicCharacterController::stepDown (const btCollisionWorld* g_dynamicsWorld, btScalar dt)
{ {
btTransform start, end; btTransform start, end;
// phase 3: down // phase 3: down
btVector3 step_drop = btVector3(btScalar(0.0), m_currentStepOffset, btScalar(0.0)); btVector3 step_drop = btVector3(0,0,g_currentStepOffset);
btVector3 gravity_drop = btVector3(btScalar(0.0), m_stepHeight, btScalar(0.0)); btVector3 gravity_drop = btVector3(0,0,g_stepHeight);
targetPosition -= (step_drop + gravity_drop);
btVector3 targetPosition = g_playerPosition - step_drop - gravity_drop;
start.setIdentity (); start.setIdentity ();
end.setIdentity (); end.setIdentity ();
@ -184,20 +209,17 @@ void KinematicCharacterController::stepDown (const btCollisionWorld* g_dynamicsW
start.setOrigin (g_playerPosition); start.setOrigin (g_playerPosition);
end.setOrigin (targetPosition); end.setOrigin (targetPosition);
ClosestNotMeConvexResultCallback callback (g_playerObject); ClosestNotMeConvexResultCallback callback;
g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); g_dynamicsWorld->convexSweepTest(g_playerShape, start, end, callback);
if (callback.hasHit()) if (callback.hasHit())
{ // we dropped a fraction of the height -> hit floor
// we dropped a fraction of the height -> hit floor g_playerPosition.setInterpolate3(g_playerPosition, targetPosition,
g_playerPosition.setInterpolate3 (g_playerPosition, targetPosition, callback.m_closestHitFraction); callback.m_closestHitFraction);
} else { else
// we dropped the full height // we dropped the full height
g_playerPosition = targetPosition;
g_playerPosition = targetPosition;
}
} }
*/
// Check if the player currently collides with anything, and adjust // Check if the player currently collides with anything, and adjust
// its position accordingly. Returns true if collisions were found. // its position accordingly. Returns true if collisions were found.
@ -292,9 +314,9 @@ void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
btVector3 walkStep = g_walkDirection * timeStep; btVector3 walkStep = g_walkDirection * timeStep;
//stepUp(); stepUp();
stepForward(walkStep); stepForward(walkStep);
//stepDown(dt); stepDown(timeStep);
// Move the player (but keep rotation) // Move the player (but keep rotation)
xform = g_playerObject->getWorldTransform (); xform = g_playerObject->getWorldTransform ();

@ -35,6 +35,8 @@ import util.random;
import bsa.bsafile; import bsa.bsafile;
import bullet.bindings;
import core.memory; import core.memory;
import core.config; import core.config;
@ -361,7 +363,7 @@ struct ResourceManager
// Load and insert nif // Load and insert nif
// TODO: Might add BSA name to the handle name, for clarity // 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 // TODO: We could clear the BSA memory mapping here to free some
// mem // mem
@ -409,6 +411,9 @@ struct MeshResource
public: public:
// Bullet collision shape. Can be null.
BulletShape shape;
NodePtr getNode() NodePtr getNode()
in in
{ {

@ -94,7 +94,7 @@ void toggleBattle()
void toggleFullscreen() void toggleFullscreen()
{ {
cpp_toggleFullscreen(); ogre_toggleFullscreen();
} }
const float volDiff = 0.05; const float volDiff = 0.05;
@ -126,7 +126,7 @@ void mainVolume(bool increase)
void takeScreenShot() void takeScreenShot()
{ {
char[] file = format("screenshot_%06d.png", config.screenShotNum++); char[] file = format("screenshot_%06d.png", config.screenShotNum++);
cpp_screenshot(toStringz(file)); ogre_screenshot(toStringz(file));
writefln("Wrote '%s'", 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.abs, state.Y.abs, state.Z.abs,
state.X.rel, state.Y.rel, state.Z.rel); state.X.rel, state.Y.rel, state.Z.rel);
cpp_rotateCamera( state.X.rel * effMX, ogre_rotateCamera( state.X.rel * effMX,
state.Y.rel * effMY ); state.Y.rel * effMY );
} }
extern(C) void d_handleMouseButton(MouseState *state, int button) 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.Mute: toggleMute(); break;
case Keys.Fullscreen: toggleFullscreen(); 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.ScreenShot: takeScreenShot(); break;
case Keys.Pause: togglePause(); break; case Keys.Pause: togglePause(); break;
case Keys.Exit: exitProgram(); break; case Keys.Exit: exitProgram(); break;
@ -247,16 +247,10 @@ void initializeInput()
// at all, and should be moved. // at all, and should be moved.
with(playerData.position) with(playerData.position)
{ {
// TODO: Think about renaming these functions to ogre_moveCamera ogre_moveCamera(position[0], position[1], position[2]);
// and bullet_movePlayer etc. ogre_setCameraRotation(rotation[0], rotation[1], rotation[2]);
cpp_moveCamera(position[0], position[1], position[2]);
cpp_setCameraRotation(rotation[0], rotation[1], rotation[2]);
cpp_movePlayer(position[0], position[1], position[2]); bullet_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]);
} }
// TODO/FIXME: This should have been in config, but DMD's module // TODO/FIXME: This should have been in config, but DMD's module
@ -269,14 +263,14 @@ void initializeInput()
float tmpTime = 0; float tmpTime = 0;
int cnt; int cnt;
extern(C) int cpp_isPressed(int keysym); extern(C) int ois_isPressed(int keysym);
// Check if a key is currently down // Check if a key is currently down
bool isPressed(Keys key) bool isPressed(Keys key)
{ {
KeyBind *b = &keyBindings.bindings[key]; KeyBind *b = &keyBindings.bindings[key];
foreach(i; b.syms) foreach(i; b.syms)
if(i != 0 && cpp_isPressed(i)) return true; if(i != 0 && ois_isPressed(i)) return true;
return false; 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. // This isn't very elegant, but it's simple and it works.
// Get the current coordinates // Get the current coordinates
cpp_getCameraPos(&ox, &oy, &oz); ogre_getCameraPos(&ox, &oy, &oz);
// Move camera using relative coordinates. TODO: We won't really // Move camera using relative coordinates. TODO: We won't really
// need to move the camera here (since it's moved below anyway), we // need to move the camera here (since it's moved below anyway), we
// only want the transformation from camera space to world // only want the transformation from camera space to world
// space. This can likely be done more efficiently. // space. This can likely be done more efficiently.
cpp_moveCameraRel(moveX, moveY, moveZ); ogre_moveCameraRel(moveX, moveY, moveZ);
// Get the result // Get the result
cpp_getCameraPos(&x, &y, &z); ogre_getCameraPos(&x, &y, &z);
// The result is the real movement direction, in world coordinates // The result is the real movement direction, in world coordinates
moveX = x-ox; moveX = x-ox;
@ -338,14 +332,14 @@ extern(C) int d_frameStarted(float time)
moveZ = z-oz; moveZ = z-oz;
// Tell Bullet that this is where we want to go // 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 // Perform a Bullet time step
cpp_timeStep(time); bullet_timeStep(time);
// Get the final (actual) player position and update the camera // Get the final (actual) player position and update the camera
cpp_getPlayerPos(&x, &y, &z); bullet_getPlayerPos(&x, &y, &z);
cpp_moveCamera(x,y,z); ogre_moveCamera(x,y,z);
sndCumTime += time; sndCumTime += time;
if(sndCumTime > sndRefresh) if(sndCumTime > sndRefresh)
@ -353,7 +347,7 @@ extern(C) int d_frameStarted(float time)
float fx, fy, fz; float fx, fy, fz;
float ux, uy, uz; 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); soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz);
sndCumTime -= sndRefresh; sndCumTime -= sndRefresh;

@ -49,49 +49,58 @@ extern(C):
// Do engine configuration. Returns 0 if we should continue, 1 if // Do engine configuration. Returns 0 if we should continue, 1 if
// not. // not.
int cpp_configure(int showConfig, // Do we show the config dialogue? int ogre_configure(int showConfig, // Do we show the config dialogue?
char *plugincfg // Name of 'plugin.cfg' file char *plugincfg // Name of 'plugin.cfg' file
); );
// Sets up the window // Sets up the window
void cpp_initWindow(); void ogre_initWindow();
// Set up an empty scene. // Set up an empty scene.
void cpp_makeScene(); void ogre_makeScene();
// Set the ambient light and "sunlight" // 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); float rs, float gs, float bs);
// Set fog color and view distance // 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); float flow, float fhigh);
// Create a simple sky dome // Create a simple sky dome
int cpp_makeSky(); int ogre_makeSky();
// Enter main rendering loop // Enter main rendering loop
void cpp_startRendering(); void ogre_startRendering();
// Cleans up after ogre // Cleans up after ogre
void cpp_cleanup(); void ogre_cleanup();
// Gets a child SceneNode from the root node, then detatches it to // Gets a child SceneNode from the root node, then detatches it to
// hide it from view. Used for creating the "template" node associated // hide it from view. Used for creating the "template" node associated
// with a NIF mesh. // 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 // Create a copy of the given scene node, with the given coordinates
// and rotation. // and rotation (as a quaternion.)
NodePtr cpp_insertNode(NodePtr base, char* name, NodePtr ogre_insertNode(NodePtr base, char* name,
Placement *pos, float scale); 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 // 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 // Creates a scene node as a child of 'parent', then translates and
// rotates it according to the data in 'trafo'. // rotates it according to the data in 'trafo'.
NodePtr cpp_createNode( NodePtr ogre_createNode(
char *name, // Name to give the node char *name, // Name to give the node
Transformation *trafo, // Transformation Transformation *trafo, // Transformation
NodePtr parent, // Parent node NodePtr parent, // Parent node
@ -99,23 +108,23 @@ NodePtr cpp_createNode(
// Create a light with the given diffuse color. Attach it to SceneNode // Create a light with the given diffuse color. Attach it to SceneNode
// 'parent'. // 'parent'.
NodePtr cpp_attachLight(char* name, NodePtr parent, NodePtr ogre_attachLight(char* name, NodePtr parent,
float r, float g, float b, float r, float g, float b,
float radius); float radius);
// Create the specified material // Create the specified material
void cpp_createMaterial(char *name, // Name to give resource void ogre_createMaterial(char *name, // Name to give resource
float *ambient, // Ambient RBG value float *ambient, // Ambient RBG value
float *diffuse, float *diffuse,
float *specular, float *specular,
float *emissive,// Self illumination float *emissive,// Self illumination
float glossiness,// Same as shininess? float glossiness,// Same as shininess?
float alpha, // Use this in all alpha values? float alpha, // Use this in all alpha values?
char *texture); // Texture name char *texture); // Texture name
// Creates a mesh and gives it a bounding box. Also creates an entity // Creates a mesh and gives it a bounding box. Also creates an entity
// and attached it to the given SceneNode 'owner'. // and attached it to the given SceneNode 'owner'.
void cpp_createMesh( void ogre_createMesh(
char* name, // Name of the mesh char* name, // Name of the mesh
int numVerts, // Number of vertices int numVerts, // Number of vertices
float* vertices, // Vertex list float* vertices, // Vertex list
@ -135,21 +144,18 @@ void cpp_createMesh(
); );
// Toggle fullscreen mode // Toggle fullscreen mode
void cpp_toggleFullscreen(); void ogre_toggleFullscreen();
// Save a screen shot to the given file name // Save a screen shot to the given file name
void cpp_screenshot(char *filename); void ogre_screenshot(char *filename);
// Camera control and information // Camera control and information
void cpp_rotateCamera(float x, float y); void ogre_rotateCamera(float x, float y);
void cpp_moveCamera(float x, float y, float z); void ogre_moveCamera(float x, float y, float z);
void cpp_setCameraRotation(float r1, float r2, float r3); void ogre_setCameraRotation(float r1, float r2, float r3);
void cpp_getCameraPos(float *x, float *y, float *z); void ogre_getCameraPos(float *x, float *y, float *z);
void cpp_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz); void ogre_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz);
void cpp_moveCameraRel(float x, float y, float z); void ogre_moveCameraRel(float x, float y, float z);
// Do some debug action. Check the menu for today's specials! // Do some debug action. Check the menu for today's specials!
void cpp_debug(int i); void ogre_debug(int i);
// Insert a 100x100x100 axis-aligned cube at x,y,z
void cpp_drawBox(float x, float y, float z);

@ -23,7 +23,7 @@
// This file inserts an archive manager for .bsa files into the ogre // This file inserts an archive manager for .bsa files into the ogre
// resource loading system. It uses callbacks to D to interact with // 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. // core/resource.d.
// Callbacks to D code // Callbacks to D code
@ -49,17 +49,19 @@ public:
bool isCaseSensitive(void) const { return false; } bool isCaseSensitive(void) const { return false; }
// The archives are already loaded in D code, and they are never // The archives are already loaded in D code, and they are never
// unloaded. // unloaded. Just ignore these.
void load() {} void load() {}
void unload() {} void unload() {}
// Open a file in the archive. We delegate the function to D-code.
DataStreamPtr open(const String& filename) const DataStreamPtr open(const String& filename) const
{ {
//std::cout << "open(" << filename << ")\n"; //std::cout << "open(" << filename << ")\n";
void *ptr; void *ptr;
uint32_t size; 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); d_bsaOpenFile(filename.c_str(), &ptr, &size);
return DataStreamPtr(new MemoryDataStream(ptr, size)); return DataStreamPtr(new MemoryDataStream(ptr, size));
@ -93,7 +95,7 @@ public:
} }
// Gets called once for each of the ogre formats, *.program, // 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, FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
bool dirs = false) bool dirs = false)
{ {

@ -101,13 +101,13 @@ InputListener mInput;
// Functions called from D during event handling // 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); return mKeyboard->isKeyDown((OIS::KeyCode)keysym);
} }
// Dump screen contents to file // Dump screen contents to file
extern "C" void cpp_screenshot(char* filename) extern "C" void ogre_screenshot(char* filename)
{ {
mWindow->writeContentsToFile(filename); mWindow->writeContentsToFile(filename);
@ -117,14 +117,14 @@ extern "C" void cpp_screenshot(char* filename)
} }
// Rotate camera as result of mouse movement // 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->yaw(Degree(-x));
mCamera->pitch(Degree(-y)); mCamera->pitch(Degree(-y));
} }
// Get current camera position // 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(); Vector3 pos = mCamera->getPosition();
*x = pos[0]; *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' // Get current camera orientation, in the form of 'front' and 'up'
// vectors. // 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) float *ux, float *uy, float *uz)
{ {
Vector3 front = mCamera->getDirection(); Vector3 front = mCamera->getDirection();
@ -148,7 +148,7 @@ extern "C" void cpp_getCameraOrientation(float *fx, float *fy, float *fz,
} }
// Move camera // 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 // Transforms Morrowind coordinates to OGRE coordinates. The camera
// is not affected by the rotation of the root node, so we must // 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 // 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 // TODO: This translation is probably not correct, but for now I
// have no reference point. Fix it later when we teleport from one // 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. // 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)); mCamera->moveRelative(Vector3(x,y,z));
} }

@ -25,7 +25,7 @@
// E X P O R T E D F U N C T I O N S // 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 // Kill the input systems. This will reset input options such as key
// repetition. // 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? int32_t showConfig, // Do we show the config dialogue?
char *plugincfg // Name of 'plugin.cfg' file 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. // 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. // Initialize OGRE.
mWindow = mRoot->initialise(true); mWindow = mRoot->initialise(true);
@ -160,11 +160,11 @@ extern "C" void cpp_initWindow()
mKeyboard -> setEventCallback( &mInput ); mKeyboard -> setEventCallback( &mInput );
mMouse -> 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 // 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 // Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->createSceneManager(ST_GENERIC); 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 // Create a sky dome. Currently disabled since we aren't including the
// Ogre example data (which has the sky material.) // 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 ); //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 r, float g, float b,
float radius) float radius)
{ {
@ -225,12 +225,12 @@ extern "C" Light* cpp_attachLight(char *name, SceneNode* base,
} }
// Toggle between fullscreen and windowed mode. // Toggle between fullscreen and windowed mode.
extern "C" void cpp_toggleFullscreen() extern "C" void ogre_toggleFullscreen()
{ {
std::cout << "Not implemented yet\n"; 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" float rs, float gs, float bs) // "Sunlight"
{ {
ColourValue c = ColourValue(r, g, b); 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); 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 float flow, float fhigh) // Fog distance
{ {
ColourValue fogColor( rf, gf, bf ); 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); //vp->setBackgroundColour(fogColor);
} }
extern "C" void cpp_startRendering() extern "C" void ogre_startRendering()
{ {
mRoot->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 // Supposed to insert a copy of the node, for now it just inserts the
// actual node. // actual node.
extern "C" SceneNode *cpp_insertNode(SceneNode *base, char* name, extern "C" SceneNode *ogre_insertNode(SceneNode *base, char* name,
float *pos, float scale) float *pos, float *quat,
float scale)
{ {
//std::cout << "cpp_insertNode(" << name << ")\n"; //std::cout << "ogre_insertNode(" << name << ")\n";
SceneNode *node = root->createChildSceneNode(name); SceneNode *node = root->createChildSceneNode(name);
// Make a copy of the node // Make a copy of the node
cloneNode(base, node, name); cloneNode(base, node, name);
// pos points to a Placement struct, which has the format // Apply transformations
// float x, y, z; // position
// float r1, r2, r3; // rotation
node->setPosition(pos[0], pos[1], pos[2]); node->setPosition(pos[0], pos[1], pos[2]);
node->setOrientation(quat[0], quat[1], quat[2], quat[3]);
// 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->setScale(scale, scale, scale); node->setScale(scale, scale, scale);
return node; 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 // Create the water plane. It doesn't really resemble "water" yet
// though. // though.
extern "C" void cpp_createWater(float level) extern "C" void ogre_createWater(float level)
{ {
// Create a plane aligned with the xy-plane. // Create a plane aligned with the xy-plane.
MeshManager::getSingleton().createPlane("water", MeshManager::getSingleton().createPlane("water",
@ -362,7 +400,7 @@ public:
String LASTNAME; String LASTNAME;
// Load the contents of a mesh // Load the contents of a mesh
extern "C" void cpp_createMesh( extern "C" void ogre_createMesh(
char* name, // Name of the mesh char* name, // Name of the mesh
int32_t numVerts, // Number of vertices int32_t numVerts, // Number of vertices
float* vertices, // Vertex list float* vertices, // Vertex list
@ -522,7 +560,7 @@ extern "C" void cpp_createMesh(
msh->_setBoundingSphereRadius(radius); msh->_setBoundingSphereRadius(radius);
} }
extern "C" void cpp_createMaterial(char *name, // Name to give extern "C" void ogre_createMaterial(char *name, // Name to give
// resource // resource
float *ambient, // Ambient RBG float *ambient, // Ambient RBG
@ -568,20 +606,20 @@ extern "C" void cpp_createMaterial(char *name, // Name to give
LASTNAME = material->getName(); LASTNAME = material->getName();
} }
extern "C" SceneNode *cpp_getDetachedNode() extern "C" SceneNode *ogre_getDetachedNode()
{ {
SceneNode *node = root->createChildSceneNode(); SceneNode *node = root->createChildSceneNode();
root->removeChild(node); root->removeChild(node);
return node; return node;
} }
extern "C" SceneNode* cpp_createNode( extern "C" SceneNode* ogre_createNode(
char *name, char *name,
float *trafo, float *trafo,
SceneNode *parent, SceneNode *parent,
int32_t noRot) int32_t noRot)
{ {
//std::cout << "cpp_createNode(" << name << ")"; //std::cout << "ogre_createNode(" << name << ")";
SceneNode *node = parent->createChildSceneNode(name); SceneNode *node = parent->createChildSceneNode(name);
//std::cout << " ... done\n"; //std::cout << " ... done\n";
@ -613,7 +651,7 @@ extern "C" SceneNode* cpp_createNode(
/* Code currently not in use /* Code currently not in use
// We need this later for animated meshes. Boy will this be a mess. // 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( SkeletonPtr skel = SkeletonManager::getSingleton().create(
name, "Closet", true); name, "Closet", true);
@ -627,7 +665,7 @@ extern "C" void* cpp_setupSkeleton(char* name)
} }
// Use this later when loading textures directly from NIF files // 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( TexturePtr texture = TextureManager::getSingleton().createManual(
name, // name 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); 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) ); return (void*) ( ((Bone*)rootBone)->createChild(index) );
} }

@ -56,23 +56,3 @@ SceneNode *root;
#include "cpp_bsaarchive.cpp" #include "cpp_bsaarchive.cpp"
#include "cpp_interface.cpp" #include "cpp_interface.cpp"
#include "cpp_overlay.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);
}

@ -27,14 +27,9 @@ PanelOverlayElement *cont;
int visible; int visible;
void crap(char *p, ColourValue v) extern "C" void ogre_debug(int32_t i)
{ {
std::cerr << p << ": " << v.getAsRGBA() << "\n"; std::cerr << "Running ogre_debug(" << i << ")\n";
}
extern "C" void cpp_debug(int32_t i)
{
std::cerr << "Running cpp_debug(" << i << ")\n";
if(om) if(om)
{ {

@ -32,6 +32,8 @@ import nif.record;
import core.resource; import core.resource;
import ogre.bindings; import ogre.bindings;
import bullet.bindings;
import util.uniquename; import util.uniquename;
/* /*
@ -52,15 +54,15 @@ struct MeshLoader
// Not sure how to handle the bounding box, just ignore it for now. // 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 char[] baseName; // NIF file name. Used in scene node names etc. so
// that we can easier identify where they came // that we can identify where they came from in
// from. // case of error messages.
// Load a NIF mesh. Assumes nifMesh is already opened. This creates // Load a NIF mesh. Assumes nifMesh is already opened. This creates
// a "template" scene node containing this mesh, and removes it from // a "template" scene node containing this mesh, and removes it from
// the main scene. This node can later be "cloned" so that multiple // the main scene. This node can later be "cloned" so that multiple
// instances of the object can be inserted into the world without // instances of the object can be inserted into the world without
// inserting the mesh more than once. // inserting the mesh more than once.
NodePtr loadMesh(char[] name) void loadMesh(char[] name, out NodePtr base, out BulletShape shape)
{ {
baseName = name; baseName = name;
@ -72,16 +74,19 @@ struct MeshLoader
// TODO: Figure out what to do in this case, we should // TODO: Figure out what to do in this case, we should
// probably throw. // probably throw.
writefln("NIF '%s' IS NOT A MESH", name); writefln("NIF '%s' IS NOT A MESH", name);
return null; return;
} }
// Get a fresh SceneNode and detatch it from the root. We use this // Get a fresh SceneNode and detatch it from the root. We use this
// as the base for our mesh. // as the base for our mesh.
NodePtr base = cpp_getDetachedNode(); base = ogre_getDetachedNode();
// Recursively insert nodes (don't rotate the first node) // Recursively insert nodes (don't rotate the first node)
insertNode(n, base, true); insertNode(n, base, true);
// Get the final shape, if any
shape = bullet_getFinalShape();
return base; return base;
} }
@ -99,7 +104,7 @@ struct MeshLoader
// problem, I don't know when to do this and when not to. Neither // 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 // is always right. Update: It looks like we should always set
// noRot to false in exterior cells. // 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); parent, cast(int)noRot);
// Handle any general properties here // Handle any general properties here
@ -206,7 +211,7 @@ struct MeshLoader
// Create the material // Create the material
if(material.length) 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.specular.array.ptr, mp.emissive.array.ptr,
mp.glossiness, mp.alpha, texturePtr); mp.glossiness, mp.alpha, texturePtr);
else if(texturePtr) else if(texturePtr)
@ -218,7 +223,7 @@ struct MeshLoader
zero[] = 0.0; zero[] = 0.0;
one[] = 1.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); texturePtr);
} }
@ -258,7 +263,23 @@ struct MeshLoader
if( vertices[i+2] > maxZ) maxZ = vertices[i+2]; 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, normalsPtr, colorsPtr, uvsPtr, triangles.length, facesPtr,
radius, material.ptr, minX, minY, minZ, maxX, maxY, maxZ, radius, material.ptr, minX, minY, minZ, maxX, maxY, maxZ,
node); node);
@ -267,19 +288,19 @@ struct MeshLoader
} }
/* /*
// Create a skeleton and get the root bone (index 0) // 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. // Reset the bone index. The next bone to be created has index 1.
boneIndex = 1; boneIndex = 1;
// Create a mesh and assign the skeleton to it // 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 // Loop through the nodes, creating submeshes, materials and
// skeleton bones in the process. // skeleton bones in the process.
handleNode(node, bone, mesh); handleNode(node, bone, mesh);
// Create the "template" entity // Create the "template" entity
EntityPtr entity = cpp_createEntity(name); EntityPtr entity = ogre_createEntity(name);
// Loop through once again, this time to set the right // Loop through once again, this time to set the right
// transformations on the entity's SkeletonInstance. The order of // 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(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match");
if(!hasBBox) if(!hasBBox)
cpp_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ); ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ);
return entity; return entity;
} }
void handleNode(Node node, BonePtr root, MeshPtr mesh) void handleNode(Node node, BonePtr root, MeshPtr mesh)
{ {
// Insert a new bone for this node // 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) void transformBones(Node node, EntityPtr entity)
{ {
cpp_transformBone(entity, &node.trafo, boneIndex++); ogre_transformBone(entity, &node.trafo, boneIndex++);
NiNode n = cast(NiNode)node; NiNode n = cast(NiNode)node;
if(n !is null) if(n !is null)

@ -27,6 +27,7 @@ import core.resource;
import core.config; import core.config;
import ogre.bindings; import ogre.bindings;
import bullet.bindings;
import util.uniquename; import util.uniquename;
import std.stdio; import std.stdio;
@ -44,7 +45,8 @@ class OgreException : Exception
// Place a mesh in the 3D scene graph, at the given // Place a mesh in the 3D scene graph, at the given
// location/scale. Returns a node pointer to the inserted object. // 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 // 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 // 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 // This must be called BEFORE UniqueName below, because it might
// possibly use UniqueName itself and overwrite the data // 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(); 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); 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) 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, c.red/255.0, c.green/255.0, c.blue/255.0,
radius); radius);
} }
// If 'true' then we must call cpp_cleanup() on exit. // If 'true' then we must call ogre_cleanup() on exit.
bool ogreSetup = false; bool ogreSetup = false;
// Make sure we clean up // Make sure we clean up
@ -91,22 +106,22 @@ void setupOgre()
// Later we will send more config info from core.config along with // Later we will send more config info from core.config along with
// this function // this function
if(cpp_configure(config.finalOgreConfig, toStringz(plugincfg))) if(ogre_configure(config.finalOgreConfig, toStringz(plugincfg)))
OgreException("Configuration abort"); OgreException("Configuration abort");
cpp_initWindow(); ogre_initWindow();
// We set up the scene manager in a separate function, since we // 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 // might have to do that for every new cell later on, and handle
// exterior cells differently, etc. // exterior cells differently, etc.
cpp_makeScene(); ogre_makeScene();
ogreSetup = true; ogreSetup = true;
} }
void setAmbient(Color amb, Color sun, Color fog, float density) 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); sun.red/255.0, sun.green/255.0, sun.blue/255.0);
// Calculate fog distance // Calculate fog distance
@ -114,7 +129,7 @@ void setAmbient(Color amb, Color sun, Color fog, float density)
float fhigh = 4500 + 9000*(1-density); float fhigh = 4500 + 9000*(1-density);
float flow = 200 + 2000*(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 // 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() void startRendering()
{ {
// Kick OGRE into gear // Kick OGRE into gear
cpp_startRendering(); ogre_startRendering();
} }
// Cleans up after OGRE. Resets things like screen resolution and // Cleans up after OGRE. Resets things like screen resolution and
@ -130,7 +145,7 @@ void startRendering()
void cleanupOgre() void cleanupOgre()
{ {
if(ogreSetup) if(ogreSetup)
cpp_cleanup(); ogre_cleanup();
ogreSetup = false; ogreSetup = false;
} }
@ -143,3 +158,4 @@ align(1) struct Placement
float[3] position; float[3] position;
float[3] rotation; float[3] rotation;
} }
static assert(Placement.sizeof == 4*6);

@ -207,13 +207,14 @@ void main(char[][] args)
} }
// Simple safety hack // 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) if(m == null)
writefln("WARNING: CANNOT PUT NULL OBJECT"); writefln("WARNING: CANNOT PUT NULL OBJECT");
else if(m.isEmpty) else if(m.isEmpty)
writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName); writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName);
else return placeObject(m, pos, scale); else return placeObject(m, pos, scale, collide);
return null; return null;
} }
@ -235,7 +236,7 @@ void main(char[][] args)
// Not all interior cells have water // Not all interior cells have water
if(cd.inCell.flags & CellFlags.HasWater) if(cd.inCell.flags & CellFlags.HasWater)
cpp_createWater(cd.water); ogre_createWater(cd.water);
} }
else else
{ {
@ -246,15 +247,15 @@ void main(char[][] args)
setAmbient(c, c, c, 0); setAmbient(c, c, c, 0);
// Put in the water // Put in the water
cpp_createWater(cd.water); ogre_createWater(cd.water);
// Create an ugly sky // Create an ugly sky
cpp_makeSky(); ogre_makeSky();
} }
// Insert the meshes of statics into the scene // Insert the meshes of statics into the scene
foreach(ref LiveStatic ls; cd.statics) 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 // Inventory lights
foreach(ref LiveLight ls; cd.lights) foreach(ref LiveLight ls; cd.lights)
{ {
@ -277,7 +278,7 @@ void main(char[][] args)
// Static lights // Static lights
foreach(ref LiveLight ls; cd.statLights) 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); ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius);
if(!noSound) if(!noSound)
{ {
@ -303,7 +304,7 @@ void main(char[][] args)
*/ */
// Containers // Containers
foreach(ref LiveContainer ls; cd.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 // Doors
foreach(ref LiveDoor ls; cd.doors) foreach(ref LiveDoor ls; cd.doors)
putObject(ls.m.model, &ls.base.pos, ls.base.scale); putObject(ls.m.model, &ls.base.pos, ls.base.scale);
@ -334,7 +335,7 @@ void main(char[][] args)
// Tools // Tools
foreach(ref LiveTool ls; cd.tools) foreach(ref LiveTool ls; cd.tools)
putObject(ls.m.model, &ls.base.pos, ls.base.scale); 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) foreach(ref LiveCreature ls; cd.creatures)
putObject(ls.m.model, &ls.base.pos, ls.base.scale); putObject(ls.m.model, &ls.base.pos, ls.base.scale);

@ -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; module sound.al;
extern(C): extern(C):

@ -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; module sound.alc;
extern(C): extern(C):

@ -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; module sound.avcodec;
extern (C): extern (C):
@ -13,19 +36,19 @@ typedef void* AVAudio;
// Open the named file, and return a unique handle representing it. // Open the named file, and return a unique handle representing it.
// Returns NULL on error // Returns NULL on error
AVFile cpp_openAVFile(char *fname); AVFile avc_openAVFile(char *fname);
// Close the file handle, invalidating all streams taken from it // 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 // 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 // is for files that can contain multiple audio streams (generally you
// would pass 0, for the first audio stream) // 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 // Get audio info representing the current stream. Returns 0 for success
// (not likely to fail) // (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 // 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 // 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. // any stream that has had a stream handle returned.
// eg. if a file has one video stream and 2 audio streams and the app // 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 // 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 // it has to read over it while decoding the audio stream. The other
// audio stream's data, however, may be discarded. // audio stream's data, however, may be discarded.
// Returns the number of bytes written to the buffer, which will be no // Returns the number of bytes written to the buffer, which will be no
// more than the provided length. // more than the provided length.
int cpp_getAVAudioData(AVAudio stream, void *data, int length); int avc_getAVAudioData(AVAudio stream, void *data, int length);

@ -53,11 +53,11 @@ struct MyFile {
}; };
// TODO: // TODO:
// extern "C" MyFile::MyStream *cpp_getAVVideoStream(MyFile *file, int streamnum); // extern "C" MyFile::MyStream *avc_getAVVideoStream(MyFile *file, int streamnum);
// extern "C" int cpp_getAVVideoInfo(MyFile::MyStream *stream, float *fps, int *width, int * height); // extern "C" int avc_getAVVideoInfo(MyFile::MyStream *stream, float *fps, int *width, int * height);
// extern "C" int cpp_getAVVideoData(MyFile::MyStream *stream, char *data, int length); // 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; static bool done = false;
if(!done) { av_register_all(); if(!done) { av_register_all();
@ -75,7 +75,7 @@ extern "C" MyFile *cpp_openAVFile(char *fname)
return NULL; return NULL;
} }
extern "C" void cpp_closeAVFile(MyFile *file) extern "C" void avc_closeAVFile(MyFile *file)
{ {
if(!file) return; if(!file) return;
@ -92,7 +92,7 @@ extern "C" void cpp_closeAVFile(MyFile *file)
delete 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; if(!file) return NULL;
for(unsigned int i = 0;i < file->FmtCtx->nb_streams;i++) 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; return NULL;
} }
extern "C" int cpp_getAVAudioInfo(MyFile::MyStream *stream, extern "C" int avc_getAVAudioInfo(MyFile::MyStream *stream,
int *rate, int *channels, int *bits) int *rate, int *channels, int *bits)
{ {
if(!stream) return 1; 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; if(!stream) return 0;

@ -167,7 +167,7 @@ struct MusicManager
checkALError("killing current track"); checkALError("killing current track");
} }
if(fileHandle) cpp_closeAVFile(fileHandle); if(fileHandle) avc_closeAVFile(fileHandle);
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
@ -185,7 +185,7 @@ struct MusicManager
{ {
// This block is only executed if an exception is thrown. // This block is only executed if an exception is thrown.
if(fileHandle) cpp_closeAVFile(fileHandle); if(fileHandle) avc_closeAVFile(fileHandle);
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
@ -208,16 +208,16 @@ struct MusicManager
if(checkALError()) if(checkALError())
fail("Couldn't generate buffers"); fail("Couldn't generate buffers");
fileHandle = cpp_openAVFile(toStringz(playlist[index])); fileHandle = avc_openAVFile(toStringz(playlist[index]));
if(!fileHandle) if(!fileHandle)
fail("Unable to open " ~ playlist[index]); fail("Unable to open " ~ playlist[index]);
audioHandle = cpp_getAVAudioStream(fileHandle, 0); audioHandle = avc_getAVAudioStream(fileHandle, 0);
if(!audioHandle) if(!audioHandle)
fail("Unable to load music track " ~ playlist[index]); fail("Unable to load music track " ~ playlist[index]);
int ch, bits, rate; 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]); fail("Unable to get info for music track " ~ playlist[index]);
bufRate = rate; bufRate = rate;
@ -248,7 +248,7 @@ struct MusicManager
foreach(int i, ref b; bIDs) 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) alBufferData(b, bufFormat, outData.ptr, length, bufRate);
if(length == 0 || checkALError()) if(length == 0 || checkALError())
{ {
@ -284,7 +284,7 @@ struct MusicManager
// Disable music // Disable music
void disableMusic() void disableMusic()
{ {
if(fileHandle) cpp_closeAVFile(fileHandle); if(fileHandle) avc_closeAVFile(fileHandle);
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
@ -333,7 +333,7 @@ struct MusicManager
for(int i = 0;i < count;i++) 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(length <= 0)
{ {
if(i == 0) if(i == 0)

@ -62,8 +62,8 @@ struct SoundFile
bID = 0; bID = 0;
ubyte[] outData; ubyte[] outData;
AVFile fileHandle = cpp_openAVFile(toStringz(file)); AVFile fileHandle = avc_openAVFile(toStringz(file));
AVAudio audioHandle = cpp_getAVAudioStream(fileHandle, 0); AVAudio audioHandle = avc_getAVAudioStream(fileHandle, 0);
if(!fileHandle) if(!fileHandle)
{ {
@ -77,7 +77,7 @@ struct SoundFile
} }
int ch, bits, rate; 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); writefln("Unable to get info for sound %s", file);
goto errclose; goto errclose;
@ -110,7 +110,7 @@ struct SoundFile
// whole sound in one or two iterations, but not allocate too much // whole sound in one or two iterations, but not allocate too much
// memory in case its short // memory in case its short
outData.length = outData.length+8192; 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; total += length;
} }
while(total == outData.length); while(total == outData.length);
@ -129,7 +129,7 @@ struct SoundFile
} }
errclose: errclose:
if(fileHandle) cpp_closeAVFile(fileHandle); if(fileHandle) avc_closeAVFile(fileHandle);
fileHandle = null; fileHandle = null;
audioHandle = null; audioHandle = null;
} }

Loading…
Cancel
Save