Merge branch 'master' of git://github.com/korslund/openmw

Conflicts:
	game/main.cpp
actorid
Ardekantur 15 years ago
commit 3bb9d06e58

1
.gitignore vendored

@ -1,3 +1,4 @@
screenshot*.png
*.o
*~
data

@ -10,35 +10,45 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
set(BSA bsa/bsa_archive.cpp bsa/bsa_file.cpp)
set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp)
source_group(bsa FILES ${BSA} ${BSA_HEADER})
set(NIF nif/nif_file.cpp)
set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp nif/record.hpp
nif/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp
nif/data.hpp nif/nif_file.hpp nif/property.hpp)
source_group(nif FILES ${NIF} ${NIF_HEADER})
set(NIFOGRE nifogre/ogre_nif_loader.cpp)
set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp)
source_group(nifogre FILES ${NIFOGRE} ${NIFOGRE_HEADER})
set(TOOLS tools/stringops.cpp tools/fileops.cpp)
set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp)
source_group(tools FILES ${TOOLS} ${TOOLS_HEADER})
set(MANGLE_VFS mangle/vfs/servers/ogre_vfs.cpp)
source_group(mangle_vfs FILES ${MANGLE_VFS})
set(OGRE ogre/renderer.cpp)
set(OGRE_HEADER ogre/renderer.hpp)
source_group(ogre FILES ${OGRE} ${OGRE_HEADER})
set(INPUT input/oismanager.cpp)
set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp)
set(INPUT_HEADER input/oismanager.hpp input/listener.hpp input/func_binder.hpp input/dispatch_map.hpp input/dispatcher.hpp input/poller.hpp)
source_group(input FILES ${INPUT} ${INPUT_HEADER})
set(GAME game/main.cpp)
set(GAME_HEADER game/mwinput/inputmanager.hpp)
set(GAME game/main.cpp game/engine.cpp)
set(GAME_HEADER game/mwinput/inputmanager.hpp game/engine.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER})
set(ESM_STORE esm_store/store.cpp esm_store/cell_store.cpp)
set(ESM_STORE_HEADER esm_store/cell_store.hpp esm_store/reclists.hpp esm_store/store.hpp)
source_group(esm_store FILES ${ESM_STORE} ${ESM_STORE_HEADER})
set(GAMEREND game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp)
set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp
game/mwrender/interior.hpp)
game/mwrender/interior.hpp game/mwrender/playerpos.hpp)
source_group(game_renderer FILES ${GAMEREND} ${GAMEREND_HEADER})
set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp
esm/loadskil.hpp
@ -50,6 +60,7 @@ set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp
esm/loadbody.hpp esm/loaddial.hpp esm/loadlevlist.hpp esm/loadrace.hpp esm/loadweap.hpp
esm/loadbook.hpp esm/loaddoor.hpp esm/loadligh.hpp esm/loadregn.hpp esm/records.hpp
esm/loadbsgn.hpp esm/loadench.hpp esm/loadlocks.hpp esm/loadscpt.hpp)
source_group(esm_header FILES ${ESM_HEADER})
# Platform specific
if (WIN32)

