forked from mirror/openmw-tes3mp
Merge branch 'master' of git://github.com/korslund/openmw
Conflicts: game/main.cpp
This commit is contained in:
commit
3bb9d06e58
30 changed files with 803 additions and 1224 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
screenshot*.png
|
||||||
*.o
|
*.o
|
||||||
*~
|
*~
|
||||||
data
|
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 bsa/bsa_archive.cpp bsa/bsa_file.cpp)
|
||||||
set(BSA_HEADER bsa/bsa_archive.hpp bsa/bsa_file.hpp)
|
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 nif/nif_file.cpp)
|
||||||
set(NIF_HEADER nif/controlled.hpp nif/effect.hpp nif/nif_types.hpp nif/record.hpp
|
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/controller.hpp nif/extra.hpp nif/node.hpp nif/record_ptr.hpp
|
||||||
nif/data.hpp nif/nif_file.hpp nif/property.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 nifogre/ogre_nif_loader.cpp)
|
||||||
set(NIFOGRE_HEADER nifogre/ogre_nif_loader.hpp)
|
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 tools/stringops.cpp tools/fileops.cpp)
|
||||||
set(TOOLS_HEADER tools/fileops.hpp tools/slice_array.hpp tools/stringops.hpp)
|
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)
|
set(MANGLE_VFS mangle/vfs/servers/ogre_vfs.cpp)
|
||||||
|
source_group(mangle_vfs FILES ${MANGLE_VFS})
|
||||||
|
|
||||||
set(OGRE ogre/renderer.cpp)
|
set(OGRE ogre/renderer.cpp)
|
||||||
set(OGRE_HEADER ogre/renderer.hpp)
|
set(OGRE_HEADER ogre/renderer.hpp)
|
||||||
|
source_group(ogre FILES ${OGRE} ${OGRE_HEADER})
|
||||||
|
|
||||||
set(INPUT input/oismanager.cpp)
|
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 game/main.cpp game/engine.cpp)
|
||||||
set(GAME_HEADER game/mwinput/inputmanager.hpp)
|
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 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)
|
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 game/mwrender/mwscene.cpp game/mwrender/cell.cpp game/mwrender/interior.cpp)
|
||||||
set(GAMEREND_HEADER game/mwrender/cell.hpp game/mwrender/mwscene.hpp
|
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
|
set(ESM_HEADER esm/defs.hpp esm/loadcell.hpp esm/loadfact.hpp esm/loadltex.hpp
|
||||||
esm/loadskil.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/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/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)
|
esm/loadbsgn.hpp esm/loadench.hpp esm/loadlocks.hpp esm/loadscpt.hpp)
|
||||||
|
source_group(esm_header FILES ${ESM_HEADER})
|
||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct Potion
|
||||||
void load(ESMReader &esm)
|
void load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
model = esm.getHNString("MODL");
|
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");
|
script = esm.getHNOString("SCRI");
|
||||||
name = esm.getHNOString("FNAM");
|
name = esm.getHNOString("FNAM");
|
||||||
esm.getHNT(data, "ALDT", 12);
|
esm.getHNT(data, "ALDT", 12);
|
||||||
|
|
|
@ -11,8 +11,9 @@ namespace ESM {
|
||||||
loading process, but are rather loaded later on demand when we are
|
loading process, but are rather loaded later on demand when we are
|
||||||
setting up a specific cell.
|
setting up a specific cell.
|
||||||
*/
|
*/
|
||||||
struct CellRef
|
class CellRef
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
int refnum; // Reference number
|
int refnum; // Reference number
|
||||||
std::string refID; // ID of object being referenced
|
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
|
// items. Also, some times we don't want to merge lists, just
|
||||||
// overwrite. Figure out a way to give the user this option.
|
// 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];
|
LevelItem &li = list[i];
|
||||||
li.id = esm.getHNString(recName);
|
li.id = esm.getHNString(recName);
|
||||||
|
|
|
@ -67,7 +67,7 @@ struct Script
|
||||||
// The tmp buffer is a null-byte separated string list, we
|
// The tmp buffer is a null-byte separated string list, we
|
||||||
// just have to pick out one string at a time.
|
// just have to pick out one string at a time.
|
||||||
char* str = tmp;
|
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);
|
varNames[i] = std::string(str);
|
||||||
str += varNames[i].size()+1;
|
str += varNames[i].size()+1;
|
||||||
|
|
|
@ -66,8 +66,9 @@ namespace ESMS
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A storage struct for one single cell reference.
|
/// A storage struct for one single cell reference.
|
||||||
struct CellStore
|
class CellStore
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
CellStore() : cell (0) {}
|
CellStore() : cell (0) {}
|
||||||
|
|
||||||
const ESM::Cell *cell;
|
const ESM::Cell *cell;
|
||||||
|
|
146
game/engine.cpp
Normal file
146
game/engine.cpp
Normal file
|
@ -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";
|
||||||
|
}
|
||||||
|
|
55
game/engine.hpp
Normal file
55
game/engine.hpp
Normal file
|
@ -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
|
105
game/main.cpp
105
game/main.cpp
|
@ -3,83 +3,17 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include "boost/program_options.hpp"
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
#include "esm_store/cell_store.hpp"
|
#include "engine.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"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
void maintest (std::string dataDir, const std::string& cellName)
|
/// Parse command line options and openmw.cfg file (if one exists). Results are directly
|
||||||
{
|
/// written to \a engine.
|
||||||
assert (!dataDir.empty());
|
/// \return Run OpenMW?
|
||||||
|
|
||||||
if (dataDir[dataDir.size()-1]!='/' && dataDir[dataDir.size()-1]!='\\')
|
bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
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();
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
boost::program_options::options_description desc (
|
boost::program_options::options_description desc (
|
||||||
"Syntax: openmw <options>\nAllowed options");
|
"Syntax: openmw <options>\nAllowed options");
|
||||||
|
@ -90,6 +24,8 @@ int main(int argc, char**argv)
|
||||||
"set data directory")
|
"set data directory")
|
||||||
("start", boost::program_options::value<std::string>()->default_value ("Beshara"),
|
("start", boost::program_options::value<std::string>()->default_value ("Beshara"),
|
||||||
"set initial cell (only interior cells supported at the moment")
|
"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;
|
boost::program_options::variables_map variables;
|
||||||
|
@ -101,6 +37,11 @@ int main(int argc, char**argv)
|
||||||
|
|
||||||
boost::program_options::store(valid_opts, variables);
|
boost::program_options::store(valid_opts, variables);
|
||||||
boost::program_options::notify(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())
|
if (configFile.is_open())
|
||||||
boost::program_options::store (
|
boost::program_options::store (
|
||||||
|
@ -109,10 +50,25 @@ int main(int argc, char**argv)
|
||||||
if (variables.count ("help"))
|
if (variables.count ("help"))
|
||||||
{
|
{
|
||||||
std::cout << desc << std::endl;
|
std::cout << desc << std::endl;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
maintest (variables["data"].as<std::string>(), variables["start"].as<std::string>());
|
try
|
||||||
|
{
|
||||||
|
OMW::Engine engine;
|
||||||
|
|
||||||
|
if (parseOptions (argc, argv, engine))
|
||||||
|
{
|
||||||
|
engine.go();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(exception &e)
|
catch(exception &e)
|
||||||
|
@ -120,5 +76,6 @@ int main(int argc, char**argv)
|
||||||
cout << "\nERROR: " << e.what() << endl;
|
cout << "\nERROR: " << e.what() << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#ifndef _INPUT_MWINPUTMANAGER_H
|
#ifndef _MWINPUT_MWINPUTMANAGER_H
|
||||||
#define _INPUT_MWINPUTMANAGER_H
|
#define _MWINPUT_MWINPUTMANAGER_H
|
||||||
|
|
||||||
#include "input/listener.hpp"
|
#include "input/listener.hpp"
|
||||||
#include "input/dispatcher.hpp"
|
#include "input/dispatcher.hpp"
|
||||||
|
#include "input/poller.hpp"
|
||||||
#include "boost/bind.hpp"
|
#include "boost/bind.hpp"
|
||||||
|
#include "game/mwrender/playerpos.hpp"
|
||||||
|
#include "platform/strings.h"
|
||||||
|
|
||||||
namespace MWInput
|
namespace MWInput
|
||||||
{
|
{
|
||||||
|
@ -11,33 +14,118 @@ namespace MWInput
|
||||||
{
|
{
|
||||||
A_Quit, // Exit the program
|
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
|
A_LAST // Marker for the last item
|
||||||
};
|
};
|
||||||
|
|
||||||
// Class that handles all input and key bindings for OpenMW
|
// 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;
|
Input::Dispatcher disp;
|
||||||
|
Render::OgreRenderer &ogre;
|
||||||
Input::OISManager input;
|
Input::OISManager input;
|
||||||
|
Input::Poller poller;
|
||||||
Input::InputListener listener;
|
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:
|
public:
|
||||||
MWInputManager(Render::OgreRenderer &ogre)
|
MWInputManager(Render::OgreRenderer &_ogre,
|
||||||
|
MWRender::PlayerPos &_player)
|
||||||
: disp(A_LAST),
|
: disp(A_LAST),
|
||||||
input(ogre),
|
ogre(_ogre),
|
||||||
listener(ogre, input, disp)
|
input(_ogre),
|
||||||
|
poller(input),
|
||||||
|
listener(_ogre, input, disp),
|
||||||
|
player(_player),
|
||||||
|
shotCount(0)
|
||||||
{
|
{
|
||||||
using namespace Input;
|
using namespace Input;
|
||||||
using namespace OIS;
|
using namespace OIS;
|
||||||
|
|
||||||
// Bind MW-specific functions
|
// Bind MW-specific functions
|
||||||
disp.funcs.bind(A_Quit,
|
disp.funcs.bind(A_Quit, boost::bind(&InputListener::exitNow, &listener),
|
||||||
boost::bind(&InputListener::exitNow, &listener),
|
|
||||||
"Quit program");
|
"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
|
// Key bindings
|
||||||
disp.bind(KC_Q, A_Quit);
|
disp.bind(KC_Q, A_Quit);
|
||||||
disp.bind(KC_ESCAPE, 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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,13 @@ void insertObj(CellRender& cellRender, const ESMS::LiveCellRef<ESM::Light>& live
|
||||||
|
|
||||||
cellRender.insertMesh ("meshes\\" + model);
|
cellRender.insertMesh ("meshes\\" + model);
|
||||||
|
|
||||||
int color = liveRef.base->data.color;
|
// Extract the color and convert to floating point
|
||||||
|
const int color = liveRef.base->data.color;
|
||||||
cellRender.insertLight(color & 255,
|
const float r = ((color >> 0) & 0xFF) / 255.0f;
|
||||||
(color >> 8) & 255,
|
const float g = ((color >> 8) & 0xFF) / 255.0f;
|
||||||
(color >> 16) & 255,
|
const float b = ((color >> 16) & 0xFF) / 255.0f;
|
||||||
liveRef.base->data.radius);
|
const float radius = float(liveRef.base->data.radius);
|
||||||
|
cellRender.insertLight(r, g, b, radius);
|
||||||
|
|
||||||
cellRender.insertEnd();
|
cellRender.insertEnd();
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ void InteriorCellRender::setAmbientMode()
|
||||||
|
|
||||||
case 1:
|
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;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
|
|
58
game/mwrender/playerpos.hpp
Normal file
58
game/mwrender/playerpos.hpp
Normal file
|
@ -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="")
|
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];
|
FuncBinding &fb = bindings[index];
|
||||||
fb.action = action;
|
fb.action = action;
|
||||||
fb.name = name;
|
fb.name = name;
|
||||||
|
@ -65,7 +66,8 @@ public:
|
||||||
*/
|
*/
|
||||||
void unbind(int index)
|
void unbind(int index)
|
||||||
{
|
{
|
||||||
assert(index >= 0 && index < bindings.size());
|
assert(index >= 0 && index < (int)bindings.size());
|
||||||
|
|
||||||
bindings[index] = FuncBinding();
|
bindings[index] = FuncBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +77,8 @@ public:
|
||||||
*/
|
*/
|
||||||
void call(int index, const void *p=NULL) const
|
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];
|
const FuncBinding &fb = bindings[index];
|
||||||
if(fb.action) fb.action(index, p);
|
if(fb.action) fb.action(index, p);
|
||||||
}
|
}
|
||||||
|
@ -83,14 +86,16 @@ public:
|
||||||
/// Check if a given index is bound to anything
|
/// Check if a given index is bound to anything
|
||||||
bool isBound(int index) const
|
bool isBound(int index) const
|
||||||
{
|
{
|
||||||
assert(index >= 0 && index < bindings.size());
|
assert(index >= 0 && index < (int)bindings.size());
|
||||||
|
|
||||||
return !bindings[index].action.empty();
|
return !bindings[index].action.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the name associated with an action (empty if not bound)
|
/// Return the name associated with an action (empty if not bound)
|
||||||
const std::string &getName(int index) const
|
const std::string &getName(int index) const
|
||||||
{
|
{
|
||||||
assert(index >= 0 && index < bindings.size());
|
assert(index >= 0 && index < (int)bindings.size());
|
||||||
|
|
||||||
return bindings[index].name;
|
return bindings[index].name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,12 +24,17 @@ namespace Input
|
||||||
mMouse = input.mouse;
|
mMouse = input.mouse;
|
||||||
mKeyboard = input.keyboard;
|
mKeyboard = input.keyboard;
|
||||||
|
|
||||||
|
assert(mKeyboard);
|
||||||
|
assert(mWindow);
|
||||||
|
|
||||||
// Add ourself to the managers
|
// Add ourself to the managers
|
||||||
rend.getRoot() -> addFrameListener(this);
|
rend.getRoot() -> addFrameListener(this);
|
||||||
mKeyboard -> setEventCallback(this);
|
mKeyboard -> setEventCallback(this);
|
||||||
mMouse -> setEventCallback(this);
|
mMouse -> setEventCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCamera(Ogre::Camera *cam) { camera = cam; }
|
||||||
|
|
||||||
// Call this to exit the main loop
|
// Call this to exit the main loop
|
||||||
void exitNow() { doExit = true; }
|
void exitNow() { doExit = true; }
|
||||||
|
|
||||||
|
@ -59,6 +64,29 @@ namespace Input
|
||||||
|
|
||||||
bool mouseMoved( const OIS::MouseEvent &arg )
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +104,7 @@ namespace Input
|
||||||
|
|
||||||
const Dispatcher &disp;
|
const Dispatcher &disp;
|
||||||
Ogre::RenderWindow *mWindow;
|
Ogre::RenderWindow *mWindow;
|
||||||
|
Ogre::Camera *camera;
|
||||||
OIS::Mouse *mMouse;
|
OIS::Mouse *mMouse;
|
||||||
OIS::Keyboard *mKeyboard;
|
OIS::Keyboard *mKeyboard;
|
||||||
bool doExit;
|
bool doExit;
|
||||||
|
|
|
@ -30,8 +30,9 @@ OISManager::OISManager(Render::OgreRenderer &rend)
|
||||||
windowHndStr << windowHnd;
|
windowHndStr << windowHnd;
|
||||||
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
|
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
|
||||||
|
|
||||||
// Non-exclusive mouse and keyboard input in debug mode
|
// Non-exclusive mouse and keyboard input in debug mode. Debug mode
|
||||||
if(true)
|
// isn't implemented yet though.
|
||||||
|
if(false)
|
||||||
{
|
{
|
||||||
#if defined OIS_WIN32_PLATFORM
|
#if defined OIS_WIN32_PLATFORM
|
||||||
pl.insert(std::make_pair(std::string("w32_mouse"),
|
pl.insert(std::make_pair(std::string("w32_mouse"),
|
||||||
|
|
51
input/poller.hpp
Normal file
51
input/poller.hpp
Normal file
|
@ -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/node.hpp"
|
||||||
#include "nif/data.hpp"
|
#include "nif/data.hpp"
|
||||||
#include "nif/property.hpp"
|
#include "nif/property.hpp"
|
||||||
|
#include "platform/strings.h"
|
||||||
|
|
||||||
// For warning messages
|
// For warning messages
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// float infinity
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
typedef unsigned char ubyte;
|
typedef unsigned char ubyte;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -58,6 +62,78 @@ static void warn(const string &msg)
|
||||||
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
|
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.
|
// Conversion of blend / test mode from NIF -> OGRE. Not in use yet.
|
||||||
static SceneBlendFactor getBlendFactor(int mode)
|
static SceneBlendFactor getBlendFactor(int mode)
|
||||||
{
|
{
|
||||||
|
@ -177,10 +253,6 @@ static void createMaterial(const String &name,
|
||||||
// make sure that all materials are given unique names.
|
// make sure that all materials are given unique names.
|
||||||
static String getUniqueName(const String &input)
|
static String getUniqueName(const String &input)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
|
||||||
#define snprintf _snprintf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int addon = 0;
|
static int addon = 0;
|
||||||
static char buf[8];
|
static char buf[8];
|
||||||
snprintf(buf, 8, "_%d", addon++);
|
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
|
// Set material if one was given
|
||||||
if(!material.empty()) sub->setMaterialName(material);
|
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!
|
// 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];
|
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
|
// Interpret flags
|
||||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||||
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
||||||
|
@ -462,9 +552,8 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
alphaFlags, alphaTest, texName);
|
alphaFlags, alphaTest, texName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // End of material block, if(!hidden) ...
|
||||||
|
|
||||||
{
|
|
||||||
/* Do in-place transformation of all the vertices and normals. This
|
/* Do in-place transformation of all the vertices and normals. This
|
||||||
is pretty messy stuff, but we need it to make the sub-meshes
|
is pretty messy stuff, but we need it to make the sub-meshes
|
||||||
appear in the correct place. Neither Ogre nor Bullet support
|
appear in the correct place. Neither Ogre nor Bullet support
|
||||||
|
@ -475,6 +564,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
int numVerts = data->vertices.length / 3;
|
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
|
// Rotate, scale and translate all the vertices
|
||||||
const Matrix &rot = shape->trafo->rotation;
|
const Matrix &rot = shape->trafo->rotation;
|
||||||
|
@ -496,13 +586,19 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(!hidden)
|
if(!hidden)
|
||||||
|
{
|
||||||
|
// Add this vertex set to the bounding box
|
||||||
|
bounds.add(optr, numVerts);
|
||||||
|
|
||||||
|
// Create the submesh
|
||||||
createOgreMesh(mesh, shape, material);
|
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
|
// Accumulate the flags from all the child nodes. This works for all
|
||||||
// the flags we currently use, at least.
|
// 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)
|
if(trafo)
|
||||||
{
|
{
|
||||||
// Get a non-const reference to the node's data, since we're
|
// 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);
|
Transformation &final = *((Transformation*)node->trafo);
|
||||||
|
|
||||||
// For both position and rotation we have that:
|
// 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);
|
matrixMul(trafo->rotation, final.rotation);
|
||||||
|
|
||||||
// Scalar values are so nice to deal with. Why can't everything
|
// Scalar values are so nice to deal with. Why can't everything
|
||||||
// just be scalars?
|
// just be scalar?
|
||||||
final.scale *= trafo->scale;
|
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++)
|
for(int i=0; i<n; i++)
|
||||||
{
|
{
|
||||||
if(list.has(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)
|
else if(node->recType == RC_NiTriShape)
|
||||||
// For shapes
|
// For shapes
|
||||||
handleNiTriShape(mesh, (NiTriShape*)node, flags);
|
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NIFLoader::loadResource(Resource *resource)
|
void NIFLoader::loadResource(Resource *resource)
|
||||||
|
@ -588,6 +684,9 @@ void NIFLoader::loadResource(Resource *resource)
|
||||||
return;
|
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
|
// 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
|
// of the early stages of development. Right now we WANT to catch
|
||||||
// every error as early and intrusively as possible, as it's most
|
// 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);
|
Record *r = nif.getRecord(0);
|
||||||
assert(r != NULL);
|
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.");
|
r->recName.toString() + ". Skipping file.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the node
|
// 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.
|
// Finally, set the bounding value.
|
||||||
mesh->_setBounds(AxisAlignedBox(-10,-10,-10,10,10,10));
|
if(bounds.isValid())
|
||||||
mesh->_setBoundingSphereRadius(10);
|
{
|
||||||
|
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,
|
MeshPtr NIFLoader::load(const std::string &name,
|
||||||
|
@ -632,3 +737,109 @@ MeshPtr NIFLoader::load(const std::string &name,
|
||||||
// Nope, create a new one.
|
// Nope, create a new one.
|
||||||
return MeshManager::getSingleton().createManual(name, group, &g_sing);
|
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;
|
mRoot = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OgreRenderer::screenshot(const std::string &file)
|
||||||
|
{
|
||||||
|
mWindow->writeContentsToFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
bool OgreRenderer::configure(bool showConfig,
|
bool OgreRenderer::configure(bool showConfig,
|
||||||
const std::string &pluginCfg,
|
const std::string &pluginCfg,
|
||||||
bool _logging)
|
bool _logging)
|
||||||
|
|
|
@ -42,6 +42,9 @@ namespace Render
|
||||||
/// Start the main rendering loop
|
/// Start the main rendering loop
|
||||||
void start() { mRoot->startRendering(); }
|
void start() { mRoot->startRendering(); }
|
||||||
|
|
||||||
|
/// Write a screenshot to file
|
||||||
|
void screenshot(const std::string &file);
|
||||||
|
|
||||||
/// Get the Root
|
/// Get the Root
|
||||||
Ogre::Root *getRoot() { return mRoot; }
|
Ogre::Root *getRoot() { return mRoot; }
|
||||||
|
|
||||||
|
|
|
@ -57,11 +57,6 @@ import input.ois;
|
||||||
bool pause = false;
|
bool pause = false;
|
||||||
int *guiMode;
|
int *guiMode;
|
||||||
|
|
||||||
void toggleFullscreen()
|
|
||||||
{
|
|
||||||
ogre_toggleFullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
const float volDiff = 0.05;
|
const float volDiff = 0.05;
|
||||||
|
|
||||||
void musVolume(bool increase)
|
void musVolume(bool increase)
|
||||||
|
@ -88,16 +83,6 @@ void mainVolume(bool increase)
|
||||||
writefln(increase?"Increasing":"Decreasing", " main volume to ", config.getMainVolume);
|
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()
|
void updateMouseSensitivity()
|
||||||
{
|
{
|
||||||
effMX = *config.mouseSensX;
|
effMX = *config.mouseSensX;
|
||||||
|
@ -112,19 +97,6 @@ void togglePause()
|
||||||
else writefln("Pause off");
|
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)
|
extern(C) void d_handleMouseButton(MouseState *state, int button)
|
||||||
{
|
{
|
||||||
debug(printMouse)
|
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
|
// Frame listener, passed to Ogre. The only thing we use this for is
|
||||||
// to capture input and pass control to D code.
|
// to capture input and pass control to D code.
|
||||||
class MorroFrameListener: public FrameListener
|
class MorroFrameListener: public FrameListener
|
||||||
|
@ -128,47 +92,6 @@ public:
|
||||||
MorroFrameListener mFrameListener;
|
MorroFrameListener mFrameListener;
|
||||||
InputListener mInput;
|
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'
|
// Get current camera orientation, in the form of 'front' and 'up'
|
||||||
// vectors.
|
// vectors.
|
||||||
extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz,
|
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
|
// is not affected by the rotation of the root node, so we must
|
||||||
// transform this manually.
|
// transform this manually.
|
||||||
mCamera->setPosition(Vector3(x,z+90,-y));
|
mCamera->setPosition(Vector3(x,z+90,-y));
|
||||||
|
|
||||||
//g_light->setPosition(mCamera->getPosition());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate camera using Morrowind rotation specifiers
|
// 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
|
// Rotates first around z, then y, then x
|
||||||
mCamera->setOrientation(xr*yr*zr);
|
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
|
// Copy a scene node and all its children
|
||||||
void cloneNode(SceneNode *from, SceneNode *to, char* name)
|
void cloneNode(SceneNode *from, SceneNode *to, char* name)
|
||||||
{
|
{
|
||||||
|
@ -185,246 +92,6 @@ extern "C" void ogre_createWater(float level)
|
||||||
ent->setCastShadows(false);
|
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()
|
extern "C" SceneNode *ogre_getDetachedNode()
|
||||||
{
|
{
|
||||||
SceneNode *node = mwRoot->createChildSceneNode();
|
SceneNode *node = mwRoot->createChildSceneNode();
|
||||||
|
@ -466,54 +133,3 @@ extern "C" SceneNode* ogre_createNode(
|
||||||
|
|
||||||
return node;
|
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 <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
|
// The global GUI object
|
||||||
MyGUI::Gui *mGUI;
|
MyGUI::Gui *mGUI;
|
||||||
|
|
||||||
|
@ -51,11 +8,5 @@ MyGUI::Gui *mGUI;
|
||||||
// input into MyGUI.
|
// input into MyGUI.
|
||||||
int32_t guiMode = 0;
|
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 "../gui/cpp_mygui.cpp"
|
||||||
#include "cpp_framelistener.cpp"
|
|
||||||
#include "cpp_bsaarchive.cpp"
|
|
||||||
#include "cpp_interface.cpp"
|
|
||||||
#include "../terrain/cpp_terrain.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;
|
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
|
// Gives the placement of an item in the scene (position and
|
||||||
// orientation). It must have this exact structure since we also use
|
// orientation). It must have this exact structure since we also use
|
||||||
// it when reading ES files.
|
// it when reading ES files.
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
#ifndef _STRINGS_WRAPPER_H
|
#ifndef _STRINGS_WRAPPER_H
|
||||||
#define _STRINGS_WRAPPER_H
|
#define _STRINGS_WRAPPER_H
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
#pragma warning(disable: 4996)
|
#pragma warning(disable: 4996)
|
||||||
|
|
||||||
#define strcasecmp stricmp
|
#define strcasecmp stricmp
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue