forked from mirror/openmw-tes3mp
More updates on Bullet code and other things
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@49 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
c094324ef2
commit
6277c7f17c
16 changed files with 184 additions and 59 deletions
|
@ -122,6 +122,9 @@ Changelog:
|
||||||
- working on collision detection with Bullet
|
- working on collision detection with Bullet
|
||||||
- working on fixing sound issues for windows (running out of sound
|
- working on fixing sound issues for windows (running out of sound
|
||||||
resources, music playback doesn't good)
|
resources, music playback doesn't good)
|
||||||
|
- new key bindings:
|
||||||
|
t - toggle physics mode (walking, flying, ghost)
|
||||||
|
n - nighteye, toggle full ambient light
|
||||||
- added build files for CMake (with CMakeD) and Code::Blocks (neither
|
- added build files for CMake (with CMakeD) and Code::Blocks (neither
|
||||||
are tested yet)
|
are tested yet)
|
||||||
- various minor changes and updates
|
- various minor changes and updates
|
||||||
|
|
|
@ -35,6 +35,9 @@ 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 bullet_init();
|
int bullet_init();
|
||||||
|
|
||||||
|
// Switch to the next physics mode
|
||||||
|
void bullet_nextMode();
|
||||||
|
|
||||||
// Warp the player to a specific location.
|
// Warp the player to a specific location.
|
||||||
void bullet_movePlayer(float x, float y, float z);
|
void bullet_movePlayer(float x, float y, float z);
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,16 @@ btTriangleIndexVertexArray *g_currentMesh;
|
||||||
btHashedOverlappingPairCache* g_pairCache;
|
btHashedOverlappingPairCache* g_pairCache;
|
||||||
CustomOverlappingPairCallback *g_customPairCallback;
|
CustomOverlappingPairCallback *g_customPairCallback;
|
||||||
|
|
||||||
|
// Three physics modes: walking (with gravity and collision), flying
|
||||||
|
// (collision but no gravity) and ghost mode (fly through walls)
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PHYS_WALK,
|
||||||
|
PHYS_FLY,
|
||||||
|
PHYS_GHOST
|
||||||
|
};
|
||||||
|
int g_physMode;
|
||||||
|
|
||||||
// Include the player physics
|
// Include the player physics
|
||||||
#include "cpp_player.cpp"
|
#include "cpp_player.cpp"
|
||||||
|
|
||||||
|
@ -181,10 +191,34 @@ extern "C" int32_t bullet_init()
|
||||||
// Make sure this is zero at startup
|
// Make sure this is zero at startup
|
||||||
g_currentMesh = NULL;
|
g_currentMesh = NULL;
|
||||||
|
|
||||||
|
// Start out walking
|
||||||
|
g_physMode = PHYS_WALK;
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch to the next physics mode
|
||||||
|
extern "C" void bullet_nextMode()
|
||||||
|
{
|
||||||
|
g_physMode++;
|
||||||
|
if(g_physMode > PHYS_GHOST)
|
||||||
|
g_physMode = PHYS_WALK;
|
||||||
|
|
||||||
|
switch(g_physMode)
|
||||||
|
{
|
||||||
|
case PHYS_WALK:
|
||||||
|
cout << "Entering walking mode\n";
|
||||||
|
break;
|
||||||
|
case PHYS_FLY:
|
||||||
|
cout << "Entering flying mode\n";
|
||||||
|
break;
|
||||||
|
case PHYS_GHOST:
|
||||||
|
cout << "Entering ghost mode\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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 bullet_movePlayer(float x, float y, float z)
|
extern "C" void bullet_movePlayer(float x, float y, float z)
|
||||||
|
@ -285,6 +319,8 @@ extern "C" void bullet_insertStatic(btCollisionShape *shape,
|
||||||
float *quat,
|
float *quat,
|
||||||
float scale)
|
float scale)
|
||||||
{
|
{
|
||||||
|
// TODO: Good test case for scaled meshes: Aharunartus, some of the
|
||||||
|
// stairs inside the cavern currently don't collide
|
||||||
if(scale != 1.0)
|
if(scale != 1.0)
|
||||||
{
|
{
|
||||||
cout << "WARNING: Cannot scale collision meshes yet (wanted "
|
cout << "WARNING: Cannot scale collision meshes yet (wanted "
|
||||||
|
|
|
@ -232,8 +232,6 @@ bool recoverFromPenetration()
|
||||||
g_dynamicsWorld->getDispatchInfo(),
|
g_dynamicsWorld->getDispatchInfo(),
|
||||||
g_dispatcher);
|
g_dispatcher);
|
||||||
|
|
||||||
g_playerPosition = g_playerObject->getWorldTransform().getOrigin();
|
|
||||||
|
|
||||||
btScalar maxPen = 0.0;
|
btScalar maxPen = 0.0;
|
||||||
for (int i = 0; i < g_pairCache->getNumOverlappingPairs(); i++)
|
for (int i = 0; i < g_pairCache->getNumOverlappingPairs(); i++)
|
||||||
{
|
{
|
||||||
|
@ -285,8 +283,24 @@ bool recoverFromPenetration()
|
||||||
// main function is responsible for player movement.
|
// main function is responsible for player movement.
|
||||||
void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
|
void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
|
||||||
{
|
{
|
||||||
// Before moving, recover from current penetrations
|
// The walking direction is set from D code each frame, and the
|
||||||
|
// final player position is read back from D code after the
|
||||||
|
// simulation.
|
||||||
|
btVector3 walkStep = g_walkDirection * timeStep;
|
||||||
|
|
||||||
|
// Get the player position
|
||||||
|
g_playerPosition = g_playerObject->getWorldTransform().getOrigin();
|
||||||
|
|
||||||
|
if(g_physMode == PHYS_GHOST)
|
||||||
|
{
|
||||||
|
// Ghost mode - just move, no collision
|
||||||
|
g_playerPosition += walkStep;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Collision detection is active
|
||||||
|
|
||||||
|
// Before moving, recover from current penetrations
|
||||||
int numPenetrationLoops = 0;
|
int numPenetrationLoops = 0;
|
||||||
g_touchingContact = false;
|
g_touchingContact = false;
|
||||||
while (recoverFromPenetration())
|
while (recoverFromPenetration())
|
||||||
|
@ -299,27 +313,26 @@ void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the player position
|
// recoverFromPenetration updates g_playerPosition and the
|
||||||
btTransform xform;
|
// collision mesh, so they are still in sync at this point
|
||||||
xform = g_playerObject->getWorldTransform ();
|
|
||||||
g_playerPosition = xform.getOrigin();
|
|
||||||
|
|
||||||
// Next, do the walk.
|
// Next, do the walk. The following functions only updates
|
||||||
|
// g_playerPosition, they do not move the collision object.
|
||||||
// TODO: Walking direction should be set from D code, and the final
|
|
||||||
// position should be retrieved back from C++. The walking direction
|
|
||||||
// always reflects the intentional action of an agent (ie. the
|
|
||||||
// player or the AI), but the end result comes from collision and
|
|
||||||
// physics.
|
|
||||||
|
|
||||||
btVector3 walkStep = g_walkDirection * timeStep;
|
|
||||||
|
|
||||||
|
if(g_physMode == PHYS_WALK)
|
||||||
|
{
|
||||||
stepUp();
|
stepUp();
|
||||||
stepForward(walkStep);
|
stepForward(walkStep);
|
||||||
stepDown(timeStep);
|
stepDown(timeStep);
|
||||||
|
}
|
||||||
|
else if(g_physMode == PHYS_FLY)
|
||||||
|
stepForward(walkStep);
|
||||||
|
else
|
||||||
|
cout << "WARNING: Unknown physics mode " << g_physMode << "!\n";
|
||||||
|
}
|
||||||
|
|
||||||
// Move the player (but keep rotation)
|
// Move the player collision mesh
|
||||||
xform = g_playerObject->getWorldTransform ();
|
btTransform xform = g_playerObject->getWorldTransform ();
|
||||||
xform.setOrigin (g_playerPosition);
|
xform.setOrigin (g_playerPosition);
|
||||||
g_playerObject->setWorldTransform (xform);
|
g_playerObject->setWorldTransform (xform);
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,8 @@ struct ConfigManager
|
||||||
bind(Keys.Fullscreen, KC.F);
|
bind(Keys.Fullscreen, KC.F);
|
||||||
|
|
||||||
bind(Keys.ToggleBattleMusic, KC.SPACE);
|
bind(Keys.ToggleBattleMusic, KC.SPACE);
|
||||||
|
bind(Keys.PhysMode, KC.T);
|
||||||
|
bind(Keys.Nighteye, KC.N);
|
||||||
bind(Keys.Debug, KC.G);
|
bind(Keys.Debug, KC.G);
|
||||||
|
|
||||||
bind(Keys.Pause, KC.PAUSE, KC.P);
|
bind(Keys.Pause, KC.PAUSE, KC.P);
|
||||||
|
|
|
@ -221,6 +221,9 @@ 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.PhysMode: bullet_nextMode(); break;
|
||||||
|
case Keys.Nighteye: ogre_toggleLight(); break;
|
||||||
|
|
||||||
case Keys.Debug: ogre_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;
|
||||||
|
|
|
@ -65,6 +65,8 @@ enum Keys
|
||||||
// These will not be part of the finished product
|
// These will not be part of the finished product
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
ToggleBattleMusic,
|
ToggleBattleMusic,
|
||||||
|
PhysMode, // Toggle physics mode between walking, flying and ghost
|
||||||
|
Nighteye, // Full ambient lighting
|
||||||
Debug,
|
Debug,
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
@ -248,6 +250,8 @@ struct KeyBindings
|
||||||
|
|
||||||
keyToString[Keys.Fullscreen] = "Toggle Fullscreen Mode";
|
keyToString[Keys.Fullscreen] = "Toggle Fullscreen Mode";
|
||||||
keyToString[Keys.ToggleBattleMusic] = "Toggle Battle Music";
|
keyToString[Keys.ToggleBattleMusic] = "Toggle Battle Music";
|
||||||
|
keyToString[Keys.PhysMode] = "Toggle Physics Mode";
|
||||||
|
keyToString[Keys.Nighteye] = "Toggle Nighteye";
|
||||||
keyToString[Keys.Debug] = "OGRE Test Action";
|
keyToString[Keys.Debug] = "OGRE Test Action";
|
||||||
|
|
||||||
keyToString[Keys.Pause] = "Pause";
|
keyToString[Keys.Pause] = "Pause";
|
||||||
|
|
|
@ -49,11 +49,10 @@ abstract class Controlled : Extra
|
||||||
// Record with name and controller/extra data link
|
// Record with name and controller/extra data link
|
||||||
abstract class Named : Controlled
|
abstract class Named : Controlled
|
||||||
{
|
{
|
||||||
|
// Name of this object. This is used to refer to the object from .kf
|
||||||
|
// files.
|
||||||
char[] name;
|
char[] name;
|
||||||
|
|
||||||
//Extra extra;
|
|
||||||
//Controller controller;
|
|
||||||
|
|
||||||
override:
|
override:
|
||||||
char[] toString()
|
char[] toString()
|
||||||
{
|
{
|
||||||
|
@ -68,23 +67,8 @@ abstract class Named : Controlled
|
||||||
//super.read(f);
|
//super.read(f);
|
||||||
name = nifFile.getString();
|
name = nifFile.getString();
|
||||||
debug(verbose) writefln("Name: %s", name);
|
debug(verbose) writefln("Name: %s", name);
|
||||||
/*
|
|
||||||
debug(verbose) writef("Extra ");
|
|
||||||
getIndex(f);
|
|
||||||
debug(verbose) writef("Controller ");
|
|
||||||
getIndex(f);
|
|
||||||
*/
|
|
||||||
super.read();
|
super.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void sortOut(Record[] list)
|
|
||||||
{
|
|
||||||
super.sortOut(list);
|
|
||||||
extra = lookup!(Extra)(list);
|
|
||||||
controller = lookup!(Controller)(list);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NiSequenceStreamHelper : Named {}
|
class NiSequenceStreamHelper : Named {}
|
||||||
|
|
10
nif/data.d
10
nif/data.d
|
@ -147,6 +147,15 @@ class ShapeData : Record
|
||||||
if(nifFile.getInt != 0)
|
if(nifFile.getInt != 0)
|
||||||
{
|
{
|
||||||
if(uvs == 0) nifFile.warn("Empty UV list");
|
if(uvs == 0) nifFile.warn("Empty UV list");
|
||||||
|
|
||||||
|
// Only the 6 first bits are used as a count, the rest are
|
||||||
|
// (unknown) flags.
|
||||||
|
if(uvs > 0b111111)
|
||||||
|
{
|
||||||
|
nifFile.warn("UV count contains (unknown) flags");
|
||||||
|
uvs = uvs & 0b111111;
|
||||||
|
}
|
||||||
|
|
||||||
uvlist = nifFile.getArraySize!(float)(uvs*verts*2);
|
uvlist = nifFile.getArraySize!(float)(uvs*verts*2);
|
||||||
}
|
}
|
||||||
else if(uvs != 0) nifFile.warn("UV list was unexpectedly missing");
|
else if(uvs != 0) nifFile.warn("UV list was unexpectedly missing");
|
||||||
|
@ -528,6 +537,7 @@ class NiTriShapeData : ShapeData
|
||||||
debug(verbose) writefln("Number of faces: ", tris);
|
debug(verbose) writefln("Number of faces: ", tris);
|
||||||
if(tris)
|
if(tris)
|
||||||
{
|
{
|
||||||
|
// Must have three times as many vertices as triangles
|
||||||
int cnt = nifFile.getIntIs(tris*3);
|
int cnt = nifFile.getIntIs(tris*3);
|
||||||
triangles = nifFile.getArraySize!(short)(cnt);
|
triangles = nifFile.getArraySize!(short)(cnt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,9 @@ void ogre_setFog(float rf, float gf, float bf,
|
||||||
// Create a simple sky dome
|
// Create a simple sky dome
|
||||||
int ogre_makeSky();
|
int ogre_makeSky();
|
||||||
|
|
||||||
|
// Toggle full ambient lighting on and off
|
||||||
|
void ogre_toggleLight();
|
||||||
|
|
||||||
// Enter main rendering loop
|
// Enter main rendering loop
|
||||||
void ogre_startRendering();
|
void ogre_startRendering();
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,8 @@ 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));
|
||||||
|
|
||||||
|
//g_light->setDirection(mCamera->getDirection());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current camera position
|
// Get current camera position
|
||||||
|
@ -154,6 +156,8 @@ extern "C" void ogre_moveCamera(float x, float y, float z)
|
||||||
// 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
|
||||||
// transform this manually.
|
// transform this manually.
|
||||||
mCamera->setPosition(Vector3(x,z,-y));
|
mCamera->setPosition(Vector3(x,z,-y));
|
||||||
|
|
||||||
|
//g_light->setPosition(mCamera->getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate camera using Morrowind rotation specifiers
|
// Rotate camera using Morrowind rotation specifiers
|
||||||
|
|
|
@ -163,7 +163,7 @@ extern "C" void ogre_initWindow()
|
||||||
std::cout << "ogre_initWindow finished\n";
|
std::cout << "ogre_initWindow finished\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a scene, set the given ambient light
|
// Make a scene
|
||||||
extern "C" void ogre_makeScene()
|
extern "C" void ogre_makeScene()
|
||||||
{
|
{
|
||||||
// Get the SceneManager, in this case a generic one
|
// Get the SceneManager, in this case a generic one
|
||||||
|
@ -198,6 +198,53 @@ extern "C" void ogre_makeScene()
|
||||||
SceneNode *rt = mSceneMgr->getRootSceneNode();
|
SceneNode *rt = mSceneMgr->getRootSceneNode();
|
||||||
root = rt->createChildSceneNode();
|
root = rt->createChildSceneNode();
|
||||||
root->pitch(Degree(-90));
|
root->pitch(Degree(-90));
|
||||||
|
|
||||||
|
/*
|
||||||
|
g_light = mSceneMgr->createLight("carry");
|
||||||
|
g_light->setDiffuseColour(1,0.7,0.3);
|
||||||
|
g_light->setAttenuation(2000, 0, 0.008, 0);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Toggle carryable light
|
||||||
|
extern "C" void ogre_toggleCarryLight()
|
||||||
|
{
|
||||||
|
if(g_spotOn == 0)
|
||||||
|
{
|
||||||
|
g_light->setVisible(true);
|
||||||
|
g_spotOn = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_light->setVisible(false);
|
||||||
|
g_spotOn = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Toggle ambient light
|
||||||
|
extern "C" void ogre_toggleLight()
|
||||||
|
{
|
||||||
|
if(g_lightOn == 0)
|
||||||
|
{
|
||||||
|
std::cout << "Turning the lights up\n";
|
||||||
|
ColourValue half = 0.7*g_ambient + 0.3*ColourValue(1,1,1);
|
||||||
|
mSceneMgr->setAmbientLight(half);
|
||||||
|
g_lightOn = 1;
|
||||||
|
}
|
||||||
|
else if(g_lightOn == 1)
|
||||||
|
{
|
||||||
|
std::cout << "Turning the lights to full\n";
|
||||||
|
g_lightOn = 2;
|
||||||
|
mSceneMgr->setAmbientLight(ColourValue(1,1,1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Setting lights to normal\n";
|
||||||
|
g_lightOn = 0;
|
||||||
|
mSceneMgr->setAmbientLight(g_ambient);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a sky dome. Currently disabled since we aren't including the
|
// Create a sky dome. Currently disabled since we aren't including the
|
||||||
|
@ -217,6 +264,7 @@ extern "C" Light* ogre_attachLight(char *name, SceneNode* base,
|
||||||
|
|
||||||
// This seems to look reasonably ok.
|
// This seems to look reasonably ok.
|
||||||
l->setAttenuation(3*radius, 0, 0, 12.0/(radius*radius));
|
l->setAttenuation(3*radius, 0, 0, 12.0/(radius*radius));
|
||||||
|
//l->setAttenuation(5*radius, 0, 0, 8.0/(radius*radius));
|
||||||
|
|
||||||
// base might be null, sometimes lights don't have meshes
|
// base might be null, sometimes lights don't have meshes
|
||||||
if(base) base->attachObject(l);
|
if(base) base->attachObject(l);
|
||||||
|
@ -233,8 +281,8 @@ extern "C" void ogre_toggleFullscreen()
|
||||||
extern "C" void ogre_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);
|
g_ambient = ColourValue(r, g, b);
|
||||||
mSceneMgr->setAmbientLight(c);
|
mSceneMgr->setAmbientLight(g_ambient);
|
||||||
|
|
||||||
// Create a "sun" that shines light downwards. It doesn't look
|
// Create a "sun" that shines light downwards. It doesn't look
|
||||||
// completely right, but leave it for now.
|
// completely right, but leave it for now.
|
||||||
|
|
|
@ -42,6 +42,14 @@ SceneManager *mSceneMgr;
|
||||||
Camera *mCamera;
|
Camera *mCamera;
|
||||||
Viewport *vp;
|
Viewport *vp;
|
||||||
|
|
||||||
|
ColourValue g_ambient;
|
||||||
|
int g_lightOn = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
int g_spotOn = 0;
|
||||||
|
Light *g_light;
|
||||||
|
*/
|
||||||
|
|
||||||
OIS::InputManager *mInputManager;
|
OIS::InputManager *mInputManager;
|
||||||
OIS::Mouse *mMouse;
|
OIS::Mouse *mMouse;
|
||||||
OIS::Keyboard *mKeyboard;
|
OIS::Keyboard *mKeyboard;
|
||||||
|
|
|
@ -102,8 +102,8 @@ struct MeshLoader
|
||||||
// names later, in order to attach arms and legs on NPCs
|
// names later, in order to attach arms and legs on NPCs
|
||||||
// etc. Always ignore transformation of the first node? This is a
|
// etc. Always ignore transformation of the first node? This is a
|
||||||
// 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: I originally thought noRot should be
|
||||||
// noRot to false in exterior cells.
|
// false for exteriors and true for interiors, but this isn't so.
|
||||||
NodePtr node = ogre_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);
|
||||||
|
|
||||||
|
|
4
openmw.d
4
openmw.d
|
@ -308,9 +308,9 @@ void main(char[][] args)
|
||||||
// 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);
|
||||||
// Activators
|
// Activators (including beds etc)
|
||||||
foreach(ref LiveActivator ls; cd.activators)
|
foreach(ref LiveActivator ls; cd.activators)
|
||||||
putObject(ls.m.model, &ls.base.pos, ls.base.scale);
|
putObject(ls.m.model, &ls.base.pos, ls.base.scale, true);
|
||||||
// Potions
|
// Potions
|
||||||
foreach(ref LivePotion ls; cd.potions)
|
foreach(ref LivePotion ls; cd.potions)
|
||||||
putObject(ls.m.model, &ls.base.pos, ls.base.scale);
|
putObject(ls.m.model, &ls.base.pos, ls.base.scale);
|
||||||
|
|
|
@ -31,6 +31,7 @@ Decrease SFX Volume=3
|
||||||
Mute Sound=m
|
Mute Sound=m
|
||||||
Toggle Fullscreen Mode=f
|
Toggle Fullscreen Mode=f
|
||||||
Toggle Battle Music=space
|
Toggle Battle Music=space
|
||||||
|
Toggle Physics Mode=t
|
||||||
OGRE Test Action=g
|
OGRE Test Action=g
|
||||||
Pause=pause,p
|
Pause=pause,p
|
||||||
Screen Shot=print_screen
|
Screen Shot=print_screen
|
||||||
|
@ -41,3 +42,6 @@ Main Volume=0.7
|
||||||
Music Volume=0.5
|
Music Volume=0.5
|
||||||
SFX Volume=0.5
|
SFX Volume=0.5
|
||||||
Enable Music=yes
|
Enable Music=yes
|
||||||
|
|
||||||
|
[Game Files]
|
||||||
|
GameFile[0]=Morrowind.esm
|
||||||
|
|
Loading…
Reference in a new issue