@ -26,7 +26,7 @@ struct Potion
void load(ESMReader &esm)
{
model = esm.getHNString("MODL");
icon = esm.getHNString("TEXT"); // not ITEX here for some reason
icon = esm.getHNOString("TEXT"); // not ITEX here for some reason
script = esm.getHNOString("SCRI");
name = esm.getHNOString("FNAM");
esm.getHNT(data, "ALDT", 12);

@ -11,8 +11,9 @@ namespace ESM {
loading process, but are rather loaded later on demand when we are
setting up a specific cell.
*/
struct CellRef
class CellRef
{
public:
int refnum; // Reference number
std::string refID; // ID of object being referenced

@ -60,7 +60,7 @@ struct LeveledListBase
// items. Also, some times we don't want to merge lists, just
// overwrite. Figure out a way to give the user this option.
for(int i=0; i<list.size(); i++)
for(size_t i=0; i<list.size(); i++)
{
LevelItem &li = list[i];
li.id = esm.getHNString(recName);

@ -67,7 +67,7 @@ struct Script
// The tmp buffer is a null-byte separated string list, we
// just have to pick out one string at a time.
char* str = tmp;
for(int i=0; i< varNames.size(); i++)
for(size_t i=0; i< varNames.size(); i++)
{
varNames[i] = std::string(str);
str += varNames[i].size()+1;

@ -66,8 +66,9 @@ namespace ESMS
};
/// A storage struct for one single cell reference.
struct CellStore
class CellStore
{
public:
CellStore() : cell (0) {}
const ESM::Cell *cell;

@ -0,0 +1,146 @@
#include "engine.hpp"
#include <cassert>
#include <iostream>
#include "esm_store/cell_store.hpp"
#include "bsa/bsa_archive.hpp"
#include "ogre/renderer.hpp"
#include "tools/fileops.hpp"
#include "mwrender/interior.hpp"
#include "mwinput/inputmanager.hpp"
#include "mwrender/playerpos.hpp"
OMW::Engine::Engine() {}
// adjust name and load bsa
void OMW::Engine::prepareMaster()
{
std::string::size_type sep = mMaster.find_last_of (".");
if (sep==std::string::npos)
{
mMaster += ".esm";
}
}
// Load all BSA files in data directory.
void OMW::Engine::loadBSA()
{
boost::filesystem::directory_iterator end;
for (boost::filesystem::directory_iterator iter (mDataDir); iter!=end; ++iter)
{
if (boost::filesystem::extension (iter->path())==".bsa")
{
std::cout << "Adding " << iter->path().string() << std::endl;
addBSA(iter->path().file_string());
}
}
}
// add resources directory
// \note This function works recursively.
void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
{
mOgre.getRoot()->addResourceLocation (path.file_string(), "FileSystem",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
}
// Set data dir
void OMW::Engine::setDataDir (const boost::filesystem::path& dataDir)
{
mDataDir = boost::filesystem::system_complete (dataDir);
}
// Set start cell name (only interiors for now)
void OMW::Engine::setCell (const std::string& cellName)
{
mCellName = cellName;
}
// Set master file (esm)
// - If the given name does not have an extension, ".esm" is added automatically
// - Currently OpenMW only supports one master at the same time.
void OMW::Engine::addMaster (const std::string& master)
{
assert (mMaster.empty());
mMaster = master;
}
// Initialise and enter main loop.
void OMW::Engine::go()
{
assert (!mDataDir.empty());
assert (!mCellName.empty());
assert (!mMaster.empty());
std::cout << "Hello, fellow traveler!\n";
std::cout << "Your data directory for today is: " << mDataDir << "\n";
std::cout << "Initializing OGRE\n";
const char* plugCfg = "plugins.cfg";
mOgre.configure(!isFile("ogre.cfg"), plugCfg, false);
addResourcesDirectory (mDataDir / "Meshes");
addResourcesDirectory (mDataDir / "Textures");
prepareMaster();
loadBSA();
boost::filesystem::path masterPath (mDataDir);
masterPath /= mMaster;
std::cout << "Loading ESM " << masterPath.string() << "\n";
ESM::ESMReader esm;
ESMS::ESMStore store;
ESMS::CellStore cell;
// This parses the ESM file and loads a sample cell
esm.open(masterPath.file_string());
store.load(esm);
cell.loadInt(mCellName, store, esm);
// Create the window
mOgre.createWindow("OpenMW");
std::cout << "\nSetting up cell rendering\n";
// Sets up camera, scene manager, and viewport.
MWRender::MWScene scene(mOgre);
// Used to control the player camera and position
MWRender::PlayerPos player(scene.getCamera());
// This connects the cell data with the rendering scene.
MWRender::InteriorCellRender rend(cell, scene);
// Load the cell and insert it into the renderer
rend.show();
std::cout << "Setting up input system\n";
// Sets up the input system
MWInput::MWInputManager input(mOgre, player);
std::cout << "\nStart! Press Q/ESC or close window to exit.\n";
// Start the main rendering loop
mOgre.start();
std::cout << "\nThat's all for now!\n";
}

@ -0,0 +1,55 @@
#ifndef ENGINE_H
#define ENGINE_H
#include <string>
#include <boost/filesystem.hpp>
#include "mwrender/mwscene.hpp"
namespace OMW
{
/// \brief Main engine class, that brings together all the components of OpenMW
class Engine
{
boost::filesystem::path mDataDir;
Render::OgreRenderer mOgre;
std::string mCellName;
std::string mMaster;
// not implemented
Engine (const Engine&);
Engine& operator= (const Engine&);
/// adjust name and load bsa
void prepareMaster();
/// add resources directory
/// \note This function works recursively.
void addResourcesDirectory (const boost::filesystem::path& path);
/// Load all BSA files in data directory.
void loadBSA();
public:
Engine();
/// Set data dir
void setDataDir (const boost::filesystem::path& dataDir);
/// Set start cell name (only interiors for now)
void setCell (const std::string& cellName);
/// Set master file (esm)
/// - If the given name does not have an extension, ".esm" is added automatically
/// - Currently OpenMW only supports one master at the same time.
void addMaster (const std::string& master);
/// Initialise and enter main loop.
void go();
};
}
#endif

@ -3,94 +3,30 @@
#include <string>
#include <fstream>
#include "boost/program_options.hpp"
#include <boost/program_options.hpp>
#include "esm_store/cell_store.hpp"
#include "bsa/bsa_archive.hpp"
#include "ogre/renderer.hpp"
#include "tools/fileops.hpp"
#include "mwrender/interior.hpp"
#include "mwrender/mwscene.hpp"
#include "mwinput/inputmanager.hpp"
#include "engine.hpp"
using namespace std;
void maintest (std::string dataDir, const std::string& cellName)
{
assert (!dataDir.empty());
if (dataDir[dataDir.size()-1]!='/' && dataDir[dataDir.size()-1]!='\\')
dataDir += "/";
const char* esmFile = "Morrowind.esm";
const char* bsaFile = "Morrowind.bsa";
const char* plugCfg = "plugins.cfg";
cout << "Hello, fellow traveler!\n";
cout << "Your data directory for today is: " << dataDir << "\n";
cout << "Initializing OGRE\n";
Render::OgreRenderer ogre;
ogre.configure(!isFile("ogre.cfg"), plugCfg, false);
cout << "Adding " << bsaFile << endl;
addBSA(dataDir + bsaFile);
cout << "Loading ESM " << esmFile << "\n";
ESM::ESMReader esm;
ESMS::ESMStore store;
ESMS::CellStore cell;
// This parses the ESM file and loads a sample cell
esm.open(dataDir + esmFile);
store.load(esm);
cell.loadInt(cellName, store, esm);
// Create the window
ogre.createWindow("OpenMW");
cout << "\nSetting up cell rendering\n";
// Sets up camera, scene manager etc
MWRender::MWScene scene(ogre);
// This connects the cell data with the rendering scene.
MWRender::InteriorCellRender rend(cell, scene);
// Load the cell and insert it into the renderer
rend.show();
/// Parse command line options and openmw.cfg file (if one exists). Results are directly
/// written to \a engine.
/// \return Run OpenMW?
cout << "Setting up input system\n";
// Sets up the input system
MWInput::MWInputManager input(ogre);
cout << "\nStart! Press Q/ESC or close window to exit.\n";
// Start the main rendering loop
ogre.start();
cout << "\nThat's all for now!\n";
}
int main(int argc, char**argv)
bool parseOptions (int argc, char**argv, OMW::Engine& engine)
{
try
{
boost::program_options::options_description desc (
"Syntax: openmw <options>\nAllowed options");
"Syntax: openmw <options>\nAllowed options");
desc.add_options()
("help", "print help message")
("data", boost::program_options::value<std::string>()->default_value ("data"),
"set data directory")
("start", boost::program_options::value<std::string>()->default_value ("Beshara"),
"set initial cell (only interior cells supported at the moment")
;
("help", "print help message")
("data", boost::program_options::value<std::string>()->default_value ("data"),
"set data directory")
("start", boost::program_options::value<std::string>()->default_value ("Beshara"),
"set initial cell (only interior cells supported at the moment")
("master", boost::program_options::value<std::string>()->default_value ("Morrowind"),
"master file")
;
boost::program_options::variables_map variables;
@ -101,24 +37,45 @@ int main(int argc, char**argv)
boost::program_options::store(valid_opts, variables);
boost::program_options::notify(variables);
/*
boost::program_options::store (
boost::program_options::parse_command_line (argc, argv, desc), variables);
boost::program_options::notify (variables);
*/
if (configFile.is_open())
boost::program_options::store (
boost::program_options::parse_config_file (configFile, desc), variables);
boost::program_options::store (
boost::program_options::parse_config_file (configFile, desc), variables);
if (variables.count ("help"))
{
std::cout << desc << std::endl;
std::cout << desc << std::endl;
return false;
}
else
{
maintest (variables["data"].as<std::string>(), variables["start"].as<std::string>());
}
}
catch(exception &e)
engine.setDataDir (variables["data"].as<std::string>());
engine.setCell (variables["start"].as<std::string>());
engine.addMaster (variables["master"].as<std::string>());
return true;
}
int main(int argc, char**argv)
{
try
{
cout << "\nERROR: " << e.what() << endl;
return 1;
OMW::Engine engine;
if (parseOptions (argc, argv, engine))
{
engine.go();
}
}
catch(exception &e)
{
cout << "\nERROR: " << e.what() << endl;
return 1;
}
return 0;
return 0;
}

@ -1,9 +1,12 @@
#ifndef _INPUT_MWINPUTMANAGER_H
#define _INPUT_MWINPUTMANAGER_H
#ifndef _MWINPUT_MWINPUTMANAGER_H
#define _MWINPUT_MWINPUTMANAGER_H
#include "input/listener.hpp"
#include "input/dispatcher.hpp"
#include "input/poller.hpp"
#include "boost/bind.hpp"
#include "game/mwrender/playerpos.hpp"
#include "platform/strings.h"
namespace MWInput
{
@ -11,33 +14,118 @@ namespace MWInput
{
A_Quit, // Exit the program
A_Screenshot, // Take a screenshot
A_MoveLeft, // Move player left / right
A_MoveRight,
A_MoveUp, // Move up / down
A_MoveDown,
A_MoveForward, // Forward / Backward
A_MoveBackward,
A_LAST // Marker for the last item
};
// Class that handles all input and key bindings for OpenMW
class MWInputManager
class MWInputManager : public Ogre::FrameListener
{
// Note: the order here is important. The OISManager must be
// initialized before poller and listener.
Input::Dispatcher disp;
Render::OgreRenderer &ogre;
Input::OISManager input;
Input::Poller poller;
Input::InputListener listener;
MWRender::PlayerPos &player;
// Count screenshots. TODO: We should move this functionality to
// OgreRender or somewhere else.
int shotCount;
// Write screenshot to file.
void screenshot()
{
// Find the first unused filename.
//
char buf[50];
do
{
snprintf(buf, 50, "screenshot%03d.png", shotCount++);
} while (boost::filesystem::exists(buf));
ogre.screenshot(buf);
}
public:
MWInputManager(Render::OgreRenderer &ogre)
MWInputManager(Render::OgreRenderer &_ogre,
MWRender::PlayerPos &_player)
: disp(A_LAST),
input(ogre),
listener(ogre, input, disp)
ogre(_ogre),
input(_ogre),
poller(input),
listener(_ogre, input, disp),
player(_player),
shotCount(0)
{
using namespace Input;
using namespace OIS;
// Bind MW-specific functions
disp.funcs.bind(A_Quit,
boost::bind(&InputListener::exitNow, &listener),
disp.funcs.bind(A_Quit, boost::bind(&InputListener::exitNow, &listener),
"Quit program");
disp.funcs.bind(A_Screenshot, boost::bind(&MWInputManager::screenshot, this),
"Screenshot");
// Add ourselves as a frame listener, to catch movement keys
ogre.getRoot()->addFrameListener(this);
// Tell the input listener about the camera
listener.setCamera(player.getCamera());
// Key bindings
disp.bind(KC_Q, A_Quit);
disp.bind(KC_ESCAPE, A_Quit);
disp.bind(KC_SYSRQ, A_Screenshot);
// Key bindings for polled keys
// Arrow keys
poller.bind(A_MoveLeft, KC_LEFT);
poller.bind(A_MoveRight, KC_RIGHT);
poller.bind(A_MoveForward, KC_UP);
poller.bind(A_MoveBackward, KC_DOWN);
// WASD keys
poller.bind(A_MoveLeft, KC_A);
poller.bind(A_MoveRight, KC_D);
poller.bind(A_MoveForward, KC_W);
poller.bind(A_MoveBackward, KC_S);
// Use shift and ctrl for up and down
poller.bind(A_MoveUp, KC_LSHIFT);
poller.bind(A_MoveDown, KC_LCONTROL);
}
// Used to check for movement keys
bool frameStarted(const Ogre::FrameEvent &evt)
{
float speed = 300 * evt.timeSinceLastFrame;
float moveX = 0, moveY = 0, moveZ = 0;
if(poller.isDown(A_MoveLeft)) moveX -= speed;
if(poller.isDown(A_MoveRight)) moveX += speed;
if(poller.isDown(A_MoveForward)) moveZ -= speed;
if(poller.isDown(A_MoveBackward)) moveZ += speed;
// TODO: These should be enabled for floating modes (like
// swimming and levitation) and disabled for everything else.
if(poller.isDown(A_MoveUp)) moveY += speed;
if(poller.isDown(A_MoveDown)) moveY -= speed;
if(moveX != 0 || moveY != 0 || moveZ != 0)
player.moveRel(moveX, moveY, moveZ);
return true;
}
};
}

@ -29,13 +29,14 @@ void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& live
cellRender.insertBegin (liveRef.ref);
cellRender.insertMesh ("meshes\\" + model);
int color = liveRef.base->data.color;
cellRender.insertLight(color & 255,
(color >> 8) & 255,
(color >> 16) & 255,
liveRef.base->data.radius);
// Extract the color and convert to floating point
const int color = liveRef.base->data.color;
const float r = ((color >> 0) & 0xFF) / 255.0f;
const float g = ((color >> 8) & 0xFF) / 255.0f;
const float b = ((color >> 16) & 0xFF) / 255.0f;
const float radius = float(liveRef.base->data.radius);
cellRender.insertLight(r, g, b, radius);
cellRender.insertEnd();
}

@ -156,7 +156,7 @@ void InteriorCellRender::setAmbientMode()
case 1:
scene.getMgr()->setAmbientLight(0.7*ambientColor + 0.3*ColourValue(1,1,1));
scene.getMgr()->setAmbientLight(0.7f*ambientColor + 0.3f*ColourValue(1,1,1));
break;
case 2:

@ -0,0 +1,58 @@
#ifndef _MWRENDER_PLAYERPOS_H
#define _MWRENDER_PLAYERPOS_H
#include "OgreCamera.h"
namespace MWRender
{
// This class keeps track of the player position. It takes care of
// camera movement, sound listener updates, and collision handling
// (to be done).
class PlayerPos
{
float x, y, z;
Ogre::Camera *camera;
public:
PlayerPos(Ogre::Camera *cam) :
camera(cam), x(0), y(0), z(0) {}
// Set the player position. Uses Morrowind coordinates.
void setPos(float _x, float _y, float _z)
{
x = _x;
y = _y;
z = _z;
// TODO: Update sound listener
}
Ogre::Camera *getCamera() { return camera; }
// Move the player relative to her own position and
// orientation. After the call, the new position is returned.
void moveRel(float &relX, float &relY, float &relZ)
{
using namespace Ogre;
// Move camera relative to its own direction
camera->moveRelative(Vector3(relX,0,relZ));
// Up/down movement is always done relative the world axis.
camera->move(Vector3(0,relY,0));
// Get new camera position, converting back to MW coords.
Vector3 pos = camera->getPosition();
relX = pos[0];
relY = -pos[2];
relZ = pos[1];
// TODO: Collision detection must be used to find the REAL new
// position.
// Set the position
setPos(relX, relY, relZ);
}
};
}
#endif

@ -54,7 +54,8 @@ public:
*/
void bind(int index, Action action, const std::string &name="")
{
assert(index >= 0 && index < bindings.size());
assert(index >= 0 && index < (int)bindings.size());
FuncBinding &fb = bindings[index];
fb.action = action;
fb.name = name;
@ -65,7 +66,8 @@ public:
*/
void unbind(int index)
{
assert(index >= 0 && index < bindings.size());
assert(index >= 0 && index < (int)bindings.size());
bindings[index] = FuncBinding();
}
@ -75,7 +77,8 @@ public:
*/
void call(int index, const void *p=NULL) const
{
assert(index >= 0 && index < bindings.size());
assert(index >= 0 && index < (int)bindings.size());
const FuncBinding &fb = bindings[index];
if(fb.action) fb.action(index, p);
}
@ -83,14 +86,16 @@ public:
/// Check if a given index is bound to anything
bool isBound(int index) const
{
assert(index >= 0 && index < bindings.size());
assert(index >= 0 && index < (int)bindings.size());
return !bindings[index].action.empty();
}
/// Return the name associated with an action (empty if not bound)
const std::string &getName(int index) const
{
assert(index >= 0 && index < bindings.size());
assert(index >= 0 && index < (int)bindings.size());
return bindings[index].name;
}
};

@ -24,12 +24,17 @@ namespace Input
mMouse = input.mouse;
mKeyboard = input.keyboard;
assert(mKeyboard);
assert(mWindow);
// Add ourself to the managers
rend.getRoot() -> addFrameListener(this);
mKeyboard -> setEventCallback(this);
mMouse -> setEventCallback(this);
}
void setCamera(Ogre::Camera *cam) { camera = cam; }
// Call this to exit the main loop
void exitNow() { doExit = true; }
@ -59,6 +64,29 @@ namespace Input
bool mouseMoved( const OIS::MouseEvent &arg )
{
using namespace Ogre;
assert(camera);
// Mouse sensitivity. Should be a config option later.
const float MS = 0.2f;
float x = arg.state.X.rel * MS;
float y = arg.state.Y.rel * MS;
camera->yaw(Degree(-x));
// The camera before pitching
Quaternion nopitch = camera->getOrientation();
camera->pitch(Degree(-y));
// Apply some failsafe measures against the camera flipping
// upside down. Is the camera close to pointing straight up or
// down?
if(camera->getUp()[1] <= 0.1)
// If so, undo the last pitch
camera->setOrientation(nopitch);
return true;
}
@ -76,6 +104,7 @@ namespace Input
const Dispatcher &disp;
Ogre::RenderWindow *mWindow;
Ogre::Camera *camera;
OIS::Mouse *mMouse;
OIS::Keyboard *mKeyboard;
bool doExit;

@ -30,8 +30,9 @@ OISManager::OISManager(Render::OgreRenderer &rend)
windowHndStr << windowHnd;
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
// Non-exclusive mouse and keyboard input in debug mode
if(true)
// Non-exclusive mouse and keyboard input in debug mode. Debug mode
// isn't implemented yet though.
if(false)
{
#if defined OIS_WIN32_PLATFORM
pl.insert(std::make_pair(std::string("w32_mouse"),

@ -0,0 +1,51 @@
#ifndef _INPUT_POLLER_H
#define _INPUT_POLLER_H
#include "dispatch_map.hpp"
#include "oismanager.hpp"
namespace Input {
/** The poller is used to check (poll) for keys rather than waiting
for events. TODO: Might make this OIS-independent later. */
struct Poller
{
DispatchMap map;
OIS::Keyboard *keyboard;
Poller(Input::OISManager &ois)
{
keyboard = ois.keyboard;
assert(keyboard);
}
/** Bind or unbind a given action with a key. The action is the first
parameter, the key is the second.
*/
void bind(int in, int out) { map.bind(in, out); }
void unbind(int in, int out) { map.unbind(in, out); }
bool isBound(int in) const { return map.isBound(in); }
/// Check whether a given action button is currently pressed.
typedef DispatchMap::OutList _O;
bool isDown(int index) const
{
assert(keyboard);
// No bindings, no action
if(!isBound(index))
return false;
// Get all the keys bound to this action, and check them.
const _O &list = map.getList(index);
_O::const_iterator it;
for(it = list.begin(); it != list.end(); it++)
// If there's any match, we're good to go.
if(keyboard->isKeyDown((OIS::KeyCode)*it)) return true;
return false;
}
};
}
#endif

@ -30,10 +30,14 @@
#include "nif/node.hpp"
#include "nif/data.hpp"
#include "nif/property.hpp"
#include "platform/strings.h"
// For warning messages
#include <iostream>
// float infinity
#include <limits>
typedef unsigned char ubyte;
using namespace std;
@ -58,6 +62,78 @@ static void warn(const string &msg)
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
}
// Helper class that computes the bounding box and of a mesh
class BoundsFinder
{
struct MaxMinFinder
{
float max, min;
MaxMinFinder()
{
min = numeric_limits<float>::infinity();
max = -min;
}
void add(float f)
{
if(f > max) max = f;
if(f < min) min = f;
}
// Return Max(max**2, min**2)
float getMaxSquared()
{
float m1 = max*max;
float m2 = min*min;
if(m1 >= m2) return m1;
return m2;
}
};
MaxMinFinder X, Y, Z;
public:
// Add 'verts' vertices to the calculation. The 'data' pointer is
// expected to point to 3*verts floats representing x,y,z for each
// point.
void add(float *data, int verts)
{
for(int i=0;i<verts;i++)
{
X.add(*(data++));
Y.add(*(data++));
Z.add(*(data++));
}
}
// True if this structure has valid values
bool isValid()
{
return
minX() <= maxX() &&
minY() <= maxY() &&
minZ() <= maxZ();
}
// Compute radius
float getRadius()
{
assert(isValid());
// The radius is computed from the origin, not from the geometric
// center of the mesh.
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
}
float minX() { return X.min; }
float maxX() { return X.max; }
float minY() { return Y.min; }
float maxY() { return Y.max; }
float minZ() { return Z.min; }
float maxZ() { return Z.max; }
};
// Conversion of blend / test mode from NIF -> OGRE. Not in use yet.
static SceneBlendFactor getBlendFactor(int mode)
{
@ -177,10 +253,6 @@ static void createMaterial(const String &name,
// make sure that all materials are given unique names.
static String getUniqueName(const String &input)
{
#ifdef WIN32
#define snprintf _snprintf
#endif
static int addon = 0;
static char buf[8];
snprintf(buf, 8, "_%d", addon++);
@ -296,6 +368,22 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
// Set material if one was given
if(!material.empty()) sub->setMaterialName(material);
/* Old commented D code. Might be useful when reimplementing
animation.
// Assign this submesh to the given bone
VertexBoneAssignment v;
v.boneIndex = ((Bone*)bone)->getHandle();
v.weight = 1.0;
std::cerr << "+ Assigning bone index " << v.boneIndex << "\n";
for(int i=0; i < numVerts; i++)
{
v.vertexIndex = i;
sub->addBoneAssignment(v);
}
*/
}
// Helper math functions. Reinventing linear algebra for the win!
@ -341,8 +429,10 @@ static void vectorMul(const Matrix &A, float *C)
C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2];
}
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
{
assert(shape != NULL);
// Interpret flags
bool hidden = (flags & 0x01) != 0; // Not displayed
bool collide = (flags & 0x02) != 0; // Use mesh for collision
@ -462,47 +552,53 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
alphaFlags, alphaTest, texName);
}
}
}
{
/* Do in-place transformation of all the vertices and normals. This
is pretty messy stuff, but we need it to make the sub-meshes
appear in the correct place. Neither Ogre nor Bullet support
nested levels of sub-meshes with transformations applied to each
level.
*/
NiTriShapeData *data = shape->data.getPtr();
int numVerts = data->vertices.length / 3;
} // End of material block, if(!hidden) ...
/* Do in-place transformation of all the vertices and normals. This
is pretty messy stuff, but we need it to make the sub-meshes
appear in the correct place. Neither Ogre nor Bullet support
nested levels of sub-meshes with transformations applied to each
level.
*/
NiTriShapeData *data = shape->data.getPtr();
int numVerts = data->vertices.length / 3;
float *ptr = (float*)data->vertices.ptr;
float *ptr = (float*)data->vertices.ptr;
float *optr = ptr;
// Rotate, scale and translate all the vertices
const Matrix &rot = shape->trafo->rotation;
const Vector &pos = shape->trafo->pos;
float scale = shape->trafo->scale;
for(int i=0; i<numVerts; i++)
{
vectorMulAdd(rot, pos, ptr, scale);
ptr += 3;
}
// Rotate, scale and translate all the vertices
const Matrix &rot = shape->trafo->rotation;
const Vector &pos = shape->trafo->pos;
float scale = shape->trafo->scale;
for(int i=0; i<numVerts; i++)
{
vectorMulAdd(rot, pos, ptr, scale);
ptr += 3;
}
// Remember to rotate all the vertex normals as well
if(data->normals.length)
{
ptr = (float*)data->normals.ptr;
for(int i=0; i<numVerts; i++)
{
vectorMul(rot, ptr);
ptr += 3;
}
}
}
// Remember to rotate all the vertex normals as well
if(data->normals.length)
{
ptr = (float*)data->normals.ptr;
for(int i=0; i<numVerts; i++)
{
vectorMul(rot, ptr);
ptr += 3;
}
}
if(!hidden)
createOgreMesh(mesh, shape, material);
{
// Add this vertex set to the bounding box
bounds.add(optr, numVerts);
// Create the submesh
createOgreMesh(mesh, shape, material);
}
}
static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformation *trafo = NULL)
static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
const Transformation *trafo, BoundsFinder &bounds)
{
// Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least.
@ -538,7 +634,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
if(trafo)
{
// Get a non-const reference to the node's data, since we're
// overwriting it.
// overwriting it. TODO: Is this necessary?
Transformation &final = *((Transformation*)node->trafo);
// For both position and rotation we have that:
@ -550,7 +646,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
matrixMul(trafo->rotation, final.rotation);
// Scalar values are so nice to deal with. Why can't everything
// just be scalars?
// just be scalar?
final.scale *= trafo->scale;
}
@ -562,12 +658,12 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
for(int i=0; i<n; i++)
{
if(list.has(i))
handleNode(mesh, &list[i], flags, node->trafo);
handleNode(mesh, &list[i], flags, node->trafo, bounds);
}
}
else if(node->recType == RC_NiTriShape)
// For shapes
handleNiTriShape(mesh, (NiTriShape*)node, flags);
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
}
void NIFLoader::loadResource(Resource *resource)
@ -588,6 +684,9 @@ void NIFLoader::loadResource(Resource *resource)
return;
}
// Helper that computes bounding boxes for us.
BoundsFinder bounds;
// Load the NIF. TODO: Wrap this in a try-catch block once we're out
// of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most
@ -604,19 +703,25 @@ void NIFLoader::loadResource(Resource *resource)
Record *r = nif.getRecord(0);
assert(r != NULL);
if(r->recType != RC_NiNode)
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
if(node == NULL)
{
warn("First record in file was not a NiNode, but a " +
warn("First record in file was not a node, but a " +
r->recName.toString() + ". Skipping file.");
return;
}
// Handle the node
handleNode(mesh, (Nif::Node*)r, 0);
handleNode(mesh, node, 0, NULL, bounds);
// Finally, set the bounding value. Just use bogus info right now.
mesh->_setBounds(AxisAlignedBox(-10,-10,-10,10,10,10));
mesh->_setBoundingSphereRadius(10);
// Finally, set the bounding value.
if(bounds.isValid())
{
mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(),
bounds.maxX(), bounds.maxY(), bounds.maxZ()));
mesh->_setBoundingSphereRadius(bounds.getRadius());
}
}
MeshPtr NIFLoader::load(const std::string &name,
@ -632,3 +737,109 @@ MeshPtr NIFLoader::load(const std::string &name,
// Nope, create a new one.
return MeshManager::getSingleton().createManual(name, group, &g_sing);
}
/* More code currently not in use, from the old D source. This was
used in the first attempt at loading NIF meshes, where each submesh
in the file was given a separate bone in a skeleton. Unfortunately
the OGRE skeletons can't hold more than 256 bones, and some NIFs go
way beyond that. The code might be of use if we implement animated
submeshes like this (the part of the NIF that is animated is
usually much less than the entire file, but the method might still
not be water tight.)
// Insert a raw RGBA image into the texture system.
extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data)
{
TexturePtr texture = TextureManager::getSingleton().createManual(
name, // name
"General", // group
TEX_TYPE_2D, // type
width, height, // width & height
0, // number of mipmaps
PF_BYTE_RGBA, // pixel format
TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for
// textures updated very often (e.g. each frame)
// Get the pixel buffer
HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();
// Lock the pixel buffer and get a pixel box
pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD!
const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
void *dest = pixelBox.data;
// Copy the data
memcpy(dest, data, width*height*4);
// Unlock the pixel buffer
pixelBuffer->unlock();
}
// We need this later for animated meshes.
extern "C" void* ogre_setupSkeleton(char* name)
{
SkeletonPtr skel = SkeletonManager::getSingleton().create(
name, "Closet", true);
skel->load();
// Create all bones at the origin and unrotated. This is necessary
// since our submeshes each have their own model space. We must
// move the bones after creating an entity, then copy this entity.
return (void*)skel->createBone();
}
extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index)
{
return (void*) ( ((Bone*)rootBone)->createChild(index) );
}
*/
/* This was the D part:
// Create a skeleton and get the root bone (index 0)
BonePtr bone = ogre_setupSkeleton(name);
// Reset the bone index. The next bone to be created has index 1.
boneIndex = 1;
// Create a mesh and assign the skeleton to it
MeshPtr mesh = ogre_setupMesh(name);
// Loop through the nodes, creating submeshes, materials and
// skeleton bones in the process.
handleNode(node, bone, mesh);
// Create the "template" entity
EntityPtr entity = ogre_createEntity(name);
// Loop through once again, this time to set the right
// transformations on the entity's SkeletonInstance. The order of
// children will be the same, allowing us to reference bones using
// their boneIndex.
int lastBone = boneIndex;
boneIndex = 1;
transformBones(node, entity);
if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match");
if(!hasBBox)
ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ);
return entity;
}
void handleNode(Node node, BonePtr root, MeshPtr mesh)
{
// Insert a new bone for this node
BonePtr bone = ogre_insertBone(node.name, root, boneIndex++);
}
void transformBones(Node node, EntityPtr entity)
{
ogre_transformBone(entity, &node.trafo, boneIndex++);
NiNode n = cast(NiNode)node;
if(n !is null)
foreach(Node nd; n.children)
transformBones(nd, entity);
}
*/

@ -15,6 +15,11 @@ void OgreRenderer::cleanup()
mRoot = NULL;
}
void OgreRenderer::screenshot(const std::string &file)
{
mWindow->writeContentsToFile(file);
}
bool OgreRenderer::configure(bool showConfig,
const std::string &pluginCfg,
bool _logging)

@ -42,6 +42,9 @@ namespace Render
/// Start the main rendering loop
void start() { mRoot->startRendering(); }
/// Write a screenshot to file
void screenshot(const std::string &file);
/// Get the Root
Ogre::Root *getRoot() { return mRoot; }

@ -57,11 +57,6 @@ import input.ois;
bool pause = false;
int *guiMode;
void toggleFullscreen()
{
ogre_toggleFullscreen();
}
const float volDiff = 0.05;
void musVolume(bool increase)
@ -88,16 +83,6 @@ void mainVolume(bool increase)
writefln(increase?"Increasing":"Decreasing", " main volume to ", config.getMainVolume);
}
void takeScreenShot()
{
char[] file = format("screenshot_%06d.png", config.screenShotNum++);
ogre_screenshot(toStringz(file));
writefln("Wrote '%s'", file);
}
// Mouse sensitivity
float effMX, effMY;
void updateMouseSensitivity()
{
effMX = *config.mouseSensX;
@ -112,19 +97,6 @@ void togglePause()
else writefln("Pause off");
}
extern(C) void d_handleMouseMove(MouseState *state)
{
debug(printMouseMove)
writefln("handleMouseMove: Abs(%s, %s, %s) Rel(%s, %s, %s)",
state.X.abs, state.Y.abs, state.Z.abs,
state.X.rel, state.Y.rel, state.Z.rel);
if(*guiMode) return;
ogre_rotateCamera( state.X.rel * effMX,
state.Y.rel * effMY );
}
extern(C) void d_handleMouseButton(MouseState *state, int button)
{
debug(printMouse)

@ -1,272 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (keys.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/ .
*/
/*
* This module handles keyboard and mouse button configuration
*/
module input.keys;
import std.string;
import std.stdio;
import input.ois;
// List of all functions we need to map to keys. If you add new keys,
// REMEMBER to add strings for them below as well. TODO: We should
// redo this entire section so that we insert actual functions into
// the system instead. That way, if we do not insert a function, the
// key gets treated as a "non-event" key. Then we will also force the
// definition of strings and function call to be in the same
// place. The Keys enum can be eliminated, really. Default keysyms can
// be added when the functions are inserted. But don't do anything
// until you know how this will interact with script code.
enum Keys
{
None = 0,
// Movement
MoveLeft, MoveRight,
TurnLeft, TurnRight,
MoveForward, MoveBackward,
// Used eg. when flying or swimming
MoveUp, MoveDown,
// These are handled as events, while the above are not.
FirstEvent,
// Sound control
MainVolUp, MainVolDown,
MusVolUp, MusVolDown,
SfxVolUp, SfxVolDown,
Mute,
// These will not be part of the finished product
Fullscreen,
ToggleBattleMusic,
PhysMode, // Toggle physics mode between walking, flying and ghost
Nighteye, // Full ambient lighting
ToggleGui,// Turn the GUI on/off
Console, // Turn console on/off
Debug,
// Misc
Pause,
ScreenShot,
Exit,
Length
}
// List of keyboard-bound functions
char[][] keyToString;
// Lookup for keyboard key names. TODO: This is currently case
// sensitive, we should use my own AA to fix that.
int[char[]] stringToKeysym;
// Represents a key binding for a single function. Each function can
// have any number keys bound to it, including mouse buttons. This
// might be extended later to allow joystick buttons and other input
// devices.
struct KeyBind
{
int syms[];
// Does the given keysym match this binding?
bool isMatch(int sym, char ch = 0)
{
assert(sym != 0, "don't send empty syms to isMatch");
// We don't match characters yet
if(sym == KC.CharOnly)
return false;
foreach(s; syms)
if(sym == s) return true;
return false;
}
// Does any of the given syms match this binding?
bool isMatchArray(int arr[] ...)
{
foreach(i; arr)
if(i!=0 && isMatch(i)) return true;
return false;
}
// Assign key bindings to this structure. Can be called multiple
// times or with multiple paramters (or an array) to bind multiple
// keys.
void bind(int symlist[] ...)
{
syms ~= symlist;
}
// Remove all bindings to this function
void clear()
{
syms = null;
}
// Remove the given syms from this binding, if found.
void remove(int symlist[] ...)
{
foreach(rs; symlist)
// Just loop though all the syms and set matching values to
// zero. isMatch() will ignore zeros.
foreach(ref s; syms)
if(s == rs) s = 0;
}
// Turn the keysym list into a comma separated list of key names
char[] getString()
{
char[] res = null;
bool notFirst = false;
foreach(int k; syms)
if(k != 0) // Ignore empty keysyms
{
if(notFirst) res ~= ",";
else notFirst = true;
res ~= keysymToString[k];
}
//writefln("getString returned %s", res);
return res;
}
}
KeyBindings keyBindings;
// This structure holds the bindings of all the functions
struct KeyBindings
{
KeyBind[] bindings;
// Bind the given function to the given key(s)
void bind(Keys func, char[] key1, char[] key2 = "")
{
bind(func, getSym(key1), getSym(key2));
}
void bind(Keys func, int syms[] ...)
{
// Find other bindings that match this key
foreach(int i, ref KeyBind kb; bindings)
if(kb.isMatchArray(syms))
kb.remove(syms);
bindings[func].bind(syms);
}
// Find the function that matches the given keysym. We could
// optimize this, but I'm not sure it's worth it.
Keys findMatch(KC keysym, dchar ch)
{
int start=cast(int)Keys.FirstEvent + 1;
foreach(int i, ref KeyBind kb; bindings[start..$])
if( kb.isMatch(keysym, ch) )
return cast(Keys)(i+start);
return cast(Keys)0; // No match
}
static int getSym(char[] key)
{
key = strip(key);
if(key.length)
{
int *p = key in stringToKeysym;
if(p) return *p;
else writefln("Warning: unknown key '%s'", key);
}
return 0;
}
// Bind a function to a comma-separated key list (intended to be
// used directly with the ini file reader.)
void bindComma(Keys func, char[] keys)
{
int index = keys.find(',');
if(index != -1)
{
// Bind the first in the list
bind(func, keys[0..index]);
// Recurse on the rest
bindComma(func, keys[index+1..$]);
}
// Last or only element in the list
else bind(func, keys);
}
// Remove all key bindings
void clear()
{
foreach(ref kb; bindings)
kb.clear();
}
void initKeys()
{
// Keyboard functions
keyToString.length = Keys.Length;
keyToString[Keys.MoveLeft] = "Move Left";
keyToString[Keys.MoveRight] = "Move Right";
keyToString[Keys.TurnLeft] = "Turn Left";
keyToString[Keys.TurnRight] = "Turn Right";
keyToString[Keys.MoveForward] = "Move Forward";
keyToString[Keys.MoveBackward] = "Move Backward";
keyToString[Keys.MoveUp] = "Move Up";
keyToString[Keys.MoveDown] = "Move Down";
keyToString[Keys.MainVolUp] = "Increase Main Volume";
keyToString[Keys.MainVolDown] = "Decrease Main Volume";
keyToString[Keys.MusVolUp] = "Increase Music Volume";
keyToString[Keys.MusVolDown] = "Decrease Music Volume";
keyToString[Keys.SfxVolUp] = "Increase SFX Volume";
keyToString[Keys.SfxVolDown] = "Decrease SFX Volume";
keyToString[Keys.Mute] = "Mute Sound";
keyToString[Keys.Fullscreen] = "Toggle Fullscreen Mode";
keyToString[Keys.ToggleBattleMusic] = "Toggle Battle Music";
keyToString[Keys.PhysMode] = "Toggle Physics Mode";
keyToString[Keys.Nighteye] = "Toggle Nighteye";
keyToString[Keys.ToggleGui] = "Toggle GUI";
keyToString[Keys.Console] = "Console";
keyToString[Keys.Debug] = "OGRE Test Action";
keyToString[Keys.Pause] = "Pause";
keyToString[Keys.ScreenShot] = "Screen Shot";
keyToString[Keys.Exit] = "Quick Exit";
//keyToString[Keys.] = "";
bindings.length = Keys.Length;
// Store all the key strings in a lookup-table
foreach(int k, ref char[] s; keysymToString)
if(s.length) stringToKeysym[s] = k;
}
}

@ -1,166 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (bindings.d) is part of the OpenMW package.
OpenMW is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
module ogre.bindings;
import nif.misc; // for Transformation
import ogre.ogre; // for Placement
import core.resource;
/*
* This module is the interface to OGRE from D. Since OGRE is written
* in C++, all the code that deals directly with the graphics engine
* is packaged in a bunch of C++ functions. These functions are
* exported from C++ through the C calling convention, and imported
* here.
*
* Note that the C calling convension is not in any way type
* safe. This is convenient, as it allows us to send pointers as one
* type and recieve them as another, without casting, but also
* dangerous since it opens for some nasty bugs.
*/
// Represents a pointer to a Node in the OGRE engine. We never use
// these directly in D code, only pass them back to the C++ code.
typedef void* NodePtr;
extern(C):
// Do engine configuration. Returns 0 if we should continue, 1 if
// not.
int ogre_configure(int showConfig, // Do we show the config dialogue?
char *plugincfg,// Name of 'plugin.cfg' file
int debutOut); // Enable or disable debug output
// Sets up the window
void ogre_initWindow();
// Set up an empty scene.
void ogre_makeScene();
// Set the ambient light and "sunlight"
void ogre_setAmbient(float r, float g, float b,
float rs, float gs, float bs);
// Set fog color and view distance
void ogre_setFog(float rf, float gf, float bf,
float flow, float fhigh);
// Create a simple sky dome
int ogre_makeSky();
// Toggle full ambient lighting on and off
void ogre_toggleLight();
// Enter main rendering loop
void ogre_startRendering();
// Cleans up after ogre
void ogre_cleanup();
// Gets a child SceneNode from the root node, then detatches it to
// hide it from view. Used for creating the "template" node associated
// with a NIF mesh.
NodePtr ogre_getDetachedNode();
// Convert a Morrowind rotation (3 floats) to a quaternion (4 floats)
void ogre_mwToQuaternion(float *mw, float *quat);
// Create a copy of the given scene node, with the given coordinates
// and rotation (as a quaternion.)
NodePtr ogre_insertNode(NodePtr base, char* name,
float *pos, float *quat, float scale);
// Get the world transformation of a node, returned as a translation
// and a matrix. The matrix includes both rotation and scaling. The
// buffers given must be large enough to store the result (3 and 9
// floats respectively.)
void ogre_getWorldTransform(NodePtr node, float *trans, float *matrix);
// Create a (very crappy looking) plane to simulate the water level
void ogre_createWater(float level);
// Creates a scene node as a child of 'parent', then translates and
// rotates it according to the data in 'trafo'.
NodePtr ogre_createNode(
char *name, // Name to give the node
Transformation *trafo, // Transformation
NodePtr parent, // Parent node
int noRot); // If 1, don't rotate node
// Create a light with the given diffuse color. Attach it to SceneNode
// 'parent'.
NodePtr ogre_attachLight(char* name, NodePtr parent,
float r, float g, float b,
float radius);
// Create the specified material
void ogre_createMaterial(char *name, // Name to give resource
float *ambient, // Ambient RBG value
float *diffuse,
float *specular,
float *emissive, // Self illumination
float glossiness,// Same as shininess?
float alpha, // Reflection alpha?
char *texture, // Texture name
int alphaFlags, // Alpha settings (see
ubyte alphaTest);// NiAlphaProperty in nif/)
// Creates a mesh and gives it a bounding box. Also creates an entity
// and attached it to the given SceneNode 'owner'.
void ogre_createMesh(
char* name, // Name of the mesh
int numVerts, // Number of vertices
float* vertices, // Vertex list
float* normals, // Normal list
float* colors, // Vertex colors
float* uvs, // Texture coordinates
int numFaces, // Number of faces*3
short* faces, // Faces
float radius, // Bounding sphere
char* material, // Material name, if any
// Bounding box
float minX,float minY,float minZ,
float maxX,float maxY,float maxZ,
NodePtr owner // Scene node to attach to.
);
// Toggle fullscreen mode
void ogre_toggleFullscreen();
// Save a screen shot to the given file name
void ogre_screenshot(char *filename);
// Camera control and information
void ogre_rotateCamera(float x, float y);
void ogre_moveCamera(float x, float y, float z);
void ogre_setCameraRotation(float r1, float r2, float r3);
void ogre_getCameraPos(float *x, float *y, float *z);
void ogre_getCameraOrientation(float *fx, float *fy, float *fz, float *ux, float *uy, float *uz);
void ogre_moveCameraRel(float x, float y, float z);
// Insert a raw RGBA image into the texture system.
//void ogre_insertTexture(char *name, int width, int height, void *data);

@ -1,39 +1,3 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (cpp_framelistener.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 <stdint.h>
// Callbacks to D code.
// Called once each frame
extern "C" int32_t d_frameStarted(float time);
// Handle events
extern "C" void d_handleKey(int keycode, uint32_t text);
extern "C" void d_handleMouseMove(const OIS::MouseState *state);
extern "C" void d_handleMouseButton(const OIS::MouseState *state,
int32_t button);
// Frame listener, passed to Ogre. The only thing we use this for is
// to capture input and pass control to D code.
class MorroFrameListener: public FrameListener
@ -128,47 +92,6 @@ public:
MorroFrameListener mFrameListener;
InputListener mInput;
// Functions called from D during event handling
extern "C" int32_t ois_isPressed(int32_t keysym)
{
return mKeyboard->isKeyDown((OIS::KeyCode)keysym);
}
// Dump screen contents to file
extern "C" void ogre_screenshot(char* filename)
{
mWindow->writeContentsToFile(filename);
//This doesn't work, I think I have to set up an overlay or
//something first and display the text manually.
//mWindow->setDebugText(String("Wrote ") + filename);
}
// Rotate camera as result of mouse movement
extern "C" void ogre_rotateCamera(float x, float y)
{
mCamera->yaw(Degree(-x));
Quaternion nopitch = mCamera->getOrientation();
mCamera->pitch(Degree(-y));
// Is the camera close to being upside down?
if(mCamera->getUp()[1] <= 0.1)
// If so, undo the last pitch
mCamera->setOrientation(nopitch);
}
// Get current camera position
extern "C" void ogre_getCameraPos(float *x, float *y, float *z)
{
Vector3 pos = mCamera->getPosition();
*x = pos[0];
*y = -pos[2];
*z = pos[1];
}
// Get current camera orientation, in the form of 'front' and 'up'
// vectors.
extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz,
@ -191,8 +114,6 @@ extern "C" void ogre_moveCamera(float x, float y, float z)
// is not affected by the rotation of the root node, so we must
// transform this manually.
mCamera->setPosition(Vector3(x,z+90,-y));
//g_light->setPosition(mCamera->getPosition());
}
// Rotate camera using Morrowind rotation specifiers
@ -212,12 +133,3 @@ extern "C" void ogre_setCameraRotation(float r1, float r2, float r3)
// Rotates first around z, then y, then x
mCamera->setOrientation(xr*yr*zr);
}
// Move camera relative to its own axis set.
extern "C" void ogre_moveCameraRel(float x, float y, float z)
{
mCamera->moveRelative(Vector3(x,0,z));
// up/down movement is always done relative the world axis
mCamera->move(Vector3(0,y,0));
}

@ -1,96 +1,3 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (cpp_interface.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/ .
*/
extern "C" Light* ogre_attachLight(char *name, SceneNode* base,
float r, float g, float b,
float radius)
{
Light *l = mSceneMgr->createLight(name);
l->setDiffuseColour(r,g,b);
radius /= 4.0f;
float cval=0.0f, lval=0.0f, qval=0.0f;
if(lightConst)
cval = lightConstValue;
if(!lightOutQuadInLin)
{
if(lightLinear)
radius *= lightLinearRadiusMult;
if(lightQuadratic)
radius *= lightQuadraticRadiusMult;
if(lightLinear)
lval = lightLinearValue / pow(radius, lightLinearMethod);
if(lightQuadratic)
qval = lightQuadraticValue / pow(radius, lightQuadraticMethod);
}
else
{
// FIXME:
// Do quadratic or linear, depending if we're in an exterior or interior
// cell, respectively. Ignore lightLinear and lightQuadratic.
}
// The first parameter is a cutoff value on which meshes to
// light. If it's set to small, some meshes will end up 'flashing'
// in and out of light depending on the camera distance from the
// light.
l->setAttenuation(10*radius, cval, lval, qval);
// base might be null, sometimes lights don't have meshes
if(base) base->attachObject(l);
return l;
}
extern "C" void ogre_setAmbient(float r, float g, float b, // Ambient light
float rs, float gs, float bs) // "Sunlight"
{
g_ambient = ColourValue(r, g, b);
mSceneMgr->setAmbientLight(g_ambient);
// Create a "sun" that shines light downwards. It doesn't look
// completely right, but leave it for now.
Light *l = mSceneMgr->createLight("Sun");
l->setDiffuseColour(rs, gs, bs);
l->setType(Light::LT_DIRECTIONAL);
l->setDirection(0,-1,0);
}
extern "C" void ogre_setFog(float rf, float gf, float bf, // Fog color
float flow, float fhigh) // Fog distance
{
ColourValue fogColor( rf, gf, bf );
mSceneMgr->setFog( FOG_LINEAR, fogColor, 0.0, flow, fhigh );
// Don't render what you can't see anyway
mCamera->setFarClipDistance(fhigh + 10);
// Leave this out for now
vp->setBackgroundColour(fogColor);
}
// Copy a scene node and all its children
void cloneNode(SceneNode *from, SceneNode *to, char* name)
{
@ -185,246 +92,6 @@ extern "C" void ogre_createWater(float level)
ent->setCastShadows(false);
}
// Manual loader for meshes. Reloading individual meshes is too
// difficult, and not worth the trouble. Later I should make one
// loader for each NIF file, and whenever it is invoked it should
// somehow reload the entire file. How this is to be done when some of
// the meshes might be loaded and in use already, I have no
// idea. Let's just ignore it for now.
class MeshLoader : public ManualResourceLoader
{
public:
void loadResource(Resource *resource)
{
}
} dummyLoader;
// Load the contents of a mesh
extern "C" void ogre_createMesh(
char* name, // Name of the mesh
int32_t numVerts, // Number of vertices
float* vertices, // Vertex list
float* normals, // Normal list
float* colors, // Vertex colors
float* uvs, // Texture coordinates
int32_t numFaces, // Number of faces*3
uint16_t* faces, // Faces
float radius, // Bounding sphere
char* material, // Material
// Bounding box
float minX,float minY,float minZ,
float maxX,float maxY,float maxZ,
SceneNode *owner
)
{
//std::cerr << "Creating mesh " << name << "\n";
MeshPtr msh = MeshManager::getSingleton().createManual(name, "Meshes",
&dummyLoader);
Entity *e = mSceneMgr->createEntity(name, name);
owner->attachObject(e);
//msh->setSkeletonName(name);
// Create vertex data structure
msh->sharedVertexData = new VertexData();
msh->sharedVertexData->vertexCount = numVerts;
/// Create declaration (memory format) of vertex data
VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration;
int nextBuf = 0;
// 1st buffer
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_POSITION);
/// Allocate vertex buffer of the requested number of vertices (vertexCount)
/// and bytes per vertex (offset)
HardwareVertexBufferSharedPtr vbuf =
HardwareBufferManager::getSingleton().createVertexBuffer(
VertexElement::getTypeSize(VET_FLOAT3),
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
/// Upload the vertex data to the card
vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);
/// Set vertex buffer binding so buffer 0 is bound to our vertex buffer
VertexBufferBinding* bind = msh->sharedVertexData->vertexBufferBinding;
bind->setBinding(nextBuf++, vbuf);
// The lists are read in the same order that they appear in NIF
// files, and likely in memory. Sequential reads might possibly
// avert an occational cache miss.
// normals
if(normals)
{
//std::cerr << "+ Adding normals\n";
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
VertexElement::getTypeSize(VET_FLOAT3),
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
vbuf->writeData(0, vbuf->getSizeInBytes(), normals, true);
bind->setBinding(nextBuf++, vbuf);
}
// vertex colors
if(colors)
{
//std::cerr << "+ Adding vertex colors\n";
// Use render system to convert colour value since colour packing varies
RenderSystem* rs = Root::getSingleton().getRenderSystem();
RGBA colorsRGB[numVerts];
RGBA *pColour = colorsRGB;
for(int i=0; i<numVerts; i++)
{
rs->convertColourValue(ColourValue(colors[0],colors[1],colors[2], colors[3]),
pColour++);
colors += 4;
}
decl->addElement(nextBuf, 0, VET_COLOUR, VES_DIFFUSE);
/// Allocate vertex buffer of the requested number of vertices (vertexCount)
/// and bytes per vertex (offset)
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
VertexElement::getTypeSize(VET_COLOUR),
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
/// Upload the vertex data to the card
vbuf->writeData(0, vbuf->getSizeInBytes(), colorsRGB, true);
/// Set vertex buffer binding so buffer 1 is bound to our colour buffer
bind->setBinding(nextBuf++, vbuf);
}
if(uvs)
{
//std::cerr << "+ Adding texture coordinates\n";
decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
VertexElement::getTypeSize(VET_FLOAT2),
numVerts, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
vbuf->writeData(0, vbuf->getSizeInBytes(), uvs, true);
bind->setBinding(nextBuf++, vbuf);
}
// Create the submesh that holds triangle data
SubMesh* sub = msh->createSubMesh(name);
sub->useSharedVertices = true;
if(numFaces)
{
//std::cerr << "+ Adding faces\n";
/// Allocate index buffer of the requested number of faces
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
createIndexBuffer(
HardwareIndexBuffer::IT_16BIT,
numFaces,
HardwareBuffer::HBU_STATIC_WRITE_ONLY);
/// Upload the index data to the card
ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true);
/// Set parameters of the submesh
sub->indexData->indexBuffer = ibuf;
sub->indexData->indexCount = numFaces;
sub->indexData->indexStart = 0;
}
// Create a material with the given texture, if any.
// If this mesh has a material, attach it.
if(material) sub->setMaterialName(name);
/*
// Assign this submesh to the given bone
VertexBoneAssignment v;
v.boneIndex = ((Bone*)bone)->getHandle();
v.weight = 1.0;
std::cerr << "+ Assigning bone index " << v.boneIndex << "\n";
for(int i=0; i < numVerts; i++)
{
v.vertexIndex = i;
sub->addBoneAssignment(v);
}
*/
/// Set bounding information (for culling)
msh->_setBounds(AxisAlignedBox(minX,minY,minZ,maxX,maxY,maxZ));
//std::cerr << "+ Radius: " << radius << "\n";
msh->_setBoundingSphereRadius(radius);
}
extern "C" void ogre_createMaterial(char *name, // Name to give
// resource
float *ambient, // Ambient RBG
// value
float *diffuse,
float *specular,
float *emissive, // Self
// illumination
float glossiness,// Same as
// shininess?
float alpha, // Use this in all
// alpha values?
char* texture, // Texture
int32_t alphaFlags,
uint8_t alphaTest) // Alpha settings
{
MaterialPtr material = MaterialManager::getSingleton().create(
name,
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// This assigns the texture to this material. If the texture
// name is a file name, and this file exists (in a resource
// directory), it will automatically be loaded when needed. If
// not, we should already have inserted a manual loader for the texture.
if(texture)
{
Pass *pass = material->getTechnique(0)->getPass(0);
TextureUnitState *txt = pass->createTextureUnitState(texture);
// Add transparencly.
if(alphaFlags != -1)
{
// The 237 alpha flags are by far the most common. Check
// NiAlphaProperty in nif/properties.d if you need to
// decode other values. 237 basically means normal
// transparencly.
if(alphaFlags == 237)
{
// Enable transparency
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
//pass->setDepthCheckEnabled(false);
pass->setDepthWriteEnabled(false);
}
else
std::cout << "UNHANDLED ALPHA FOR " << texture << ": " << alphaFlags << "\n";
}
}
// Set bells and whistles
material->setAmbient(ambient[0], ambient[1], ambient[2]);
material->setDiffuse(diffuse[0], diffuse[1], diffuse[2], alpha);
material->setSpecular(specular[0], specular[1], specular[2], alpha);
material->setSelfIllumination(emissive[0], emissive[1], emissive[2]);
material->setShininess(glossiness);
}
extern "C" SceneNode *ogre_getDetachedNode()
{
SceneNode *node = mwRoot->createChildSceneNode();
@ -466,54 +133,3 @@ extern "C" SceneNode* ogre_createNode(
return node;
}
/* Code currently not in use
// Insert a raw RGBA image into the texture system.
extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data)
{
TexturePtr texture = TextureManager::getSingleton().createManual(
name, // name
"General", // group
TEX_TYPE_2D, // type
width, height, // width & height
0, // number of mipmaps
PF_BYTE_RGBA, // pixel format
TU_DEFAULT); // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for
// textures updated very often (e.g. each frame)
// Get the pixel buffer
HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();
// Lock the pixel buffer and get a pixel box
pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD!
const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
void *dest = pixelBox.data;
// Copy the data
memcpy(dest, data, width*height*4);
// Unlock the pixel buffer
pixelBuffer->unlock();
}
// We need this later for animated meshes.
extern "C" void* ogre_setupSkeleton(char* name)
{
SkeletonPtr skel = SkeletonManager::getSingleton().create(
name, "Closet", true);
skel->load();
// Create all bones at the origin and unrotated. This is necessary
// since our submeshes each have their own model space. We must
// move the bones after creating an entity, then copy this entity.
return (void*)skel->createBone();
}
extern "C" void *ogre_insertBone(char* name, void* rootBone, int32_t index)
{
return (void*) ( ((Bone*)rootBone)->createChild(index) );
}
*/

@ -1,48 +1,5 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (cpp_ogre.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 <Ogre.h>
#include <iostream>
#include <Ogre.h>
#include <OgreConfigFile.h>
#include <OgreStringConverter.h>
#include <OgreException.h>
#include <OgreArchive.h>
#include <OgreArchiveFactory.h>
#include <MyGUI.h>
#include "../util/dbg.h"
using namespace Ogre;
ColourValue g_ambient;
int g_lightOn = 0;
// Set to nonzero if debug mode is enabled
int g_isDebug = 0;
// The global GUI object
MyGUI::Gui *mGUI;
@ -51,11 +8,5 @@ MyGUI::Gui *mGUI;
// input into MyGUI.
int32_t guiMode = 0;
// Include the other parts of the code, and make one big happy object
// file. This is extremely against the grain of C++ "recomended
// practice", but I don't care.
#include "../gui/cpp_mygui.cpp"
#include "cpp_framelistener.cpp"
#include "cpp_bsaarchive.cpp"
#include "cpp_interface.cpp"
#include "../terrain/cpp_terrain.cpp"

@ -389,50 +389,3 @@ struct MeshLoader
}
}
}
/*
// Create a skeleton and get the root bone (index 0)
BonePtr bone = ogre_setupSkeleton(name);
// Reset the bone index. The next bone to be created has index 1.
boneIndex = 1;
// Create a mesh and assign the skeleton to it
MeshPtr mesh = ogre_setupMesh(name);
// Loop through the nodes, creating submeshes, materials and
// skeleton bones in the process.
handleNode(node, bone, mesh);
// Create the "template" entity
EntityPtr entity = ogre_createEntity(name);
// Loop through once again, this time to set the right
// transformations on the entity's SkeletonInstance. The order of
// children will be the same, allowing us to reference bones using
// their boneIndex.
int lastBone = boneIndex;
boneIndex = 1;
transformBones(node, entity);
if(lastBone != boneIndex) writefln("WARNING: Bone number doesn't match");
if(!hasBBox)
ogre_setMeshBoundingBox(mesh, minX, minY, minZ, maxX, maxY, maxZ);
return entity;
}
void handleNode(Node node, BonePtr root, MeshPtr mesh)
{
// Insert a new bone for this node
BonePtr bone = ogre_insertBone(node.name, root, boneIndex++);
}
void transformBones(Node node, EntityPtr entity)
{
ogre_transformBone(entity, &node.trafo, boneIndex++);
NiNode n = cast(NiNode)node;
if(n !is null)
foreach(Node nd; n.children)
transformBones(nd, entity);
}
*/

@ -30,19 +30,6 @@ NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale,
return node;
}
void setAmbient(Color amb, Color sun, Color fog, float density)
{
ogre_setAmbient(amb.red/255.0, amb.green/255.0, amb.blue/255.0,
sun.red/255.0, sun.green/255.0, sun.blue/255.0);
// Calculate fog distance
// TODO: Mesh with absolute view distance later
float fhigh = 4500 + 9000*(1-density);
float flow = 200 + 2000*(1-density);
ogre_setFog(fog.red/255.0, fog.green/255.0, fog.blue/255.0, 200, fhigh);
}
// Gives the placement of an item in the scene (position and
// orientation). It must have this exact structure since we also use
// it when reading ES files.

@ -2,8 +2,10 @@
#ifndef _STRINGS_WRAPPER_H
#define _STRINGS_WRAPPER_H
#ifdef WIN32
#pragma warning(disable: 4996)
#define strcasecmp stricmp
#define snprintf _snprintf
#endif
#endif

Loading…
Cancel
Save