diff --git a/.gitignore b/.gitignore index ada874bb22..8e8f6449ce 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ Docs/mainpage.hpp CMakeFiles */CMakeFiles CMakeCache.txt +Makefile +makefile +data diff --git a/apps/esmtool/Makefile b/apps/esmtool/Makefile deleted file mode 100644 index ee92cf4c84..0000000000 --- a/apps/esmtool/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -esmtool_cmd.c: esmtool.ggo - gengetopt < esmtool.ggo - -clean: - rm esmtool_cmd.c esmtool_cmd.h diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index bbee7498ab..359a7b4e79 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -1,22 +1,20 @@ -project(OpenMW) # config file - -configure_file ("${OpenMW_SOURCE_DIR}/config.hpp.cmake" "${OpenMW_SOURCE_DIR}/config.hpp") +configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp") # local files - set(GAME main.cpp engine.cpp - ) +) set(GAME_HEADER engine.hpp - config.hpp) + config.hpp +) source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender - renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface + renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface ) add_openmw_dir (mwinput @@ -25,7 +23,7 @@ add_openmw_dir (mwinput add_openmw_dir (mwgui layouts text_input widgets race class birth review window_manager console dialogue - dialogue_history window_base stats_window messagebox journalwindow + dialogue_history window_base stats_window messagebox journalwindow charactercreation ) add_openmw_dir (mwdialogue @@ -46,7 +44,7 @@ add_openmw_dir (mwsound add_openmw_dir (mwworld refdata world physicssystem scene environment globals class action nullaction actionteleport containerstore actiontalk actiontake containerstore manualref containerutil player cellfunctors - cells localscripts + cells localscripts customdata ) add_openmw_dir (mwclass @@ -61,13 +59,11 @@ add_openmw_dir (mwmechanics # Main executable add_executable(openmw ${OPENMW_LIBS} ${OPENMW_LIBS_HEADER} - ${CONPONENT_FILES} + ${COMPONENT_FILES} ${OPENMW_FILES} ${GAME} ${GAME_HEADER} ${APPLE_BUNDLE_RESOURCES} - ) - -target_link_libraries (openmw components) +) # Sound stuff - here so CMake doesn't stupidly recompile EVERYTHING # when we change the backend. @@ -75,26 +71,27 @@ include_directories(${SOUND_INPUT_INCLUDES} ${BULLET_INCLUDE_DIRS}) add_definitions(${SOUND_DEFINE}) target_link_libraries(openmw - ${OGRE_LIBRARIES} - ${OIS_LIBRARIES} - ${Boost_LIBRARIES} - ${OPENAL_LIBRARY} - ${SOUND_INPUT_LIBRARY} - ${BULLET_LIBRARIES} - caelum - MyGUIEngine - MyGUIOgrePlatform + ${OGRE_LIBRARIES} + ${OIS_LIBRARIES} + ${Boost_LIBRARIES} + ${OPENAL_LIBRARY} + ${SOUND_INPUT_LIBRARY} + ${BULLET_LIBRARIES} + caelum + components + MyGUIEngine + MyGUIOgrePlatform ) -if (APPLE) +if(APPLE) find_library(CARBON_FRAMEWORK Carbon) target_link_libraries(openmw ${CARBON_FRAMEWORK}) install(TARGETS openmw BUNDLE DESTINATION . RUNTIME DESTINATION ../MacOS COMPONENT Runtime) -endif (APPLE) +endif(APPLE) if(DPKG_PROGRAM) INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw) -endif() +endif(DPKG_PROGRAM) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e1a16accf6..c8dca2d3aa 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -35,8 +35,6 @@ #include "mwsound/soundmanager.hpp" #include "mwworld/world.hpp" -#include "mwworld/ptr.hpp" -#include "mwworld/environment.hpp" #include "mwworld/class.hpp" #include "mwworld/player.hpp" @@ -147,7 +145,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) mEnvironment.mWorld->advanceTime ( mEnvironment.mFrameDuration*mEnvironment.mWorld->getTimeScaleFactor()/3600); - + if (changed) // keep change flag for another frame, if cell changed happend in local script mEnvironment.mWorld->markCellAsUnchanged(); @@ -158,6 +156,9 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) if (mEnvironment.mWindowManager->getMode()==MWGui::GM_Game) mEnvironment.mWorld->doPhysics (movement, mEnvironment.mFrameDuration); + // update world + mEnvironment.mWorld->update (evt.timeSinceLastFrame); + // report focus object (for debugging) if (mReportFocus) updateFocusReport (mEnvironment.mFrameDuration); @@ -172,7 +173,6 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt) OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) : mOgre (0) - , mPhysicEngine (0) , mFpsLevel(0) , mDebug (false) , mVerboseScripts (false) @@ -183,7 +183,6 @@ OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) , mFocusTDiff (0) , mScriptManager (0) , mScriptContext (0) - , mGuiManager (0) , mFSStrict (false) , mCfgMgr(configurationManager) { @@ -193,7 +192,6 @@ OMW::Engine::Engine(Cfg::ConfigurationManager& configurationManager) OMW::Engine::~Engine() { - delete mGuiManager; delete mEnvironment.mWorld; delete mEnvironment.mSoundManager; delete mEnvironment.mGlobalScripts; @@ -202,7 +200,6 @@ OMW::Engine::~Engine() delete mEnvironment.mJournal; delete mScriptManager; delete mScriptContext; - delete mPhysicEngine; delete mOgre; } @@ -330,26 +327,15 @@ void OMW::Engine::go() loadBSA(); - /// \todo move this into the physics manager - // Create physics. shapeLoader is deleted by the physic engine - NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); - mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader); - // Create the world - mEnvironment.mWorld = new MWWorld::World (*mOgre, mPhysicEngine, mFileCollections, mMaster, + mEnvironment.mWorld = new MWWorld::World (*mOgre, mFileCollections, mMaster, mResDir, mNewGame, mEnvironment, mEncoding); - /// \todo move this into the GUI manager (a.k.a WindowManager) - // Set up the GUI system - mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, - mCfgMgr.getLogPath().string() + std::string("/")); - - // Create window manager - this manages all the MW-specific GUI windows MWScript::registerExtensions (mExtensions); - mEnvironment.mWindowManager = new MWGui::WindowManager(mGuiManager->getGui(), mEnvironment, - mExtensions, mFpsLevel, mNewGame); + mEnvironment.mWindowManager = new MWGui::WindowManager(mEnvironment, + mExtensions, mFpsLevel, mNewGame, mOgre, mCfgMgr.getLogPath().string() + std::string("/")); // Create sound system mEnvironment.mSoundManager = new MWSound::SoundManager(mOgre->getRoot(), diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 02d108f0ab..b224df7d15 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -7,8 +7,6 @@ #include -#include - #include #include #include @@ -63,7 +61,6 @@ namespace OMW boost::filesystem::path mDataDir; boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; - OEngine::Physic::PhysicEngine* mPhysicEngine; std::string mCellName; std::string mMaster; int mFpsLevel; @@ -80,7 +77,7 @@ namespace OMW MWScript::ScriptManager *mScriptManager; Compiler::Extensions mExtensions; Compiler::Context *mScriptContext; - OEngine::GUI::MyGUIManager *mGuiManager; + Files::Collections mFileCollections; bool mFSStrict; diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 30b308e70f..cc30c6955c 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/apparatus.hpp b/apps/openmw/mwclass/apparatus.hpp index 4c8a2c0e2d..9a5a9b9558 100644 --- a/apps/openmw/mwclass/apparatus.hpp +++ b/apps/openmw/mwclass/apparatus.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_APPARATUS_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index a8a431acf3..9a09360270 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index c5f9812b79..123713a38b 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_ARMOR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 011fd2c320..8c5be4793b 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/book.hpp b/apps/openmw/mwclass/book.hpp index f0e38cceba..6e38ea0d3d 100644 --- a/apps/openmw/mwclass/book.hpp +++ b/apps/openmw/mwclass/book.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_BOOK_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 0214c72adf..3826c6c40a 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 76c2c4a3e4..353f7f606c 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_CLOTHING_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 4157ce17ad..13a6241d48 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -6,9 +6,41 @@ #include #include "../mwworld/ptr.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/customdata.hpp" + +#include "../mwrender/objects.hpp" + +namespace +{ + struct CustomData : public MWWorld::CustomData + { + MWWorld::ContainerStore mContainerStore; + + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} namespace MWClass { + void Container::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { ESMS::LiveCellRef *ref = @@ -16,7 +48,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -50,17 +82,9 @@ namespace MWClass MWWorld::ContainerStore& Container::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content - - ptr.getRefData().getContainerStore() = store; - } - - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Container::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/container.hpp b/apps/openmw/mwclass/container.hpp index 01763870ad..78552ffe90 100644 --- a/apps/openmw/mwclass/container.hpp +++ b/apps/openmw/mwclass/container.hpp @@ -2,12 +2,13 @@ #define GAME_MWCLASS_CONTAINER_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { class Container : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 5aa203a49a..12a56e0ee6 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -4,16 +4,62 @@ #include #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" +#include "../mwworld/customdata.hpp" +#include "../mwworld/containerstore.hpp" +namespace +{ + struct CustomData : public MWWorld::CustomData + { + MWMechanics::CreatureStats mCreatureStats; + MWWorld::ContainerStore mContainerStore; -#include "../mwmechanics/mechanicsmanager.hpp" + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } +} namespace MWClass { + void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + ESMS::LiveCellRef *ref = ptr.get(); + + // creature stats + data->mCreatureStats.mAttributes[0].set (ref->base->data.strength); + data->mCreatureStats.mAttributes[1].set (ref->base->data.intelligence); + data->mCreatureStats.mAttributes[2].set (ref->base->data.willpower); + data->mCreatureStats.mAttributes[3].set (ref->base->data.agility); + data->mCreatureStats.mAttributes[4].set (ref->base->data.speed); + data->mCreatureStats.mAttributes[5].set (ref->base->data.endurance); + data->mCreatureStats.mAttributes[6].set (ref->base->data.personality); + data->mCreatureStats.mAttributes[7].set (ref->base->data.luck); + data->mCreatureStats.mDynamic[0].set (ref->base->data.health); + data->mCreatureStats.mDynamic[1].set (ref->base->data.mana); + data->mCreatureStats.mDynamic[2].set (ref->base->data.fatigue); + + data->mCreatureStats.mLevel = ref->base->data.level; + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + std::string Creature::getId (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -24,18 +70,8 @@ namespace MWClass void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { - - /*ESMS::LiveCellRef *ref = - ptr.get(); - - assert (ref->base != NULL); - const std::string &model = ref->base->model; - - if (!model.empty()) - {*/ - MWRender::Actors& actors = renderingInterface.getActors(); - actors.insertCreature(ptr); - + MWRender::Actors& actors = renderingInterface.getActors(); + actors.insertCreature(ptr); } void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWWorld::Environment& environment) const @@ -49,7 +85,6 @@ namespace MWClass if(!model.empty()){ physics.insertActorPhysics(ptr, "meshes\\" + model); } - } void Creature::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const @@ -72,31 +107,9 @@ namespace MWClass MWMechanics::CreatureStats& Creature::getCreatureStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getCreatureStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::CreatureStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - stats->mAttributes[0].set (ref->base->data.strength); - stats->mAttributes[1].set (ref->base->data.intelligence); - stats->mAttributes[2].set (ref->base->data.willpower); - stats->mAttributes[3].set (ref->base->data.agility); - stats->mAttributes[4].set (ref->base->data.speed); - stats->mAttributes[5].set (ref->base->data.endurance); - stats->mAttributes[6].set (ref->base->data.personality); - stats->mAttributes[7].set (ref->base->data.luck); - stats->mDynamic[0].set (ref->base->data.health); - stats->mDynamic[1].set (ref->base->data.mana); - stats->mDynamic[2].set (ref->base->data.fatigue); - - stats->mLevel = ref->base->data.level; - - ptr.getRefData().getCreatureStats() = stats; - } - - return *ptr.getRefData().getCreatureStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } boost::shared_ptr Creature::activate (const MWWorld::Ptr& ptr, @@ -108,17 +121,9 @@ namespace MWClass MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content - - ptr.getRefData().getContainerStore() = store; - } - - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Creature::getScript (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index b7b654bc01..9afeffea82 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -10,6 +10,8 @@ namespace MWClass { class Creature : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 26436a0128..d0b3b11a2f 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -14,8 +14,6 @@ #include "../mwrender/objects.hpp" -#include - namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -25,7 +23,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -39,13 +37,11 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } std::string Door::getName (const MWWorld::Ptr& ptr) const @@ -86,7 +82,7 @@ namespace MWClass } else { - // another NPC or a create is using the door + // another NPC or a creature is using the door // TODO return action for teleporting other NPC/creature return boost::shared_ptr (new MWWorld::NullAction); } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index c230cf3576..aecb4224cc 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_DOOR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 5e55010ebf..ac1076cc1b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); @@ -34,14 +35,11 @@ namespace MWClass ESMS::LiveCellRef *ref = ptr.get(); - - const std::string &model = ref->base->model; assert (ref->base != NULL); if(!model.empty()){ physics.insertObjectPhysics(ptr, "meshes\\" + model); } - } std::string Ingredient::getName (const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/ingredient.hpp b/apps/openmw/mwclass/ingredient.hpp index 47bd1a9e5c..6c74096659 100644 --- a/apps/openmw/mwclass/ingredient.hpp +++ b/apps/openmw/mwclass/ingredient.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_INGREDIENT_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 3890899b0e..2c50472aca 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -12,6 +12,8 @@ #include "../mwsound/soundmanager.hpp" +#include "../mwrender/objects.hpp" + #include "containerutil.hpp" namespace MWClass @@ -23,7 +25,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 34421ff513..7df82ae5db 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_LIGHT_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index 636a8f0be5..67756f23cf 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -8,6 +8,8 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" + #include "containerutil.hpp" namespace MWClass @@ -19,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/lockpick.hpp b/apps/openmw/mwclass/lockpick.hpp index c5f1539b4c..074a07c936 100644 --- a/apps/openmw/mwclass/lockpick.hpp +++ b/apps/openmw/mwclass/lockpick.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_LOCKPICK_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index a2642d8d5b..81e018d966 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -8,6 +8,8 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" + #include "containerutil.hpp" namespace MWClass @@ -19,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/misc.hpp b/apps/openmw/mwclass/misc.hpp index 36ee2c1b26..cb18583156 100644 --- a/apps/openmw/mwclass/misc.hpp +++ b/apps/openmw/mwclass/misc.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_MISC_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 2b98a3d604..d94920041f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1,27 +1,88 @@ #include "npc.hpp" +#include + +#include + #include #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/movement.hpp" +#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/actiontalk.hpp" #include "../mwworld/environment.hpp" #include "../mwworld/world.hpp" - -#include "../mwmechanics/mechanicsmanager.hpp" -#include +#include "../mwworld/containerstore.hpp" +#include "../mwworld/customdata.hpp" namespace { const Ogre::Radian kOgrePi (Ogre::Math::PI); const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0)); + + struct CustomData : public MWWorld::CustomData + { + MWMechanics::NpcStats mNpcStats; + MWMechanics::CreatureStats mCreatureStats; + MWMechanics::Movement mMovement; + MWWorld::ContainerStore mContainerStore; + + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *CustomData::clone() const + { + return new CustomData (*this); + } } namespace MWClass { + void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data (new CustomData); + + ESMS::LiveCellRef *ref = ptr.get(); + + // NPC stats + if (!ref->base->faction.empty()) + { + // TODO research how initial rank is stored. The information in loadnpc.hpp are at + // best very unclear. + data->mNpcStats.mFactionRank[ref->base->faction] = 0; + } + + for (int i=0; i<27; ++i) + data->mNpcStats.mSkill[i].setBase (ref->base->npdt52.skills[i]); + + // creature stats + data->mCreatureStats.mAttributes[0].set (ref->base->npdt52.strength); + data->mCreatureStats.mAttributes[1].set (ref->base->npdt52.intelligence); + data->mCreatureStats.mAttributes[2].set (ref->base->npdt52.willpower); + data->mCreatureStats.mAttributes[3].set (ref->base->npdt52.agility); + data->mCreatureStats.mAttributes[4].set (ref->base->npdt52.speed); + data->mCreatureStats.mAttributes[5].set (ref->base->npdt52.endurance); + data->mCreatureStats.mAttributes[6].set (ref->base->npdt52.personality); + data->mCreatureStats.mAttributes[7].set (ref->base->npdt52.luck); + data->mCreatureStats.mDynamic[0].set (ref->base->npdt52.health); + data->mCreatureStats.mDynamic[1].set (ref->base->npdt52.mana); + data->mCreatureStats.mDynamic[2].set (ref->base->npdt52.fatigue); + + data->mCreatureStats.mLevel = ref->base->npdt52.level; + + // \todo add initial container content + + // store + ptr.getRefData().setCustomData (data.release()); + } + } + std::string Npc::getId (const MWWorld::Ptr& ptr) const { ESMS::LiveCellRef *ref = @@ -77,56 +138,16 @@ namespace MWClass MWMechanics::CreatureStats& Npc::getCreatureStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getCreatureStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::CreatureStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - stats->mAttributes[0].set (ref->base->npdt52.strength); - stats->mAttributes[1].set (ref->base->npdt52.intelligence); - stats->mAttributes[2].set (ref->base->npdt52.willpower); - stats->mAttributes[3].set (ref->base->npdt52.agility); - stats->mAttributes[4].set (ref->base->npdt52.speed); - stats->mAttributes[5].set (ref->base->npdt52.endurance); - stats->mAttributes[6].set (ref->base->npdt52.personality); - stats->mAttributes[7].set (ref->base->npdt52.luck); - stats->mDynamic[0].set (ref->base->npdt52.health); - stats->mDynamic[1].set (ref->base->npdt52.mana); - stats->mDynamic[2].set (ref->base->npdt52.fatigue); - - stats->mLevel = ref->base->npdt52.level; - - ptr.getRefData().getCreatureStats() = stats; - } - - return *ptr.getRefData().getCreatureStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mCreatureStats; } MWMechanics::NpcStats& Npc::getNpcStats (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getNpcStats().get()) - { - boost::shared_ptr stats ( - new MWMechanics::NpcStats); + ensureCustomData (ptr); - ESMS::LiveCellRef *ref = ptr.get(); - - if (!ref->base->faction.empty()) - { - // TODO research how initial rank is stored. The information in loadnpc.hpp are at - // best very unclear. - stats->mFactionRank[ref->base->faction] = 0; - } - - for (int i=0; i<27; ++i) - stats->mSkill[i].setBase (ref->base->npdt52.skills[i]); - - ptr.getRefData().getNpcStats() = stats; - } - - return *ptr.getRefData().getNpcStats(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mNpcStats; } boost::shared_ptr Npc::activate (const MWWorld::Ptr& ptr, @@ -138,17 +159,9 @@ namespace MWClass MWWorld::ContainerStore& Npc::getContainerStore (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getContainerStore().get()) - { - boost::shared_ptr > store ( - new MWWorld::ContainerStore); + ensureCustomData (ptr); - // TODO add initial content - - ptr.getRefData().getContainerStore() = store; - } - - return *ptr.getRefData().getContainerStore(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore; } std::string Npc::getScript (const MWWorld::Ptr& ptr) const @@ -239,29 +252,20 @@ namespace MWClass MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { - if (!ptr.getRefData().getMovement().get()) - { - boost::shared_ptr movement ( - new MWMechanics::Movement); + ensureCustomData (ptr); - ptr.getRefData().getMovement() = movement; - } - - return *ptr.getRefData().getMovement(); + return dynamic_cast (*ptr.getRefData().getCustomData()).mMovement; } Ogre::Vector3 Npc::getMovementVector (const MWWorld::Ptr& ptr) const { Ogre::Vector3 vector (0, 0, 0); - if (ptr.getRefData().getMovement().get()) - { - vector.x = - ptr.getRefData().getMovement()->mLeftRight * 200; - vector.y = ptr.getRefData().getMovement()->mForwardBackward * 200; + vector.x = - getMovementSettings (ptr).mLeftRight * 200; + vector.y = getMovementSettings (ptr).mForwardBackward * 200; - if (getStance (ptr, Run, false)) - vector *= 2; - } + if (getStance (ptr, Run, false)) + vector *= 2; return vector; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index cc9dbef7fa..bb9b131c96 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -3,11 +3,12 @@ #include "../mwworld/class.hpp" - namespace MWClass { class Npc : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual std::string getId (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 86d1e2a985..08047a2e8e 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/potion.hpp b/apps/openmw/mwclass/potion.hpp index 85678121fb..e1a54db3c7 100644 --- a/apps/openmw/mwclass/potion.hpp +++ b/apps/openmw/mwclass/potion.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_POTION_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/probe.hpp b/apps/openmw/mwclass/probe.hpp index d7b9df7385..287dd04755 100644 --- a/apps/openmw/mwclass/probe.hpp +++ b/apps/openmw/mwclass/probe.hpp @@ -3,7 +3,6 @@ #include "../mwworld/class.hpp" - namespace MWClass { class Probe : public MWWorld::Class diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index f8755b2ebc..79c18d4263 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -8,6 +8,8 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" + #include "containerutil.hpp" namespace MWClass @@ -19,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/repair.hpp b/apps/openmw/mwclass/repair.hpp index 1e0ea51785..174197d9a9 100644 --- a/apps/openmw/mwclass/repair.hpp +++ b/apps/openmw/mwclass/repair.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_REPAIR_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 946da311da..fbd96182f9 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -5,6 +5,7 @@ #include "../mwworld/ptr.hpp" +#include "../mwrender/objects.hpp" namespace MWClass { @@ -15,7 +16,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/static.hpp b/apps/openmw/mwclass/static.hpp index be3fdb1804..a4b1d8c547 100644 --- a/apps/openmw/mwclass/static.hpp +++ b/apps/openmw/mwclass/static.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_STATIC_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 1fbd21f7cf..f136aaccda 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -8,6 +8,7 @@ #include "../mwworld/ptr.hpp" #include "../mwworld/actiontake.hpp" +#include "../mwrender/objects.hpp" #include "containerutil.hpp" @@ -20,7 +21,7 @@ namespace MWClass assert (ref->base != NULL); const std::string &model = ref->base->model; - + if (!model.empty()) { MWRender::Objects& objects = renderingInterface.getObjects(); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 79bc4d4dec..84c633ab31 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -2,7 +2,6 @@ #define GAME_MWCLASS_WEAPON_H #include "../mwworld/class.hpp" -#include "../mwrender/objects.hpp" namespace MWClass { diff --git a/apps/openmw/mwdialogue/dialoguemanager.cpp b/apps/openmw/mwdialogue/dialoguemanager.cpp index 2aea3fcc38..9c099f7dbc 100644 --- a/apps/openmw/mwdialogue/dialoguemanager.cpp +++ b/apps/openmw/mwdialogue/dialoguemanager.cpp @@ -209,7 +209,7 @@ namespace MWDialogue for(std::deque::const_iterator it = mEnvironment.mJournal->begin();it!=mEnvironment.mJournal->end();it++) { - if(it->mTopic == name) isInJournal = true; + if(it->mTopic == name) isInJournal = true; } } else @@ -239,7 +239,7 @@ namespace MWDialogue { ESMS::LiveCellRef* npc = actor.get(); int isFaction = int(toLower(npc->base->faction) == toLower(name)); - if(selectCompare(comp,!isFaction,select.i)) + if(selectCompare(comp,!isFaction,select.i)) return false; } else @@ -253,7 +253,7 @@ namespace MWDialogue { ESMS::LiveCellRef* npc = actor.get(); int isClass = int(toLower(npc->base->cls) == toLower(name)); - if(selectCompare(comp,!isClass,select.i)) + if(selectCompare(comp,!isClass,select.i)) return false; } else @@ -268,7 +268,7 @@ namespace MWDialogue ESMS::LiveCellRef* npc = actor.get(); int isRace = int(toLower(npc->base->race) == toLower(name)); //std::cout << "isRace"<(comp,!isRace,select.i)) + if(selectCompare(comp,!isRace,select.i)) return false; } else @@ -462,7 +462,7 @@ namespace MWDialogue for(ESMS::RecListT::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++) { ESM::Dialogue ndialogue = it->second; - if(ndialogue.type == ESM::Dialogue::Type::Topic) + if(ndialogue.type == ESM::Dialogue::Topic) { for (std::vector::const_iterator iter (it->second.mInfo.begin()); iter!=it->second.mInfo.end(); ++iter) @@ -488,7 +488,7 @@ namespace MWDialogue for(ESMS::RecListT::MapType::iterator it = dialogueList.begin(); it!=dialogueList.end();it++) { ESM::Dialogue ndialogue = it->second; - if(ndialogue.type == ESM::Dialogue::Type::Greeting) + if(ndialogue.type == ESM::Dialogue::Greeting) { if (greetingFound) break; for (std::vector::const_iterator iter (it->second.mInfo.begin()); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp new file mode 100644 index 0000000000..1cb0593e73 --- /dev/null +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -0,0 +1,629 @@ +#include "charactercreation.hpp" + +#include "text_input.hpp" +#include "race.hpp" +#include "class.hpp" +#include "birth.hpp" +#include "review.hpp" +#include "dialogue.hpp" +#include "mode.hpp" + +namespace +{ + struct Step + { + const char* mText; + const char* mButtons[3]; + ESM::Class::Specialization mSpecializations[3]; // The specialization for each answer + }; + + static boost::array sGenerateClassSteps = { { + // Question 1 + {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", + {"Draw your dagger, mercifully endings its life with a single thrust.", + "Use herbs from your pack to put it to sleep.", + "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 2 + {"One Summer afternoon your father gives you a choice of chores.", + {"Work in the forge with him casting iron for a new plow.", + "Gather herbs for your mother who is preparing dinner.", + "Go catch fish at the stream using a net and line."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 3 + {"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.", + {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", + "Make up a story that makes your nickname a badge of honor instead of something humiliating.", + "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 4 + {"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.", + {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", + "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", + "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 5 + {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", + {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", + "Decide to put the extra money to good use and purchase items that would help your family?", + "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 6 + {"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.", + {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", + "Leave the bag there, knowing that it is better not to get involved.", + "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 7 + {"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.", + {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", + "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", + "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 8 + {"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.", + {"Position yourself between the pipe and your mother.", + "Grab the hot pipe and try to push it away.", + "Push your mother out of the way."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 9 + {"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.", + {"Drop the sweetroll and step on it, then get ready for the fight.", + "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", + "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + }, + // Question 10 + {"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.", + {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", + "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", + "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, + {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} + } + } }; +} + +using namespace MWGui; + +CharacterCreation::CharacterCreation(WindowManager* _wm, MWWorld::Environment* _environment) + : mNameDialog(0) + , mRaceDialog(0) + , mDialogueWindow(0) + , mClassChoiceDialog(0) + , mGenerateClassQuestionDialog(0) + , mGenerateClassResultDialog(0) + , mPickClassDialog(0) + , mCreateClassDialog(0) + , mBirthSignDialog(0) + , mReviewDialog(0) + , mWM(_wm) + , mEnvironment(_environment) +{ + mCreationStage = CSE_NotStarted; +} + +void CharacterCreation::spawnDialog(const char id) +{ + switch (id) + { + case GM_Name: + if(mNameDialog) + mWM->removeDialog(mNameDialog); + mNameDialog = new TextInputDialog(*mWM); + mNameDialog->setTextLabel(mWM->getGameSettingString("sName", "Name")); + mNameDialog->setTextInput(mPlayerName); + mNameDialog->setNextButtonShow(mCreationStage >= CSE_NameChosen); + mNameDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onNameDialogDone); + mNameDialog->open(); + break; + + case GM_Race: + if (mRaceDialog) + mWM->removeDialog(mRaceDialog); + mRaceDialog = new RaceDialog(*mWM); + mRaceDialog->setNextButtonShow(mCreationStage >= CSE_RaceChosen); + mRaceDialog->setRaceId(mPlayerRaceId); + mRaceDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogDone); + mRaceDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onRaceDialogBack); + mRaceDialog->open(); + break; + + case GM_Class: + if (mClassChoiceDialog) + mWM->removeDialog(mClassChoiceDialog); + mClassChoiceDialog = new ClassChoiceDialog(*mWM); + mClassChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &CharacterCreation::onClassChoice); + mClassChoiceDialog->open(); + break; + + case GM_ClassPick: + if (mPickClassDialog) + mWM->removeDialog(mPickClassDialog); + mPickClassDialog = new PickClassDialog(*mWM); + mPickClassDialog->setNextButtonShow(mCreationStage >= CSE_ClassChosen); + mPickClassDialog->setClassId(mPlayerClass.name); + mPickClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogDone); + mPickClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onPickClassDialogBack); + mPickClassDialog->open(); + break; + + case GM_Birth: + if (mBirthSignDialog) + mWM->removeDialog(mBirthSignDialog); + mBirthSignDialog = new BirthDialog(*mWM); + mBirthSignDialog->setNextButtonShow(mCreationStage >= CSE_BirthSignChosen); + mBirthSignDialog->setBirthId(mPlayerBirthSignId); + mBirthSignDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogDone); + mBirthSignDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onBirthSignDialogBack); + mBirthSignDialog->open(); + break; + + case GM_ClassCreate: + if (mCreateClassDialog) + mWM->removeDialog(mCreateClassDialog); + mCreateClassDialog = new CreateClassDialog(*mWM); + mCreateClassDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogDone); + mCreateClassDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onCreateClassDialogBack); + mCreateClassDialog->open(); + break; + case GM_ClassGenerate: + mGenerateClassStep = 0; + mGenerateClass = ""; + mGenerateClassSpecializations[0] = 0; + mGenerateClassSpecializations[1] = 0; + mGenerateClassSpecializations[2] = 0; + showClassQuestionDialog(); + break; + case GM_Review: + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + mReviewDialog = new ReviewDialog(*mWM); + mReviewDialog->setPlayerName(mPlayerName); + mReviewDialog->setRace(mPlayerRaceId); + mReviewDialog->setClass(mPlayerClass); + mReviewDialog->setBirthSign(mPlayerBirthSignId); + + mReviewDialog->setHealth(mPlayerHealth); + mReviewDialog->setMagicka(mPlayerMagicka); + mReviewDialog->setFatigue(mPlayerFatigue); + + { + std::map >::iterator end = mPlayerAttributes.end(); + for (std::map >::iterator it = mPlayerAttributes.begin(); it != end; ++it) + { + mReviewDialog->setAttribute(it->first, it->second); + } + } + + { + std::map >::iterator end = mPlayerSkillValues.end(); + for (std::map >::iterator it = mPlayerSkillValues.begin(); it != end; ++it) + { + mReviewDialog->setSkillValue(it->first, it->second); + } + mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills); + } + + mReviewDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogDone); + mReviewDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onReviewDialogBack); + mReviewDialog->eventActivateDialog = MyGUI::newDelegate(this, &CharacterCreation::onReviewActivateDialog); + mReviewDialog->open(); + break; + } +} + +void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) +{ + mPlayerHealth = value; +} + +void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) +{ + mPlayerMagicka = value; +} + +void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) +{ + mPlayerFatigue = value; +} + +void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + + mWM->setGuiMode(GM_Game); +} + +void CharacterCreation::onReviewDialogBack() +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + + mWM->setGuiMode(GM_Birth); +} + +void CharacterCreation::onReviewActivateDialog(int parDialog) +{ + if (mReviewDialog) + mWM->removeDialog(mReviewDialog); + mCreationStage = CSE_ReviewNext; + + switch(parDialog) + { + case ReviewDialog::NAME_DIALOG: + mWM->setGuiMode(GM_Name); + break; + case ReviewDialog::RACE_DIALOG: + mWM->setGuiMode(GM_Race); + break; + case ReviewDialog::CLASS_DIALOG: + mWM->setGuiMode(GM_Class); + break; + case ReviewDialog::BIRTHSIGN_DIALOG: + mWM->setGuiMode(GM_Birth); + }; +} + +void CharacterCreation::onPickClassDialogDone(WindowBase* parWindow) +{ + if (mPickClassDialog) + { + const std::string &classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + mEnvironment->mMechanicsManager->setPlayerClass(classId); + const ESM::Class *klass = mEnvironment->mWorld->getStore().classes.find(classId); + if (klass) + { + mPlayerClass = *klass; + mWM->setPlayerClass(mPlayerClass); + } + mWM->removeDialog(mPickClassDialog); + } + + //TODO This bit gets repeated a few times; wrap it in a function + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onPickClassDialogBack() +{ + if (mPickClassDialog) + { + const std::string classId = mPickClassDialog->getClassId(); + if (!classId.empty()) + mEnvironment->mMechanicsManager->setPlayerClass(classId); + mWM->removeDialog(mPickClassDialog); + } + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onClassChoice(int _index) +{ + if (mClassChoiceDialog) + { + mWM->removeDialog(mClassChoiceDialog); + } + + switch(_index) + { + case ClassChoiceDialog::Class_Generate: + mWM->setGuiMode(GM_ClassGenerate); + break; + case ClassChoiceDialog::Class_Pick: + mWM->setGuiMode(GM_ClassPick); + break; + case ClassChoiceDialog::Class_Create: + mWM->setGuiMode(GM_ClassCreate); + break; + case ClassChoiceDialog::Class_Back: + mWM->setGuiMode(GM_Race); + break; + + }; +} + +void CharacterCreation::onNameDialogDone(WindowBase* parWindow) +{ + if (mNameDialog) + { + mPlayerName = mNameDialog->getTextInput(); + mWM->setValue("name", mPlayerName); + mEnvironment->mMechanicsManager->setPlayerName(mPlayerName); + mWM->removeDialog(mNameDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_NameChosen) + mWM->setGuiMode(GM_Race); + else + { + mCreationStage = CSE_NameChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onRaceDialogBack() +{ + if (mRaceDialog) + { + mPlayerRaceId = mRaceDialog->getRaceId(); + if (!mPlayerRaceId.empty()) + mEnvironment->mMechanicsManager->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); + mWM->removeDialog(mRaceDialog); + } + + mWM->setGuiMode(GM_Name); +} + +void CharacterCreation::onRaceDialogDone(WindowBase* parWindow) +{ + if (mRaceDialog) + { + mPlayerRaceId = mRaceDialog->getRaceId(); + mWM->setValue("race", mPlayerRaceId); + if (!mPlayerRaceId.empty()) + mEnvironment->mMechanicsManager->setPlayerRace(mPlayerRaceId, mRaceDialog->getGender() == RaceDialog::GM_Male); + mWM->removeDialog(mRaceDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if(mCreationStage >= CSE_RaceChosen) + mWM->setGuiMode(GM_Class); + else + { + mCreationStage = CSE_RaceChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) +{ + if (mBirthSignDialog) + { + mPlayerBirthSignId = mBirthSignDialog->getBirthId(); + mWM->setBirthSign(mPlayerBirthSignId); + if (!mPlayerBirthSignId.empty()) + mEnvironment->mMechanicsManager->setPlayerBirthsign(mPlayerBirthSignId); + mWM->removeDialog(mBirthSignDialog); + } + + if (mCreationStage >= CSE_BirthSignChosen) + mWM->setGuiMode(GM_Review); + else + { + mCreationStage = CSE_BirthSignChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onBirthSignDialogBack() +{ + if (mBirthSignDialog) + { + mEnvironment->mMechanicsManager->setPlayerBirthsign(mBirthSignDialog->getBirthId()); + mWM->removeDialog(mBirthSignDialog); + } + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onCreateClassDialogDone(WindowBase* parWindow) +{ + if (mCreateClassDialog) + { + ESM::Class klass; + klass.name = mCreateClassDialog->getName(); + klass.description = mCreateClassDialog->getDescription(); + klass.data.specialization = mCreateClassDialog->getSpecializationId(); + klass.data.isPlayable = 0x1; + + std::vector attributes = mCreateClassDialog->getFavoriteAttributes(); + assert(attributes.size() == 2); + klass.data.attribute[0] = attributes[0]; + klass.data.attribute[1] = attributes[1]; + + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + assert(majorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); + assert(minorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); + for (size_t i = 0; i < sizeof(klass.data.skills)/sizeof(klass.data.skills[0]); ++i) + { + klass.data.skills[i][1] = majorSkills[i]; + klass.data.skills[i][0] = minorSkills[i]; + } + mEnvironment->mMechanicsManager->setPlayerClass(klass); + mPlayerClass = klass; + mWM->setPlayerClass(klass); + + mWM->removeDialog(mCreateClassDialog); + } + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +void CharacterCreation::onCreateClassDialogBack() +{ + if (mCreateClassDialog) + mWM->removeDialog(mCreateClassDialog); + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onClassQuestionChosen(int _index) +{ + if (mGenerateClassQuestionDialog) + mWM->removeDialog(mGenerateClassQuestionDialog); + if (_index < 0 || _index >= 3) + { + mWM->setGuiMode(GM_Class); + return; + } + + ESM::Class::Specialization specialization = sGenerateClassSteps[mGenerateClassStep].mSpecializations[_index]; + if (specialization == ESM::Class::Stealth) + ++mGenerateClassSpecializations[0]; + else if (specialization == ESM::Class::Combat) + ++mGenerateClassSpecializations[1]; + else if (specialization == ESM::Class::Magic) + ++mGenerateClassSpecializations[2]; + ++mGenerateClassStep; + showClassQuestionDialog(); +} + +void CharacterCreation::showClassQuestionDialog() +{ + if (mGenerateClassStep == sGenerateClassSteps.size()) + { + static boost::array classes = { { + {"Acrobat", {6, 2, 2}}, + {"Agent", {6, 1, 3}}, + {"Archer", {3, 5, 2}}, + {"Archer", {5, 5, 0}}, + {"Assassin", {6, 3, 1}}, + {"Barbarian", {3, 6, 1}}, + {"Bard", {3, 3, 3}}, + {"Battlemage", {1, 3, 6}}, + {"Crusader", {1, 6, 3}}, + {"Healer", {3, 1, 6}}, + {"Knight", {2, 6, 2}}, + {"Monk", {5, 3, 2}}, + {"Nightblade", {4, 2, 4}}, + {"Pilgrim", {5, 2, 3}}, + {"Rogue", {3, 4, 3}}, + {"Rogue", {4, 4, 2}}, + {"Rogue", {5, 4, 1}}, + {"Scout", {2, 5, 3}}, + {"Sorcerer", {2, 2, 6}}, + {"Spellsword", {2, 4, 4}}, + {"Spellsword", {5, 1, 4}}, + {"Witchhunter", {2, 3, 5}}, + {"Witchhunter", {5, 0, 5}} + } }; + + int match = -1; + for (unsigned i = 0; i < classes.size(); ++i) + { + if (mGenerateClassSpecializations[0] == classes[i].points[0] && + mGenerateClassSpecializations[1] == classes[i].points[1] && + mGenerateClassSpecializations[2] == classes[i].points[2]) + { + match = i; + mGenerateClass = classes[i].id; + break; + } + } + + if (match == -1) + { + if (mGenerateClassSpecializations[0] >= 7) + mGenerateClass = "Thief"; + else if (mGenerateClassSpecializations[1] >= 7) + mGenerateClass = "Warrior"; + else if (mGenerateClassSpecializations[2] >= 7) + mGenerateClass = "Mage"; + else + { + std::cerr << "Failed to deduce class from chosen answers in generate class dialog" << std::endl; + mGenerateClass = "Thief"; + } + } + + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mGenerateClassResultDialog = new GenerateClassResultDialog(*mWM); + mGenerateClassResultDialog->setClassId(mGenerateClass); + mGenerateClassResultDialog->eventBack = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassBack); + mGenerateClassResultDialog->eventDone = MyGUI::newDelegate(this, &CharacterCreation::onGenerateClassDone); + mGenerateClassResultDialog->open(); + return; + } + + if (mGenerateClassStep > sGenerateClassSteps.size()) + { + mWM->setGuiMode(GM_Class); + return; + } + + if (mGenerateClassQuestionDialog) + mWM->removeDialog(mGenerateClassQuestionDialog); + mGenerateClassQuestionDialog = new InfoBoxDialog(*mWM); + + InfoBoxDialog::ButtonList buttons; + mGenerateClassQuestionDialog->setText(sGenerateClassSteps[mGenerateClassStep].mText); + buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[0]); + buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[1]); + buttons.push_back(sGenerateClassSteps[mGenerateClassStep].mButtons[2]); + mGenerateClassQuestionDialog->setButtons(buttons); + mGenerateClassQuestionDialog->eventButtonSelected = MyGUI::newDelegate(this, &CharacterCreation::onClassQuestionChosen); + mGenerateClassQuestionDialog->open(); +} + +void CharacterCreation::onGenerateClassBack() +{ + if(mCreationStage < CSE_ClassChosen) + mCreationStage = CSE_ClassChosen; + + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mEnvironment->mMechanicsManager->setPlayerClass(mGenerateClass); + + mWM->setGuiMode(GM_Class); +} + +void CharacterCreation::onGenerateClassDone(WindowBase* parWindow) +{ + if (mGenerateClassResultDialog) + mWM->removeDialog(mGenerateClassResultDialog); + mEnvironment->mMechanicsManager->setPlayerClass(mGenerateClass); + + if (mCreationStage == CSE_ReviewNext) + mWM->setGuiMode(GM_Review); + else if (mCreationStage >= CSE_ClassChosen) + mWM->setGuiMode(GM_Birth); + else + { + mCreationStage = CSE_ClassChosen; + mWM->setGuiMode(GM_Game); + } +} + +CharacterCreation::~CharacterCreation() +{ + delete mNameDialog; + delete mRaceDialog; + delete mDialogueWindow; + delete mClassChoiceDialog; + delete mGenerateClassQuestionDialog; + delete mGenerateClassResultDialog; + delete mPickClassDialog; + delete mCreateClassDialog; + delete mBirthSignDialog; + delete mReviewDialog; +} diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp new file mode 100644 index 0000000000..b01e754d9d --- /dev/null +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -0,0 +1,120 @@ +#ifndef CHARACTER_CREATION_HPP +#define CHARACTER_CREATION_HPP + +#include "window_manager.hpp" + +#include "../mwmechanics/mechanicsmanager.hpp" +#include "../mwmechanics/stat.hpp" +#include "../mwworld/world.hpp" +#include + +namespace MWGui +{ + class WindowManager; + class WindowBase; + + class TextInputDialog; + class InfoBoxDialog; + class RaceDialog; + class DialogueWindow; + class ClassChoiceDialog; + class GenerateClassResultDialog; + class PickClassDialog; + class CreateClassDialog; + class BirthDialog; + class ReviewDialog; + class MessageBoxManager; + + class CharacterCreation + { + public: + typedef std::vector SkillList; + + CharacterCreation(WindowManager* _wm, MWWorld::Environment* _environment); + ~CharacterCreation(); + + //Show a dialog + void spawnDialog(const char id); + + void setPlayerHealth (const MWMechanics::DynamicStat& value); + + void setPlayerMagicka (const MWMechanics::DynamicStat& value); + + void setPlayerFatigue (const MWMechanics::DynamicStat& value); + + private: + //Dialogs + TextInputDialog* mNameDialog; + RaceDialog* mRaceDialog; + DialogueWindow* mDialogueWindow; + ClassChoiceDialog* mClassChoiceDialog; + InfoBoxDialog* mGenerateClassQuestionDialog; + GenerateClassResultDialog* mGenerateClassResultDialog; + PickClassDialog* mPickClassDialog; + CreateClassDialog* mCreateClassDialog; + BirthDialog* mBirthSignDialog; + ReviewDialog* mReviewDialog; + + WindowManager* mWM; + MWWorld::Environment* mEnvironment; + + //Player data + std::string mPlayerName; + std::string mPlayerRaceId; + std::string mPlayerBirthSignId; + ESM::Class mPlayerClass; + std::map > mPlayerAttributes; + SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::map > mPlayerSkillValues; + MWMechanics::DynamicStat mPlayerHealth; + MWMechanics::DynamicStat mPlayerMagicka; + MWMechanics::DynamicStat mPlayerFatigue; + + //Class generation vars + unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog + unsigned mGenerateClassSpecializations[3]; // A counter for each specialization which is increased when an answer is chosen + std::string mGenerateClass; // In order: Stealth, Combat, Magic + + ////Dialog events + //Name dialog + void onNameDialogDone(WindowBase* parWindow); + + //Race dialog + void onRaceDialogDone(WindowBase* parWindow); + void onRaceDialogBack(); + + //Class dialogs + void onClassChoice(int _index); + void onPickClassDialogDone(WindowBase* parWindow); + void onPickClassDialogBack(); + void onCreateClassDialogDone(WindowBase* parWindow); + void onCreateClassDialogBack(); + void showClassQuestionDialog(); + void onClassQuestionChosen(int _index); + void onGenerateClassBack(); + void onGenerateClassDone(WindowBase* parWindow); + + //Birthsign dialog + void onBirthSignDialogDone(WindowBase* parWindow); + void onBirthSignDialogBack(); + + //Review dialog + void onReviewDialogDone(WindowBase* parWindow); + void onReviewDialogBack(); + void onReviewActivateDialog(int parDialog); + + enum CSE //Creation Stage Enum + { + CSE_NotStarted, + CSE_NameChosen, + CSE_RaceChosen, + CSE_ClassChosen, + CSE_BirthSignChosen, + CSE_ReviewNext + }; + + CSE mCreationStage; // Which state the character creating is in, controls back/next/ok buttons + }; +} + +#endif diff --git a/apps/openmw/mwgui/window_manager.cpp b/apps/openmw/mwgui/window_manager.cpp index 5302489f1a..baf8548143 100644 --- a/apps/openmw/mwgui/window_manager.cpp +++ b/apps/openmw/mwgui/window_manager.cpp @@ -1,9 +1,6 @@ #include "window_manager.hpp" #include "layouts.hpp" #include "text_input.hpp" -#include "race.hpp" -#include "class.hpp" -#include "birth.hpp" #include "review.hpp" #include "dialogue.hpp" #include "dialogue_history.hpp" @@ -15,6 +12,7 @@ #include "console.hpp" #include "journalwindow.hpp" +#include "charactercreation.hpp" #include #include @@ -22,20 +20,10 @@ using namespace MWGui; -WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment, - const Compiler::Extensions& extensions, int fpsLevel, bool newGame) +WindowManager::WindowManager(MWWorld::Environment& environment, + const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath) : environment(environment) - , nameDialog(nullptr) - , raceDialog(nullptr) , dialogueWindow(nullptr) - , classChoiceDialog(nullptr) - , generateClassQuestionDialog(nullptr) - , generateClassResultDialog(nullptr) - , pickClassDialog(nullptr) - , createClassDialog(nullptr) - , birthSignDialog(nullptr) - , reviewDialog(nullptr) - , gui(_gui) , mode(GM_Game) , nextMode(GM_Game) , needModeChange(false) @@ -44,7 +32,9 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment { showFPSLevel = fpsLevel; - creationStage = NotStarted; + // Set up the GUI system + mGuiManager = new OEngine::GUI::MyGUIManager(mOgre->getWindow(), mOgre->getScene(), false, logpath); + gui = mGuiManager->getGui(); //Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); @@ -58,9 +48,6 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment menu = new MainMenu(w,h); map = new MapWindow(); stats = new StatsWindow(*this); -#if 0 - inventory = new InventoryWindow (); -#endif console = new Console(w,h, environment, extensions); mJournal = new JournalWindow(*this); mMessageBoxManager = new MessageBoxManager(this); @@ -69,6 +56,8 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment // The HUD is always on hud->setVisible(true); + mCharGen = new CharacterCreation(this, &environment); + // Setup player stats for (int i = 0; i < ESM::Attribute::Length; ++i) { @@ -92,6 +81,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment WindowManager::~WindowManager() { + delete mGuiManager; delete console; delete mMessageBoxManager; delete hud; @@ -99,20 +89,9 @@ WindowManager::~WindowManager() delete menu; delete stats; delete mJournal; -#if 0 - delete inventory; -#endif - - delete nameDialog; - delete raceDialog; delete dialogueWindow; - delete classChoiceDialog; - delete generateClassQuestionDialog; - delete generateClassResultDialog; - delete pickClassDialog; - delete createClassDialog; - delete birthSignDialog; - delete reviewDialog; + + delete mCharGen; cleanupGarbage(); } @@ -169,9 +148,6 @@ void WindowManager::updateVisible() map->setVisible(false); menu->setVisible(false); stats->setVisible(false); -#if 0 - inventory->setVisible(false); -#endif console->disable(); mJournal->setVisible(false); dialogueWindow->setVisible(false); @@ -180,7 +156,7 @@ void WindowManager::updateVisible() gui->setVisiblePointer(isGuiMode()); // If in game mode, don't show anything. - if(mode == GM_Game) + if(mode == GM_Game) //Use a switch/case structure { return; } @@ -198,126 +174,10 @@ void WindowManager::updateVisible() return; } - if (mode == GM_Name) + //There must be a more elegant solution + if (mode == GM_Name || mode == GM_Race || mode == GM_Class || mode == GM_ClassPick || mode == GM_ClassCreate || mode == GM_Birth || mode == GM_ClassGenerate || mode == GM_Review) { - if (nameDialog) - removeDialog(nameDialog); - nameDialog = new TextInputDialog(*this); - std::string sName = getGameSettingString("sName", "Name"); - nameDialog->setTextLabel(sName); - nameDialog->setTextInput(playerName); - nameDialog->setNextButtonShow(creationStage >= NameChosen); - nameDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onNameDialogDone); - nameDialog->open(); - return; - } - - if (mode == GM_Race) - { - if (raceDialog) - removeDialog(raceDialog); - raceDialog = new RaceDialog(*this); - raceDialog->setNextButtonShow(creationStage >= RaceChosen); - raceDialog->setRaceId(playerRaceId); - raceDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onRaceDialogDone); - raceDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onRaceDialogBack); - raceDialog->open(); - return; - } - - if (mode == GM_Class) - { - if (classChoiceDialog) - removeDialog(classChoiceDialog); - classChoiceDialog = new ClassChoiceDialog(*this); - classChoiceDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassChoice); - classChoiceDialog->open(); - return; - } - - if (mode == GM_ClassGenerate) - { - generateClassStep = 0; - generateClass = ""; - generateClassSpecializations[0] = 0; - generateClassSpecializations[1] = 0; - generateClassSpecializations[2] = 0; - showClassQuestionDialog(); - return; - } - - if (mode == GM_ClassPick) - { - if (pickClassDialog) - removeDialog(pickClassDialog); - pickClassDialog = new PickClassDialog(*this); - pickClassDialog->setNextButtonShow(creationStage >= ClassChosen); - pickClassDialog->setClassId(playerClass.name); - pickClassDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onPickClassDialogDone); - pickClassDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onPickClassDialogBack); - pickClassDialog->open(); - return; - } - - if (mode == GM_ClassCreate) - { - if (createClassDialog) - removeDialog(createClassDialog); - createClassDialog = new CreateClassDialog(*this); - createClassDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onCreateClassDialogDone); - createClassDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onCreateClassDialogBack); - createClassDialog->open(); - return; - } - - if (mode == GM_Birth) - { - if (birthSignDialog) - removeDialog(birthSignDialog); - birthSignDialog = new BirthDialog(*this); - birthSignDialog->setNextButtonShow(creationStage >= BirthSignChosen); - birthSignDialog->setBirthId(playerBirthSignId); - birthSignDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onBirthSignDialogDone); - birthSignDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onBirthSignDialogBack); - birthSignDialog->open(); - return; - } - - if (mode == GM_Review) - { - if (reviewDialog) - removeDialog(reviewDialog); - reviewDialog = new ReviewDialog(*this); - reviewDialog->setPlayerName(playerName); - reviewDialog->setRace(playerRaceId); - reviewDialog->setClass(playerClass); - reviewDialog->setBirthSign(playerBirthSignId); - - reviewDialog->setHealth(playerHealth); - reviewDialog->setMagicka(playerMagicka); - reviewDialog->setFatigue(playerFatigue); - - { - std::map >::iterator end = playerAttributes.end(); - for (std::map >::iterator it = playerAttributes.begin(); it != end; ++it) - { - reviewDialog->setAttribute(it->first, it->second); - } - } - - { - std::map >::iterator end = playerSkillValues.end(); - for (std::map >::iterator it = playerSkillValues.begin(); it != end; ++it) - { - reviewDialog->setSkillValue(it->first, it->second); - } - reviewDialog->configureSkills(playerMajorSkills, playerMinorSkills); - } - - reviewDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onReviewDialogDone); - reviewDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onReviewDialogBack); - reviewDialog->eventActivateDialog = MyGUI::newDelegate(this, &WindowManager::onReviewActivateDialog); - reviewDialog->open(); + mCharGen->spawnDialog(mode); return; } @@ -332,9 +192,6 @@ void WindowManager::updateVisible() // Show the windows we want map -> setVisible( (eff & GW_Map) != 0 ); stats -> setVisible( (eff & GW_Stats) != 0 ); -#if 0 - // inventory -> setVisible( eff & GW_Inventory ); -#endif return; } @@ -359,7 +216,6 @@ void WindowManager::updateVisible() return; } - // Unsupported mode, switch back to game // Note: The call will eventually end up this method again but // will stop at the check if(mode == GM_Game) above. @@ -407,13 +263,34 @@ void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicS stats->setValue (id, value); hud->setValue (id, value); if (id == "HBar") + { playerHealth = value; + mCharGen->setPlayerHealth (value); + } else if (id == "MBar") + { playerMagicka = value; + mCharGen->setPlayerMagicka (value); + } else if (id == "FBar") + { playerFatigue = value; + mCharGen->setPlayerFatigue (value); + } } +#if 0 +MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) +{ + if(id == "HBar") + return playerHealth; + else if (id == "MBar") + return playerMagicka; + else if (id == "FBar") + return playerFatigue; +} +#endif + void WindowManager::setValue (const std::string& id, const std::string& value) { stats->setValue (id, value); @@ -503,49 +380,6 @@ const std::string &WindowManager::getGameSettingString(const std::string &id, co return default_; } -void WindowManager::onNameDialogDone(WindowBase* parWindow) -{ - if (nameDialog) - { - playerName = nameDialog->getTextInput(); - environment.mMechanicsManager->setPlayerName(playerName); - removeDialog(nameDialog); - } - - // Go to next dialog if name was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= NameChosen) - setGuiMode(GM_Race); - else - { - creationStage = NameChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onRaceDialogDone(WindowBase* parWindow) -{ - if (raceDialog) - { - playerRaceId = raceDialog->getRaceId(); - if (!playerRaceId.empty()) - environment.mMechanicsManager->setPlayerRace(playerRaceId, raceDialog->getGender() == RaceDialog::GM_Male); - removeDialog(raceDialog); - } - - // Go to next dialog if race was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if(creationStage >= RaceChosen) - setGuiMode(GM_Class); - else - { - creationStage = RaceChosen; - setGuiMode(GM_Game); - } -} - void WindowManager::onDialogueWindowBye() { if (dialogueWindow) @@ -557,434 +391,11 @@ void WindowManager::onDialogueWindowBye() setGuiMode(GM_Game); } -void WindowManager::onRaceDialogBack() -{ - if (raceDialog) - { - playerRaceId = raceDialog->getRaceId(); - if (!playerRaceId.empty()) - environment.mMechanicsManager->setPlayerRace(playerRaceId, raceDialog->getGender() == RaceDialog::GM_Male); - removeDialog(raceDialog); - } - - setGuiMode(GM_Name); -} - -void WindowManager::onClassChoice(int _index) -{ - if (classChoiceDialog) - { - removeDialog(classChoiceDialog); - } - - switch(_index) - { - case ClassChoiceDialog::Class_Generate: - setGuiMode(GM_ClassGenerate); - break; - case ClassChoiceDialog::Class_Pick: - setGuiMode(GM_ClassPick); - break; - case ClassChoiceDialog::Class_Create: - setGuiMode(GM_ClassCreate); - break; - case ClassChoiceDialog::Class_Back: - setGuiMode(GM_Race); - break; - - }; -} - void WindowManager::onFrame (float frameDuration) { mMessageBoxManager->onFrame(frameDuration); } -namespace MWGui -{ - - struct Step - { - const char* text; - const char* buttons[3]; - // The specialization for each answer - ESM::Class::Specialization specializations[3]; - }; - - static boost::array generateClassSteps = { { - // Question 1 - {"On a clear day you chance upon a strange animal, its legs trapped in a hunter's clawsnare. Judging from the bleeding, it will not survive long.", - {"Draw your dagger, mercifully endings its life with a single thrust.", - "Use herbs from your pack to put it to sleep.", - "Do not interfere in the natural evolution of events, but rather take the opportunity to learn more about a strange animal that you have never seen before."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 2 - {"One Summer afternoon your father gives you a choice of chores.", - {"Work in the forge with him casting iron for a new plow.", - "Gather herbs for your mother who is preparing dinner.", - "Go catch fish at the stream using a net and line."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 3 - {"Your cousin has given you a very embarrassing nickname and, even worse, likes to call you it in front of your friends. You asked him to stop, but he finds it very amusing to watch you blush.", - {"Beat up your cousin, then tell him that if he ever calls you that nickname again, you will bloody him worse than this time.", - "Make up a story that makes your nickname a badge of honor instead of something humiliating.", - "Make up an even more embarrassing nickname for him and use it constantly until he learns his lesson."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 4 - {"There is a lot of heated discussion at the local tavern over a grouped of people called 'Telepaths'. They have been hired by certain City-State kings. Rumor has it these Telepaths read a person's mind and tell their lord whether a follower is telling the truth or not.", - {"This is a terrible practice. A person's thoughts are his own and no one, not even a king, has the right to make such an invasion into another human's mind.", - "Loyal followers to the king have nothing to fear from a Telepath. It is important to have a method of finding assassins and spies before it is too late.", - "In these times, it is a necessary evil. Although you do not necessarily like the idea, a Telepath could have certain advantages during a time of war or in finding someone innocent of a crime."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 5 - {"Your mother sends you to the market with a list of goods to buy. After you finish you find that by mistake a shopkeeper has given you too much money back in exchange for one of the items.", - {"Return to the store and give the shopkeeper his hard-earned money, explaining to him the mistake?", - "Decide to put the extra money to good use and purchase items that would help your family?", - "Pocket the extra money, knowing that shopkeepers in general tend to overcharge customers anyway?"}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 6 - {"While in the market place you witness a thief cut a purse from a noble. Even as he does so, the noble notices and calls for the city guards. In his haste to get away, the thief drops the purse near you. Surprisingly no one seems to notice the bag of coins at your feet.", - {"Pick up the bag and signal to the guard, knowing that the only honorable thing to do is return the money to its rightful owner.", - "Leave the bag there, knowing that it is better not to get involved.", - "Pick up the bag and pocket it, knowing that the extra windfall will help your family in times of trouble."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 7 - {"Your father sends you on a task which you loathe, cleaning the stables. On the way there, pitchfork in hand, you run into your friend from the homestead near your own. He offers to do it for you, in return for a future favor of his choosing.", - {"Decline his offer, knowing that your father expects you to do the work, and it is better not to be in debt.", - "Ask him to help you, knowing that two people can do the job faster than one, and agree to help him with one task of his choosing in the future.", - "Accept his offer, reasoning that as long as the stables are cleaned, it matters not who does the cleaning."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 8 - {"Your mother asks you to help fix the stove. While you are working, a very hot pipe slips its mooring and falls towards her.", - {"Position yourself between the pipe and your mother.", - "Grab the hot pipe and try to push it away.", - "Push your mother out of the way."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 9 - {"While in town the baker gives you a sweetroll. Delighted, you take it into an alley to enjoy only to be intercepted by a gang of three other kids your age. The leader demands the sweetroll, or else he and his friends will beat you and take it.", - {"Drop the sweetroll and step on it, then get ready for the fight.", - "Give him the sweetroll now without argument, knowing that later this afternoon you will have all your friends with you and can come and take whatever he owes you.", - "Act like you're going to give him the sweetroll, but at the last minute throw it in the air, hoping that they'll pay attention to it long enough for you to get a shot in on the leader."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - }, - // Question 10 - {"Entering town you find that you are witness to a very well-dressed man running from a crowd. He screams to you for help. The crowd behind him seem very angry.", - {"Rush to the town's aid immediately, despite your lack of knowledge of the circumstances.", - "Stand aside and allow the man and the mob to pass, realizing it is probably best not to get involved.", - "Rush to the man's aid immediately, despite your lack of knowledge of the circumstances."}, - {ESM::Class::Combat, ESM::Class::Magic, ESM::Class::Stealth} - } - } }; -} - -void WindowManager::showClassQuestionDialog() -{ - if (generateClassStep == generateClassSteps.size()) - { - - static boost::array classes = { { - {"Acrobat", {6, 2, 2}}, - {"Agent", {6, 1, 3}}, - {"Archer", {3, 5, 2}}, - {"Archer", {5, 5, 0}}, - {"Assassin", {6, 3, 1}}, - {"Barbarian", {3, 6, 1}}, - {"Bard", {3, 3, 3}}, - {"Battlemage", {1, 3, 6}}, - {"Crusader", {1, 6, 3}}, - {"Healer", {3, 1, 6}}, - {"Knight", {2, 6, 2}}, - {"Monk", {5, 3, 2}}, - {"Nightblade", {4, 2, 4}}, - {"Pilgrim", {5, 2, 3}}, - {"Rogue", {3, 4, 3}}, - {"Rogue", {4, 4, 2}}, - {"Rogue", {5, 4, 1}}, - {"Scout", {2, 5, 3}}, - {"Sorcerer", {2, 2, 6}}, - {"Spellsword", {2, 4, 4}}, - {"Spellsword", {5, 1, 4}}, - {"Witchhunter", {2, 3, 5}}, - {"Witchhunter", {5, 0, 5}} - } }; - - int match = -1; - for (unsigned i = 0; i < classes.size(); ++i) - { - if (generateClassSpecializations[0] == classes[i].points[0] && - generateClassSpecializations[1] == classes[i].points[1] && - generateClassSpecializations[2] == classes[i].points[2]) - { - match = i; - generateClass = classes[i].id; - break; - } - } - - if (match == -1) - { - if (generateClassSpecializations[0] >= 7) - generateClass = "Thief"; - else if (generateClassSpecializations[1] >= 7) - generateClass = "Warrior"; - else if (generateClassSpecializations[2] >= 7) - generateClass = "Mage"; - else - { - std::cerr - << "Failed to deduce class from chosen answers in generate class dialog" - << std::endl; - generateClass = "Thief"; - } - } - - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - generateClassResultDialog = new GenerateClassResultDialog(*this); - generateClassResultDialog->setClassId(generateClass); - generateClassResultDialog->eventBack = MyGUI::newDelegate(this, &WindowManager::onGenerateClassBack); - generateClassResultDialog->eventDone = MyGUI::newDelegate(this, &WindowManager::onGenerateClassDone); - generateClassResultDialog->open(); - return; - } - - if (generateClassStep > generateClassSteps.size()) - { - setGuiMode(GM_Class); - return; - } - - if (generateClassQuestionDialog) - removeDialog(generateClassQuestionDialog); - generateClassQuestionDialog = new InfoBoxDialog(*this); - - InfoBoxDialog::ButtonList buttons; - generateClassQuestionDialog->setText(generateClassSteps[generateClassStep].text); - buttons.push_back(generateClassSteps[generateClassStep].buttons[0]); - buttons.push_back(generateClassSteps[generateClassStep].buttons[1]); - buttons.push_back(generateClassSteps[generateClassStep].buttons[2]); - generateClassQuestionDialog->setButtons(buttons); - generateClassQuestionDialog->eventButtonSelected = MyGUI::newDelegate(this, &WindowManager::onClassQuestionChosen); - generateClassQuestionDialog->open(); -} - -void WindowManager::onClassQuestionChosen(int _index) -{ - if (generateClassQuestionDialog) - removeDialog(generateClassQuestionDialog); - if (_index < 0 || _index >= 3) - { - setGuiMode(GM_Class); - return; - } - - ESM::Class::Specialization specialization = generateClassSteps[generateClassStep].specializations[_index]; - if (specialization == ESM::Class::Stealth) - ++generateClassSpecializations[0]; - else if (specialization == ESM::Class::Combat) - ++generateClassSpecializations[1]; - else if (specialization == ESM::Class::Magic) - ++generateClassSpecializations[2]; - ++generateClassStep; - showClassQuestionDialog(); -} - -void WindowManager::onGenerateClassBack() -{ - if(creationStage < ClassChosen) - creationStage = ClassChosen; - - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - environment.mMechanicsManager->setPlayerClass(generateClass); - - setGuiMode(GM_Class); -} - -void WindowManager::onGenerateClassDone(WindowBase* parWindow) -{ - if (generateClassResultDialog) - removeDialog(generateClassResultDialog); - environment.mMechanicsManager->setPlayerClass(generateClass); - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - - -void WindowManager::onPickClassDialogDone(WindowBase* parWindow) -{ - if (pickClassDialog) - { - const std::string &classId = pickClassDialog->getClassId(); - if (!classId.empty()) - environment.mMechanicsManager->setPlayerClass(classId); - const ESM::Class *klass = environment.mWorld->getStore().classes.find(classId); - if (klass) - playerClass = *klass; - removeDialog(pickClassDialog); - } - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onPickClassDialogBack() -{ - if (pickClassDialog) - { - const std::string classId = pickClassDialog->getClassId(); - if (!classId.empty()) - environment.mMechanicsManager->setPlayerClass(classId); - removeDialog(pickClassDialog); - } - - setGuiMode(GM_Class); -} - -void WindowManager::onCreateClassDialogDone(WindowBase* parWindow) -{ - if (createClassDialog) - { - ESM::Class klass; - klass.name = createClassDialog->getName(); - klass.description = createClassDialog->getDescription(); - klass.data.specialization = createClassDialog->getSpecializationId(); - klass.data.isPlayable = 0x1; - - std::vector attributes = createClassDialog->getFavoriteAttributes(); - assert(attributes.size() == 2); - klass.data.attribute[0] = attributes[0]; - klass.data.attribute[1] = attributes[1]; - - std::vector majorSkills = createClassDialog->getMajorSkills(); - std::vector minorSkills = createClassDialog->getMinorSkills(); - assert(majorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); - assert(minorSkills.size() >= sizeof(klass.data.skills)/sizeof(klass.data.skills[0])); - for (size_t i = 0; i < sizeof(klass.data.skills)/sizeof(klass.data.skills[0]); ++i) - { - klass.data.skills[i][1] = majorSkills[i]; - klass.data.skills[i][0] = minorSkills[i]; - } - environment.mMechanicsManager->setPlayerClass(klass); - playerClass = klass; - - removeDialog(createClassDialog); - } - - // Go to next dialog if class was previously chosen - if (creationStage == ReviewNext) - setGuiMode(GM_Review); - else if (creationStage >= ClassChosen) - setGuiMode(GM_Birth); - else - { - creationStage = ClassChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onCreateClassDialogBack() -{ - if (createClassDialog) - removeDialog(createClassDialog); - - setGuiMode(GM_Class); -} - -void WindowManager::onBirthSignDialogDone(WindowBase* parWindow) -{ - if (birthSignDialog) - { - playerBirthSignId = birthSignDialog->getBirthId(); - if (!playerBirthSignId.empty()) - environment.mMechanicsManager->setPlayerBirthsign(playerBirthSignId); - removeDialog(birthSignDialog); - } - - // Go to next dialog if birth sign was previously chosen - if (creationStage >= BirthSignChosen) - setGuiMode(GM_Review); - else - { - creationStage = BirthSignChosen; - setGuiMode(GM_Game); - } -} - -void WindowManager::onBirthSignDialogBack() -{ - if (birthSignDialog) - { - environment.mMechanicsManager->setPlayerBirthsign(birthSignDialog->getBirthId()); - removeDialog(birthSignDialog); - } - - setGuiMode(GM_Class); -} - -void WindowManager::onReviewDialogDone(WindowBase* parWindow) -{ - if (reviewDialog) - removeDialog(reviewDialog); - - setGuiMode(GM_Game); -} - -void WindowManager::onReviewDialogBack() -{ - if (reviewDialog) - removeDialog(reviewDialog); - - setGuiMode(GM_Birth); -} - -void WindowManager::onReviewActivateDialog(int parDialog) -{ - if (reviewDialog) - removeDialog(reviewDialog); - creationStage = ReviewNext; - - switch(parDialog) - { - case ReviewDialog::NAME_DIALOG: - setGuiMode(GM_Name); - break; - case ReviewDialog::RACE_DIALOG: - setGuiMode(GM_Race); - break; - case ReviewDialog::CLASS_DIALOG: - setGuiMode(GM_Class); - break; - case ReviewDialog::BIRTHSIGN_DIALOG: - setGuiMode(GM_Birth); - }; -} - const ESMS::ESMStore& WindowManager::getStore() const { return environment.mWorld->getStore(); diff --git a/apps/openmw/mwgui/window_manager.hpp b/apps/openmw/mwgui/window_manager.hpp index 9dbe72eabc..0124c92392 100644 --- a/apps/openmw/mwgui/window_manager.hpp +++ b/apps/openmw/mwgui/window_manager.hpp @@ -8,13 +8,15 @@ MyGUI should be initialized separately before creating instances of this class. - */ +**/ #include #include #include #include +#include +#include #include "../mwmechanics/stat.hpp" #include "mode.hpp" @@ -32,6 +34,12 @@ namespace Compiler namespace MWWorld { class Environment; + class World; +} + +namespace MWMechanics +{ + class MechanicsManager; } namespace OEngine @@ -52,17 +60,11 @@ namespace MWGui class InventoryWindow; class Console; class JournalWindow; + class CharacterCreation; class TextInputDialog; class InfoBoxDialog; - class RaceDialog; class DialogueWindow; - class ClassChoiceDialog; - class GenerateClassResultDialog; - class PickClassDialog; - class CreateClassDialog; - class BirthDialog; - class ReviewDialog; class MessageBoxManager; struct ClassPoint @@ -80,95 +82,11 @@ namespace MWGui typedef std::vector FactionList; typedef std::vector SkillList; - private: - MWWorld::Environment& environment; - HUD *hud; - MapWindow *map; - MainMenu *menu; - StatsWindow *stats; - MessageBoxManager *mMessageBoxManager; -#if 0 - InventoryWindow *inventory; -#endif - Console *console; - JournalWindow* mJournal; - - // Character creation - TextInputDialog *nameDialog; - RaceDialog *raceDialog; - DialogueWindow *dialogueWindow; - ClassChoiceDialog *classChoiceDialog; - InfoBoxDialog *generateClassQuestionDialog; - GenerateClassResultDialog *generateClassResultDialog; - PickClassDialog *pickClassDialog; - CreateClassDialog *createClassDialog; - BirthDialog *birthSignDialog; - ReviewDialog *reviewDialog; - - // Keeps track of current step in Generate Class dialogs - unsigned generateClassStep; - // A counter for each specialization which is increased when an answer is chosen, in order: Stealth, Combat, Magic - unsigned generateClassSpecializations[3]; - std::string generateClass; - - // Various stats about player as needed by window manager - std::string playerName; - ESM::Class playerClass; - std::string playerRaceId, playerBirthSignId; - std::map > playerAttributes; - SkillList playerMajorSkills, playerMinorSkills; - std::map > playerSkillValues; - MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; - - // Gui - MyGUI::Gui *gui; - - // Current gui mode - GuiMode mode; - - /** - * Next mode to activate in update(). - */ - GuiMode nextMode; - /** - * Whether a mode change is needed in update(). - * Will use @a nextMode as the new mode. - */ - bool needModeChange; - - std::vector garbageDialogs; - void cleanupGarbage(); - - // Currently shown windows in inventory mode - GuiWindow shown; - - /* Currently ALLOWED windows in inventory mode. This is used at - the start of the game, when windows are enabled one by one - through script commands. You can manipulate this through using - allow() and disableAll(). - - The setting should also affect visibility of certain HUD - elements, but this is not done yet. - */ - GuiWindow allowed; - - // Update visibility of all windows based on mode, shown and - // allowed settings. - void updateVisible(); + WindowManager(MWWorld::Environment& environment, const Compiler::Extensions& extensions, int fpsLevel, bool newGame, OEngine::Render::OgreRenderer *mOgre, const std::string logpath); + virtual ~WindowManager(); void setGuiMode(GuiMode newMode); - int showFPSLevel; - float mFPS; - size_t mTriangleCount; - size_t mBatchCount; - - public: - /// The constructor needs the main Gui object - WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment, - const Compiler::Extensions& extensions, int fpsLevel, bool newGame); - virtual ~WindowManager(); - /** * Should be called each frame to update windows/gui elements. * This could mean updating sizes of gui elements or opening @@ -190,8 +108,7 @@ namespace MWGui GuiMode getMode() const { return mode; } - // Everything that is not game mode is considered "gui mode" - bool isGuiMode() const { return getMode() != GM_Game; } + bool isGuiMode() const { return getMode() != GM_Game; } // Everything that is not game mode is considered "gui mode" // Disallow all inventory mode windows void disallowAll() @@ -218,54 +135,31 @@ namespace MWGui mBatchCount = batchCount; } +// MWMechanics::DynamicStat getValue(const std::string& id); + + ///< Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - ///< Set value for the given ID. - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); - ///< Set value for the given ID. - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); - ///< Set value for the given ID. - void setValue (const std::string& id, const std::string& value); - ///< set value for the given ID. - void setValue (const std::string& id, int value); - ///< set value for the given ID. - void setPlayerClass (const ESM::Class &class_); - ///< set current class of player + void setPlayerClass (const ESM::Class &class_); ///< set current class of player + void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. + void setFactions (const FactionList& factions); ///< set faction and rank to display on stat window, use an empty vector to disable + void setBirthSign (const std::string &signId); ///< set birth sign to display on stat window, use an empty string to disable. + void setReputation (int reputation); ///< set the current reputation value + void setBounty (int bounty); ///< set the current bounty value + void updateSkillArea(); ///< update display of skills, factions, birth sign, reputation and bounty - void configureSkills (const SkillList& major, const SkillList& minor); - ///< configure skill groups, each set contains the skill ID for that group. - - void setFactions (const FactionList& factions); - ///< set faction and rank to display on stat window, use an empty vector to disable - - void setBirthSign (const std::string &signId); - ///< set birth sign to display on stat window, use an empty string to disable. - - void setReputation (int reputation); - ///< set the current reputation value - - void setBounty (int bounty); - ///< set the current bounty value - - void updateSkillArea(); - ///< update display of skills, factions, birth sign, reputation and bounty template - void removeDialog(T*& dialog); - ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. + void removeDialog(T*& dialog); ///< Casts to OEngine::GUI::Layout and calls removeDialog, then resets pointer to nullptr. + void removeDialog(OEngine::GUI::Layout* dialog); ///< Hides dialog and schedules dialog to be deleted. - void removeDialog(OEngine::GUI::Layout* dialog); - ///< Hides dialog and schedules dialog to be deleted. - void messageBox (const std::string& message, const std::vector& buttons); - - int readPressedButton (); - ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) - + int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) + void onFrame (float frameDuration); /** @@ -280,54 +174,58 @@ namespace MWGui const ESMS::ESMStore& getStore() const; private: + OEngine::GUI::MyGUIManager *mGuiManager; + MWWorld::Environment& environment; + HUD *hud; + MapWindow *map; + MainMenu *menu; + StatsWindow *stats; + MessageBoxManager *mMessageBoxManager; + Console *console; + JournalWindow* mJournal; + DialogueWindow *dialogueWindow; + + CharacterCreation* mCharGen; + + // Various stats about player as needed by window manager + ESM::Class playerClass; + std::string playerName; + std::string playerRaceId; + std::string playerBirthSignId; + std::map > playerAttributes; + SkillList playerMajorSkills, playerMinorSkills; + std::map > playerSkillValues; + MWMechanics::DynamicStat playerHealth, playerMagicka, playerFatigue; + + + MyGUI::Gui *gui; // Gui + GuiMode mode; // Current gui mode + GuiMode nextMode; // Next mode to activate in update() + bool needModeChange; //Whether a mode change is needed in update() [will use nextMode] + + std::vector garbageDialogs; + void cleanupGarbage(); + + GuiWindow shown; // Currently shown windows in inventory mode + + /* Currently ALLOWED windows in inventory mode. This is used at + the start of the game, when windows are enabled one by one + through script commands. You can manipulate this through using + allow() and disableAll(). + + The setting should also affect visibility of certain HUD + elements, but this is not done yet. + */ + GuiWindow allowed; + + void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings + + int showFPSLevel; + float mFPS; + size_t mTriangleCount; + size_t mBatchCount; void onDialogueWindowBye(); - - // Character generation: Name dialog - void onNameDialogDone(WindowBase* parWindow); - - // Character generation: Race dialog - void onRaceDialogDone(WindowBase* parWindow); - void onRaceDialogBack(); - - // Character generation: Choose class process - void onClassChoice(int _index); - - // Character generation: Generate Class - void showClassQuestionDialog(); - void onClassQuestionChosen(int _index); - void onGenerateClassBack(); - void onGenerateClassDone(WindowBase* parWindow); - - // Character generation: Pick Class dialog - void onPickClassDialogDone(WindowBase* parWindow); - void onPickClassDialogBack(); - - // Character generation: Create Class dialog - void onCreateClassDialogDone(WindowBase* parWindow); - void onCreateClassDialogBack(); - - // Character generation: Birth sign dialog - void onBirthSignDialogDone(WindowBase* parWindow); - void onBirthSignDialogBack(); - - // Character generation: Review dialog - void onReviewDialogDone(WindowBase* parWindow); - void onReviewDialogBack(); - void onReviewActivateDialog(int parDialog); - - enum CreationStageEnum - { - NotStarted, - NameChosen, - RaceChosen, - ClassChosen, - BirthSignChosen, - ReviewNext - }; - - // Which state the character creating is in, controls back/next/ok buttons - CreationStageEnum creationStage; }; template diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 3b94b19f75..acc6554049 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -12,15 +12,11 @@ void Actors::setMwRoot(Ogre::SceneNode* root){ mMwRoot = root; } void Actors::insertNPC(const MWWorld::Ptr& ptr){ - + insertBegin(ptr, true, true); NpcAnimation* anim = new MWRender::NpcAnimation(ptr, mEnvironment, mRend); - - // - - mAllActors[ptr] = anim; - - + + mAllActors[ptr] = anim; } void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ Ogre::SceneNode* cellnode; @@ -61,7 +57,7 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ } void Actors::insertCreature (const MWWorld::Ptr& ptr){ - + insertBegin(ptr, true, true); CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr, mEnvironment, mRend); //mAllActors.insert(std::pair(ptr,anim)); @@ -75,7 +71,7 @@ bool Actors::deleteObject (const MWWorld::Ptr& ptr) mAllActors.erase(ptr); if (Ogre::SceneNode *base = ptr.getRefData().getBaseNode()) { - + Ogre::SceneNode *parent = base->getParentSceneNode(); for (std::map::const_iterator iter ( @@ -110,7 +106,7 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store){ mAllActors.erase(iter); } } - + } void Actors::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number){ @@ -121,10 +117,9 @@ void Actors::skipAnimation (const MWWorld::Ptr& ptr){ if(mAllActors.find(ptr) != mAllActors.end()) mAllActors[ptr]->stopScript(); } -void Actors::addTime(){ - //std::cout << "Adding time in actors\n"; +void Actors::update (float duration){ for(std::map::iterator iter = mAllActors.begin(); iter != mAllActors.end(); iter++) { - (iter->second)->runAnimation(mEnvironment.mFrameDuration); + (iter->second)->runAnimation(duration); } } diff --git a/apps/openmw/mwrender/actors.hpp b/apps/openmw/mwrender/actors.hpp index 93ec07f98f..7179c08fb4 100644 --- a/apps/openmw/mwrender/actors.hpp +++ b/apps/openmw/mwrender/actors.hpp @@ -26,7 +26,7 @@ namespace MWRender{ MWWorld::Environment& mEnvironment; std::map mAllActors; - + public: Actors(OEngine::Render::OgreRenderer& _rend, MWWorld::Environment& _env): mRend(_rend), mEnvironment(_env){} @@ -52,8 +52,8 @@ namespace MWRender{ ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the rendered scene should be ignored. - void addTime(); - + void update (float duration); + }; } -#endif \ No newline at end of file +#endif diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index ef5c105590..863f7577b7 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -269,22 +269,6 @@ void NpcAnimation::runAnimation(float timepassed){ handleAnimationTransforms(); Ogre::Vector3 current = insert->_getWorldAABB().getCenter(); - //This is the attempt at npc physics - //mEnvironment.mWorld->setObjectPhysicsPosition(insert->getName(), current); - - - - /*if(base->hasSkeleton()) - { - - Ogre::Quaternion boneQuat = rotate; - Ogre::Vector3 boneTrans = trans; - mEnvironment.mWorld->setObjectPhysicsPosition(insert->getName(), boneTrans + insert->getPosition()); - //mEnvironment.mWorld->setObjectPhysicsRotation(insert->getName(), boneQuat * insert->getOrientation()); - - }*/ - - std::vector*>::iterator shapepartsiter = shapeparts.begin(); std::vector::iterator entitypartsiter = entityparts.begin(); while(shapepartsiter != shapeparts.end()) @@ -301,7 +285,7 @@ void NpcAnimation::runAnimation(float timepassed){ handleShapes(shapes, theentity, theentity->getSkeleton()); shapepartsiter++; entitypartsiter++; - } + } } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index adc0b23f99..2a75c2445b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -19,8 +19,6 @@ using namespace Ogre; namespace MWRender { - - RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment) :mRendering(_rend), mObjects(mRendering), mActors(mRendering, environment), mDebugging(engine) { @@ -123,7 +121,7 @@ void RenderingManager::moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Ve void RenderingManager::update (float duration){ - + mActors.update (duration); } void RenderingManager::skyEnable () @@ -245,9 +243,5 @@ void RenderingManager::skipAnimation (const MWWorld::Ptr& ptr) { mActors.skipAnimation(ptr); } -void RenderingManager::addTime(){ - mActors.addTime(); - //Notify each animation that time has passed -} } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f25dbfb540..ca5d95a3f1 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -105,14 +105,12 @@ class RenderingManager: private RenderingInterface { ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the rendered scene should be ignored. - void addTime(); - private: void setAmbientMode(); SkyManager* mSkyManager; OEngine::Render::OgreRenderer &mRendering; - + MWRender::Objects mObjects; MWRender::Actors mActors; diff --git a/apps/openmw/mwscript/controlextensions.cpp b/apps/openmw/mwscript/controlextensions.cpp index 893af259fc..384e13e450 100644 --- a/apps/openmw/mwscript/controlextensions.cpp +++ b/apps/openmw/mwscript/controlextensions.cpp @@ -1,5 +1,5 @@ -#include "statsextensions.hpp" +#include "controlextensions.hpp" #include @@ -48,7 +48,7 @@ namespace MWScript bool enabled = context.getWorld().toggleCollisionMode(); - context.report (enabled ? "Collsion -> On" : "Collision -> Off"); + context.report (enabled ? "Collision -> On" : "Collision -> Off"); } }; diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index ca82830d93..0e97a39cf4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -10,6 +10,7 @@ #include "../mwworld/class.hpp" #include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" #include "interpretercontext.hpp" #include "ref.hpp" diff --git a/apps/openmw/mwworld/customdata.hpp b/apps/openmw/mwworld/customdata.hpp new file mode 100644 index 0000000000..588991fe40 --- /dev/null +++ b/apps/openmw/mwworld/customdata.hpp @@ -0,0 +1,17 @@ +#ifndef GAME_MWWORLD_CUSTOMDATA_H +#define GAME_MWWORLD_CUSTOMDATA_H + +namespace MWWorld +{ + /// \brief Base class for the MW-class-specific part of RefData + class CustomData + { + public: + + virtual ~CustomData() {} + + virtual CustomData *clone() const = 0; + }; +} + +#endif diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index e74e201f53..f32cf97030 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -3,6 +3,7 @@ #include "physicssystem.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/world.hpp" // FIXME +#include #include "OgreRoot.h" #include "OgreRenderWindow.h" @@ -16,16 +17,24 @@ using namespace Ogre; namespace MWWorld { - PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng) : - mRender(_rend), mEngine(physEng), mFreeFly (true) + PhysicsSystem::PhysicsSystem(OEngine::Render::OgreRenderer &_rend) : + mRender(_rend), mEngine(0), mFreeFly (true) { - + // Create physics. shapeLoader is deleted by the physic engine + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); + mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); } PhysicsSystem::~PhysicsSystem() { - + delete mEngine; + } + OEngine::Physic::PhysicEngine* PhysicsSystem::getEngine() + { + return mEngine; + } + std::pair PhysicsSystem::getFacedHandle (MWWorld::World& world) { std::string handle = ""; diff --git a/apps/openmw/mwworld/physicssystem.hpp b/apps/openmw/mwworld/physicssystem.hpp index a447d7bc19..e534ee2080 100644 --- a/apps/openmw/mwworld/physicssystem.hpp +++ b/apps/openmw/mwworld/physicssystem.hpp @@ -12,7 +12,7 @@ namespace MWWorld class PhysicsSystem { public: - PhysicsSystem (OEngine::Render::OgreRenderer &_rend , OEngine::Physic::PhysicEngine* physEng); + PhysicsSystem (OEngine::Render::OgreRenderer &_rend); ~PhysicsSystem (); std::vector< std::pair > doPhysics (float duration, @@ -39,6 +39,8 @@ namespace MWWorld void insertActorPhysics(const MWWorld::Ptr&, std::string model); + OEngine::Physic::PhysicEngine* getEngine(); + private: OEngine::Render::OgreRenderer &mRender; OEngine::Physic::PhysicEngine* mEngine; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 4eb41ebf5c..5bfb82138c 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -3,6 +3,8 @@ #include "../mwrender/player.hpp" +#include "../mwmechanics/movement.hpp" + #include "world.hpp" #include "class.hpp" diff --git a/apps/openmw/mwworld/ptr.hpp b/apps/openmw/mwworld/ptr.hpp index 8bf75aa3cb..389c9349d5 100644 --- a/apps/openmw/mwworld/ptr.hpp +++ b/apps/openmw/mwworld/ptr.hpp @@ -5,6 +5,8 @@ #include +#include + #include #include "refdata.hpp" diff --git a/apps/openmw/mwworld/refdata.cpp b/apps/openmw/mwworld/refdata.cpp new file mode 100644 index 0000000000..528f49c33e --- /dev/null +++ b/apps/openmw/mwworld/refdata.cpp @@ -0,0 +1,144 @@ + +#include "refdata.hpp" + +#include + +#include "customdata.hpp" + +namespace MWWorld +{ + void RefData::copy (const RefData& refData) + { + mBaseNode = refData.mBaseNode; + mLocals = refData.mLocals; + mHasLocals = refData.mHasLocals; + mEnabled = refData.mEnabled; + mCount = refData.mCount; + mPosition = refData.mPosition; + + mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0; + } + + void RefData::cleanup() + { + mBaseNode = 0; + + delete mCustomData; + mCustomData = 0; + } + + RefData::RefData (const ESM::CellRef& cellRef) + : mBaseNode(0), mHasLocals (false), mEnabled (true), mCount (1), mPosition (cellRef.pos), + mCustomData (0) + {} + + RefData::RefData (const RefData& refData) + : mBaseNode(0), mCustomData (0) + { + try + { + copy (refData); + } + catch (...) + { + cleanup(); + throw; + } + } + + RefData& RefData::operator= (const RefData& refData) + { + try + { + cleanup(); + copy (refData); + } + catch (...) + { + cleanup(); + throw; + } + + return *this; + } + + RefData::~RefData() + { + try + { + cleanup(); + } + catch (...) + {} + } + + std::string RefData::getHandle() + { + return mBaseNode->getName(); + } + + Ogre::SceneNode* RefData::getBaseNode() + { + return mBaseNode; + } + + void RefData::setBaseNode(Ogre::SceneNode* base) + { + mBaseNode = base; + } + + int RefData::getCount() const + { + return mCount; + } + + void RefData::setLocals (const ESM::Script& script) + { + if (!mHasLocals) + { + mLocals.configure (script); + mHasLocals = true; + } + } + + void RefData::setCount (int count) + { + mCount = count; + } + + MWScript::Locals& RefData::getLocals() + { + return mLocals; + } + + bool RefData::isEnabled() const + { + return mEnabled; + } + + void RefData::enable() + { + mEnabled = true; + } + + void RefData::disable() + { + mEnabled = true; + } + + ESM::Position& RefData::getPosition() + { + return mPosition; + } + + void RefData::setCustomData (CustomData *data) + { + delete mCustomData; + mCustomData = data; + } + + CustomData *RefData::getCustomData() + { + return mCustomData; + } +} diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 9a91a27b56..30d676f130 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -3,24 +3,22 @@ #include -#include +#include + +#include #include "../mwscript/locals.hpp" -#include "../mwmechanics/creaturestats.hpp" -#include "../mwmechanics/npcstats.hpp" -#include "../mwmechanics/movement.hpp" - -#include "containerstore.hpp" -#include - namespace ESM { class Script; + class CellRef; } namespace MWWorld { + class CustomData; + class RefData { Ogre::SceneNode* mBaseNode; @@ -33,102 +31,58 @@ namespace MWWorld bool mEnabled; int mCount; // 0: deleted - // we are using shared pointer here to avoid having to create custom copy-constructor, - // assignment operator and destructor. As a consequence though copying a RefData object - // manually will probably give unexcepted results. This is not a problem since RefData - // are never copied outside of container operations. - boost::shared_ptr mCreatureStats; - boost::shared_ptr mNpcStats; - boost::shared_ptr mMovement; - - boost::shared_ptr > mContainerStore; - ESM::Position mPosition; + CustomData *mCustomData; + + void copy (const RefData& refData); + + void cleanup(); public: - /// @param cr Used to copy constant data such as position into this class where it can - /// be altered without effecting the original data. This makes it possible - /// to reset the position as the orignal data is still held in the CellRef - RefData(const ESMS::CellRef& cr) : mBaseNode(0), mHasLocals (false), mEnabled (true), - mCount (1), mPosition(cr.pos) {} + /// @param cellRef Used to copy constant data such as position into this class where it can + /// be altered without effecting the original data. This makes it possible + /// to reset the position as the orignal data is still held in the CellRef + RefData (const ESM::CellRef& cellRef); - std::string getHandle() - { - return mBaseNode->getName(); - } - Ogre::SceneNode* getBaseNode(){ - return mBaseNode; - } - void setBaseNode(Ogre::SceneNode* base){ - mBaseNode = base; - } + RefData (const RefData& refData); - int getCount() const - { - return mCount; - } + ~RefData(); - void setLocals (const ESM::Script& script) - { - if (!mHasLocals) - { - mLocals.configure (script); - mHasLocals = true; - } - } + RefData& operator= (const RefData& refData); + /// Return OGRE handle (may be empty). + std::string getHandle(); - void setCount (int count) - { - mCount = count; - } + /// Return OGRE base node (can be a null pointer). + Ogre::SceneNode* getBaseNode(); - MWScript::Locals& getLocals() - { - return mLocals; - } + /// Set OGRE base node (can be a null pointer). + void setBaseNode (Ogre::SceneNode* base); - bool isEnabled() const - { - return mEnabled; - } + int getCount() const; - void enable() - { - mEnabled = true; - } + void setLocals (const ESM::Script& script); - void disable() - { - mEnabled = true; - } + void setCount (int count); - boost::shared_ptr& getCreatureStats() - { - return mCreatureStats; - } + MWScript::Locals& getLocals(); - boost::shared_ptr& getNpcStats() - { - return mNpcStats; - } + bool isEnabled() const; - boost::shared_ptr& getMovement() - { - return mMovement; - } + void enable(); - boost::shared_ptr >& getContainerStore() - { - return mContainerStore; - } + void disable(); - ESM::Position& getPosition() - { - return mPosition; - } + ESM::Position& getPosition(); + + void setCustomData (CustomData *data); + ///< Set custom data (potentially replacing old custom data). The ownership of \æ data is + /// transferred to this. + + CustomData *getCustomData(); + ///< May return a 0-pointer. The ownership of the return data object is not transferred. }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index b08a52aac6..2f0d33c971 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -52,8 +52,8 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme namespace MWWorld { - void Scene::advanceTime(){ - mRendering.addTime(); + void Scene::update (float duration){ + mRendering.update (duration); } void Scene::unloadCell (CellStoreCollection::iterator iter) { @@ -61,9 +61,9 @@ namespace MWWorld ListHandles functor; MWWorld::Ptr::CellStore* active = *iter; - - - + + + active->forEach(functor); @@ -83,7 +83,7 @@ namespace MWWorld mEnvironment.mMechanicsManager->dropActors (active); mEnvironment.mSoundManager->stopSound (active); mActiveCells.erase(active); - + } void Scene::loadCell (Ptr::CellStore *cell) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index d3df7c0f5f..4d1bd6fb51 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,7 +101,8 @@ namespace MWWorld void markCellAsUnchanged(); void insertCell(ESMS::CellStore &cell, MWWorld::Environment& environment); - void advanceTime(); + + void update (float duration); }; } diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 2232e8a1d4..17c2279536 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -143,16 +143,17 @@ namespace MWWorld } } - World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, + World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment, const std::string& encoding) - : mRendering (renderer,resDir, physEng, environment),mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), + : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (false), mEnvironment (environment), mNextDynamicRecord (0), mCells (mStore, mEsm, *this) { - mPhysEngine = physEng; - - mPhysics = new PhysicsSystem(renderer, physEng); + mPhysics = new PhysicsSystem(renderer); + mPhysEngine = mPhysics->getEngine(); + + mRendering = new MWRender::RenderingManager(renderer, resDir, mPhysEngine, environment); boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master)); @@ -163,7 +164,7 @@ namespace MWWorld mEsm.open (masterPath.string()); mStore.load (mEsm); - MWRender::Player* play = &(mRendering.getPlayer()); + MWRender::Player* play = &(mRendering->getPlayer()); mPlayer = new MWWorld::Player (play, mStore.npcs.find ("player"), *this); mPhysics->addActor (mPlayer->getPlayer().getRefData().getHandle(), "", Ogre::Vector3 (0, 0, 0)); @@ -176,9 +177,7 @@ namespace MWWorld mGlobalVariables->setInt ("chargenstate", 1); } - mPhysEngine = physEng; - - mWorldScene = new Scene(environment, this, mRendering, mPhysics); + mWorldScene = new Scene(environment, this, *mRendering, mPhysics); } @@ -186,7 +185,7 @@ namespace MWWorld { delete mWorldScene; delete mGlobalVariables; - + delete mRendering; delete mPhysics; delete mPlayer; @@ -348,14 +347,13 @@ namespace MWWorld void World::advanceTime (double hours) { hours += mGlobalVariables->getFloat ("gamehour"); - + setHour (hours); int days = hours / 24; if (days>0) mGlobalVariables->setInt ("dayspassed", days + mGlobalVariables->getInt ("dayspassed")); - mWorldScene->advanceTime(); } void World::setHour (double hour) @@ -369,7 +367,7 @@ namespace MWWorld mGlobalVariables->setFloat ("gamehour", hour); - mRendering.skySetHour (hour); + mRendering->skySetHour (hour); if (days>0) setDay (days + mGlobalVariables->getInt ("day")); @@ -404,7 +402,7 @@ namespace MWWorld mGlobalVariables->setInt ("day", day); mGlobalVariables->setInt ("month", month); - mRendering.skySetDate (day, month); + mRendering->skySetDate (day, month); } void World::setMonth (int month) @@ -425,7 +423,7 @@ namespace MWWorld if (years>0) mGlobalVariables->setInt ("year", years+mGlobalVariables->getInt ("year")); - mRendering.skySetDate (mGlobalVariables->getInt ("day"), month); + mRendering->skySetDate (mGlobalVariables->getInt ("day"), month); } bool World::toggleSky() @@ -433,34 +431,34 @@ namespace MWWorld if (mSky) { mSky = false; - mRendering.skyDisable(); + mRendering->skyDisable(); return false; } else { mSky = true; // TODO check for extorior or interior with sky. - mRendering.skySetHour (mGlobalVariables->getFloat ("gamehour")); - mRendering.skySetDate (mGlobalVariables->getInt ("day"), + mRendering->skySetHour (mGlobalVariables->getFloat ("gamehour")); + mRendering->skySetDate (mGlobalVariables->getInt ("day"), mGlobalVariables->getInt ("month")); - mRendering.skyEnable(); + mRendering->skyEnable(); return true; } } int World::getMasserPhase() const { - return mRendering.skyGetMasserPhase(); + return mRendering->skyGetMasserPhase(); } int World::getSecundaPhase() const { - return mRendering.skyGetSecundaPhase(); + return mRendering->skyGetSecundaPhase(); } void World::setMoonColour (bool red) { - mRendering.skySetMoonColour (red); + mRendering->skySetMoonColour (red); } float World::getTimeScaleFactor() const @@ -506,7 +504,7 @@ namespace MWWorld mEnvironment.mSoundManager->stopSound3D (ptr); mPhysics->removeObject (ptr.getRefData().getHandle()); - mRendering.removeObject(ptr); + mRendering->removeObject(ptr); mLocalScripts.remove (ptr); } @@ -543,7 +541,7 @@ namespace MWWorld /// \todo cell change for non-player ref - mRendering.moveObject (ptr, Ogre::Vector3 (x, y, z)); + mRendering->moveObject (ptr, Ogre::Vector3 (x, y, z)); } void World::moveObject (Ptr ptr, float x, float y, float z) @@ -617,7 +615,7 @@ namespace MWWorld bool World::toggleRenderMode (RenderMode mode) { - return mRendering.toggleRenderMode (mode); + return mRendering->toggleRenderMode (mode); } std::pair World::createRecord (const ESM::Potion& record) @@ -678,17 +676,16 @@ namespace MWWorld void World::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number) { - mRendering.playAnimationGroup (ptr, groupName, mode, number); + mRendering->playAnimationGroup (ptr, groupName, mode, number); } void World::skipAnimation (const MWWorld::Ptr& ptr) { - mRendering.skipAnimation (ptr); + mRendering->skipAnimation (ptr); } - void World::setObjectPhysicsRotation(const std::string& handle, Ogre::Quaternion quat){ - mPhysics->rotateObject(handle, quat); - } - void World::setObjectPhysicsPosition(const std::string& handle, Ogre::Vector3 vec){ - mPhysics->moveObject(handle, vec); + + void World::update (float duration) + { + mWorldScene->update (duration); } } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 235d203a60..8939804562 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -65,7 +65,7 @@ namespace MWWorld private: - MWRender::RenderingManager mRendering; + MWRender::RenderingManager* mRendering; MWWorld::Scene *mWorldScene; MWWorld::Player *mPlayer; @@ -95,7 +95,7 @@ namespace MWWorld public: - World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, + World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, const std::string& master, const boost::filesystem::path& resDir, bool newGame, Environment& environment, const std::string& encoding); @@ -140,12 +140,16 @@ namespace MWWorld void disable (Ptr reference); void advanceTime (double hours); + ///< Advance in-game time. void setHour (double hour); + ///< Set in-game time hour. void setMonth (int month); + ///< Set in-game time month. void setDay (int day); + ///< Set in-game time day. bool toggleSky(); ///< \return Resulting mode @@ -218,8 +222,8 @@ namespace MWWorld void skipAnimation (const MWWorld::Ptr& ptr); ///< Skip the animation for the given MW-reference for one frame. Calls to this function for /// references that are currently not in the rendered scene should be ignored. - void setObjectPhysicsRotation(const std::string& handle,Ogre::Quaternion quat); - void setObjectPhysicsPosition(const std::string& handle,Ogre::Vector3 vector); + + void update (float duration); }; } diff --git a/cmake/FindOGRE.cmake b/cmake/FindOGRE.cmake index ce3993805b..01fb04ac19 100644 --- a/cmake/FindOGRE.cmake +++ b/cmake/FindOGRE.cmake @@ -1,110 +1,542 @@ -# Find OGRE includes and library +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ # -# This module defines -# OGRE_INCLUDE_DIR -# OGRE_LIBRARIES, the libraries to link against to use OGRE. -# OGRE_LIB_DIR, the location of the libraries -# OGRE_FOUND, If false, do not try to use OGRE +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +# - Try to find OGRE +# If you have multiple versions of Ogre installed, use the CMake or +# the environment variable OGRE_HOME to point to the path where the +# desired Ogre version can be found. +# By default this script will look for a dynamic Ogre build. If you +# need to link against static Ogre libraries, set the CMake variable +# OGRE_STATIC to TRUE. # -# Copyright © 2007, Matt Williams +# Once done, this will define # -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -CMAKE_POLICY(PUSH) +# OGRE_FOUND - system has OGRE +# OGRE_INCLUDE_DIRS - the OGRE include directories +# OGRE_LIBRARIES - link these to use the OGRE core +# OGRE_BINARY_REL - location of the main Ogre binary (win32 non-static only, release) +# OGRE_BINARY_DBG - location of the main Ogre binaries (win32 non-static only, debug) +# +# Additionally this script searches for the following optional +# parts of the Ogre package: +# Plugin_BSPSceneManager, Plugin_CgProgramManager, +# Plugin_OctreeSceneManager, Plugin_OctreeZone, +# Plugin_ParticleFX, Plugin_PCZSceneManager, +# RenderSystem_GL, RenderSystem_Direct3D9, +# Paging, Terrain +# +# For each of these components, the following variables are defined: +# +# OGRE_${COMPONENT}_FOUND - ${COMPONENT} is available +# OGRE_${COMPONENT}_INCLUDE_DIRS - additional include directories for ${COMPONENT} +# OGRE_${COMPONENT}_LIBRARIES - link these to use ${COMPONENT} +# OGRE_${COMPONENT}_BINARY_REL - location of the component binary (win32 non-static only, release) +# OGRE_${COMPONENT}_BINARY_DBG - location of the component binary (win32 non-static only, debug) +# +# Finally, the following variables are defined: +# +# OGRE_PLUGIN_DIR_REL - The directory where the release versions of +# the OGRE plugins are located +# OGRE_PLUGIN_DIR_DBG - The directory where the debug versions of +# the OGRE plugins are located +# OGRE_MEDIA_DIR - The directory where the OGRE sample media is +# located, if available -IF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR) - SET(OGRE_FIND_QUIETLY TRUE) # Already in cache, be silent -ENDIF (OGRE_LIBRARIES AND OGRE_INCLUDE_DIR AND OGRE_LIB_DIR AND OGRE_PLUGIN_DIR) +include(FindPkgMacros) +include(PreprocessorUtils) +findpkg_begin(OGRE) -IF (WIN32) #Windows - MESSAGE(STATUS "Looking for OGRE") - SET(OGRESDK $ENV{OGRE_HOME}) - SET(OGRESOURCE $ENV{OGRE_SRC}) - IF (OGRESDK) - MESSAGE(STATUS "Using OGRE SDK") - STRING(REGEX REPLACE "[\\]" "/" OGRESDK "${OGRESDK}") - SET(OGRE_INCLUDE_DIR ${OGRESDK}/include) - SET(OGRE_LIB_DIR ${OGRESDK}/lib) - SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESDK) - IF (OGRESOURCE) - MESSAGE(STATUS "Using OGRE built from source") - SET(OGRE_INCLUDE_DIR $ENV{OGRE_SRC}/OgreMain/include) - SET(OGRE_LIB_DIR $ENV{OGRE_SRC}/lib) - SET(OGRE_LIBRARIES debug OgreMain_d optimized OgreMain) - ENDIF (OGRESOURCE) -ENDIF (WIN32) -IF (UNIX AND NOT APPLE) - CMAKE_MINIMUM_REQUIRED(VERSION 2.4.7 FATAL_ERROR) - FIND_PACKAGE(PkgConfig REQUIRED) - # Don't mark REQUIRED, but use PKG_CHECK_MODULES below (otherwise PkgConfig - # complains even if OGRE_* are set by hand). - PKG_SEARCH_MODULE(OGRE OGRE) - SET(OGRE_INCLUDE_DIR ${OGRE_INCLUDE_DIRS}) - SET(OGRE_LIB_DIR ${OGRE_LIBDIR}) - SET(OGRE_LIBRARIES ${OGRE_LIBRARIES} CACHE STRING "") - PKG_CHECK_MODULES(OGRE OGRE) -ENDIF (UNIX AND NOT APPLE) +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(OGRE_HOME) +getenv_path(OGRE_SDK) +getenv_path(OGRE_SOURCE) +getenv_path(OGRE_BUILD) +getenv_path(OGRE_DEPENDENCIES_DIR) +getenv_path(PROGRAMFILES) -# on OS X we need Ogre SDK because framework doesn't include all libs, just Ogre Main lib -IF (APPLE) - IF (OGRESDK) - MESSAGE(STATUS "Using Ogre SDK") - SET(OGRE_LIB_DIR ${OGRESDK}/lib) - ELSE (OGRESDK) - MESSAGE(FATAL_ERROR "Path to Ogre SDK not specified. Specify OGRESDK.") - ENDIF (OGRESDK) - +# Determine whether to search for a dynamic or static build +if (OGRE_STATIC) + set(OGRE_LIB_SUFFIX "Static") +else () + set(OGRE_LIB_SUFFIX "") +endif () - FIND_PATH(OGRE_INCLUDE_DIR Ogre.h - PATHS - /Library/Frameworks - /opt/local - ) - FIND_LIBRARY(OGRE_LIBRARIES - NAMES Ogre - PATHS - /Library/Frameworks - /opt/local - ) -ENDIF (APPLE) -#Do some preparation -SEPARATE_ARGUMENTS(OGRE_INCLUDE_DIR) -SEPARATE_ARGUMENTS(OGRE_LIBRARIES) +set(OGRE_LIBRARY_NAMES "OgreMain${OGRE_LIB_SUFFIX}") +get_debug_names(OGRE_LIBRARY_NAMES) -SET(OGRE_INCLUDE_DIR ${OGRE_INCLUDE_DIR} CACHE PATH "") -SET(OGRE_LIBRARIES ${OGRE_LIBRARIES} CACHE STRING "") -SET(OGRE_LIB_DIR ${OGRE_LIB_DIR} CACHE PATH "") +# construct search paths from environmental hints and +# OS specific guesses +if (WIN32) + set(OGRE_PREFIX_GUESSES + ${ENV_PROGRAMFILES}/OGRE + C:/OgreSDK + ) +elseif (UNIX) + set(OGRE_PREFIX_GUESSES + /opt/ogre + /opt/OGRE + /usr/lib${LIB_SUFFIX}/ogre + /usr/lib${LIB_SUFFIX}/OGRE + /usr/local/lib${LIB_SUFFIX}/ogre + /usr/local/lib${LIB_SUFFIX}/OGRE + $ENV{HOME}/ogre + $ENV{HOME}/OGRE + ) +endif () +set(OGRE_PREFIX_PATH + ${OGRE_HOME} ${OGRE_SDK} ${ENV_OGRE_HOME} ${ENV_OGRE_SDK} + ${OGRE_PREFIX_GUESSES} +) +create_search_paths(OGRE) +# If both OGRE_BUILD and OGRE_SOURCE are set, prepare to find Ogre in a build dir +set(OGRE_PREFIX_SOURCE ${OGRE_SOURCE} ${ENV_OGRE_SOURCE}) +set(OGRE_PREFIX_BUILD ${OGRE_BUILD} ${ENV_OGRE_BUILD}) +set(OGRE_PREFIX_DEPENDENCIES_DIR ${OGRE_DEPENDENCIES_DIR} ${ENV_OGRE_DEPENDENCIES_DIR}) +if (OGRE_PREFIX_SOURCE AND OGRE_PREFIX_BUILD) + foreach(dir ${OGRE_PREFIX_SOURCE}) + set(OGRE_INC_SEARCH_PATH ${dir}/OgreMain/include ${dir}/Dependencies/include ${dir}/iPhoneDependencies/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${dir}/lib ${dir}/Dependencies/lib ${dir}/iPhoneDependencies/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/Samples/Common/bin ${OGRE_BIN_SEARCH_PATH}) + endforeach(dir) + foreach(dir ${OGRE_PREFIX_BUILD}) + set(OGRE_INC_SEARCH_PATH ${dir}/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${dir}/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/bin ${OGRE_BIN_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${dir}/Samples/Common/bin ${OGRE_BIN_SEARCH_PATH}) + endforeach(dir) + + if (OGRE_PREFIX_DEPENDENCIES_DIR) + set(OGRE_INC_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/include ${OGRE_INC_SEARCH_PATH}) + set(OGRE_LIB_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/lib ${OGRE_LIB_SEARCH_PATH}) + set(OGRE_BIN_SEARCH_PATH ${OGRE_PREFIX_DEPENDENCIES_DIR}/bin ${OGRE_BIN_SEARCH_PATH}) + endif() +else() + set(OGRE_PREFIX_SOURCE "NOTFOUND") + set(OGRE_PREFIX_BUILD "NOTFOUND") +endif () -if(OGRE_LIB_DIR) - CMAKE_POLICY(SET CMP0009 NEW) - IF (NOT APPLE) - FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.so") - ENDIF (NOT APPLE) - IF (APPLE) - FILE(GLOB_RECURSE OGRE_PLUGINS "${OGRE_LIB_DIR}/Plugin_*.dylib") - ENDIF (APPLE) - FOREACH (OGRE_PLUGINS_FILE ${OGRE_PLUGINS}) - STRING(REGEX REPLACE "/[^/]*$" "" OGRE_PLUGIN_DIR ${OGRE_PLUGINS_FILE}) - ENDFOREACH(OGRE_PLUGINS_FILE) +# redo search if any of the environmental hints changed +set(OGRE_COMPONENTS Paging Terrain + Plugin_BSPSceneManager Plugin_CgProgramManager Plugin_OctreeSceneManager + Plugin_OctreeZone Plugin_PCZSceneManager Plugin_ParticleFX + RenderSystem_Direct3D11 RenderSystem_Direct3D9 RenderSystem_GL RenderSystem_GLES RenderSystem_GLES2) +set(OGRE_RESET_VARS + OGRE_CONFIG_INCLUDE_DIR OGRE_INCLUDE_DIR + OGRE_LIBRARY_FWK OGRE_LIBRARY_REL OGRE_LIBRARY_DBG + OGRE_PLUGIN_DIR_DBG OGRE_PLUGIN_DIR_REL OGRE_MEDIA_DIR) +foreach (comp ${OGRE_COMPONENTS}) + set(OGRE_RESET_VARS ${OGRE_RESET_VARS} + OGRE_${comp}_INCLUDE_DIR OGRE_${comp}_LIBRARY_FWK + OGRE_${comp}_LIBRARY_DBG OGRE_${comp}_LIBRARY_REL + ) +endforeach (comp) +set(OGRE_PREFIX_WATCH ${OGRE_PREFIX_PATH} ${OGRE_PREFIX_SOURCE} ${OGRE_PREFIX_BUILD}) +clear_if_changed(OGRE_PREFIX_WATCH ${OGRE_RESET_VARS}) + +# try to locate Ogre via pkg-config +use_pkgconfig(OGRE_PKGC "OGRE${OGRE_LIB_SUFFIX}") + +if(NOT OGRE_BUILD_PLATFORM_APPLE_IOS) + # try to find framework on OSX + findpkg_framework(OGRE) +else() + set(OGRE_LIBRARY_FWK "") endif() -IF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) - SET(OGRE_FOUND TRUE) -ENDIF (OGRE_INCLUDE_DIR AND OGRE_LIBRARIES) +# locate Ogre include files +find_path(OGRE_CONFIG_INCLUDE_DIR NAMES OgreBuildSettings.h HINTS ${OGRE_INC_SEARCH_PATH} ${OGRE_FRAMEWORK_INCLUDES} ${OGRE_PKGC_INCLUDE_DIRS} PATH_SUFFIXES "OGRE") +find_path(OGRE_INCLUDE_DIR NAMES OgreRoot.h HINTS ${OGRE_CONFIG_INCLUDE_DIR} ${OGRE_INC_SEARCH_PATH} ${OGRE_FRAMEWORK_INCLUDES} ${OGRE_PKGC_INCLUDE_DIRS} PATH_SUFFIXES "OGRE") +set(OGRE_INCOMPATIBLE FALSE) -IF (OGRE_FOUND) - IF (NOT OGRE_FIND_QUIETLY) - MESSAGE(STATUS " libraries : ${OGRE_LIBRARIES} from ${OGRE_LIB_DIR}") - MESSAGE(STATUS " includes : ${OGRE_INCLUDE_DIR}") - MESSAGE(STATUS " plugins : ${OGRE_PLUGIN_DIR}") - ENDIF (NOT OGRE_FIND_QUIETLY) -ELSE (OGRE_FOUND) - IF (OGRE_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find OGRE") - ENDIF (OGRE_FIND_REQUIRED) -ENDIF (OGRE_FOUND) +if (OGRE_INCLUDE_DIR) + if (NOT OGRE_CONFIG_INCLUDE_DIR) + set(OGRE_CONFIG_INCLUDE_DIR ${OGRE_INCLUDE_DIR}) + endif () + # determine Ogre version + file(READ ${OGRE_INCLUDE_DIR}/OgrePrerequisites.h OGRE_TEMP_VERSION_CONTENT) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_MAJOR OGRE_VERSION_MAJOR) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_MINOR OGRE_VERSION_MINOR) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_PATCH OGRE_VERSION_PATCH) + get_preprocessor_entry(OGRE_TEMP_VERSION_CONTENT OGRE_VERSION_NAME OGRE_VERSION_NAME) + set(OGRE_VERSION "${OGRE_VERSION_MAJOR}.${OGRE_VERSION_MINOR}.${OGRE_VERSION_PATCH}") + pkg_message(OGRE "Found Ogre ${OGRE_VERSION_NAME} (${OGRE_VERSION})") + + # determine configuration settings + set(OGRE_CONFIG_HEADERS + ${OGRE_CONFIG_INCLUDE_DIR}/OgreBuildSettings.h + ${OGRE_CONFIG_INCLUDE_DIR}/OgreConfig.h + ) + foreach(CFG_FILE ${OGRE_CONFIG_HEADERS}) + if (EXISTS ${CFG_FILE}) + set(OGRE_CONFIG_HEADER ${CFG_FILE}) + break() + endif() + endforeach() + if (OGRE_CONFIG_HEADER) + file(READ ${OGRE_CONFIG_HEADER} OGRE_TEMP_CONFIG_CONTENT) + has_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_STATIC_LIB OGRE_CONFIG_STATIC) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_THREAD_SUPPORT OGRE_CONFIG_THREADS) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_THREAD_PROVIDER OGRE_CONFIG_THREAD_PROVIDER) + get_preprocessor_entry(OGRE_TEMP_CONFIG_CONTENT OGRE_NO_FREEIMAGE OGRE_CONFIG_FREEIMAGE) + if (OGRE_CONFIG_STATIC AND OGRE_STATIC) + elseif (OGRE_CONFIG_STATIC OR OGRE_STATIC) + pkg_message(OGRE "Build type (static, dynamic) does not match the requested one.") + set(OGRE_INCOMPATIBLE TRUE) + endif () + else () + pkg_message(OGRE "Could not determine Ogre build configuration.") + set(OGRE_INCOMPATIBLE TRUE) + endif () +else () + set(OGRE_INCOMPATIBLE FALSE) +endif () + +find_library(OGRE_LIBRARY_REL NAMES ${OGRE_LIBRARY_NAMES} HINTS ${OGRE_LIB_SEARCH_PATH} ${OGRE_PKGC_LIBRARY_DIRS} ${OGRE_FRAMEWORK_SEARCH_PATH} PATH_SUFFIXES "" "release" "relwithdebinfo" "minsizerel") +find_library(OGRE_LIBRARY_DBG NAMES ${OGRE_LIBRARY_NAMES_DBG} HINTS ${OGRE_LIB_SEARCH_PATH} ${OGRE_PKGC_LIBRARY_DIRS} ${OGRE_FRAMEWORK_SEARCH_PATH} PATH_SUFFIXES "" "debug") +make_library_set(OGRE_LIBRARY) + +if(APPLE) + set(OGRE_LIBRARY_DBG ${OGRE_LIB_SEARCH_PATH}) +endif() +if (OGRE_INCOMPATIBLE) + set(OGRE_LIBRARY "NOTFOUND") +endif () + +set(OGRE_INCLUDE_DIR ${OGRE_CONFIG_INCLUDE_DIR} ${OGRE_INCLUDE_DIR}) +list(REMOVE_DUPLICATES OGRE_INCLUDE_DIR) +findpkg_finish(OGRE) +add_parent_dir(OGRE_INCLUDE_DIRS OGRE_INCLUDE_DIR) +if (OGRE_SOURCE) + # If working from source rather than SDK, add samples include + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} "${OGRE_SOURCE}/Samples/Common/include") +endif() + +mark_as_advanced(OGRE_CONFIG_INCLUDE_DIR OGRE_MEDIA_DIR OGRE_PLUGIN_DIR_REL OGRE_PLUGIN_DIR_DBG) + +if (NOT OGRE_FOUND) + return() +endif () + + +# look for required Ogre dependencies in case of static build and/or threading +if (OGRE_STATIC) + set(OGRE_DEPS_FOUND TRUE) + find_package(Cg QUIET) + find_package(DirectX QUIET) + find_package(FreeImage QUIET) + find_package(Freetype QUIET) + find_package(OpenGL QUIET) + find_package(OpenGLES QUIET) + find_package(OpenGLES2 QUIET) + find_package(ZLIB QUIET) + find_package(ZZip QUIET) + if (UNIX AND NOT APPLE) + find_package(X11 QUIET) + find_library(XAW_LIBRARY NAMES Xaw Xaw7 PATHS ${DEP_LIB_SEARCH_DIR} ${X11_LIB_SEARCH_PATH}) + if (NOT XAW_LIBRARY OR NOT X11_Xt_FOUND) + set(X11_FOUND FALSE) + endif () + endif () + if (APPLE AND NOT OGRE_BUILD_PLATFORM_APPLE_IOS) + find_package(Cocoa QUIET) + find_package(Carbon QUIET) + find_package(CoreVideo QUIET) + if (NOT Cocoa_FOUND OR NOT Carbon_FOUND OR NOT CoreVideo_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + if (APPLE AND OGRE_BUILD_PLATFORM_APPLE_IOS) + find_package(iPhoneSDK QUIET) + if (NOT iPhoneSDK_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${ZZip_LIBRARIES} ${ZLIB_LIBRARIES} ${FreeImage_LIBRARIES} ${FREETYPE_LIBRARIES} ) + + if (APPLE AND NOT OGRE_BUILD_PLATFORM_APPLE_IOS) + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${X11_LIBRARIES} ${X11_Xt_LIBRARIES} ${XAW_LIBRARY} ${X11_Xrandr_LIB} ${Carbon_LIBRARIES} ${Cocoa_LIBRARIES}) + endif() + + if (NOT ZLIB_FOUND OR NOT ZZip_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (NOT FreeImage_FOUND AND NOT OGRE_CONFIG_FREEIMAGE) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (NOT FREETYPE_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + if (UNIX AND NOT APPLE) + if (NOT X11_FOUND) + set(OGRE_DEPS_FOUND FALSE) + endif () + endif () + + if (OGRE_CONFIG_THREADS) + if (OGRE_CONFIG_THREAD_PROVIDER EQUAL 1) + find_package(Boost COMPONENTS thread QUIET) + if (NOT Boost_THREAD_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${Boost_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + endif () + elseif (OGRE_CONFIG_THREAD_PROVIDER EQUAL 2) + find_package(POCO QUIET) + if (NOT POCO_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${POCO_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${POCO_INCLUDE_DIRS}) + endif () + elseif (OGRE_CONFIG_THREAD_PROVIDER EQUAL 3) + find_package(TBB QUIET) + if (NOT TBB_FOUND) + set(OGRE_DEPS_FOUND FALSE) + else () + set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${TBB_LIBRARIES}) + set(OGRE_INCLUDE_DIRS ${OGRE_INCLUDE_DIRS} ${TBB_INCLUDE_DIRS}) + endif () + endif () + endif () + + if (NOT OGRE_DEPS_FOUND) + pkg_message(OGRE "Could not find all required dependencies for the Ogre package.") + set(OGRE_FOUND FALSE) + endif () +endif () + +if (NOT OGRE_FOUND) + return() +endif () + + +get_filename_component(OGRE_LIBRARY_DIR_REL "${OGRE_LIBRARY_REL}" PATH) +get_filename_component(OGRE_LIBRARY_DIR_DBG "${OGRE_LIBRARY_DBG}" PATH) +set(OGRE_LIBRARY_DIRS ${OGRE_LIBRARY_DIR_REL} ${OGRE_LIBRARY_DIR_DBG}) + +# find binaries +if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_BINARY_REL NAMES "OgreMain.dll" HINTS ${OGRE_BIN_SEARCH_PATH} + PATH_SUFFIXES "" release relwithdebinfo minsizerel) + find_file(OGRE_BINARY_DBG NAMES "OgreMain_d.dll" HINTS ${OGRE_BIN_SEARCH_PATH} + PATH_SUFFIXES "" debug ) + endif() + mark_as_advanced(OGRE_BINARY_REL OGRE_BINARY_DBG) +endif() + + +######################################################### +# Find Ogre components +######################################################### + +set(OGRE_COMPONENT_SEARCH_PATH_REL + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_BIN_SEARCH_PATH} +) +set(OGRE_COMPONENT_SEARCH_PATH_DBG + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_BIN_SEARCH_PATH} +) + +macro(ogre_find_component COMPONENT HEADER) + findpkg_begin(OGRE_${COMPONENT}) + find_path(OGRE_${COMPONENT}_INCLUDE_DIR NAMES ${HEADER} HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE} PATH_SUFFIXES ${COMPONENT} OGRE/${COMPONENT} Components/${COMPONENT}/include) + set(OGRE_${COMPONENT}_LIBRARY_NAMES "Ogre${COMPONENT}${OGRE_LIB_SUFFIX}") + get_debug_names(OGRE_${COMPONENT}_LIBRARY_NAMES) + find_library(OGRE_${COMPONENT}_LIBRARY_REL NAMES ${OGRE_${COMPONENT}_LIBRARY_NAMES} HINTS ${OGRE_LIBRARY_DIR_REL} PATH_SUFFIXES "" "release" "relwithdebinfo" "minsizerel") + find_library(OGRE_${COMPONENT}_LIBRARY_DBG NAMES ${OGRE_${COMPONENT}_LIBRARY_NAMES_DBG} HINTS ${OGRE_LIBRARY_DIR_DBG} PATH_SUFFIXES "" "debug") + make_library_set(OGRE_${COMPONENT}_LIBRARY) + findpkg_finish(OGRE_${COMPONENT}) + if (OGRE_${COMPONENT}_FOUND) + # find binaries + if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_${COMPONENT}_BINARY_REL NAMES "Ogre${COMPONENT}.dll" HINTS ${OGRE_COMPONENT_SEARCH_PATH_REL} PATH_SUFFIXES "" bin bin/release bin/relwithdebinfo bin/minsizerel release) + find_file(OGRE_${COMPONENT}_BINARY_DBG NAMES "Ogre${COMPONENT}_d.dll" HINTS ${OGRE_COMPONENT_SEARCH_PATH_DBG} PATH_SUFFIXES "" bin bin/debug debug) + endif() + mark_as_advanced(OGRE_${COMPONENT}_BINARY_REL OGRE_${COMPONENT}_BINARY_DBG) + endif() + endif() +endmacro() + +# look for Paging component +ogre_find_component(Paging OgrePaging.h) +# look for Terrain component +ogre_find_component(Terrain OgreTerrain.h) +# look for Property component +ogre_find_component(Property OgreProperty.h) +# look for RTShaderSystem component +ogre_find_component(RTShaderSystem OgreRTShaderSystem.h) + + +######################################################### +# Find Ogre plugins +######################################################### + +macro(ogre_find_plugin PLUGIN HEADER) + # On Unix, the plugins might have no prefix + if (CMAKE_FIND_LIBRARY_PREFIXES) + set(TMP_CMAKE_LIB_PREFIX ${CMAKE_FIND_LIBRARY_PREFIXES}) + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "") + endif() + + # strip RenderSystem_ or Plugin_ prefix from plugin name + string(REPLACE "RenderSystem_" "" PLUGIN_TEMP ${PLUGIN}) + string(REPLACE "Plugin_" "" PLUGIN_NAME ${PLUGIN_TEMP}) + + # header files for plugins are not usually needed, but find them anyway if they are present + set(OGRE_PLUGIN_PATH_SUFFIXES + PlugIns PlugIns/${PLUGIN_NAME} Plugins Plugins/${PLUGIN_NAME} ${PLUGIN} + RenderSystems RenderSystems/${PLUGIN_NAME} ${ARGN}) + find_path(OGRE_${PLUGIN}_INCLUDE_DIR NAMES ${HEADER} + HINTS ${OGRE_INCLUDE_DIRS} ${OGRE_PREFIX_SOURCE} + PATH_SUFFIXES ${OGRE_PLUGIN_PATH_SUFFIXES}) + # find link libraries for plugins + set(OGRE_${PLUGIN}_LIBRARY_NAMES "${PLUGIN}${OGRE_LIB_SUFFIX}") + get_debug_names(OGRE_${PLUGIN}_LIBRARY_NAMES) + set(OGRE_${PLUGIN}_LIBRARY_FWK ${OGRE_LIBRARY_FWK}) + find_library(OGRE_${PLUGIN}_LIBRARY_REL NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES} + HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt release release/opt relwithdebinfo relwithdebinfo/opt minsizerel minsizerel/opt) + find_library(OGRE_${PLUGIN}_LIBRARY_DBG NAMES ${OGRE_${PLUGIN}_LIBRARY_NAMES_DBG} + HINTS ${OGRE_LIBRARY_DIRS} PATH_SUFFIXES "" OGRE opt debug debug/opt) + make_library_set(OGRE_${PLUGIN}_LIBRARY) + + if (OGRE_${PLUGIN}_LIBRARY OR OGRE_${PLUGIN}_INCLUDE_DIR) + set(OGRE_${PLUGIN}_FOUND TRUE) + if (OGRE_${PLUGIN}_INCLUDE_DIR) + set(OGRE_${PLUGIN}_INCLUDE_DIRS ${OGRE_${PLUGIN}_INCLUDE_DIR}) + endif() + set(OGRE_${PLUGIN}_LIBRARIES ${OGRE_${PLUGIN}_LIBRARY}) + endif () + + mark_as_advanced(OGRE_${PLUGIN}_INCLUDE_DIR OGRE_${PLUGIN}_LIBRARY_REL OGRE_${PLUGIN}_LIBRARY_DBG OGRE_${PLUGIN}_LIBRARY_FWK) + + # look for plugin dirs + if (OGRE_${PLUGIN}_FOUND) + if (NOT OGRE_PLUGIN_DIR_REL OR NOT OGRE_PLUGIN_DIR_DBG) + if (WIN32) + set(OGRE_PLUGIN_SEARCH_PATH_REL + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_BIN_SEARCH_PATH} + ) + set(OGRE_PLUGIN_SEARCH_PATH_DBG + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_BIN_SEARCH_PATH} + ) + find_path(OGRE_PLUGIN_DIR_REL NAMES "${PLUGIN}.dll" HINTS ${OGRE_PLUGIN_SEARCH_PATH_REL} + PATH_SUFFIXES "" bin bin/release bin/relwithdebinfo bin/minsizerel release) + find_path(OGRE_PLUGIN_DIR_DBG NAMES "${PLUGIN}_d.dll" HINTS ${OGRE_PLUGIN_SEARCH_PATH_DBG} + PATH_SUFFIXES "" bin bin/debug debug) + elseif (UNIX) + get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_REL} PATH) + set(OGRE_PLUGIN_DIR_REL ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (release)" FORCE) + get_filename_component(OGRE_PLUGIN_DIR_TMP ${OGRE_${PLUGIN}_LIBRARY_DBG} PATH) + set(OGRE_PLUGIN_DIR_DBG ${OGRE_PLUGIN_DIR_TMP} CACHE STRING "Ogre plugin dir (debug)" FORCE) + endif () + endif () + + # find binaries + if (NOT OGRE_STATIC) + if (WIN32) + find_file(OGRE_${PLUGIN}_REL NAMES "${PLUGIN}.dll" HINTS ${OGRE_PLUGIN_DIR_REL}) + find_file(OGRE_${PLUGIN}_DBG NAMES "${PLUGIN}_d.dll" HINTS ${OGRE_PLUGIN_DIR_DBG}) + endif() + mark_as_advanced(OGRE_${PLUGIN}_REL OGRE_${PLUGIN}_DBG) + endif() + + endif () + + if (TMP_CMAKE_LIB_PREFIX) + set(CMAKE_FIND_LIBRARY_PREFIXES ${TMP_CMAKE_LIB_PREFIX}) + endif () +endmacro(ogre_find_plugin) + +ogre_find_plugin(Plugin_PCZSceneManager OgrePCZSceneManager.h PCZ PlugIns/PCZSceneManager/include) +ogre_find_plugin(Plugin_OctreeZone OgreOctreeZone.h PCZ PlugIns/OctreeZone/include) +ogre_find_plugin(Plugin_BSPSceneManager OgreBspSceneManager.h PlugIns/BSPSceneManager/include) +ogre_find_plugin(Plugin_CgProgramManager OgreCgProgram.h PlugIns/CgProgramManager/include) +ogre_find_plugin(Plugin_OctreeSceneManager OgreOctreeSceneManager.h PlugIns/OctreeSceneManager/include) +ogre_find_plugin(Plugin_ParticleFX OgreParticleFXPrerequisites.h PlugIns/ParticleFX/include) +ogre_find_plugin(RenderSystem_GL OgreGLRenderSystem.h RenderSystems/GL/include) +ogre_find_plugin(RenderSystem_GLES OgreGLESRenderSystem.h RenderSystems/GLES/include) +ogre_find_plugin(RenderSystem_GLES2 OgreGLES2RenderSystem.h RenderSystems/GLES2/include) +ogre_find_plugin(RenderSystem_Direct3D9 OgreD3D9RenderSystem.h RenderSystems/Direct3D9/include) +ogre_find_plugin(RenderSystem_Direct3D11 OgreD3D11RenderSystem.h RenderSystems/Direct3D11/include) + +if (OGRE_STATIC) + # check if dependencies for plugins are met + if (NOT DirectX_FOUND) + set(OGRE_RenderSystem_Direct3D9_FOUND FALSE) + endif () + if (NOT DirectX_D3D11_FOUND) + set(OGRE_RenderSystem_Direct3D11_FOUND FALSE) + endif () + if (NOT OPENGL_FOUND) + set(OGRE_RenderSystem_GL_FOUND FALSE) + endif () + if (NOT OPENGLES_FOUND) + set(OGRE_RenderSystem_GLES_FOUND FALSE) + endif () + if (NOT OPENGLES2_FOUND) + set(OGRE_RenderSystem_GLES2_FOUND FALSE) + endif () + if (NOT Cg_FOUND) + set(OGRE_Plugin_CgProgramManager_FOUND FALSE) + endif () + + set(OGRE_RenderSystem_Direct3D9_LIBRARIES ${OGRE_RenderSystem_Direct3D9_LIBRARIES} + ${DirectX_LIBRARIES} + ) + + set(OGRE_RenderSystem_Direct3D11_LIBRARIES ${OGRE_RenderSystem_Direct3D11_LIBRARIES} + ${DirectX_D3D11_LIBRARIES} + ) + set(OGRE_RenderSystem_GL_LIBRARIES ${OGRE_RenderSystem_GL_LIBRARIES} + ${OPENGL_LIBRARIES} + ) + set(OGRE_RenderSystem_GLES_LIBRARIES ${OGRE_RenderSystem_GLES_LIBRARIES} + ${OPENGLES_LIBRARIES} + ) + set(OGRE_RenderSystem_GLES2_LIBRARIES ${OGRE_RenderSystem_GLES2_LIBRARIES} + ${OPENGLES2_LIBRARIES} + ) + set(OGRE_Plugin_CgProgramManager_LIBRARIES ${OGRE_Plugin_CgProgramManager_LIBRARIES} + ${Cg_LIBRARIES} + ) +endif () + +# look for the media directory +set(OGRE_MEDIA_SEARCH_PATH + ${OGRE_SOURCE} + ${OGRE_LIBRARY_DIR_REL}/.. + ${OGRE_LIBRARY_DIR_DBG}/.. + ${OGRE_LIBRARY_DIR_REL}/../.. + ${OGRE_LIBRARY_DIR_DBG}/../.. + ${OGRE_PREFIX_SOURCE} +) +set(OGRE_MEDIA_SEARCH_SUFFIX + Samples/Media + Media + media + share/OGRE/media +) + +clear_if_changed(OGRE_PREFIX_WATCH OGRE_MEDIA_DIR) +find_path(OGRE_MEDIA_DIR NAMES packs/cubemapsJS.zip HINTS ${OGRE_MEDIA_SEARCH_PATH} + PATHS ${OGRE_PREFIX_PATH} PATH_SUFFIXES ${OGRE_MEDIA_SEARCH_SUFFIX}) -CMAKE_POLICY(POP) diff --git a/cmake/FindPkgMacros.cmake b/cmake/FindPkgMacros.cmake index da3303ac5c..473b27b2a7 100644 --- a/cmake/FindPkgMacros.cmake +++ b/cmake/FindPkgMacros.cmake @@ -50,6 +50,11 @@ endmacro(create_search_paths) # clear cache variables if a certain variable changed macro(clear_if_changed TESTVAR) # test against internal check variable + # HACK: Apparently, adding a variable to the cache cleans up the list + # a bit. We need to also remove any empty strings from the list, but + # at the same time ensure that we are actually dealing with a list. + list(APPEND ${TESTVAR} "") + list(REMOVE_ITEM ${TESTVAR} "") if (NOT "${${TESTVAR}}" STREQUAL "${${TESTVAR}_INT_CHECK}") message(STATUS "${TESTVAR} changed.") foreach(var ${ARGN}) @@ -129,9 +134,18 @@ MACRO(findpkg_framework fwk) /System/Library/Frameworks /Network/Library/Frameworks /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library/Frameworks/ - ${CMAKE_CURRENT_SOURCE_DIR}/../lib/Release - ${CMAKE_CURRENT_SOURCE_DIR}/../lib/Debug + ${CMAKE_CURRENT_SOURCE_DIR}/lib/Release + ${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug ) + # These could be arrays of paths, add each individually to the search paths + foreach(i ${OGRE_PREFIX_PATH}) + set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) + endforeach(i) + + foreach(i ${OGRE_PREFIX_BUILD}) + set(${fwk}_FRAMEWORK_PATH ${${fwk}_FRAMEWORK_PATH} ${i}/lib/Release ${i}/lib/Debug) + endforeach(i) + FOREACH(dir ${${fwk}_FRAMEWORK_PATH}) SET(fwkpath ${dir}/${fwk}.framework) IF(EXISTS ${fwkpath}) diff --git a/cmake/OpenMWMacros.cmake b/cmake/OpenMWMacros.cmake index c409366912..024338d3ad 100644 --- a/cmake/OpenMWMacros.cmake +++ b/cmake/OpenMWMacros.cmake @@ -2,7 +2,7 @@ macro (add_openmw_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.*") +file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND OPENMW_FILES "${f}") @@ -14,7 +14,7 @@ endmacro (add_openmw_dir) macro (add_component_dir dir) set (files) foreach (u ${ARGN}) -file (GLOB ALL RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.*") +file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp") foreach (f ${ALL}) list (APPEND files "${f}") list (APPEND COMPONENT_FILES "${f}") diff --git a/cmake/PreprocessorUtils.cmake b/cmake/PreprocessorUtils.cmake new file mode 100644 index 0000000000..38462a98d4 --- /dev/null +++ b/cmake/PreprocessorUtils.cmake @@ -0,0 +1,60 @@ +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +macro(get_preprocessor_entry CONTENTS KEYWORD VARIABLE) + string(REGEX MATCH + "# *define +${KEYWORD} +((\"([^\n]*)\")|([^ \n]*))" + PREPROC_TEMP_VAR + ${${CONTENTS}} + ) + if (CMAKE_MATCH_3) + set(${VARIABLE} ${CMAKE_MATCH_3}) + else () + set(${VARIABLE} ${CMAKE_MATCH_4}) + endif () +endmacro() + +macro(has_preprocessor_entry CONTENTS KEYWORD VARIABLE) + string(REGEX MATCH + "\n *# *define +(${KEYWORD})" + PREPROC_TEMP_VAR + ${${CONTENTS}} + ) + if (CMAKE_MATCH_1) + set(${VARIABLE} TRUE) + else () + set(${VARIABLE} FALSE) + endif () +endmacro() + +macro(replace_preprocessor_entry VARIABLE KEYWORD NEW_VALUE) + string(REGEX REPLACE + "(// *)?# *define +${KEYWORD} +[^ \n]*" + "#define ${KEYWORD} ${NEW_VALUE}" + ${VARIABLE}_TEMP + ${${VARIABLE}} + ) + set(${VARIABLE} ${${VARIABLE}_TEMP}) +endmacro() + +macro(set_preprocessor_entry VARIABLE KEYWORD ENABLE) + if (${ENABLE}) + set(TMP_REPLACE_STR "#define ${KEYWORD}") + else () + set(TMP_REPLACE_STR "// #define ${KEYWORD}") + endif () + string(REGEX REPLACE + "(// *)?# *define +${KEYWORD} *\n" + ${TMP_REPLACE_STR} + ${VARIABLE}_TEMP + ${${VARIABLE}} + ) + set(${VARIABLE} ${${VARIABLE}_TEMP}) +endmacro() + diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 75b8aff8c7..7fcac6eb8c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -63,6 +63,10 @@ add_component_dir (interpreter include_directories(${BULLET_INCLUDE_DIRS}) -add_library (components STATIC ${COMPONENT_FILES}) +add_library(components STATIC ${COMPONENT_FILES}) + +target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) + +# Make the variable accessible for other subdirectories +set(COMPONENT_FILES ${COMPONENT_FILES} PARENT_SCOPE) -target_link_libraries (components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index d064312f10..c4bcf84d81 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -12,7 +12,6 @@ #include "store.hpp" #include "components/esm/records.hpp" -#include "components/esm/loadcell.hpp" #include #include @@ -36,7 +35,7 @@ namespace ESMS { LiveCellRef(const CellRef& cref, const X* b = NULL) : base(b), ref(cref), mData(ref) {} - + LiveCellRef(const X* b = NULL) : base(b), mData(ref) {} @@ -187,7 +186,7 @@ namespace ESMS ++iter) if (!functor (iter->ref, iter->mData)) return false; - + return true; } diff --git a/extern/caelum/include/CaelumPrecompiled.h b/extern/caelum/include/CaelumPrecompiled.h index e93bd8404e..c7d9d4ca44 100644 --- a/extern/caelum/include/CaelumPrecompiled.h +++ b/extern/caelum/include/CaelumPrecompiled.h @@ -19,7 +19,7 @@ along with Caelum. If not, see . */ #ifdef __APPLE__ -#include "Ogre/Ogre.h" +#include "OGRE/Ogre.h" #else #include "Ogre.h" #endif diff --git a/extern/caelum/src/InternalUtilities.cpp b/extern/caelum/src/InternalUtilities.cpp index f70b0f2670..a308fcb7f3 100644 --- a/extern/caelum/src/InternalUtilities.cpp +++ b/extern/caelum/src/InternalUtilities.cpp @@ -75,7 +75,7 @@ namespace Caelum stream.unsetf(std::ios::dec); stream.setf(std::ios::hex); stream.setf(std::ios::uppercase); - stream << reinterpret_cast(pointer); + stream << reinterpret_cast(pointer); return stream.str(); } diff --git a/files/plugins.cfg.linux b/files/plugins.cfg.linux index b6e104351d..3781f48e07 100644 --- a/files/plugins.cfg.linux +++ b/files/plugins.cfg.linux @@ -1,7 +1,7 @@ # Defines plugins to load # Define plugin folder -PluginFolder=${OGRE_PLUGIN_DIR} +PluginFolder=${OGRE_PLUGIN_DIR_REL} # Define plugins Plugin=RenderSystem_GL diff --git a/libs/openengine b/libs/openengine index 2f5eca9d87..6c7e5d00e4 160000 --- a/libs/openengine +++ b/libs/openengine @@ -1 +1 @@ -Subproject commit 2f5eca9d878526bdd9dce93ece7f42093b481545 +Subproject commit 6c7e5d00e4f5bf954afe15f10e56f03520abfee4 diff --git a/old_d_version/.gitignore b/old_d_version/.gitignore deleted file mode 100644 index 23e6b6fd24..0000000000 --- a/old_d_version/.gitignore +++ /dev/null @@ -1,66 +0,0 @@ - -# / -/cache -/later -/openmw.ini.* -/rr.sh -/fontdump -/MyGUI.log -/upm.sh -/raw.txt -/vids -/include -/includes -/.thumbnails -/*.jpg -/*.dll -/*.exe -/*.def -/*.a -/*.map -/*.rsp -/ogre.cfg -/openmw -/bored -/bsatool -/niftool -/esmtool -/bored.highscores -/Ogre.log -/openmw.ini -/openmw.ini.old -/dsss_* -/dsss.last -/objs -/nifobjs - -# /bullet/ -/bullet/OgreOpcode* -/bullet/demo -/bullet/*.a - -# /media_mygui/ -/media_mygui/core.skin.orig -/media_mygui/.thumbnails - -# /monster/ -/monster/*openmw_last - -# /mscripts/ -/mscripts/draft - -# /nif/ -/nif/bumpmap -/nif/*.nif - -# /ogre/ -/ogre/*.nif -/ogre/cs - -# /util/ -/util/iconv - -*.o -*.patch -*.diff -.directory diff --git a/old_d_version/bullet/bindings.d b/old_d_version/bullet/bindings.d deleted file mode 100644 index 9dbd76197b..0000000000 --- a/old_d_version/bullet/bindings.d +++ /dev/null @@ -1,85 +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 bullet.bindings; - -/* - * This module is the interface between D and the C++ code that - * handles Bullet. - */ - -typedef void* BulletShape; - -extern(C): - -// Initialize the dynamic world. Returns non-zero if an error occurs. -int bullet_init(); - -// Set physics modes -void bullet_nextMode(); -void bullet_walk(); -void bullet_fly(); -void bullet_ghost(); - -// Warp the player to a specific location. -void bullet_movePlayer(float x, float y, float z); - -// Request that the player moves in this direction -void bullet_setPlayerDir(float x, float y, float z); - -// Get the current player position, after physics and collision have -// been applied. -void bullet_getPlayerPos(float *x, float *y, float *z); - -// Create a box shape. Used for bounding boxes. The box is a trimesh -// and is hollow (you can walk inside it.) -void bullet_createBoxShape(float minX, float minY, float minZ, - float maxX, float maxY, float maxZ, - float *trans,float *matrix); - -// Create a triangle shape. This is cumulative, all meshes created -// with this function are added to the same shape. Since the various -// parts of a mesh can be differently transformed and we are putting -// them all in one shape, we must transform the vertices manually. -void bullet_createTriShape(int numFaces, - void *triArray, - int numVerts, - void *vertArray, - float *trans,float *matrix); - -// "Flushes" the meshes created with createTriShape, returning the -// pointer to the final shape object. -BulletShape bullet_getFinalShape(); - -// Insert a static mesh with the given translation, quaternion -// rotation and scale. The quaternion is assumed to be in Ogre format, -// ie. with the W first. -void bullet_insertStatic(BulletShape shp, float *pos, - float *quat, float scale); - -// Move the physics simulation 'delta' seconds forward in time -void bullet_timeStep(float delta); - -// Deallocate objects -void bullet_cleanup(); - diff --git a/old_d_version/bullet/bullet.d b/old_d_version/bullet/bullet.d deleted file mode 100644 index d33a22b7cc..0000000000 --- a/old_d_version/bullet/bullet.d +++ /dev/null @@ -1,34 +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 (bullet.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module bullet.bullet; - -import bullet.bindings; - -void initBullet() -{ - if(bullet_init()) - throw new Exception("Bullet setup failed"); -} - -void cleanupBullet() { bullet_cleanup(); } diff --git a/old_d_version/bullet/cpp_bullet.cpp b/old_d_version/bullet/cpp_bullet.cpp deleted file mode 100644 index 50af01b57e..0000000000 --- a/old_d_version/bullet/cpp_bullet.cpp +++ /dev/null @@ -1,502 +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 (cpp_bullet.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -#include "btBulletDynamicsCommon.h" - -#include - -#include "../util/dbg.h" - -using namespace std; - -class CustomOverlappingPairCallback; - -enum - { - MASK_PLAYER = 1, - MASK_STATIC = 2 - }; - -// System variables -btDefaultCollisionConfiguration* g_collisionConfiguration; -btCollisionDispatcher *g_dispatcher; -//btBroadphaseInterface *g_broadphase; -btAxisSweep3 *g_broadphase; -btSequentialImpulseConstraintSolver* g_solver; -btDynamicsWorld *g_dynamicsWorld; - -// Player variables -btCollisionObject* g_playerObject; -btConvexShape *g_playerShape; - -// Player position. This is updated automatically by the physics -// system based on g_walkDirection and collisions. It is read by D -// code through bullet_getPlayerPos(). -btVector3 g_playerPosition; - -// Walking vector - defines direction and speed that the player -// intends to move right now. This is updated from D code each frame -// through bullet_setPlayerDir(), based on player input (and later, AI -// decisions.) The units of the vector are points per second. -btVector3 g_walkDirection; - -// The current trimesh shape being built. All new inserted meshes are -// added into this, until bullet_getFinalShape() is called. -btTriangleIndexVertexArray *g_currentMesh; - -// These variables and the class below are used in player collision -// detection. The callback is injected into the broadphase and keeps a -// continuously updated list of what objects are colliding with the -// player (in g_pairCache). This list is used in the function called -// recoverFromPenetration(). -btHashedOverlappingPairCache* g_pairCache; -CustomOverlappingPairCallback *g_customPairCallback; - -// Three physics modes: walking (with gravity and collision), flying -// (collision but no gravity) and ghost mode (fly through walls) -enum - { - PHYS_WALK, - PHYS_FLY, - PHYS_GHOST - }; -int g_physMode; - -// Include the player physics -#include "cpp_player.cpp" - -// Include the uniform shape scaler -#include "cpp_scale.cpp" - -class CustomOverlappingPairCallback : public btOverlappingPairCallback -{ -public: - virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0, - btBroadphaseProxy* proxy1) - { - if (proxy0->m_clientObject==g_playerObject || - proxy1->m_clientObject==g_playerObject) - return g_pairCache->addOverlappingPair(proxy0,proxy1); - return 0; - } - - virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0, - btBroadphaseProxy* proxy1, - btDispatcher* dispatcher) - { - if (proxy0->m_clientObject==g_playerObject || - proxy1->m_clientObject==g_playerObject) - return g_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); - - return 0; - } - - void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0, - btDispatcher* dispatcher) - { if (proxy0->m_clientObject==g_playerObject) - g_pairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher); - } -}; - -extern "C" int32_t bullet_init() -{ - // ------- SET UP THE WORLD ------- - - // Set up basic objects - g_collisionConfiguration = new btDefaultCollisionConfiguration(); - g_dispatcher = new btCollisionDispatcher(g_collisionConfiguration); - //g_broadphase = new btDbvtBroadphase(); - g_solver = new btSequentialImpulseConstraintSolver; - - // TODO: Figure out what to do with this. We need the user callback - // function used below (I think), but this is only offered by this - // broadphase implementation (as far as I can see.) Maybe we can - // scan through the cell first and find good values that covers all - // the objects before we set up the dynamic world. Another option is - // to create a custom broadphase designed for our purpose. (We - // should probably use different ones for interior and exterior - // cells in any case.) - btVector3 worldMin(-20000,-20000,-20000); - btVector3 worldMax(20000,20000,20000); - g_broadphase = new btAxisSweep3(worldMin,worldMax); - - g_dynamicsWorld = - new btDiscreteDynamicsWorld(g_dispatcher, - g_broadphase, - g_solver, - g_collisionConfiguration); - - //g_dynamicsWorld->setGravity(btVector3(0,-10,0)); - - - // ------- SET UP THE PLAYER ------- - - // Create the player collision shape. - float width = 30; - - /* - float height = 50; - btVector3 spherePositions[2]; - btScalar sphereRadii[2]; - sphereRadii[0] = width; - sphereRadii[1] = width; - spherePositions[0] = btVector3 (0,0,0); - spherePositions[1] = btVector3 (0,0,-height); - - // One possible shape is the convex hull around two spheres - g_playerShape = new btMultiSphereShape(btVector3(width/2.0, height/2.0, - width/2.0), &spherePositions[0], &sphereRadii[0], 2); - */ - - // Other posibilities - most are too slow, except the sphere - //g_playerShape = new btCylinderShapeZ(btVector3(width, width, height)); - g_playerShape = new btSphereShape(width); - //g_playerShape = new btCapsuleShapeZ(width, height); - - // Create the collision object - g_playerObject = new btCollisionObject (); - g_playerObject->setCollisionShape (g_playerShape); - g_playerObject->setCollisionFlags (btCollisionObject::CF_NO_CONTACT_RESPONSE); - - - // ------- OTHER STUFF ------- - - // Create a custom callback to pick out all the objects colliding - // with the player. We use this in the collision recovery phase. - g_pairCache = new btHashedOverlappingPairCache(); - g_customPairCallback = new CustomOverlappingPairCallback(); - g_broadphase->setOverlappingPairUserCallback(g_customPairCallback); - - // Set up the callback that moves the player at the end of each - // simulation step. - g_dynamicsWorld->setInternalTickCallback(playerStepCallback); - - // Add the character collision object to the world. - g_dynamicsWorld->addCollisionObject(g_playerObject, - MASK_PLAYER, - MASK_STATIC); - - // Make sure these is zero at startup - g_currentMesh = NULL; - - // Start out walking - g_physMode = PHYS_WALK; - - // Success! - return 0; -} - -// Set physics modes -extern "C" void bullet_walk() -{ - g_physMode = PHYS_WALK; - cout << "Walk mode\n"; -} - -extern "C" void bullet_fly() -{ - g_physMode = PHYS_FLY; - cout << "Fly mode\n"; -} - -extern "C" void bullet_ghost() -{ - g_physMode = PHYS_GHOST; - cout << "Ghost mode\n"; -} - -// Switch to the next physics mode -extern "C" void bullet_nextMode() -{ - switch(g_physMode) - { - case PHYS_WALK: - bullet_fly(); - break; - case PHYS_FLY: - bullet_ghost(); - break; - case PHYS_GHOST: - bullet_walk(); - break; - } -} - -// Warp the player to a specific location. We do not bother setting -// rotation, since it's completely irrelevant for collision detection, -// and doubly so since the collision mesh is a sphere. -extern "C" void bullet_movePlayer(float x, float y, float z) -{ - btTransform tr; - tr.setIdentity(); - tr.setOrigin(btVector3(x,y,z)); - g_playerObject->setWorldTransform(tr); -} - -// Request that the player moves in this direction -extern "C" void bullet_setPlayerDir(float x, float y, float z) -{ g_walkDirection.setValue(x,y,z); } - -// Get the current player position, after physics and collision have -// been applied. -extern "C" void bullet_getPlayerPos(float *x, float *y, float *z) -{ - *x = g_playerPosition.getX(); - *y = g_playerPosition.getY(); - *z = g_playerPosition.getZ(); -} - -void* copyBuffer(const void *buf, int elemSize, int len) -{ - int size = elemSize * len; - void *res = malloc(size); - memcpy(res, buf, size); - - return res; -} - -// Internal version that does not copy buffers -void createTriShape(int32_t numFaces, const void *triArray, - int32_t numVerts, const void *vertArray, - const float *trans, const float *matrix) -{ - // This struct holds the index and vertex buffers of a single - // trimesh. - btIndexedMesh im; - - // Set up the triangles - int numTriangles = numFaces / 3; - im.m_numTriangles = numTriangles; - im.m_triangleIndexStride = 6; // 3 indices * 2 bytes per short - im.m_triangleIndexBase = (unsigned char*)triArray; - - // Set up the vertices - im.m_numVertices = numVerts; - im.m_vertexStride = 12; // 4 bytes per float * 3 floats per vertex - im.m_vertexBase = (unsigned char*)vertArray; - - // Transform vertex values in vb according to 'trans' and 'matrix' - float *vb = (float*)im.m_vertexBase; - for(int i=0; iaddIndexedMesh(im, PHY_SHORT); -} - -// Define a cube with coordinates 0,0,0 - 1,1,1. -const float cube_verts[] = - { - 0,0,0, 1,0,0, 0,1,0, - 1,1,0, 0,0,1, 1,0,1, - 0,1,1, 1,1,1 - }; - -// Triangles of the cube. The orientation of each triange doesn't -// matter. -const short cube_tris[] = - { - // bottom side - 0, 1, 2, - 1, 2, 3, - // top side - 4, 5, 6, - 5, 6, 7, - // front side - 0, 4, 5, - 0, 1, 5, - // back side - 2, 3, 7, - 2, 6, 7, - // left side - 0, 2, 4, - 2, 4, 6, - // right side - 1, 3, 5, - 3, 5, 7 - }; - -const int cube_num_verts = 8; -const int cube_num_tris = 12; - -// Create a (trimesh) box with the given dimensions. Used for bounding -// boxes. TODO: I guess we should use the NIF-specified bounding box -// for this, not our automatically calculated one. -extern "C" void bullet_createBoxShape(float xmin, float ymin, float zmin, - float xmax, float ymax, float zmax, - float *trans, float *matrix) -{ - // Make a copy of the vertex buffer, since we need to change it - float *vbuffer = (float*)copyBuffer(cube_verts, 12, cube_num_verts); - - // Calculate the widths - float xwidth = xmax-xmin; - float ywidth = ymax-ymin; - float zwidth = zmax-zmin; - - // Transform the cube to (xmin,xmax) etc - float *vb = vbuffer; - for(int i=0; isetCollisionShape(shape); - obj->setWorldTransform(trafo); - g_dynamicsWorld->addCollisionObject(obj, MASK_STATIC, MASK_PLAYER); -} - -// Move the physics simulation 'delta' seconds forward in time -extern "C" void bullet_timeStep(float delta) -{ - TRACE("bullet_timeStep"); - // TODO: We might experiment with the number of time steps. Remember - // that the function also returns the number of steps performed. - g_dynamicsWorld->stepSimulation(delta,2); -} - -// Cleanup in the reverse order of creation/initialization -extern "C" void bullet_cleanup() -{ - // Remove the rigidbodies from the dynamics world and delete them - for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) - { - btCollisionObject* obj = g_dynamicsWorld->getCollisionObjectArray()[i]; - btRigidBody* body = btRigidBody::upcast(obj); - - if (body && body->getMotionState()) - delete body->getMotionState(); - - g_dynamicsWorld->removeCollisionObject( obj ); - delete obj; - } - - delete g_dynamicsWorld; - delete g_solver; - delete g_broadphase; - delete g_dispatcher; - delete g_collisionConfiguration; -} diff --git a/old_d_version/bullet/cpp_player.cpp b/old_d_version/bullet/cpp_player.cpp deleted file mode 100644 index 14b04bf84b..0000000000 --- a/old_d_version/bullet/cpp_player.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - (see additional copyrights for this file below) - - This file (cpp_player.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - ---- - - Parts of this file is based on the kinematic character controller - demo included with the Bullet library. The copyright statement for - these parts follow: - - Bullet Continuous Collision Detection and Physics Library - Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any - damages arising from the use of this software. Permission is - granted to anyone to use this software for any purpose, including - commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must - not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source - distribution. -*/ - - -// This file handles player-specific physics and collision detection - -// TODO: Later we might handle various physics modes, eg. dynamic -// (full physics), player_walk, player_fall, player_swim, -// player_float, player_levitate, player_ghost. These would be -// applicable to any object (through Monster script), allowing the -// physics code to be shared between NPCs, creatures and the player. - -// Variables used internally in this file. Once we make per-object -// player collision, these will be member variables. -bool g_touchingContact; -btVector3 g_touchingNormal; -btScalar g_currentStepOffset; -float g_stepHeight = 5; - -// Returns the reflection direction of a ray going 'direction' hitting -// a surface with normal 'normal' -btVector3 reflect (const btVector3& direction, const btVector3& normal) -{ return direction - (btScalar(2.0) * direction.dot(normal)) * normal; } - -// Returns the portion of 'direction' that is perpendicular to -// 'normal' -btVector3 perpComponent (const btVector3& direction, const btVector3& normal) -{ return direction - normal * direction.dot(normal); } - -btManifoldArray manifoldArray; - -// Callback used for collision detection sweep tests. It prevents self -// collision and is used in calls to convexSweepTest(). TODO: It might -// be enough to just set the filters on this. If we set the group and -// mask so that we only collide with static objects, self collision -// would never happen. The sweep test function should have had a -// version where you only specify the filters - I might add that -// myself. -class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback -{ -public: - ClosestNotMeConvexResultCallback() - : btCollisionWorld::ClosestConvexResultCallback - (btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - m_collisionFilterGroup = g_playerObject-> - getBroadphaseHandle()->m_collisionFilterGroup; - - m_collisionFilterMask = g_playerObject-> - getBroadphaseHandle()->m_collisionFilterMask; - } - - btScalar addSingleResult(btCollisionWorld::LocalConvexResult& - convexResult, bool normalInWorldSpace) - { - if (convexResult.m_hitCollisionObject == g_playerObject) return 1.0; - - return ClosestConvexResultCallback::addSingleResult - (convexResult, normalInWorldSpace); - } -}; - -// Used to step up small steps and slopes. -void stepUp() -{ - // phase 1: up - btVector3 targetPosition = g_playerPosition + - btVector3(0.0, 0.0, g_stepHeight); - btTransform start, end; - - start.setIdentity (); - end.setIdentity (); - - // FIXME: Handle penetration properly - start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0)); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); - - if (callback.hasHit()) - { - // we moved up only a fraction of the step height - g_currentStepOffset = g_stepHeight * callback.m_closestHitFraction; - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - } - else - { - g_currentStepOffset = g_stepHeight; - g_playerPosition = targetPosition; - } -} - -void updateTargetPositionBasedOnCollision (const btVector3& hitNormal, - btVector3 &targetPosition) -{ - btVector3 movementDirection = targetPosition - g_playerPosition; - btScalar movementLength = movementDirection.length(); - - if (movementLength <= SIMD_EPSILON) - return; - - // Is this needed? - movementDirection.normalize(); - - btVector3 reflectDir = reflect(movementDirection, hitNormal); - reflectDir.normalize(); - - btVector3 perpendicularDir = perpComponent (reflectDir, hitNormal); - - targetPosition = g_playerPosition; - targetPosition += perpendicularDir * movementLength; -} - -// This covers all normal forward movement and collision, including -// walking sideways when hitting a wall at an angle. It does NOT -// handle walking up slopes and steps, or falling/gravity. -void stepForward(btVector3& walkMove) -{ - btVector3 originalDir = walkMove.normalized(); - - // If no walking direction is given, we still run the function. This - // allows moving forces to push the player around even if she is - // standing still. - if (walkMove.length() < SIMD_EPSILON) - originalDir.setValue(0.f,0.f,0.f); - - btTransform start, end; - btVector3 targetPosition = g_playerPosition + walkMove; - start.setIdentity (); - end.setIdentity (); - - btScalar fraction = 1.0; - btScalar distance2 = (g_playerPosition-targetPosition).length2(); - - if (g_touchingContact) - if (originalDir.dot(g_touchingNormal) > btScalar(0.0)) - updateTargetPositionBasedOnCollision (g_touchingNormal, targetPosition); - - int maxIter = 10; - - while (fraction > btScalar(0.01) && maxIter-- > 0) - { - start.setOrigin (g_playerPosition); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); - - fraction -= callback.m_closestHitFraction; - - if (callback.hasHit()) - { - // We moved only a fraction - btScalar hitDistance = (callback.m_hitPointWorld - g_playerPosition).length(); - // If the distance is further than the collision margin, - // move - if (hitDistance > 0.05) - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - - updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld, - targetPosition); - btVector3 currentDir = targetPosition - g_playerPosition; - distance2 = currentDir.length2(); - - if (distance2 <= SIMD_EPSILON) - break; - - currentDir.normalize(); - - if (currentDir.dot(originalDir) <= btScalar(0.0)) - break; - } - else - // we moved the whole way - g_playerPosition = targetPosition; - } -} - -void stepDown (btScalar dt) -{ - btTransform start, end; - - // phase 3: down - btVector3 step_drop = btVector3(0,0,g_currentStepOffset); - btVector3 gravity_drop = btVector3(0,0,g_stepHeight); - - btVector3 targetPosition = g_playerPosition - step_drop - gravity_drop; - - start.setIdentity (); - end.setIdentity (); - - start.setOrigin (g_playerPosition); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest(g_playerShape, start, end, callback); - - if (callback.hasHit()) - // we dropped a fraction of the height -> hit floor - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - else - // we dropped the full height - g_playerPosition = targetPosition; -} - -// Check if the player currently collides with anything, and adjust -// its position accordingly. Returns true if collisions were found. -bool recoverFromPenetration() -{ - bool penetration = false; - - // Update the collision pair cache - g_dispatcher->dispatchAllCollisionPairs(g_pairCache, - g_dynamicsWorld->getDispatchInfo(), - g_dispatcher); - - btScalar maxPen = 0.0; - for (int i = 0; i < g_pairCache->getNumOverlappingPairs(); i++) - { - manifoldArray.resize(0); - - btBroadphasePair* collisionPair = &g_pairCache->getOverlappingPairArray()[i]; - // Get the contact points - if (collisionPair->m_algorithm) - collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); - - // And handle them - for (int j=0;jgetBody0() == - g_playerObject ? btScalar(-1.0) : btScalar(1.0); - - for (int p=0;pgetNumContacts();p++) - { - const btManifoldPoint &pt = manifold->getContactPoint(p); - - if (pt.getDistance() < 0.0) - { - // Pick out the maximum penetration normal and store - // it - if (pt.getDistance() < maxPen) - { - maxPen = pt.getDistance(); - g_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? - - } - g_playerPosition += pt.m_normalWorldOnB * directionSign * - pt.getDistance() * btScalar(0.2); - - penetration = true; - } - } - } - } - - btTransform newTrans = g_playerObject->getWorldTransform(); - newTrans.setOrigin(g_playerPosition); - g_playerObject->setWorldTransform(newTrans); - - return penetration; -} - -// Callback called at the end of each simulation cycle. This is the -// main function is responsible for player movement. -void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep) -{ - // The walking direction is set from D code each frame, and the - // final player position is read back from D code after the - // simulation. - btVector3 walkStep = g_walkDirection * timeStep; - - float len = walkStep.length(); - - // In walk mode, it shouldn't matter whether or not we look up or - // down. Rotate the vector back to the horizontal plane. - if(g_physMode == PHYS_WALK) - { - walkStep.setZ(0); - float len2 = walkStep.length(); - if(len2 > 0) - walkStep *= len/len2; - } - - // Get the player position - g_playerPosition = g_playerObject->getWorldTransform().getOrigin(); - - if(g_physMode == PHYS_GHOST) - { - // Ghost mode - just move, no collision - g_playerPosition += walkStep; - } - else - { - // Collision detection is active - - // Before moving, recover from current penetrations - int numPenetrationLoops = 0; - g_touchingContact = false; - while (recoverFromPenetration()) - { - numPenetrationLoops++; - g_touchingContact = true; - - // Make sure we don't stay here indefinitely - if (numPenetrationLoops > 4) - break; - } - - // recoverFromPenetration updates g_playerPosition and the - // collision mesh, so they are still in sync at this point - - // Next, do the walk. The following functions only updates - // g_playerPosition, they do not move the collision object. - - if(g_physMode == PHYS_WALK) - { - stepUp(); - stepForward(walkStep); - stepDown(timeStep); - } - else if(g_physMode == PHYS_FLY) - stepForward(walkStep); - else - cout << "WARNING: Unknown physics mode " << g_physMode << "!\n"; - } - - // Move the player collision mesh - btTransform xform = g_playerObject->getWorldTransform (); - xform.setOrigin (g_playerPosition); - g_playerObject->setWorldTransform (xform); -} diff --git a/old_d_version/bullet/cpp_scale.cpp b/old_d_version/bullet/cpp_scale.cpp deleted file mode 100644 index 3dfd34a305..0000000000 --- a/old_d_version/bullet/cpp_scale.cpp +++ /dev/null @@ -1,98 +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 (cpp_scale.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/ . - -*/ - -// WARNING: This file does NOT work, and it is not used yet. - -class ScaleCallback : public btTriangleCallback -{ - btTriangleCallback *call; - float factor; - -public: - ScaleCallback(btTriangleCallback *c, float f) - { call = c; factor = f; } - - void processTriangle(btVector3 *tri, int partid, int triindex) - { - btVector3 vecs[3]; - vecs[0] = tri[0]*factor; - vecs[1] = tri[1]*factor; - vecs[2] = tri[2]*factor; - - call->processTriangle(vecs, partid, triindex); - } -}; - -// This class is used to uniformly scale a triangle mesh by a -// factor. It wraps around an existing shape and does not copy the -// data. -class ScaleShape : public btConcaveShape -{ - btConcaveShape* child; - float factor, fact3, facthalf; - -public: - - ScaleShape(btConcaveShape* ch, float ft) - { - child = ch; - factor = ft; - fact3 = factor*factor*factor; - facthalf = factor*0.5; - } - - void calculateLocalInertia(btScalar mass,btVector3& inertia) const - { - btVector3 tmpInertia; - child->calculateLocalInertia(mass,tmpInertia); - inertia = tmpInertia * fact3; - } - - const char* getName()const { return "ScaleShape"; } - - void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const - { - child->getAabb(t,aabbMin,aabbMax); - btVector3 aabbCenter = (aabbMax+aabbMin)*0.5; - btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*facthalf; - - aabbMin = aabbCenter - scaledAabbHalfExtends; - aabbMax = aabbCenter + scaledAabbHalfExtends; - } - - void processAllTriangles(btTriangleCallback *callback,const btVector3& aabbMin,const btVector3& aabbMax) const - { - ScaleCallback scb(callback, factor); - - child->processAllTriangles(&scb, aabbMin, aabbMax); - } - - void setLocalScaling(const btVector3& scaling) - { child->setLocalScaling(scaling); } - - const btVector3& getLocalScaling() const - { return child->getLocalScaling(); } - - int getShapeType() const - { return TRIANGLE_MESH_SHAPE_PROXYTYPE; } -}; diff --git a/old_d_version/core/config.d b/old_d_version/core/config.d deleted file mode 100644 index 55daa2be15..0000000000 --- a/old_d_version/core/config.d +++ /dev/null @@ -1,437 +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 (config.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 core.config; - -import std.string; -import std.file; -import std.path; -import std.stdio; - -import monster.monster; -import monster.util.string; - -import core.inifile; -import core.filefinder; - -import sound.audio; - -import input.keys; -import input.ois; - -import ogre.ogre; - -ConfigManager config; - -/* - * Structure that handles all user adjustable configuration options, - * including things like file paths, plugins, graphics resolution, - * game settings, window positions, etc. It is also responsible for - * reading and writing configuration files, for importing settings - * from Morrowind.ini and for configuring OGRE. It doesn't currently - * DO all of this, but it is supposed to in the future. - */ - -struct ConfigManager -{ - MonsterObject *mo; - - IniWriter iniWriter; - - // Mouse sensitivity - float *mouseSensX; - float *mouseSensY; - bool *flipMouseY; - - // Ogre configuration - bool showOgreConfig; // The configuration setting - // The actual result, overridable by a command line switch, and also - // set to true if firstRun is true. - bool finalOgreConfig; - - // Other settings - bool firstRun; - - // Set to true if sound is completely disabled - bool noSound = false; - - // Number of current screen shot. Saved upon exit, so that shots - // from separate sessions don't overwrite each other. - int screenShotNum; - - // Game files to load (max 255) - char[][] gameFiles; - - // Directories - char[] dataDir; - char[] esmDir; - char[] bsaDir; - char[] sndDir; - char[] fontDir; - char[] musDir; // Explore music - char[] musDir2; // Battle music - - // Configuration file - char[] confFile = "openmw.ini"; - - // Cell to load at startup - char[] defaultCell; - - // These set the volume to a new value and updates all sounds to - // take notice. - void setMusicVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setMusicVolume"); - } - float getMusicVolume() - { return mo.getFloat("musicVolume"); } - - void setSfxVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setSfxVolume"); - } - float getSfxVolume() - { return mo.getFloat("sfxVolume"); } - - void setMainVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setMainVolume"); - } - float getMainVolume() - { return mo.getFloat("mainVolume"); } - - // Initialize the config manager. Send a 'true' parameter to reset - // all keybindings to the default. A lot of this stuff will be moved - // to script code at some point. In general, all input mechanics and - // distribution of key events should happen in native code, while - // all setup and control should be handled in script code. - void initialize(bool reset = false) - { - // Initialize variables from Monster. - assert(mo !is null); - mouseSensX = mo.getFloatPtr("mouseSensX"); - mouseSensY = mo.getFloatPtr("mouseSensY"); - flipMouseY = mo.getBoolPtr("flipMouseY"); - - // Initialize the key binding manager - keyBindings.initKeys(); - - /* Disable this at the moment. It's a good idea to put - configuration in a central location, but it's useless as long - as Ogre expects to find it's files in the current working - directory. The best permanent solution would be to let the - locations of ogre.cfg and plugins.cfg be determined by - openmw.ini - I will fix that later. - - version(Posix) - { - if(!exists(confFile)) - confFile = expandTilde("~/.openmw/openmw.ini"); - } - */ - - readIni(reset); - } - - // Read config from morro.ini, if it exists. The reset parameter is - // set to true if we should use default key bindings instead of the - // ones from the config file. - void readIni(bool reset) - { - // Read configuration file, if it exists. - IniReader ini; - - ini.readFile(confFile); - - screenShotNum = ini.getInt("General", "Screenshots", 0); - float mainVolume = saneVol(ini.getFloat("Sound", "Main Volume", 0.7)); - float musicVolume = saneVol(ini.getFloat("Sound", "Music Volume", 0.5)); - float sfxVolume = saneVol(ini.getFloat("Sound", "SFX Volume", 0.5)); - bool useMusic = ini.getBool("Sound", "Enable Music", true); - - - lightConst = ini.getInt("LightAttenuation", "UseConstant", 0); - lightConstValue = ini.getFloat("LightAttenuation", "ConstantValue", 0.0); - - lightLinear = ini.getInt("LightAttenuation", "UseLinear", 1); - lightLinearMethod = ini.getInt("LightAttenuation", "LinearMethod", 1); - lightLinearValue = ini.getFloat("LightAttenuation", "LinearValue", 3.0); - lightLinearRadiusMult = ini.getFloat("LightAttenuation", "LinearRadiusMult", 1.0); - - lightQuadratic = ini.getInt("LightAttenuation", "UseQuadratic", 0); - lightQuadraticMethod = ini.getInt("LightAttenuation", "QuadraticMethod", 2); - lightQuadraticValue = ini.getFloat("LightAttenuation", "QuadraticValue", 16.0); - lightQuadraticRadiusMult = ini.getFloat("LightAttenuation", "QuadraticRadiusMult", 1.0); - - lightOutQuadInLin = ini.getInt("LightAttenuation", "OutQuadInLin", 0); - - - *mouseSensX = ini.getFloat("Controls", "Mouse Sensitivity X", 0.2); - *mouseSensY = ini.getFloat("Controls", "Mouse Sensitivity Y", 0.2); - *flipMouseY = ini.getBool("Controls", "Flip Mouse Y Axis", false); - - mo.setFloat("mainVolume", mainVolume); - mo.setFloat("musicVolume", musicVolume); - mo.setFloat("sfxVolume", sfxVolume); - mo.setBool("useMusic", useMusic); - - defaultCell = ini.getString("General", "Default Cell", "Assu"); - - firstRun = ini.getBool("General", "First Run", true); - showOgreConfig = ini.getBool("General", "Show Ogre Config", false); - - // This flag determines whether we will actually show the Ogre - // config dialogue. The EITHER of the following are true, the - // config box will be shown: - // - The program is being run for the first time - // - The "Show Ogre Config" option in openmw.ini is set. - // - The -oc option is specified on the command line - // - The file ogre.cfg is missing - - finalOgreConfig = showOgreConfig || firstRun || - !exists("ogre.cfg"); - - // Set default key bindings first. - with(keyBindings) - { - // Bind some default keys - bind(Keys.MoveLeft, KC.A, KC.LEFT); - bind(Keys.MoveRight, KC.D, KC.RIGHT); - bind(Keys.MoveForward, KC.W, KC.UP); - bind(Keys.MoveBackward, KC.S, KC.DOWN); - bind(Keys.MoveUp, KC.LSHIFT); - bind(Keys.MoveDown, KC.LCONTROL); - - bind(Keys.MainVolUp, KC.ADD); - bind(Keys.MainVolDown, KC.SUBTRACT); - bind(Keys.MusVolDown, KC.N1); - bind(Keys.MusVolUp, KC.N2); - bind(Keys.SfxVolDown, KC.N3); - bind(Keys.SfxVolUp, KC.N4); - bind(Keys.Mute, KC.M); - - bind(Keys.Fullscreen, KC.F); - - bind(Keys.ToggleBattleMusic, KC.SPACE); - bind(Keys.PhysMode, KC.T); - bind(Keys.Nighteye, KC.N); - bind(Keys.ToggleGui, KC.Mouse1); - bind(Keys.Console, KC.F1, KC.GRAVE); - bind(Keys.Debug, KC.G); - - bind(Keys.Pause, KC.PAUSE, KC.P); - bind(Keys.ScreenShot, KC.SYSRQ); - bind(Keys.Exit, KC.Q, KC.ESCAPE); - } - - // Unless the ini file was missing or we were asked to reset all - // keybindings to default, replace all present bindings with the - // values from the ini. - if(!reset && ini.wasRead) - { - // Read key bindings - for(int i; i - WWW: http://openmw.snaptoad.com/ - - This file (filefinder.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 core.filefinder; - -import std.file; -import std.string; - -import monster.util.string; -import monster.util.aa; - -import core.memory; - -import std.stdio; - -class FileFinderException : Exception -{ - this(char[] msg, char[] ext, char[] dir) - { - if(ext.length) super(format("FileFinder for %s files in %s: %s", ext, dir, msg)); - else super(format("FileFinder for %s: %s", dir, msg)); - } -} - -// Do we traverse directories recursively? Default is yes. -enum Recurse { Yes, No } - -// The file finder is used to list all files in a directory so we can -// look up files without searching the filesystem each time. It is -// case insensitive on all platforms, and transparently converts to -// the right directory separator character (\ or /). We might extend -// it later with code from other projects. -class FileFinder -{ - private: - char[][] files; // Use GC for this, it's not too big and we don't - // have to manage roots pointing to the filenames. - HashTable!(char[], int, ESMRegionAlloc, FilenameHasher) lookup; - - char[] dir; // Base directory to search - char[] ext; // Extensions to pick out - - void fail(char[] err) - { - throw new FileFinderException(err, ext, dir); - } - - // Removes the part of a path that is stored in 'dir' - char[] removeDir(char[] path) - { - //TODO: Should this be case insensitive? - assert(path[0..dir.length] == dir); - - return path[dir.length..$]; - } - - void insert(char[] filename) - { - // Only keep the part of the filename not given in 'dir'. - char[] name = removeDir(filename); - - if(!name.iEnds(ext)) return; - - // We start counting from 1 - uint newVal = files.length+1; - - // Insert it, or get the old value if it already exists - uint oldVal = lookup[name, newVal]; - if(oldVal != newVal) - fail("Already have " ~ name ~ "\nPreviously inserted as " ~ files[oldVal-1]); - - // Store it - files ~= filename; - } - - public: - - static char[] addSlash(char[] dir) - { - // Add a trailing slash - version(Windows) if(!dir.ends("\\")) dir ~= '\\'; - version(Posix) if(!dir.ends("/")) dir ~= '/'; - return dir; - } - - int length() { return lookup.length; } - - this(char[] dir, char[] ext = null, Recurse r = Recurse.Yes) - in - { - if(!dir.length) fail("'dir' can not be empty"); - } - out - { - assert(files.length == lookup.length); - } - body - { - // Add a trailing slash - dir = addSlash(dir); - - this.dir = dir; - - if(ext.length && ext[0] != '.') ext = "." ~ ext; - this.ext = ext; - - bool callback(DirEntry* de) - { - if (de.isdir) - { - if(r == Recurse.Yes) - listdir(de.name, &callback); - } - else - insert(de.name); - return true; - } - - try listdir(dir, &callback); - catch(FileException e) - fail(e.toString); - } - - char[] opIndex(int i) { return files[i-1]; } - - int opIndex(char[] file) - { - int i; - - // Get value if it exists - if(lookup.inList(file, i)) - return i; - return 0; - } - - bool has(char[] file) - { - return lookup.inList(file); - } - - int opApply(int delegate(ref char[]) del) - { - int res = 0; - - foreach(char[] s; files) - { - char[] tmp = removeDir(s); - res = del(tmp); - if(res) break; - } - return res; - } - - char[] toString() - { - char[] result; - foreach(char[] s; this) - result ~= s ~ "\n"; - return result; - } -} - -// Hash functions that does not differentiate between linux and -// windows file names. This means that it is case insensitive, and -// treats '\' and '/' as the same character. Only needed in linux, in -// windows just use CITextHasher. -version(Posix) -struct FilenameHasher -{ - static const char conv = 'a'-'A'; - - static int isEqual(char[] aa, char[] bb) - { - if(aa.length != bb.length) return 0; - - foreach(int i, char a; aa) - { - char b = bb[i]; - - if(a == b) - continue; - - // Convert both to lowercase and "/ case" - if(a <= 'Z' && a >= 'A') a += conv; - else if(a == '\\') a = '/'; - if(b <= 'Z' && b >= 'A') b += conv; - else if(b == '\\') b = '/'; - - if(a != b) return 0; - } - - // No differences were found - return 1; - } - - static uint hash(char[] s) - { - uint hash; - foreach (char c; s) - { - if(c <= 'Z' && c >= 'A') c += conv; - else if(c == '\\') c = '/'; - hash = (hash * 37) + c; - } - return hash; - } -} - -version(Windows) alias CITextHash FilenameHasher; diff --git a/old_d_version/esm/defs.d b/old_d_version/esm/defs.d deleted file mode 100644 index 337db02fc3..0000000000 --- a/old_d_version/esm/defs.d +++ /dev/null @@ -1,187 +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 (defs.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 esm.defs; - -public import std.string; -public import monster.util.string; -import monster.monster; - -/* - * Types and definitions related to parsing esm and esp files - */ - -alias char[4] NAME; -alias char[32] NAME32; -alias char[256] NAME256; - -union Color -{ - align(1) struct - { - ubyte red, green, blue, alpha; - } - - ubyte[4] array; - uint value; - - char[] toString() { return format("RGBA:%s", array); } -} -static assert(Color.sizeof==4); - -// State of a record struct -enum LoadState - { - Unloaded, // This record is not loaded, it has just been - // referenced. - Loaded, // This record has been loaded by the current file - Previous // The record has been loaded by a previous file - - // Finalized - might be the case for some record types, but I - // don't know if this actual state value would be used for - // anything. - } - -enum VarType { Unknown, None, Short, Int, Long, Float, String, Ignored } - -enum SpellSchool : int - { - Alteration = 0, - Conjuration = 1, - Destruction = 2, - Illusion = 3, - Mysticism = 4, - Restoration = 5, - Length - } - -enum Attribute : int - { - Strength = 0, - Intelligence = 1, - Willpower = 2, - Agility = 3, - Speed = 4, - Endurance = 5, - Personality = 6, - Luck = 7, - Length - } - -enum SkillEnum : int - { - Block = 0, - Armorer = 1, - MediumArmor = 2, - HeavyArmor = 3, - BluntWeapon = 4, - LongBlade = 5, - Axe = 6, - Spear = 7, - Athletics = 8, - Enchant = 9, - Destruction = 10, - Alteration = 11, - Illusion = 12, - Conjuration = 13, - Mysticism = 14, - Restoration = 15, - Alchemy = 16, - Unarmored = 17, - Security = 18, - Sneak = 19, - Acrobatics = 20, - LightArmor = 21, - ShortBlade = 22, - Marksman = 23, - Mercantile = 24, - Speechcraft = 25, - HandToHand = 26, - Length - } - -// Shared between SPEL (Spells), ALCH (Potions) and ENCH (Item -// enchantments) records -align(1) struct ENAMstruct -{ - // Magical effect - short effectID; // ID of magic effect - - // Which skills/attributes are affected (for restore/drain spells etc.) - byte skill, attribute; // -1 if N/A - - // Other spell parameters - int range; // 0 - self, 1 - touch, 2 - target - int area, duration, magnMin, magnMax; - - static assert(ENAMstruct.sizeof==24); -} - -// Common stuff for all the load* structs -template LoadTT(T) -{ - LoadState state; - char[] name, id; - - MonsterObject *proto; - static MonsterClass mc; - - void makeProto(char[] clsName = null) - { - // Set up a prototype object - if(mc is null) - { - // Use the template type name as the Monster class name if - // none is specified. - if(clsName == "") - { - clsName = typeid(T).toString; - - // Remove the module name - int i = clsName.rfind('.'); - if(i != -1) - clsName = clsName[i+1..$]; - } - - // All the game objects are in the 'game' package - clsName = "game." ~ clsName; - mc = vm.load(clsName); - } - - proto = mc.createObject(); - - proto.setString8("id", id); - proto.setString8("name", name); - - static if(is(typeof(data.weight) == float)) - { - proto.setFloat("weight", data.weight); - proto.setInt("value", data.value); - } - - static if(is(typeof(data.enchant)==int)) - proto.setInt("enchant", data.enchant); - } -} - -template LoadT() { mixin LoadTT!(typeof(*this)); } diff --git a/old_d_version/esm/esmmain.d b/old_d_version/esm/esmmain.d deleted file mode 100644 index 2340ea59e9..0000000000 --- a/old_d_version/esm/esmmain.d +++ /dev/null @@ -1,167 +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 (esmmain.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 esm.esmmain; - -public import esm.records; - -import ogre.ogre; - -/* This file is the main module for loading from ESM, ESP and ESS - files. It stores all the data in the appropriate data structures - for later referal. TODO: Put this in a class or whatever? Nah, we - definately only need one structure like this at any one - time. However, we have to deal with unloading and reloading it, - even though that should be the exceptional case (change of plugins, - etc), not the rule (loading a savegame should not alter the base - data set, I think, but it's to early do decide.)*/ - -// Load a set of esm and esp files. For now, we just traverse in the -// order given. Later, we should sort these into 'masters' and -// 'plugins', because esms are always supposed to be loaded -// first. TODO: I'm not sure if I should load all these in one -// function. Do we need to be able to respond to errors in each file? -// Nah, if anything fails, give a general error message, remove the -// file from the list and try again. We have to be able to get a list -// of which files depend upon which, though... this can be done before -// this function is called. -void loadTESFiles(char[][] files) -{ - // Set up all the lists to hold our data - initializeLists(); - - foreach(char[] filename; files) - { - esFile.open(filename, esmRegion); - while(esFile.hasMoreRecs()) - { - uint flags; - - // Read record header - char[] recName = esFile.getRecName(); - esFile.getRecHeader(flags); - - if(flags & RecordFlags.Unknown) - esFile.fail(format("UNKNOWN record flags: %xh", flags)); - - loadRecord(recName); - } - - // We have to loop through the lists and check for broken - // references at this point, and if all forward references were - // loaded. There might be other end-of-file things to do also. - endFiles(); - } - - esFile.close(); - - // Put all inventory items into one list - items.addList(appas, ItemType.Apparatus); - items.addList(lockpicks, ItemType.Pick); - items.addList(probes, ItemType.Probe); - items.addList(repairs, ItemType.Repair); - items.addList(lights, ItemType.Light); - items.addList(ingreds, ItemType.Ingredient); - items.addList(potions, ItemType.Potion); - items.addList(armors, ItemType.Armor); - items.addList(weapons, ItemType.Weapon); - items.addList(books, ItemType.Book); - items.addList(clothes, ItemType.Clothing); - items.addList(miscItems, ItemType.Misc); - items.addList(itemLists, ItemType.ItemList); // Leveled item lists - - // Same with all actors - actors.addList(creatures, ItemType.Creature); - actors.addList(creatureLists, ItemType.CreatureList); - actors.addList(npcs, ItemType.NPC); - - // Finally, add everything that might be looked up in a cell into - // one list - cellRefs.addList(items); - cellRefs.addList(actors); - cellRefs.addList(doors, ItemType.Door); - cellRefs.addList(activators, ItemType.Activator); - cellRefs.addList(statics, ItemType.Static); - cellRefs.addList(containers, ItemType.Container); - - // Check that all references are resolved - items.endMerge(); - actors.endMerge(); - cellRefs.endMerge(); - - // Put all NPC dialogues into the hyperlink list - foreach(char[] id, ref Dialogue dl; dialogues.names) - hyperlinks.add(id, &dl); - - // Finally, sort the hyperlink lists - hyperlinks.sort(); -} - -// Contains the small bits of information that we currently extract -// from savegames. -struct PlayerSaveInfo -{ - char[] cellName; - char[] playerName; - Placement pos; -} - -// Load a TES savegame file (.ess). Currently VERY limited, reads the -// player's cell name and position -PlayerSaveInfo importSavegame(char[] file) -{ - PlayerSaveInfo pi; - - esFile.open(file, esmRegion); - scope(exit) esFile.close(); - - if(esFile.getFileType != FileType.Ess) - throw new TES3FileException(file ~ " is not a savegame"); - - with(esFile.saveData) - { - pi.cellName = stripz(cell); - pi.playerName = stripz(player); - } - - with(esFile) - { - while(hasMoreRecs()) - { - if(isNextHRec("REFR")) - { - while(hasMoreSubs()) - { - getSubName(); - if(retSubName() == "DATA") - readHExact(&pi.pos, pi.pos.sizeof); - else - skipHSub(); - } - } - else - skipHRecord(); - } - } - return pi; -} diff --git a/old_d_version/esm/filereader.d b/old_d_version/esm/filereader.d deleted file mode 100644 index 4d1dfb2d90..0000000000 --- a/old_d_version/esm/filereader.d +++ /dev/null @@ -1,783 +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 (filereader.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 esm.filereader; - -private: -import std.stdio; -import std.stream; -import std.string; - -import util.regions; -import util.utfconvert; -import monster.util.string; -import core.resource; - -import esm.listkeeper; -import esm.defs; - -public: - -/* - * Exception class for TES3File - */ - -class TES3FileException: Exception -{ - this(char[] msg) {super("Error reading TES3 file: " ~ msg);} - this() {this("Unknown error");} -} - -// Some flags are in use that we don't know. But we don't really know -// any of them. -enum RecordFlags : uint - { - Flag6 = 0x20, // Eg. adventurers_v2.0.esp (only once per file?) - Persistent = 0x400, - Flag13 = 0x1000, // Eg. Astarsis_BR.esm (several times per file?) - Blocked = 0x2000, - - Unknown = 0xffffffff - 0x3420 - } - -enum FileType - { - Unknown, - Esp, Plugin = Esp, - Esm, Master = Esm, - Ess, Savegame = Ess - } - -// Special files -enum SpecialFile - { - Other, - Morrowind, - Tribunal, - Bloodmoon - } - -enum Version { Unknown, v12, v13 } - -// This struct should contain enough data to put a TES3File object -// back into a specific file position and state. We use it to save the -// "position" of objects in a file (eg. a cell), so we can return -// there later and continue where we stopped (eg. when we want to load -// that specific cell.) -struct TES3FileContext -{ - char[] filename; - uint leftRec, leftSub; - ulong leftFile; - NAME recName, subName; - FileType type; - Version ver; - - ulong filepos; -} - -/** - * Instance used to read TES3 files. Since we will only be reading one - * file at a time, we might as well make one global instance. - */ -TES3File esFile; - -/** - * This struct reads an Elder Scrolls 3 file (esp, esm or ess) - * - * Makes heavy use of private variables to represent current - * state. - * - * Relevant exceptions are - * TES3FileException - error interpreting file - * StreamFileException - file IO error - */ -struct TES3File -{ - private: - BufferedFile file;// Input file - - // These are only used by getRecHeader and getSubHeader for - // asserting the file's integrity. - ulong leftFile; // Number of unread bytes in file - uint leftRec; // Number of unread bytes in record - - // This is used by sub-record readers for integrity checking. - uint leftSub; // Number of bytes in subrecord - - // Name of current record and current sub-record. - NAME recName, subName; - - char[] filename; // Filename - FileType type; // File type - Version ver; // File format version - char[] author; // File author (max 32 bytes (with null?)) - char[] desc; // Description (max 256 bytes (ditto?)) - uint records; // Number of records in the file (doesn't seem to be right?) - SpecialFile spf; // Is this a file we have to treat in a special way? - - struct _mast - { - char[] name; // File name of an esm master for this file - ulong size; // The master file's size in bytes (used for - // version control) - } - - // List of esm masters for this file. For savegames this list also - // contains all plugins. - _mast masters[]; - - - // TES3.HEDR, file header struct - align(1) struct HEDRstruct - { - union - { - float ver; // File format version, 1.2 and 1.3 supported. - uint verHex; // 1.2 = 0x3f99999a, 1.3 = 0x3fa66666 - } - int type; // 0=esp, 1=esm, 32=ess - NAME32 author; // Author's name - NAME256 desc; // File description blurb - uint records; // Number of records in file (?) - } - - static assert(HEDRstruct.sizeof == 300); - - // Which memory region to use for allocations. - RegionManager region; - - public: - - // A struct found in the headers of savegame files. Contains quick - // information to get us going, like the cell name and the player - // name. - struct _saveData - { - float[6] unknown; - char[64] cell; // Cell name - float unk2; // Unknown value - char[32] player; // Player name - } - static assert(_saveData.sizeof == 124); - _saveData saveData; - - // Get file information - char[] getFilename() { return filename; } - ulong getFileSize() { return file.size; } - ulong getPosition() { return file.position; } - SpecialFile getSpecial() { return spf; } - - char[] retSubName() { return subName; } - - bool isVer12() { return ver == Version.v12;} - bool isVer13() { return ver == Version.v13;} - FileType getFileType() { return type; } - _mast[] getMasters() { return masters; } - uint getRecords() { return records; } - char[] getAuthor() { return author; } - RegionManager getRegion() { return region; } - - // Store the current file state (position, file name, version, debug - // info). The info should be enough to get us back on track for - // reading from a file, without having to reread the header or any - // previous records. - void getContext(ref TES3FileContext c) - { - c.filename = filename; - c.leftFile = leftFile; - c.leftRec = leftRec; - c.leftSub = leftSub; - c.recName[] = recName; - c.subName[] = subName; - c.type = type; - c.ver = ver; - c.filepos = file.position; - } - - // Opens the file if it is not already opened. A region manager has - // to be specified. - void restoreContext(TES3FileContext c, RegionManager r) - { - if(filename != c.filename) - openFile(c.filename, r); - file.seekSet(cast(long)c.filepos); - - // File is now open, copy state information - filename = c.filename; - leftFile = c.leftFile; - leftRec = c.leftRec; - leftSub = c.leftSub; - recName[] = c.recName; - subName[] = c.subName; - type = c.type; - ver = c.ver; - } - - // Open a new file and assign a region - private void openFile(char[] filename, RegionManager r) - { - close(); - debug writefln("Opening file"); - if(file is null) file = new BufferedFile(new File()); - file.open(filename); - - region = r; - } - - void open(char[] filename, RegionManager r) - { - uint flags; - - debug writefln("openFile(%s, %s)", filename, r); - openFile(filename, r); - - if(iEnds(filename, "Morrowind.esm")) spf = SpecialFile.Morrowind; - else if(iEnds(filename, "Tribunal.esm")) spf = SpecialFile.Tribunal; - else if(iEnds(filename, "Bloodmoon.esm")) spf = SpecialFile.Bloodmoon; - else spf = SpecialFile.Other; - - debug writefln("Reading header"); - - // Do NOT .dup this filename, since it is referenced outside the - // GC's reach and might be deleted. - this.filename = filename; - - leftFile = file.size; - - // First things first - if(getRecName() != "TES3") - fail("Not a valid Morrowind file"); - - // Record header - getRecHeader(flags); - if(flags) - writefln("WARNING: Header flags are non-zero"); - - // Read and analyse the header data - HEDRstruct hedr; - readHNExact(&hedr, hedr.sizeof, "HEDR"); - - // The float hedr.ver signifies the file format version. It can - // take on these two values: - // 0x3f99999a = 1.2 - // 0x3fa66666 = 1.3 - if( hedr.verHex == 0x3f99999a ) - ver = Version.v12; - else if( hedr.verHex == 0x3fa66666 ) - ver = Version.v13; - else - { - ver = Version.Unknown; - writefln("WARNING: Unknown version: ", hedr.ver); - writefln(" Hex: %X h", *(cast(uint*)&hedr.ver)); - } - - switch(hedr.type) - { - case 0: type = FileType.Esp; break; - case 1: type = FileType.Esm; break; - case 32: type = FileType.Ess; break; - default: - type = FileType.Unknown; - writefln("WARNING: Unknown file type: ", hedr.type); - } - - author = region.copy(stripz(hedr.author)); - desc = region.copy(stripz(hedr.desc)); - records = hedr.records; - - masters = null; - // Reads a MAST and a DATA fields - while(isNextSub("MAST")) - { - _mast ma; - - // MAST entry - master file name - ma.name = getHString(); - - // DATA entry - master file size - ma.size = getHNUlong("DATA"); - - // Add to the master list! - masters ~= ma; - } - - if(type == FileType.Savegame) - { - // Savegame-related data - - // Cell name, player name and player position - readHNExact(&saveData, 124, "GMDT"); - - // Contains eg. 0xff0000, 0xff00, 0xff, 0x0, 0x20. No idea. - getSubNameIs("SCRD"); - skipHSubSize(20); - - // Screenshot. Fits with 128x128x4 bytes - getSubNameIs("SCRS"); - skipHSubSize(65536); - } - } - - // Close the file. We do not clear any object data at this point. - void close() - { - debug writefln("close()"); - if(file !is null) - file.close(); - leftFile = leftRec = leftSub = 0; - debug writefln("Clearing strings"); - - recName[] = '\0'; - subName[] = '\0'; - - // This tells restoreContext() that we have to reopen the file - filename = null; - - debug writefln("exit close()"); - } - - /* - * Error reporting - */ - - void fail(char[] msg) - { - throw new TES3FileException - (msg ~ "\nFile: " ~ filename ~ "\nRecord name: " ~ recName - ~ "\nSubrecord name: " ~ subName); - } - - /************************************************************************ - * - * Highest level readers, reads a name and looks it up in the given - * list. - * - ************************************************************************/ - - // This should be more than big enough for references. - private char lookupBuffer[200]; - - // Get a temporary string. This is faster and more memory efficient - // that the other string functions (because it is allocation free), - // but the returned string is only valid until tmpHString() is - // called again. - char[] tmpHString() - { - getSubHeader(); - assert(leftSub <= lookupBuffer.length, "lookupBuffer wasn't large enough"); - - // Use this to test the difference in memory consumption. - return getString(lookupBuffer[0..leftSub]); - } - - // These are used for file lookups - MeshIndex getMesh() - { getSubNameIs("MODL"); return resources.lookupMesh(tmpHString()); } - SoundIndex getSound() - { getSubNameIs("FNAM"); return resources.lookupSound(tmpHString()); } - IconIndex getIcon(char[] s = "ITEX") - { getSubNameIs(s); return resources.lookupIcon(tmpHString()); } - TextureIndex getTexture() - { getSubNameIs("DATA"); return resources.lookupTexture(tmpHString()); } - - // The getO* functions read optional records. If they are not - // present, return null. - - MeshIndex getOMesh() - { return isNextSub("MODL") ? resources.lookupMesh(tmpHString()) : MeshIndex.init; } - /* - SoundIndex getOSound() - { return isNextSub("FNAM") ? resources.lookupSound(tmpHString()) : SoundIndex.init; } - */ - IconIndex getOIcon() - { return isNextSub("ITEX") ? resources.lookupIcon(tmpHString()) : IconIndex.init; } - TextureIndex getOTexture(char[] s="TNAM") - { return isNextSub(s) ? resources.lookupTexture(tmpHString()) : TextureIndex.init; } - - // Reference with name s - template getHNPtr(Type) - { - Type* getHNPtr(char[] s, ListKeeper list) - { getSubNameIs(s); return cast(Type*) list.lookup(tmpHString()); } - } - - // Reference, only get header - template getHPtr(Type) - { - Type* getHPtr(ListKeeper list) - { return cast(Type*) list.lookup(tmpHString()); } - } - - // Optional reference with name s - template getHNOPtr(Type) - { - Type* getHNOPtr(char[] s, ListKeeper list) - { return isNextSub(s) ? cast(Type*)list.lookup(tmpHString()) : null; } - } - - /************************************************************************ - * - * Somewhat high level reading methods. Knows about headers and - * leftFile/leftRec/leftSub. - * - ************************************************************************/ - - // "Automatic" versions. Sets and returns recName and subName and - // updates leftFile/leftRec. - char[] getRecName() - { - if(!hasMoreRecs()) - fail("No more records, getRecName() failed"); - getName(recName); - leftFile-= 4; - return recName; - } - - // This is specially optimized for LoadINFO - bool isEmptyOrGetName() - { - if(leftRec) - { - file.readBlock(subName.ptr, 4); - leftRec -= 4; - return false; - } - return true; - } - - // I've tried to optimize this slightly, since it gets called a LOT. - void getSubName() - { - if(leftRec <= 0) - fail("No more sub-records, getSubName() failed"); - - // Don't bother with error checking, we will catch an EOF upon - // reading the subrecord data anyway. - file.readBlock(subName.ptr, 4); - - leftRec -= 4; - } - - // We often expect a certain subrecord type, this makes it easy to - // check. - void getSubNameIs(char[] s) - { - getSubName(); - if( subName != s ) - fail("Expected subrecord "~s~" but got "~subName); - } - - // Checks if the next sub-record is called s. If it is, run - // getSubName, if not, return false. - bool isNextSub(char[] s) - { - if(!leftRec) return false; - - getName(subName); - if(subName != s) - { - file.seekCur(-4); - return false; - } - leftRec -= 4; - - //getSubName(); - return true; - } - - // Same as isNextSub, only it works on records instead of - // sub-records. It also loads the record header. - bool isNextHRec(char[] s) - { - if(!leftFile) return false; - getName(recName); - if(recName != s) - { - file.seekCur(-4); - return false; - } - leftFile -= 4; - - uint flags; - getRecHeader(flags); - - return true; - } - - bool hasMoreSubs() { return leftRec > 0; } - bool hasMoreRecs() { return leftFile > 0; } - - // Remaining size of current record - uint getRecLeft() { return leftRec; } - // Size of current sub record - uint getSubSize() { return leftSub; } - - // Skip the rest of this record. Assumes the name and header have - // already been read - void skipRecord() - { - file.seekCur(leftRec); - leftRec = 0; - } - - // Skip an entire record - void skipHRecord() - { - if(!leftFile) return; - - uint flags; - - getRecName(); - getRecHeader(flags); - skipRecord(); - } - - // Skip current sub record and return size - uint skipHSub() - { - getSubHeader(); - file.seekCur(leftSub); - return leftSub; - } - - // Skip sub record and check it's size - void skipHSubSize(uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Size mismatch: got %d, wanted %d", leftSub, size)); - file.seekCur(leftSub); - } - - // Check the name and size before skipping - void skipHNSub(char[] name, uint size) - { - getSubNameIs(name); - skipHSubSize(size); - } - - // These read an entire sub-record, including the header. They also - // adjust and check leftSub and leftRecord variables through calling - // getSubHeader(). - void readHExact(void * p, uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Size mismatch: got %d, wanted %d", leftSub, size)); - readExact(p, leftSub); - } - - template TgetHType(T) - { T TgetHType() { T t; readHExact(&t, t.sizeof); return t;} } - - // To make these easier to use (and to further distinguish them from - // the above "raw" versions), these return their value instead of - // using an ref argument. - alias TgetHType!(uint) getHUint; - alias TgetHType!(int) getHInt; - alias TgetHType!(float) getHFloat; - alias TgetHType!(ulong) getHUlong; - alias TgetHType!(byte) getHByte; - - // Reads a string sub-record, including header - char[] getHString() - { - getSubHeader(); - - // Hack to make MultiMark.esp load. Zero-length strings do not - // occur in any of the official mods, but MultiMark makes use of - // them. For some reason, they break the rules, and contain a - // byte (value 0) even if the header says there is no data. If - // Morrowind accepts it, so should we. - if(leftSub == 0) - { - // Skip the following zero byte - leftRec--; - assert(file.getc() == 0); - // TODO: Report this by setting a flag or something? - return null; - } - - return getString(region.getString(leftSub)); - } - - // Other quick aliases (this is starting to get messy) - // Get string sub record string with name s - char[] getHNString(char[] s) - { getSubNameIs(s); return getHString(); } - - // Get optional sub record string with name s - char[] getHNOString(char[] s) - { return isNextSub(s) ? getHString() : null; } - - template TgetHNType(T) - { T TgetHNType(char[] s) { T t; readHNExact(&t, t.sizeof, s); return t;} } - - template TgetHNOType(T) - { - T TgetHNOType(char[] s, T def) - { - if(isNextSub(s)) - { - T t; - readHExact(&t, t.sizeof); - return t; - } - else return def; - } - } - - alias TgetHNType!(uint) getHNUint; - alias TgetHNType!(int) getHNInt; - alias TgetHNType!(float) getHNFloat; - alias TgetHNType!(ulong) getHNUlong; - alias TgetHNType!(byte) getHNByte; - alias TgetHNType!(short) getHNShort; - alias TgetHNType!(byte) getHNByte; - - alias TgetHNOType!(float) getHNOFloat; - alias TgetHNOType!(int) getHNOInt; - alias TgetHNOType!(byte) getHNOByte; - - void readHNExact(void* p, uint size, char[] s) - { getSubNameIs(s); readHExact(p,size); } - - // Record header - // This updates the leftFile variable BEYOND the data that follows - // the header, ie beyond the entire record. You are supposed to use - // the leftRec variable when reading record data. - void getRecHeader(out uint flags) - { - // General error checking - if(leftFile < 12) - fail("End of file while reading record header"); - if(leftRec) - fail(format("Previous record contains %d unread bytes", leftRec)); - - getUint(leftRec); - getUint(flags);// This header entry is always zero - assert(flags == 0); - getUint(flags); - leftFile -= 12; - - // Check that sizes add up - if(leftFile < leftRec) - fail(format(leftFile, " bytes left in file, but next record contains ", - leftRec," bytes")); - - // Adjust number of bytes left in file - leftFile -= leftRec; - } - - // Sub-record head - // This updates leftRec beyond the current sub-record as - // well. leftSub contains size of current sub-record. - void getSubHeader() - { - if(leftRec < 4) - fail("End of record while reading sub-record header"); - - if(file.readBlock(&leftSub, 4) != 4) - fail("getSubHeader could not read header length"); - - leftRec -= 4; - - // Adjust number of record bytes left - leftRec -= leftSub; - - // Check that sizes add up - if(leftRec < 0) - fail(format(leftRec+leftSub, - " bytes left in record, but next sub-record contains ", - leftSub," bytes")); - } - - void getSubHeaderIs(uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Expected header size to be ", size, ", not ", leftSub)); - } - - /************************************************************************* - * - * Low level reading methods - * - *************************************************************************/ - - /// Raw data of any size - void readExact(void *buf, uint size) - { - assert(size != 0); - file.readExact(buf,size); - } - - // One byte - void getByte(out byte b) { file.read(b); } - void getUByte(out ubyte b) { file.read(b); } - // Two bytes - void getUShort(out ushort s) { file.read(s); } - // Four bytes - void getUint(out uint u) { file.read(u); } - void getInt(out int i) { file.read(i); } - void getFloat(out float f) { file.read(f); } - // Eight bytes - void getUlong(out ulong l) { file.read(l); } - - // Get a record or subrecord name, four bytes - void getName(NAME name) - { - file.readBlock(name.ptr, 4); - /* - if(file.readBlock(name.ptr, 4) != 4) - fail("getName() could not find more data"); - */ - } - - // Fill buffer of predefined length. If actual string is shorter - // (ie. null terminated), the buffer length is set - // accordingly. Chopped string is returned. All strings pass through - // this function, so any character encoding conversions should - // happen here. - char[] getString(char[] str) - { - if(str.length != file.readBlock(str.ptr,str.length)) - fail("getString() could not find enough data in stream"); - - str = stripz(str); - makeUTF8(str); // TODO: A hack. Will replace non-utf characters - // with question marks. This is neither a very - // desirable result nor a very optimized - // implementation of it. - return str; - } - - // Use this to allocate and read strings of predefined length - char[] getString(int l) - { - char[] str = region.getString(l); - return getString(str); - } -} diff --git a/old_d_version/esm/imports.d b/old_d_version/esm/imports.d deleted file mode 100644 index 69f1d33dce..0000000000 --- a/old_d_version/esm/imports.d +++ /dev/null @@ -1,47 +0,0 @@ -module esm.imports; - -/* This is a file that imports common modules used by the load*.d - record loaders. It is really a cut down version of what used to be - the start of records.d. - - This file MUST NOT import records.d - directly or indirectly - - because that will trigger a nice three page long list of template - forwarding errors from the compiler. - - What happens is that when DMD/GDC compiles one of the load* files, - it is forced to read records.d first (since it is an imported - module) - but then it sees a template that referes to a struct in - the current load* file, before that struct is defined. Curriously - enough, DMD has no problems when you specify all the source files - on the command line simultaneously. This trick doesn't work with - GDC though, and DSSS doesn't use it either. - - This file was created to work around this compiler bug. -*/ - -public -{ -import esm.defs; -import esm.filereader; -import esm.listkeeper; - -import core.resource; -import core.memory; - -import util.regions; -import monster.util.aa; - -import std.stdio; -import std.string; - -alias RegionBuffer!(ENAMstruct) EffectList; - -// Records that are cross referenced often -import esm.loadscpt; -import esm.loadsoun; -import esm.loadspel; -import esm.loadench; - -import monster.monster; -} - diff --git a/old_d_version/esm/listkeeper.d b/old_d_version/esm/listkeeper.d deleted file mode 100644 index 3722a5d12e..0000000000 --- a/old_d_version/esm/listkeeper.d +++ /dev/null @@ -1,330 +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 (listkeeper.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 esm.listkeeper; - -import monster.util.aa; - -import core.memory; - -import esm.filereader; -import esm.defs; - -import std.stdio; - -// Item types, used in the lookup table for inventory items, creature -// lists and leveled lists. We also use it for all types of references -// that can exist in cells. -enum ItemType - { - // Items - None = 0, Potion, Apparatus, Armor, Weapon, Book, Clothing, - Light, Ingredient, Pick, Probe, Repair, Misc, ItemList, - - // Used for creature lists - Creature, CreatureList, NPC, - - // Other cell references - Door, Activator, Static, Container//, SoundGen - } - -abstract class ListKeeper -{ - int listIndex; - - new(uint size) - { - return esmRegion.allocate(size).ptr; - } - - delete(void *p) { assert(0); } - - this() - { - // Store our index for later use - listIndex = recordLists.length; - - // Add the class to the global list - recordLists ~= this; - } - - // Load a record from a master or plugin file - void load(); - - // Looks up a reference. If it does not exist it is assumed to be a - // forward reference within a file, and is inserted. - void* lookup(char[] s); - - // Tell the loader that current file has ended, so it can do things - // like check that all referenced objects have been loaded. - void endFile(); - - // Number of inserted elements - uint length(); - - void addToList(ref ItemBaseList l, ItemType t) { assert(0); } -} - -ListKeeper recordLists[]; - -// Keep the list of Type structures for records where the first -// subrecord is an id string called NAME. This id is used for -// lookup. Although almost all lookups match in case, there are a few -// sounds that don't, so we treat these id lookups as generally case -// insensitive. This hasn't posed any problems so far. -class ListID(Type) : ListKeeper -{ - HashTable!(char[], Type, ESMRegionAlloc, CITextHash) names; - - this(uint size) - { - names = names.init; - if(size) names.rehash(size); - } - - // Reads the id for this header. Override if the id is not simply - // getHNString("NAME") - char[] getID() - { - return esFile.getHNString("NAME"); - } - - // Load a record from a master of plugin file - void load() - { - assert(esFile.getFileType == FileType.Esm || - esFile.getFileType == FileType.Esp); - - // Get the identifier of this record - char[] id = getID(); - - // Get pointer to a new or existing object. - Type *p; - if(names.insertEdit(id, p)) - // A new item was inserted - { - p.state = LoadState.Unloaded; - p.id = id; - p.load(); - p.state = LoadState.Loaded; - } - else - // Item already existed, either from a previous file or as a - // forward reference from this file. Load on top of it. The - // LoadState tells the struct whether it contains loaded data. - { - /* - if(p.state == LoadState.Loaded) - // Make a special case for this, perhaps, or just ignore it. - writefln("WARNING: Duplicate record in file %s: '%s'", - esFile.getFilename(), id); - */ - - assert(icmp(p.id, id) == 0); - p.load(); - p.state = LoadState.Loaded; - } - } - - // Looks up a reference. If it does not exist it is assumed to be a - // forward reference within a file, and is inserted. - void* lookup(char[] id) - { - if(!id.length) return null; // Empty reference - - Type *p = names.lookup(id); - // Is the value in the list? - if(!p) - // No, assume it is a forward reference. - { - // Since the lookup name is stored in an internal buffer in - // esFile, we have to copy it. - id = esmRegion.copy(id); - - // To avoid copying the string on every lookup, we have to - // insert in a separate step. But a double lookup isn't - // really THAT expensive. Besides, my tests show that this - // is used in less than 10% of the cases. - names.insertEdit(id, p); - p.id = id; - p.state = LoadState.Unloaded; - } - return cast(void*)p; - } - - // Check that all referenced objects are actually loaded. - void endFile() - in - { - // We can skip this in release builds - names.validate(); - } - body - { - foreach(char[] id, ref Type t; names) - // Current file is now counted as done - if(t.state == LoadState.Loaded) t.state = LoadState.Previous; - else if(t.state == LoadState.Unloaded) - //writefln("WARNING: Unloaded reference " ~ id); - esFile.fail("Unloaded reference " ~ id); - } - - // Number of inserted elements - uint length() {return names.length;} - - // Add the names in this list to an ItemList - void addToList(ref ItemBaseList l, ItemType t) - { - foreach(char[] id, ref Type s; names) - l.insert(id, &s, t); - } -} - -// A pointer to an item -struct ItemBase -{ - ItemType type; - void *p; -} - -struct ItemBaseList -{ - HashTable!(char[],ItemBase,ESMRegionAlloc) list; - - void addList(ItemBaseList l) - { - foreach(char[] id, ItemBase b; l.list) - insert(id, b.p, b.type); - } - - void addList(ListKeeper source, ItemType type) - { - source.addToList(*this, type); - } - - void insert(char[] id, void* p, ItemType type) - { - ItemBase *b; - if(!list.insertEdit(id, b)) - { - //writefln("Replacing item ", id); - if(b.type != ItemType.None) - esFile.fail("Replaced valid item: " ~ id); - } - //else writefln("Inserting new item ", id); - - b.type = type; - b.p = p; - } - - // Called at the end to check that all referenced items have been resolved - void endMerge() - { - foreach(char[] id, ref ItemBase t; list) - // Current file is now counted as done - if(t.type == ItemType.None) - // TODO: Don't use esFile.fail for this - esFile.fail("ItemBaseList: Unresolved forward reference: " ~ id); - } - - // Look up an item, return a pointer to the ItemBase representing - // it. If it does not exist, it is inserted. - ItemBase *lookup(char[] id) - { - if(!id.length) return null; // Empty reference - ItemBase *b = list.lookup(id); - // Is the value in the list? - if(!b) - // No, assume it is a forward reference. - { - // Since the lookup name is stored in an internal buffer in - // esFile, we have to copy it. - id = esmRegion.copy(id); - - // To avoid copying the string on every lookup, we have to - // insert in a separate step. But a double lookup isn't - // really THAT expensive. - list.insertEdit(id, b); - - b.p = null; - b.type = ItemType.None; - } - return b; - } -} - -// An item. Contains a reference to an ItemBase, which again is a -// reference to an item. The ItemBase might change after we have -// looked it up (for forward references), so we have to use a pointer. -struct Item -{ - ItemBase *i; - - void* getPtr(ItemType type) - { - if(i != null && i.type == type) return i.p; - return null; - } - - T* getType(T, ItemType Type)() - { - return cast(T*)getPtr(Type); - } -} - -struct ItemList -{ - private: - ItemBaseList list; - - public: - void addList(ItemList l) - { list.addList(l.list); } - - void addList(ListKeeper source, ItemType type) - { list.addList(source, type); } - - Item lookup(char[] id) - { - Item i; - i.i = list.lookup(id); - return i; - } - - void endMerge() - { list.endMerge(); } - - void endFile() - in { list.list.validate(); } - body {} - - void rehash(uint size) - { list.list.rehash(size); } - - uint length() { return list.list.length(); } -} - -// Aggregate lists, made by concatinating several other lists. -ItemList items; // All inventory items, including leveled item lists -ItemList actors; // All actors, ie. NPCs, creatures and leveled lists -ItemList cellRefs; // All things that are referenced from cells diff --git a/old_d_version/esm/loadcell.d b/old_d_version/esm/loadcell.d deleted file mode 100644 index 6f987e2e05..0000000000 --- a/old_d_version/esm/loadcell.d +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file contains some leftovers which have not yet been ported to - C++. - */ - -align(1) struct AMBIStruct -{ - Color ambient, sunlight, fog; - float fogDensity; - - static assert(AMBIStruct.sizeof == 16); -} - -int max(int x, int y) -{ return x>=y?x:y; } - -struct ExtCellHash -{ - // This is a pretty good hash, gives no collisions for all of - // Morrowind.esm when the table size is 2048, and it gives very few - // collisions overall. Not that it matters that much. - static uint hash(uint val) - { - uint res = cast(ushort)val; - res += *(cast(ushort*)&val+1)*41; - return res; - } - - static bool isEqual(uint a, uint b) { return a==b; } -} - -class CellList : ListKeeper -{ - // Again, these are here to avoid DMD template bugs - alias _aaNode!(char[], InteriorCell) _unused1; - alias _aaNode!(uint, ExteriorCell) _unused2; - - HashTable!(char[], InteriorCell, ESMRegionAlloc) in_cells; - HashTable!(uint, ExteriorCell, ESMRegionAlloc, ExtCellHash) ex_cells; - - // Store the maximum x or y coordinate (in absolute value). This is - // used in the landscape pregen process. - int maxXY; - - this() - { - in_cells = in_cells.init; - in_cells.rehash(1600); - - ex_cells = ex_cells.init; - ex_cells.rehash(1800); - } - - align(1) struct DATAstruct - { - CellFlags flags; - int gridX, gridY; - static assert(DATAstruct.sizeof==12); - } - - DATAstruct data; - - // Look up an interior cell, throws an error if not found (might - // change later) - InteriorCell *getInt(char[] s) - { - return in_cells.getPtr(s); - } - - // Exterior cell, same as above - ExteriorCell *getExt(int x, int y) - { - return ex_cells.getPtr(compound(x,y)); - } - - // Check whether we have a given exterior cell - bool hasExt(int x, int y) - { - return ex_cells.inList(compound(x,y)); - } - - void *lookup(char[] s) - { assert(0); } - - void endFile() - out - { - in_cells.validate(); - ex_cells.validate(); - } - body - { - foreach(id, ref c; in_cells) - { - if(c.state == LoadState.Loaded) c.state = LoadState.Previous; - // We never forward reference cells! - assert(c.state != LoadState.Unloaded); - } - - foreach(id, ref c; ex_cells) - { - if(c.state == LoadState.Loaded) c.state = LoadState.Previous; - // We never forward reference cells! - assert(c.state != LoadState.Unloaded); - } - } - - uint length() { return numInt() + numExt(); } - uint numInt() { return in_cells.length; } - uint numExt() { return ex_cells.length; } - - // Turn an exterior cell grid position into a unique number - static uint compound(int gridX, int gridY) - { - return cast(ushort)gridX + ((cast(ushort)gridY)<<16); - } - - static void decompound(uint val, out int gridX, out int gridY) - { - gridX = cast(short)(val&0xffff); - gridY = cast(int)(val&0xffff0000) >> 16; - } - - void load() - {with(esFile){ - char[] id = getHNString("NAME"); - - // Just ignore this, don't know what it does. I assume it - // deletes the cell, but we can't handle that yet. - if(isNextSub("DELE")) getHInt(); - - readHNExact(&data, data.sizeof, "DATA"); - - if(data.flags & CellFlags.Interior) - { - InteriorCell *p; - if(in_cells.insertEdit(id, p)) - // New item was inserted - { - p.state = LoadState.Unloaded; - p.id = id; - p.flags = data.flags; - p.load(); - p.state = LoadState.Loaded; - } - else - // Overloading an existing cell - { - if(p.state != LoadState.Previous) - fail("Duplicate interior cell " ~ id); - - assert(id == p.id); - p.load(); - p.state = LoadState.Loaded; - } - } - else // Exterior cell - { - uint key = compound(data.gridX, data.gridY); - - ExteriorCell *p; - if(ex_cells.insertEdit(key, p)) - // New cell - { - p.state = LoadState.Unloaded; - p.name = id; - p.flags = data.flags; - p.gridX = data.gridX; - p.gridY = data.gridY; - p.load(); - p.state = LoadState.Loaded; - - int mx = max(abs(p.gridX), abs(p.gridY)); - maxXY = max(maxXY, mx); - } - else - { - if(p.state != LoadState.Previous) - fail(format("Duplicate exterior cell %d %d", - data.gridX, data.gridY)); - assert(p.gridX == data.gridX); - assert(p.gridY == data.gridY); - p.load(); - p.state = LoadState.Loaded; - } - } - }} -} -CellList cells; diff --git a/old_d_version/esm/loaddial.d b/old_d_version/esm/loaddial.d deleted file mode 100644 index 6e36f0b887..0000000000 --- a/old_d_version/esm/loaddial.d +++ /dev/null @@ -1,289 +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 (loaddial.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 esm.loaddial; -import esm.imports; -import esm.loadinfo; - -/* - * Dialogue topic and journal entries. The acutal data is contained in - * the following INFO records. - */ - -// Keep a list of possible hyper links. This list is used when parsing -// text and finding references to topic names. Eg. if a character says -// "Would you like to join the mages guild?", we must be able to pick -// out the phrase "join the mages guild?" and make a hyperlink of -// it. Each link is indexed by their first word. The structure -// contains the rest of the phrase, so the phrase above would be -// indexed by "join" and contain the string "the mages guild?", for -// quick comparison with the text are currently parsing. It also -// contains a pointer to the corresponding dialogue struct. The lists -// are sorted by descending string length, in order to match the -// longest possible term first. - -struct Hyperlink -{ - Dialogue *ptr; - char[] rest; - - // Returns a < b if a.length > b.length. - int opCmp(Hyperlink *h) {return h.rest.length - rest.length;} -} - -alias RegionBuffer!(Hyperlink) HyperlinkArray; - -// This is much nicer now that we use our own AA. -struct HyperlinkList -{ - // Make a case insensitive hash table of Hyperlink arrays. - HashTable!(char[], HyperlinkArray, ESMRegionAlloc, CITextHash) list; - - void add(char[] topic, Dialogue* ptr) - { - // Only add dialogues - if(ptr.type != Dialogue.Type.Topic) return; - - Hyperlink l; - l.ptr = ptr; - l.rest = topic; - - // Use the first word as the index - topic = nextWord(l.rest); - - // Insert a new array, or get an already existing one - HyperlinkArray ha = list.get(topic, - // Create a new array - delegate void (ref HyperlinkArray a) - { a = esmRegion.getBuffer!(Hyperlink)(0,1); } - ); - - // Finally, add it to the list - ha ~= l; - } - - Hyperlink[] getList(char[] word) - { - HyperlinkArray p; - if(list.inList(word, p)) return p.array(); - return null; - } - - void rehash(uint size) - { - list.rehash(size); - } - - // We wouldn't need this if we only dealt with one file, since the - // topics are already sorted in Morrowind.esm. However, other files - // might add items out of order later, so we have to sort it. To - // understand why this is needed, consider the following example: - // - // Morrowind.esm contains the topic 'join us'. When ever the text - // ".. join us blahblah ..." is encountered, this match is - // found. However, if a plugin adds the topic 'join us today', we - // have to place this _before_ 'join us' in the list, or else it - // will never be matched. - void sort() - { - foreach(char[] s, HyperlinkArray l; list) - { - l.array().sort; - /* - writefln("%s: ", s, l.length); - foreach(Hyperlink h; l.array()) - writefln(" %s (%s)", h.rest, h.ptr.id); - */ - } - } -} - -// List of dialogue hyperlinks -HyperlinkList hyperlinks; - -struct Dialogue -{ - enum Type - { - Topic = 0, - Voice = 1, - Greeting = 2, - Persuasion = 3, - Journal = 4, - Deleted = -1 - } - - //Type type; - DialogueType type; - - DialInfoList infoList; - - char[] id; // This is the 'dialogue topic' that the user actually - // sees. - LoadState state; - - void load() - {with(esFile){ - getSubNameIs("DATA"); - - getSubHeader(); - int si = getSubSize(); - if(si == 1) - { - byte b; - getByte(b); - DialogueType t = cast(DialogueType)b; - - // Meet the new type, same as the old type - if(t != this.type && state == LoadState.Previous) - fail("Type changed in dialogue " ~ id); - - this.type = t; - } - else if(si == 4) - { - // These are just markers, their values are not used. - int i; - getInt(i); - //writefln("In file %s:", getFilename()); - //writefln(" WARNING: DIAL.DATA was size 4 and contains: ", i); - i = getHNInt("DELE"); - //writefln(" DELE contains ", i); - this.type = Type.Deleted; - } - else fail("Unknown sub record size " ~ toString(si)); - - infoList.state = state; - while(isNextHRec("INFO")) - infoList.load(this.type); - //skipRecord(); - }} -} - -typedef Dialogue.Type DialogueType; - -/+ - // I don't remember when I commented out this code or what state - // it is in. Probably highly experimental. - // -------------- - - // Loop through the info blocks in this dialogue, and update the - // master as necessary. - - // TODO: Note that the dialogue system in Morrowind isn't very - // robust. If several mods insert dialogues at exactly the same - // place, the mods loaded last might overwrite the previous mods, - // completely removing the previous entry even if the two entries - // do not have the same id. This is because a change also - // overwrites the previous and the next entry, in order to update - // their "previous" and "next" fields. Furthermore, we might put - // ourselves in a situation where the forward and backward chains - // do not match, or in a situation where we update a deleted - // info. For now I do nothing about it, but I will have to develop - // a "conflict manager" later on. It SHOULD be possible to merge - // these info lists automatically in most cases, but it - // complicates the code. - - // Whoa, seems we have a case study already with just tribunal and - // bloodmoon loaded! See comments below. - - foreach(char[] id, ref DialInfoLoad m; mod.infoList) - { - // Remove the response if it is marked as deleted. - if(m.deleted) - { - if((id in master.infoList) == null) - writefln("Cannot delete info %s, does not exist", id); - else master.infoList.remove(id); - } - else - // Just plain copy it in. - master.infoList[id] = m; - } - } - - // Here we have to fix inconsistencies. A good case example is the - // dialogue "Apelles Matius" in trib/blood. Trib creates a - // dialogue of a few items, bloodmoon adds another. But since the - // two are independent, the list in bloodmoon does not change the - // one in trib but rather creates a new one. In other words, we - // will have to deal with the possibility of several "independent" - // lists within each topic. We can do this by looking for several - // start points (ie. infos with prev="") and just latch them onto - // each other. I'm not sure it gives the correct result, - // though. For example, which list comes first would be rather - // arbitrarily decided by the order we traverse the infoList AA. I - // will just have to assume that they truly are "independent". - - // There still seems to be a problem though. Bloodmoon overwrites - // some stuff added by Tribunal, see "Boots of the Apostle" for an - // example. Looks like the editor handles it just fine... We need - // to make sure that all the items in our AA are put into the - // list, and in the right place too. We obviously cannot fully - // trust the 'next' and 'prev' fields, but they are the only - // guidance we have. Deal with it later! - - // At this point we assume "master" to contain the final dialogue - // list, so at this point we can set it in stone. - infoList.length = master.infoList.length; - - // Find the first entry - DialInfoLoad* starts[]; // starting points for linked lists - DialInfoLoad *current; - foreach(char[] id, ref DialInfoLoad l; master.infoList) - if(l.prev == "") starts ~= &l; - - foreach(int num, ref DialInfo m; infoList) - { - if(current == null) - { - if(starts.length == 0) - { - writefln("Error: No starting points!"); - infoList.length = num; - break; - } - // Pick the next starting point - current = starts[0]; - starts = starts[1..$]; - } - m.copy(*current, this); - - if((*current).next == "") - current = null; - else - { - current = (*current).next in master.infoList; - if(current == null) - { - writefln("Error in dialouge info lookup!"); - break; - } - } - } - if(infoList.length != master.infoList.length) - writefln("Dialogue list lengths do not match, %d != %d", - infoList.length, master.infoList.length); - } -} -+/ diff --git a/old_d_version/esm/records.d b/old_d_version/esm/records.d deleted file mode 100644 index d5dd211a19..0000000000 --- a/old_d_version/esm/records.d +++ /dev/null @@ -1,219 +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 (records.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 esm.records; - -public -{ - import monster.util.aa; - import util.regions; - - import core.memory; - import core.resource; - - import esm.filereader; - import esm.defs; - import esm.listkeeper; - - import std.stdio; // Remove later -} - -public import - esm.loadacti, esm.loaddoor, esm.loadglob, esm.loadscpt, esm.loadsoun, esm.loadgmst, - esm.loadfact, esm.loadstat, esm.loadspel, esm.loadalch, esm.loadappa, esm.loadarmo, - esm.loadbody, esm.loadench, esm.loadbook, esm.loadbsgn, esm.loadltex, esm.loadmgef, - esm.loadweap, esm.loadlocks,esm.loadcell, esm.loadregn, esm.loadligh, esm.loadskil, - esm.loadsndg, esm.loadrace, esm.loadmisc, esm.loadclot, esm.loadingr, esm.loadclas, - esm.loadcont, esm.loadcrea, esm.loadnpc, esm.loaddial, esm.loadinfo, esm.loadsscr, - esm.loadlevlist; - -void loadRecord(char[] recName) -{ - switch(recName) - { - case "ACTI": activators.load(); break; - case "DOOR": doors.load(); break; - case "GLOB": globals.load(); break; - case "SCPT": scripts.load(); break; - case "SOUN": sounds.load(); break; - case "GMST": gameSettings.load(); break; - case "FACT": factions.load(); break; - case "STAT": statics.load(); break; - case "SPEL": spells.load(); break; - case "ALCH": potions.load(); break; - case "APPA": appas.load(); break; - case "ARMO": armors.load(); break; - case "BODY": bodyParts.load(); break; - case "ENCH": enchants.load(); break; - case "BOOK": books.load(); break; - case "BSGN": birthSigns.load(); break; - case "LTEX": landTextures.load(); break; - case "MGEF": effects.load(); break; - case "WEAP": weapons.load(); break; - case "REPA": repairs.load(); break; - case "LOCK": lockpicks.load(); break; - case "PROB": probes.load(); break; - case "CELL": cells.load(); break; - case "REGN": regions.load(); break; - case "LIGH": lights.load(); break; - case "SKIL": skills.load(); break; - case "SNDG": soundGens.load(); break; - case "RACE": races.load(); break; - case "MISC": miscItems.load(); break; - case "CLOT": clothes.load(); break; - case "INGR": ingreds.load(); break; - case "CLAS": classes.load(); break; - case "CONT": containers.load(); break; - case "CREA": creatures.load(); break; - case "LEVI": itemLists.load(); break; - case "LEVC": creatureLists.load(); break; - case "NPC_": npcs.load(); break; - case "DIAL": dialogues.load(); break; - case "SSCR": startScripts.load(); break; - /* - - // Tribunal / Bloodmoon only - case "SSCR": loadSSCR.load(); break; - - */ - // For save games: - // case "NPCC": loadNPCC; - // case "CNTC": loadCNTC; - // case "CREC": loadCREC; - - // These should never be looked up - case "TES3": - case "INFO": - case "LAND": - case "PGRD": - esFile.fail("Misplaced record " ~ recName); - default: - esFile.fail("Unknown record type " ~ recName); - } - //*/ -} - -// Um, this has to be in this file for some reason. -ListID!(Dialogue) dialogues; - -struct ItemT -{ - Item item; - ItemBase *i; - - T* getType(T, ItemType Type)() - { - return item.getType!(T, Type)(); - } - - alias getType!(Potion, ItemType.Potion) getPotion; - alias getType!(Apparatus, ItemType.Apparatus) getApparatus; - alias getType!(Armor, ItemType.Armor) getArmor; - alias getType!(Weapon, ItemType.Weapon) getWeapon; - alias getType!(Book, ItemType.Book) getBook; - alias getType!(Clothing, ItemType.Clothing) getClothing; - alias getType!(Light, ItemType.Light) getLight; - alias getType!(Ingredient, ItemType.Ingredient) getIngredient; - alias getType!(Tool, ItemType.Pick) getPick; - alias getType!(Tool, ItemType.Probe) getProbe; - alias getType!(Tool, ItemType.Repair) getRepair; - alias getType!(Misc, ItemType.Misc) getMisc; - alias getType!(LeveledItems, ItemType.ItemList) getItemList; - alias getType!(Creature, ItemType.Creature) getCreature; - alias getType!(LeveledCreatures, ItemType.CreatureList) getCreatureList; - alias getType!(NPC, ItemType.NPC) getNPC; - alias getType!(Door, ItemType.Door) getDoor; - alias getType!(Activator, ItemType.Activator) getActivator; - alias getType!(Static, ItemType.Static) getStatic; - alias getType!(Container, ItemType.Container) getContainer; - - static ItemT opCall(Item it) - { - ItemT itm; - itm.item = it; - itm.i = it.i; - return itm; - } -} - -void endFiles() -{ - foreach(ListKeeper l; recordLists) - l.endFile(); - - items.endFile(); -} - -void initializeLists() -{ - recordLists = null; - - // Initialize all the lists here. The sizes have been chosen big - // enough to hold the main ESM files and a large number of mods - // without rehashing. - - activators = new ListID!(Activator)(1400); - doors = new ListID!(Door)(300); - globals = new ListID!(Global)(300); - scripts = new ScriptList(1800); - sounds = new ListID!(Sound)(1000); - gameSettings = new ListID!(GameSetting)(1600); - factions = new ListID!(Faction)(30); - statics = new ListID!(Static)(4000); - spells = new ListID!(Spell)(1300); - potions = new ListID!(Potion)(300); - appas = new ListID!(Apparatus)(30); - armors = new ListID!(Armor)(500); - bodyParts = new ListID!(BodyPart)(2300); - enchants = new ListID!(Enchantment)(1000); - books = new ListID!(Book)(700); - birthSigns = new ListID!(BirthSign)(30); - landTextures = new LandTextureList; - effects = new MagicEffectList; - weapons = new ListID!(Weapon)(700); - lockpicks = new ListID!(Tool)(10); - probes = new ListID!(Tool)(10); - repairs = new ListID!(Tool)(10); - cells = new CellList; - regions = new ListID!(Region)(20); - lights = new ListID!(Light)(1000); - skills = new SkillList; - soundGens = new ListID!(SoundGenerator)(500); - races = new ListID!(Race)(100); - miscItems = new ListID!(Misc)(700); - clothes = new ListID!(Clothing)(700); - ingreds = new ListID!(Ingredient)(200); - classes = new ListID!(Class)(100); - containers = new ListID!(Container)(1200); - creatures = new ListID!(Creature)(800); - itemLists = new ListID!(LeveledItems)(600); - creatureLists = new ListID!(LeveledCreatures)(400); - npcs = new ListID!(NPC)(3500); - dialogues = new ListID!(Dialogue)(3000); - startScripts.init(); - - hyperlinks.rehash(1600); - - items.rehash(5500); - actors.rehash(5000); - cellRefs.rehash(17000); -} diff --git a/old_d_version/esmtool.d b/old_d_version/esmtool.d deleted file mode 100644 index a43361803d..0000000000 --- a/old_d_version/esmtool.d +++ /dev/null @@ -1,377 +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 (esmtool.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 esmtool; - -import std.stdio; - -import core.memory; -import esm.esmmain; -import monster.util.string; -import mscripts.setup; - -import std.gc; -import gcstats; - - -// Not used, but we have to link it in along with the C++ stuff. -import input.events; - -void poolSize() -{ - GCStats gc; - getStats(gc); - writefln("Pool size: ", comma(gc.poolsize)); - writefln("Used size: ", comma(gc.usedsize)); -} - -//alias int[Dialogue*] TopicList; - -void main(char[][] args) -{ - char[][] files; - bool raw; - - bool scptList; // List scripts - bool scptShow; // Show a script - char[] scptName; // Script to show - - bool ciList; // List interior cells - bool ceList; // List exterior cells that have names - - bool weList; // List weapons - - bool gmst; // List game settings - - bool numbers; // List how many there are of each record type - - foreach(char[] a; args[1..$]) - if(a == "-r") raw = true; - - else if(a == "-sl") scptList = true; - else if(a == "-s") scptShow = true; - else if(scptShow && scptName == "") scptName = a; - - else if(a == "-g") gmst = true; - else if(a == "-cil") ciList = true; - else if(a == "-cel") ceList = true; - - else if(a == "-wl") weList = true; - - else if(a == "-n") numbers = true; - - else if(a.begins("-")) writefln("Ignoring unknown option %s", a); - else files ~= a; - - int help(char[] msg) - { - writefln("%s", msg); - writefln("Syntax: %s [options] esm-file [esm-file ... ]", args[0]); - writefln(" Options:"); - writefln(" -r Display all records in raw format"); - writefln(" -n List the number of each record type"); - writefln(" -sl List scripts"); - writefln(" -g List game settings (GMST)"); - writefln(" -s name Show given script"); - writefln(" -cil List interior cells"); - writefln(" -cel List exterior cells with names"); - writefln(" -wl List weapons"); - return 1; - } - if(files.length == 0) return help("No input files given"); - - if(scptShow && scptName == "") return help("No script name given"); - - initializeMemoryRegions(); - initMonsterScripts(); - - if(raw) - { - foreach(int fileNum, char[] filename; files) - { - try - { - esFile.open(filename, esmRegion); - printRaw(); - } - catch(Exception e) - { - try {writefln(e);} - catch {} - writefln("Error on file %s", filename); - } - catch - { - writefln("Error: Unkown failure on file %s", filename); - } - } - return; - } - - // Disable resource lookups. - resources.dummy = true; - - try loadTESFiles(files); - catch(Exception e) - { - writefln(e); - } - catch { writefln("Error: Unkown failure"); } - - // List weapons - if(weList) foreach(n, m; weapons.names) - { - alias Weapon.Type WT; - switch(m.data.type) - { - case WT.ShortBladeOneHand: writef("Short Sword"); break; - case WT.LongBladeOneHand: writef("Long Sword, One-Handed"); break; - case WT.LongBladeTwoHand: writef("Long Sword, Two-Handed"); break; - case WT.BluntOneHand: writef("Blunt, One-Handed"); break; - case WT.BluntTwoClose: writef("Blunt, Two-Handed"); break; - case WT.BluntTwoWide: writef("Blunt, Two-Handed Wide"); break; - case WT.SpearTwoWide: writef("Spear, Two-Handed"); break; - case WT.AxeOneHand: writef("Axe, One-Handed"); break; - case WT.AxeTwoHand: writef("Axe, Two-Handed"); break; - case WT.MarksmanBow: writef("Bow"); break; - case WT.MarksmanCrossbow: writef("Crossbow"); break; - case WT.MarksmanThrown: writef("Thrown weapon"); break; - case WT.Arrow: writef("Arrow"); break; - case WT.Bolt: writef("Bolt"); break; - default: assert(0); - } - writefln(" id '%s': name '%s'", n, m.name); - - if(m.data.flags & Weapon.Flags.Magical) - writefln("Magical"); - if(m.data.flags & Weapon.Flags.Silver) - writefln("Silver"); - - writefln("Weight: ", m.data.weight); - writefln("Value: ", m.data.value); - writefln("Health: ", m.data.health); - writefln("Speed: ", m.data.speed); - writefln("Reach: ", m.data.reach); - writefln("Enchantment points: ", m.data.enchant); - writefln("Combat: ", m.data.chop, m.data.slash, m.data.thrust); - - if(m.enchant) writefln("Has enchantment '%s'", m.enchant.id); - if(m.script) writefln("Has script '%s'", m.script.id); - - writefln(); - } - - if(numbers) - { - writefln("Activators: ", activators.length); - writefln("Doors: ", doors.length); - writefln("Globals: ", globals.length); - writefln("Sounds: ", sounds.length); - writefln("Game Settings: ", gameSettings.length); - writefln("Factions: ", factions.length); - writefln("Statics: ", statics.length); - writefln("Spells: ", spells.length); - writefln("Potions: ", potions.length); - writefln("Apparatus: ", appas.length); - writefln("Armors: ", armors.length); - writefln("Body parts: ", bodyParts.length); - writefln("Enchantments: ", enchants.length); - writefln("Books: ", books.length); - writefln("Birth signs: ", birthSigns.length); - writefln("Land texture files: ", landTextures.length); - writefln("Weapons: ", weapons.length); - writefln("Lockpicks: ", lockpicks.length); - writefln("Probes: ", probes.length); - writefln("Repairs: ", repairs.length); - writefln("Cells: ", cells.length); - writefln(" Interior: ", cells.numInt); - writefln(" Exterior: ", cells.numExt); - writefln("Regions: ", regions.length); - writefln("Lights: ", lights.length); - writefln("Skills: ", skills.length); - writefln("Sound generators: ", soundGens.length); - writefln("Races: ", races.length); - writefln("Misc items: ", miscItems.length); - writefln("Cloths: ", clothes.length); - writefln("Ingredients: ", ingreds.length); - writefln("Classes: ", classes.length); - writefln("Containers: ", containers.length); - writefln("Creatures: ", creatures.length); - writefln("Leveled item lists: ", itemLists.length); - writefln("Leveled creature lists: ", creatureLists.length); - writefln("NPCs: ", npcs.length); - writefln("Scripts: ", scripts.length); - writefln("Dialogues: ", dialogues.length); - writefln("Hyperlinks: ", hyperlinks.list.length); - writefln("Start scripts: ", startScripts.length); - writefln("\nTotal items: ", items.length); - writefln("Total actors: ", actors.length); - writefln("Total cell placable items: ", cellRefs.length); - } - if(gmst) - { - foreach(a, b; gameSettings.names) - { - writef(a, " ("); - if(b.type == VarType.Int) writefln("int) = ", b.i); - else if(b.type == VarType.Float) writefln("float) = ", b.f); - else if(b.type == VarType.String) writefln("string) = '%s'", b.str); - else writefln("no value)", cast(int)b.type); - } - } - - if(scptList) foreach(a, b; scripts.names) writefln(a); - if(ciList) - foreach(a, b; cells.in_cells) - writefln(a); - if(ceList) - foreach(uint i, c; .cells.ex_cells) - { - int x, y; - CellList.decompound(i, x, y); - if(c.name.length) - writefln("%s,%s: %s", x, y, c.name); - } - - if(scptShow) - { - Script *p = scripts.names.lookup(scptName); - if(p) - writefln("Script '%s', text is:\n-------\n%s\n-------", p.id, p.scriptText); - else writefln("Script '%s' not found", scptName); - writefln(); - } - - writefln(esmRegion); - - poolSize(); -} - -// Quick function that simply iterates through an ES file and prints -// out all the records and subrecords. Some of this code is really old -// (about 2004-2005) -void printRaw() -{ - with(esFile) - { - // Variable length integer (this only works for unsigned ints!) - ulong getHVUint() - { - ulong l; - - getSubHeader(); - if( (getSubSize != 4) && - (getSubSize != 2) && - (getSubSize != 8) ) - fail(format("Unknown integer size: ", getSubSize)); - - readExact(&l, getSubSize); - return l; - } - - writefln("Filename: ", getFilename); - writef("Filetype: "); - switch(getFileType()) - { - case FileType.Plugin: writefln("Plugin"); break; - case FileType.Master: writefln("Master"); break; - case FileType.Savegame: writefln("Savegame"); break; - case FileType.Unknown: writefln("Unknown"); break; - default: assert(0); - } - writef("Version: "); - if(isVer12()) writefln("1.2"); - else if(isVer13()) writefln("1.3"); - else writefln("Unknown"); - - writefln("Records: ", getRecords); - writefln("Master files:"); - for(int i; i - WWW: http://openmw.snaptoad.com/ - - This file (events.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 input.events; - -import std.stdio; -import std.string; - -import sound.audio; - -import core.config; - -import scene.soundlist; -import scene.player; - -import bullet.bindings; - -import monster.monster; -import monster.vm.dbg; - -import ogre.bindings; -import gui.bindings; -import gui.gui; - -import input.keys; -import input.ois; - -// Debug output -//debug=printMouse; // Mouse button events -//debug=printMouseMove; // Mouse movement events -//debug=printKeys; // Keypress events - -// TODO: Jukebox controls and other state-related data will later be -// handled entirely in script code, as will some of the key bindings. - -// Pause? -bool pause = false; -int *guiMode; - -const float volDiff = 0.05; - -void musVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setMusicVolume(diff + config.getMusicVolume); - writefln(increase?"Increasing":"Decreasing", " music volume to ", config.getMusicVolume); -} - -void sfxVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setSfxVolume(diff + config.getSfxVolume); - writefln(increase?"Increasing":"Decreasing", " sound effect volume to ", config.getSfxVolume); -} - -void mainVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setMainVolume(diff + config.getMainVolume); - writefln(increase?"Increasing":"Decreasing", " main volume to ", config.getMainVolume); -} - -void updateMouseSensitivity() -{ - effMX = *config.mouseSensX; - effMY = *config.mouseSensY; - if(*config.flipMouseY) effMY = -effMY; -} - -void togglePause() -{ - pause = !pause; - if(pause) writefln("Pause"); - else writefln("Pause off"); -} - -extern(C) void d_handleMouseButton(MouseState *state, int button) -{ - debug(printMouse) - writefln("handleMouseButton %s: Abs(%s, %s, %s)", button, - state.X.abs, state.Y.abs, state.Z.abs); - - // For the moment, just treat mouse clicks as normal key presses. - d_handleKey(cast(KC) (KC.Mouse0 + button)); -} - -// Handle a keyboard event through key bindings. Direct movement -// (eg. arrow keys) is not handled here, see d_frameStarted() below. -extern(C) void d_handleKey(KC keycode, dchar text = 0) -{ - // Do some preprocessing on the data to account for OIS - // shortcommings. - - // Some keys (especially international keys) have no key code but - // return a character instead. - if(keycode == 0) - { - // If no character is given, just drop this event since OIS did - // not manage to give us any useful data at all. - if(text == 0) return; - - keycode = KC.CharOnly; - } - - // Debug output - debug(printKeys) - { - char[] str; - if(keycode >= 0 && keycode < keysymToString.length) - str = keysymToString[keycode]; - else str = "OUT_OF_RANGE"; - writefln("Key %s, text '%s', name '%s'", keycode, text, str); - } - - // Look up the key binding. We have to send both the keycode and the - // text. - Keys k = keyBindings.findMatch(keycode, text); - - // These are handled even if we are in gui mode: - if(k) - switch(k) - { - case Keys.ToggleGui: gui_toggleGui(); return; - case Keys.Console: gui_toggleConsole(); return; - case Keys.ScreenShot: takeScreenShot(); return; - default: - } - - if(*guiMode) return; - - if(k) - switch(k) - { - case Keys.ToggleBattleMusic: - Music.toggle(); - return; - - case Keys.MainVolUp: mainVolume(true); return; - case Keys.MainVolDown: mainVolume(false); return; - case Keys.MusVolUp: musVolume(true); return; - case Keys.MusVolDown: musVolume(false); return; - case Keys.SfxVolUp: sfxVolume(true); return; - case Keys.SfxVolDown: sfxVolume(false); return; - case Keys.Mute: Music.toggleMute(); return; - case Keys.Fullscreen: toggleFullscreen(); return; - - case Keys.PhysMode: bullet_nextMode(); return; - case Keys.Nighteye: ogre_toggleLight(); return; - - case Keys.Debug: return; - case Keys.Pause: togglePause(); return; - case Keys.Exit: exitProgram(); return; - default: - assert(k >= 0 && k < keyToString.length); - writefln("WARNING: Event %s has no effect", keyToString[k]); - } - return; -} - -// Refresh rate for sfx placements, in seconds. -const float sndRefresh = 0.17; - -// Refresh rate for music fadeing, seconds. -const float musRefresh = 0.05; - -// Walking / floating speed, in points per second. -float speed = 300; - -float sndCumTime = 0; -float musCumTime = 0; - -// Move the player according to playerData.position -void movePlayer() -{ - // Move the player into place. TODO: This isn't really input-related - // at all, and should be moved. - with(*playerData.position) - { - ogre_moveCamera(position[0], position[1], position[2]); - ogre_setCameraRotation(rotation[0], rotation[1], rotation[2]); - - bullet_movePlayer(position[0], position[1], position[2]); - } -} - -void initializeInput() -{ - movePlayer(); - - // TODO/FIXME: This should have been in config, but DMD's module - // system is on the brink of collapsing, and it won't compile if I - // put another import in core.config. I should probably check the - // bug list and report it. - updateMouseSensitivity(); - - // Get a pointer to the 'guiMode' flag in cpp_ogre.cpp - guiMode = gui_getGuiModePtr(); -} - -extern(C) int ois_isPressed(int keysym); - -// Check if a key is currently down -bool isPressed(Keys key) -{ - KeyBind *b = &keyBindings.bindings[key]; - foreach(i; b.syms) - if(i != 0 && ois_isPressed(i)) return true; - return false; -} - -// Enable superman mode, ie. flight and super-speed. Only used for -// debugging the terrain mode. -extern(C) void d_terr_superman() -{ - bullet_fly(); - speed = 8000; - - with(*playerData.position) - { - position[0] = 20000; - position[1] = -70000; - position[2] = 30000; - } - movePlayer(); -} - -extern(C) int d_frameStarted(float time) -{ - if(doExit) return 0; - - dbg.trace("d_frameStarted"); - scope(exit) dbg.untrace(); - - // Run the Monster scheduler - vm.frame(time); - - musCumTime += time; - if(musCumTime > musRefresh) - { - Music.updateBuffers(); - musCumTime -= musRefresh; - } - - // The rest is ignored in pause or GUI mode - if(pause || *guiMode > 0) return 1; - - // Check if the movement keys are pressed - float moveX = 0, moveY = 0, moveZ = 0; - float x, y, z, ox, oy, oz; - - if(isPressed(Keys.MoveLeft)) moveX -= speed; - if(isPressed(Keys.MoveRight)) moveX += speed; - if(isPressed(Keys.MoveForward)) moveZ -= speed; - if(isPressed(Keys.MoveBackward)) moveZ += speed; - - // TODO: These should be enabled for floating modes (like swimming - // and levitation) and disabled for everything else. - if(isPressed(Keys.MoveUp)) moveY += speed; - if(isPressed(Keys.MoveDown)) moveY -= speed; - - // This isn't very elegant, but it's simple and it works. - - // Get the current coordinates - ogre_getCameraPos(&ox, &oy, &oz); - - // Move camera using relative coordinates. TODO: We won't really - // need to move the camera here (since it's moved below anyway), we - // only want the transformation from camera space to world - // space. This can likely be done more efficiently. - ogre_moveCameraRel(moveX, moveY, moveZ); - - // Get the result - ogre_getCameraPos(&x, &y, &z); - - // The result is the real movement direction, in world coordinates - moveX = x-ox; - moveY = y-oy; - moveZ = z-oz; - - // Tell Bullet that this is where we want to go - bullet_setPlayerDir(moveX, moveY, moveZ); - - // Perform a Bullet time step - bullet_timeStep(time); - - // Get the final (actual) player position and update the camera - bullet_getPlayerPos(&x, &y, &z); - ogre_moveCamera(x,y,z); - - // Store it in the player object - playerData.position.position[0] = x; - playerData.position.position[1] = y; - playerData.position.position[2] = z; - - if(!config.noSound) - { - // Tell the sound scene that the player has moved - sndCumTime += time; - if(sndCumTime > sndRefresh) - { - float fx, fy, fz; - float ux, uy, uz; - - ogre_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz); - - soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz); - sndCumTime -= sndRefresh; - } - } - return 1; -} - -bool collides = false; diff --git a/old_d_version/input/ois.d b/old_d_version/input/ois.d deleted file mode 100644 index 60129cadf2..0000000000 --- a/old_d_version/input/ois.d +++ /dev/null @@ -1,428 +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 (ois.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 input.ois; - -// Mouse buttons -enum MB : int - { - Button0 = 0, - Left = Button0, - Button1 = 1, - Right = Button1, - Button2 = 2, - Middle = Button2, - - Button3 = 3, - Button4 = 4, - Button5 = 5, - Button6 = 6, - Button7 = 7, - - LastMouse - } - - // Keyboard scan codes -enum KC : int - { - UNASSIGNED = 0x00, - ESCAPE = 0x01, - N1 = 0x02, - N2 = 0x03, - N3 = 0x04, - N4 = 0x05, - N5 = 0x06, - N6 = 0x07, - N7 = 0x08, - N8 = 0x09, - N9 = 0x0A, - N0 = 0x0B, - MINUS = 0x0C, // - on main keyboard - EQUALS = 0x0D, - BACK = 0x0E, // backspace - TAB = 0x0F, - Q = 0x10, - W = 0x11, - E = 0x12, - R = 0x13, - T = 0x14, - Y = 0x15, - U = 0x16, - I = 0x17, - O = 0x18, - P = 0x19, - LBRACKET = 0x1A, - RBRACKET = 0x1B, - RETURN = 0x1C, // Enter on main keyboard - LCONTROL = 0x1D, - A = 0x1E, - S = 0x1F, - D = 0x20, - F = 0x21, - G = 0x22, - H = 0x23, - J = 0x24, - K = 0x25, - L = 0x26, - SEMICOLON = 0x27, - APOSTROPHE = 0x28, - GRAVE = 0x29, // accent - LSHIFT = 0x2A, - BACKSLASH = 0x2B, - Z = 0x2C, - X = 0x2D, - C = 0x2E, - V = 0x2F, - B = 0x30, - N = 0x31, - M = 0x32, - COMMA = 0x33, - PERIOD = 0x34, // . on main keyboard - SLASH = 0x35, // / on main keyboard - RSHIFT = 0x36, - MULTIPLY = 0x37, // * on numeric keypad - LMENU = 0x38, // left Alt - SPACE = 0x39, - CAPITAL = 0x3A, - F1 = 0x3B, - F2 = 0x3C, - F3 = 0x3D, - F4 = 0x3E, - F5 = 0x3F, - F6 = 0x40, - F7 = 0x41, - F8 = 0x42, - F9 = 0x43, - F10 = 0x44, - NUMLOCK = 0x45, - SCROLL = 0x46, // Scroll Lock - NUMPAD7 = 0x47, - NUMPAD8 = 0x48, - NUMPAD9 = 0x49, - SUBTRACT = 0x4A, // - on numeric keypad - NUMPAD4 = 0x4B, - NUMPAD5 = 0x4C, - NUMPAD6 = 0x4D, - ADD = 0x4E, // + on numeric keypad - NUMPAD1 = 0x4F, - NUMPAD2 = 0x50, - NUMPAD3 = 0x51, - NUMPAD0 = 0x52, - DECIMAL = 0x53, // . on numeric keypad - OEM_102 = 0x56, // < > | on UK/Germany keyboards - F11 = 0x57, - F12 = 0x58, - F13 = 0x64, // (NEC PC98) - F14 = 0x65, // (NEC PC98) - F15 = 0x66, // (NEC PC98) - KANA = 0x70, // (Japanese keyboard) - ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards - CONVERT = 0x79, // (Japanese keyboard) - NOCONVERT = 0x7B, // (Japanese keyboard) - YEN = 0x7D, // (Japanese keyboard) - ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards - NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) - PREVTRACK = 0x90, // Previous Track (CIRCUMFLEX on Japanese keyboard) - AT = 0x91, // (NEC PC98) - COLON = 0x92, // (NEC PC98) - UNDERLINE = 0x93, // (NEC PC98) - KANJI = 0x94, // (Japanese keyboard) - STOP = 0x95, // (NEC PC98) - AX = 0x96, // (Japan AX) - UNLABELED = 0x97, // (J3100) - NEXTTRACK = 0x99, // Next Track - NUMPADENTER = 0x9C, // Enter on numeric keypad - RCONTROL = 0x9D, - MUTE = 0xA0, // Mute - CALCULATOR = 0xA1, // Calculator - PLAYPAUSE = 0xA2, // Play / Pause - MEDIASTOP = 0xA4, // Media Stop - VOLUMEDOWN = 0xAE, // Volume - - VOLUMEUP = 0xB0, // Volume + - WEBHOME = 0xB2, // Web home - NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) - DIVIDE = 0xB5, // / on numeric keypad - SYSRQ = 0xB7, // Also called print screen - RMENU = 0xB8, // right Alt - PAUSE = 0xC5, // Pause - HOME = 0xC7, // Home on arrow keypad - UP = 0xC8, // UpArrow on arrow keypad - PGUP = 0xC9, // PgUp on arrow keypad - LEFT = 0xCB, // LeftArrow on arrow keypad - RIGHT = 0xCD, // RightArrow on arrow keypad - END = 0xCF, // End on arrow keypad - DOWN = 0xD0, // DownArrow on arrow keypad - PGDOWN = 0xD1, // PgDn on arrow keypad - INSERT = 0xD2, // Insert on arrow keypad - DELETE = 0xD3, // Delete on arrow keypad - LWIN = 0xDB, // Left Windows key - RWIN = 0xDC, // Right Windows key - APPS = 0xDD, // AppMenu key - POWER = 0xDE, // System Power - SLEEP = 0xDF, // System Sleep - WAKE = 0xE3, // System Wake - WEBSEARCH = 0xE5, // Web Search - WEBFAVORITES= 0xE6, // Web Favorites - WEBREFRESH = 0xE7, // Web Refresh - WEBSTOP = 0xE8, // Web Stop - WEBFORWARD = 0xE9, // Web Forward - WEBBACK = 0xEA, // Web Back - MYCOMPUTER = 0xEB, // My Computer - MAIL = 0xEC, // Mail - MEDIASELECT = 0xED, // Media Select - - CharOnly = 0xFF, // Set when the keysym is 0 but the - // character is set. This happens with many - // international characters or reassigned - // characters. - - Mouse0 = 0x100, // Mouse button events can be handled as - Mouse1 = 0x101, // keypresses too. - Mouse2 = 0x102, - Mouse3 = 0x103, - Mouse4 = 0x104, - Mouse5 = 0x105, - Mouse6 = 0x106, - Mouse7 = 0x107, - } - -// Sigh. I guess we have to do this for Monster at some poing anyway, -// so this work isn't completely wasted. Later we can make a generic -// conversion between OIS-keysyms, SDL-keysyms and others to the -// Monster keysyms. It sucks that everybody tries to reinvent the -// wheel as often as they can, but that's the way it goes. - -const char[][] keysymToString = -[ - KC.UNASSIGNED : "UNASSIGNED", - KC.ESCAPE : "escape", - KC.N1 : "1", - KC.N2 : "2", - KC.N3 : "3", - KC.N4 : "4", - KC.N5 : "5", - KC.N6 : "6", - KC.N7 : "7", - KC.N8 : "8", - KC.N9 : "9", - KC.N0 : "0", - KC.MINUS : "minus", - KC.EQUALS : "equals", - KC.BACK : "backspace", - KC.TAB : "tab", - KC.Q : "q", - KC.W : "w", - KC.E : "e", - KC.R : "r", - KC.T : "t", - KC.Y : "y", - KC.U : "u", - KC.I : "i", - KC.O : "o", - KC.P : "p", - KC.LBRACKET : "{", - KC.RBRACKET : "}", - KC.RETURN : "enter", - KC.LCONTROL : "left_ctrl", - KC.A : "a", - KC.S : "s", - KC.D : "d", - KC.F : "f", - KC.G : "g", - KC.H : "h", - KC.J : "j", - KC.K : "k", - KC.L : "l", - KC.SEMICOLON : "semicolon", - KC.APOSTROPHE : "apostrophe", - KC.GRAVE : "grave", - KC.LSHIFT : "left_shift", - KC.BACKSLASH : "backslash", - KC.Z : "z", - KC.X : "x", - KC.C : "c", - KC.V : "v", - KC.B : "b", - KC.N : "n", - KC.M : "m", - KC.COMMA : "comma", - KC.PERIOD : "period", - KC.SLASH : "slash", - KC.RSHIFT : "right_shift", - KC.MULTIPLY : "numpad_mult", - KC.LMENU : "left_alt", - KC.SPACE : "space", - KC.CAPITAL : "capital", - KC.F1 : "f1", - KC.F2 : "f2", - KC.F3 : "f3", - KC.F4 : "f4", - KC.F5 : "f5", - KC.F6 : "f6", - KC.F7 : "f7", - KC.F8 : "f8", - KC.F9 : "f9", - KC.F10 : "f10", - KC.NUMLOCK : "numlock", - KC.SCROLL : "scroll", - KC.NUMPAD7 : "numpad_7", - KC.NUMPAD8 : "numpad_8", - KC.NUMPAD9 : "numpad_9", - KC.SUBTRACT : "numpad_minus", - KC.NUMPAD4 : "numpad_4", - KC.NUMPAD5 : "numpad_5", - KC.NUMPAD6 : "numpad_6", - KC.ADD : "numpad_plus", - KC.NUMPAD1 : "numpad_1", - KC.NUMPAD2 : "numpad_2", - KC.NUMPAD3 : "numpad_3", - KC.NUMPAD0 : "numpad_0", - KC.DECIMAL : "numpad_period", - KC.OEM_102 : "oem102", - KC.F11 : "f11", - KC.F12 : "f12", - KC.F13 : "f13", - KC.F14 : "f14", - KC.F15 : "f15", - KC.KANA : "kana", - KC.ABNT_C1 : "abnt_c1", - KC.CONVERT : "convert", - KC.NOCONVERT : "noconvert", - KC.YEN : "yen", - KC.ABNT_C2 : "abnt_c2", - KC.NUMPADEQUALS: "numpad_equals", - KC.PREVTRACK : "prev_track", - KC.AT : "at", - KC.COLON : "colon", - KC.UNDERLINE : "underline", - KC.KANJI : "kanji", - KC.STOP : "stop", - KC.AX : "ax", - KC.UNLABELED : "unlabeled", - KC.NEXTTRACK : "next_track", - KC.NUMPADENTER : "numpad_enter", - KC.RCONTROL : "right_control", - KC.MUTE : "mute", - KC.CALCULATOR : "calculator", - KC.PLAYPAUSE : "play_pause", - KC.MEDIASTOP : "media_stop", - KC.VOLUMEDOWN : "volume_down", - KC.VOLUMEUP : "volume_up", - KC.WEBHOME : "webhome", - KC.NUMPADCOMMA : "numpad_comma", - KC.DIVIDE : "numpad_divide", - KC.SYSRQ : "print_screen", - KC.RMENU : "right_alt", - KC.PAUSE : "pause", - KC.HOME : "home", - KC.UP : "up", - KC.PGUP : "page_up", - KC.LEFT : "left", - KC.RIGHT : "right", - KC.END : "end", - KC.DOWN : "down", - KC.PGDOWN : "page_down", - KC.INSERT : "insert", - KC.DELETE : "delete", - KC.LWIN : "left_win", - KC.RWIN : "right_win", - KC.APPS : "app_menu", - KC.POWER : "power", - KC.SLEEP : "sleep", - KC.WAKE : "wake", - KC.WEBSEARCH : "web_search", - KC.WEBFAVORITES: "web_favorites", - KC.WEBREFRESH : "web_refresh", - KC.WEBSTOP : "web_stop", - KC.WEBFORWARD : "web_forward", - KC.WEBBACK : "web_back", - KC.MYCOMPUTER : "my_computer", - KC.MAIL : "mail", - KC.MEDIASELECT : "media_select", - - - KC.CharOnly : "CHAR_ONLY", // Set when the keysym is 0 but the - // character is set. This happens - // with many international - // characters or reassigned - // characters in OIS (and it - // SUCKS.) - - KC.Mouse0 : "mouse0", - KC.Mouse1 : "mouse1", - KC.Mouse2 : "mouse2", - KC.Mouse3 : "mouse3", - KC.Mouse4 : "mouse4", - KC.Mouse5 : "mouse5", - KC.Mouse6 : "mouse6", - KC.Mouse7 : "mouse7", - ]; - -enum ComponentType : int - { - Unknown = 0, - Button = 1, // ie. Key, mouse button, joy button, etc - Axis = 2, // ie. A joystick or mouse axis - Slider = 3, // - POV = 4, // ie. Arrow direction keys - Vector3 = 5 // ie. WiiMote orientation - } - -align(4) struct Axis -{ - ComponentType type; - int abs, rel; - bool absOnly; -} - -// The C++ size of Axis is 16 -static assert(Axis.sizeof == 16); - -struct MouseState -{ - /* Represents the height/width of your display area.. used if mouse - clipping or mouse grabbed in case of X11 - defaults to 50.. Make - sure to set this and change when your size changes.. */ - int width, height; - - // X Axis component - Axis X; - - // Y Axis Component - Axis Y; - - // Z Axis Component - Axis Z; - - // represents all buttons - bit position indicates button down - int buttons; - - // Button down test - bool buttonDown( MB button ) - { - return (buttons & ( 1 << button )) != 0; - } -} - -// Check that we match the C++ size -static assert(MouseState.sizeof == 60); diff --git a/old_d_version/ogre/cpp_framelistener.cpp b/old_d_version/ogre/cpp_framelistener.cpp deleted file mode 100644 index 8c7a370441..0000000000 --- a/old_d_version/ogre/cpp_framelistener.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Get current camera orientation, in the form of 'front' and 'up' -// vectors. -extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz, - float *ux, float *uy, float *uz) -{ - Vector3 front = mCamera->getDirection(); - Vector3 up = mCamera->getUp(); - *fx = front[0]; - *fy = -front[2]; - *fz = front[1]; - *ux = up[0]; - *uy = -up[2]; - *uz = up[1]; -} - -// Move camera -extern "C" void ogre_moveCamera(float x, float y, float z) -{ - // Transforms Morrowind coordinates to OGRE coordinates. The camera - // is not affected by the rotation of the root node, so we must - // transform this manually. - mCamera->setPosition(Vector3(x,z+90,-y)); -} - -// Rotate camera using Morrowind rotation specifiers -extern "C" void ogre_setCameraRotation(float r1, float r2, float r3) -{ - // TODO: This translation is probably not correct, but for now I - // have no reference point. Fix it later when we teleport from one - // cell to another, so we have something to compare against. - - // Rotate around X axis - Quaternion xr(Radian(-r1), Vector3::UNIT_X); - // Rotate around Y axis - Quaternion yr(Radian(r3+3.14), Vector3::UNIT_Y); - // Rotate around Z axis - Quaternion zr(Radian(-r2), Vector3::UNIT_Z); - - // Rotates first around z, then y, then x - mCamera->setOrientation(xr*yr*zr); -} diff --git a/old_d_version/ogre/cpp_interface.cpp b/old_d_version/ogre/cpp_interface.cpp deleted file mode 100644 index 98df9125e0..0000000000 --- a/old_d_version/ogre/cpp_interface.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copy a scene node and all its children -void cloneNode(SceneNode *from, SceneNode *to, char* name) -{ - to->setPosition(from->getPosition()); - to->setOrientation(from->getOrientation()); - to->setScale(from->getScale()); - - SceneNode::ObjectIterator it = from->getAttachedObjectIterator(); - while(it.hasMoreElements()) - { - // We can't handle non-entities. - Entity *e = dynamic_cast (it.getNext()); - if(e) - { - e = e->clone(String(name) + ":" + e->getName()); - to->attachObject(e); - } - } - - // Recursively clone all child nodes - SceneNode::ChildNodeIterator it2 = from->getChildIterator(); - while(it2.hasMoreElements()) - { - cloneNode((SceneNode*)it2.getNext(), to->createChildSceneNode(), name); - } -} - -// Supposed to insert a copy of the node, for now it just inserts the -// actual node. -extern "C" SceneNode *ogre_insertNode(SceneNode *base, char* name, - float *pos, float *quat, - float scale) -{ - //std::cout << "ogre_insertNode(" << name << ")\n"; - SceneNode *node = mwRoot->createChildSceneNode(name); - - // Make a copy of the node - cloneNode(base, node, name); - - // Apply transformations - node->setPosition(pos[0], pos[1], pos[2]); - node->setOrientation(quat[0], quat[1], quat[2], quat[3]); - - node->setScale(scale, scale, scale); - - return node; -} - -// Get the world transformation of a node (the total transformation of -// this node and all parent nodes). Return it as a translation -// (3-vector) and a rotation / scaling part (3x3 matrix) -extern "C" void ogre_getWorldTransform(SceneNode *node, - float *trans, // Storage for translation - float *matrix)// For 3x3 matrix -{ - // Get the world transformation first - Matrix4 trafo; - node->getWorldTransforms(&trafo); - - // Extract the translation part and pass it to the caller - Vector3 tr = trafo.getTrans(); - trans[0] = tr[0]; - trans[1] = tr[1]; - trans[2] = tr[2]; - - // Next extract the matrix - Matrix3 mat; - trafo.extract3x3Matrix(mat); - matrix[0] = mat[0][0]; - matrix[1] = mat[0][1]; - matrix[2] = mat[0][2]; - matrix[3] = mat[1][0]; - matrix[4] = mat[1][1]; - matrix[5] = mat[1][2]; - matrix[6] = mat[2][0]; - matrix[7] = mat[2][1]; - matrix[8] = mat[2][2]; -} - -// Create the water plane. It doesn't really resemble "water" yet -// though. -extern "C" void ogre_createWater(float level) -{ - // Create a plane aligned with the xy-plane. - MeshManager::getSingleton().createPlane("water", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Plane(Vector3::UNIT_Z, level), - 150000,150000 - ); - Entity *ent = mSceneMgr->createEntity( "WaterEntity", "water" ); - mwRoot->createChildSceneNode()->attachObject(ent); - ent->setCastShadows(false); -} - -extern "C" SceneNode *ogre_getDetachedNode() -{ - SceneNode *node = mwRoot->createChildSceneNode(); - mwRoot->removeChild(node); - return node; -} - -extern "C" SceneNode* ogre_createNode( - char *name, - float *trafo, - SceneNode *parent, - int32_t noRot) -{ - //std::cout << "ogre_createNode(" << name << ")"; - SceneNode *node = parent->createChildSceneNode(name); - //std::cout << " ... done\n"; - - // First is the translation vector - - // TODO should be "if(!noRot)" only for exterior cells!? Yay for - // consistency. Apparently, the displacement of the base node in NIF - // files must be ignored for meshes in interior cells, but not for - // exterior cells. Or at least that's my hypothesis, and it seems - // work. There might be some other NIF trickery going on though, you - // never know when you're reverse engineering someone else's file - // format. We will handle this later. - if(!noRot) - node->setPosition(trafo[0], trafo[1], trafo[2]); - - // Then a 3x3 rotation matrix. - if(!noRot) - node->setOrientation(Quaternion(Matrix3(trafo[3], trafo[4], trafo[5], - trafo[6], trafo[7], trafo[8], - trafo[9], trafo[10], trafo[11] - ))); - - // Scale is at the end - node->setScale(trafo[12],trafo[12],trafo[12]); - - return node; -} diff --git a/old_d_version/ogre/meshloader.d b/old_d_version/ogre/meshloader.d deleted file mode 100644 index 5065cc87a9..0000000000 --- a/old_d_version/ogre/meshloader.d +++ /dev/null @@ -1,391 +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 (meshloader.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.meshloader; - -import std.stdio; -import std.stream; - -import nif.nif; -import nif.record; - -import core.resource; -import ogre.bindings; - -import bullet.bindings; - -import util.uniquename; - -/* - There are some problems that will have to be looked into later: - - - Some meshes crash Ogre when shadows are turned on. (Not tested in - newer versions of Ogre). Shadows are completely disabled for now. - - There are obviously some boundry problems, some times the mesh - disappears even though some part of it is inside the screen. This - is especially a problem with animated meshes, since the animation - might step outside the original bounding box. - */ - -MeshLoader meshLoader; - -struct MeshLoader -{ - // Not sure how to handle the bounding box, just ignore it for now. - - char[] baseName; // NIF file name. Used in scene node names etc. so - // that we can identify where they came from in - // case of error messages. - - // Load a NIF mesh. Assumes nifMesh is already opened. This creates - // a "template" scene node containing this mesh, and removes it from - // the main scene. This node can later be "cloned" so that multiple - // instances of the object can be inserted into the world without - // inserting the mesh more than once. - void loadMesh(char[] name, out NodePtr base, out BulletShape shape) - { - baseName = name; - - // Check if the first record is a node - Node n = cast(Node) nifMesh.records[0]; - - if(n is null) - { - // TODO: Figure out what to do in this case, we should - // probably throw. - writefln("NIF '%s' IS NOT A MESH", name); - return; - } - - // Get a fresh SceneNode and detatch it from the root. We use this - // as the base for our mesh. - base = ogre_getDetachedNode(); - - // Recursively insert nodes (don't rotate the first node) - insertNode(n, base, 0, true); - - // Get the final shape, if any - shape = bullet_getFinalShape(); - - return base; - } - - private: - - void insertNode(Node data, NodePtr parent, - int flags, - bool noRot = false) - { - // Add the flags to the previous node's flags - flags = data.flags | flags; - - // Create a scene node, move and rotate it into place. The name - // must be unique, however we might have to recognize some special - // names later, in order to attach arms and legs on NPCs - // etc. Always ignore transformation of the first node? This is a - // problem, I don't know when to do this and when not to. Neither - // is always right. Update: I originally thought noRot should be - // false for exteriors and true for interiors, but this isn't so. - NodePtr node = ogre_createNode(UniqueName(data.name).ptr, &data.trafo, - parent, cast(int)noRot); - - // Handle any general properties here - - // Call functions that do node-specific things, like handleNiNode - // or handleNiTriShape. - { - NiNode n = cast(NiNode)data; - if(n !is null) - // Handle the NiNode, and any children it might have - handleNiNode(n, node, flags); - } - - { - NiTriShape n = cast(NiTriShape)data; - if(n !is null) - // Trishape, with a mesh - handleNiTriShape(n, node, flags); - } - } - - void handleNiNode(NiNode data, NodePtr node, int flags) - { - // Ignore sound activators and similar objects. - NiStringExtraData d = cast(NiStringExtraData) data.extra; - if(d !is null) - { - // Marker objects are only visible in the editor. We - // completely ignore them. - if(d.string == "MRK") - return; - - // No collision - if(d.string == "NCO") - flags |= 0x800; // Temporary internal marker - } - - // Handle any effects here - - // In the cases where meshes have skeletal animations, we must - // insert the children as bones in a skeleton instead, like we - // originally did for all nodes. Update: A much better way is to - // first insert the nodes normally, and then create the - // skeleton. The nodes can then be moved one by one over to the - // appropriate bones. - - // Check the controller - auto cont = data.controller; - while(cont !is null) - { - auto kc = cast(NiKeyframeController)cont; - auto pc = cast(NiPathController)cont; - if(kc !is null) - { - /* - writefln("Found keyframe controller"); - writefln(" Node name was: %s", data.name); - assert(cont.target is data); - - auto kcd = kc.data; - writefln(" Types: %s %s %s", - kcd.rotType, kcd.traType, kcd.scaleType); - */ - - /* - Adding keyframes: - - Skeleton -> Animation -> NodeAnimationTrack nt; - - TransformKeyFrame * tf = nt->createNodeKeyFrame(time); - tf->setTranslate(Vector3); - tf->setScale(Vector3); - tf->setRotation(Quaternion); - - nt->applyToNode(node, time); - evt - Animation an; - an->apply(skeleton, time); - */ - } - else if(pc !is null) - { - //writefln("Found path controller"); - assert(cont.target is data); - } - //else writefln("Other controller (%s)", cont); - cont = cont.next; - } - - // Loop through children - foreach(Node n; data.children) - insertNode(n, node, flags); - } - - void handleNiTriShape(NiTriShape shape, NodePtr node, int flags) - { - char[] texture; - char[] material; - char[] newName = UniqueName(baseName); - NiMaterialProperty mp; - - // Special alpha settings, if the NiAlphaProperty is present - int alphaFlags = -1; - ubyte alphaTest; - - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use this mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for - // collision - // Always use mesh collision for now - if(bbcollide) collide = true; - bbcollide = false; - - // Things marked "NCO" should not collide with anything. - if(flags & 0x800) - { collide = false; bbcollide=false; } - - // Skip the entire material phase for hidden nodes - if(hidden) goto nomaterial; - - // Scan the property list for textures - foreach(Property p; shape.properties) - { - // NiTexturingProperty block - { - NiTexturingProperty t = cast(NiTexturingProperty) p; - if(t !is null && t.textures[0].inUse) - { - // Ignore all other options for now - NiSourceTexture st = t.textures[0].texture; - if(st.external) - { - // Find the resource for this texture - TextureIndex ti = resources.lookupTexture(st.filename); - // Insert a manual loader into OGRE - // ti.load(); - - // Get the resource name. We use getNewName to get - // the real texture name, not the lookup - // name. NewName has been converted to .dds if - // necessary, to match the file name in the bsa - // archives. - texture = ti.getNewName(); - } - else - { - // Internal textures - texture = "BLAH"; - writefln("Internal texture, cannot read this yet."); - writefln("Final resource name: '%s'", texture); - } - continue; - } - } - - // NiAlphaProperty - { - NiAlphaProperty a = cast(NiAlphaProperty) p; - if(a !is null) - { - alphaFlags = a.flags; - alphaTest = a.threshold; - } - } - - // NiMaterialProperty block - { - NiMaterialProperty tmp = cast(NiMaterialProperty) p; - if(tmp !is null) - { - if(mp !is null) writefln("WARNING: More than one material!"); - mp = tmp; - material = newName; - continue; - } - } - } - // Get a pointer to the texture name - char* texturePtr; - if(texture.length) texturePtr = toStringz(texture); - else texturePtr = null; - - // Create the material - if(material.length) - // A material is present. Use it. - ogre_createMaterial(material.ptr, mp.ambient.array.ptr, - mp.diffuse.array.ptr, - mp.specular.array.ptr, mp.emissive.array.ptr, - mp.glossiness, mp.alpha, texturePtr, - alphaFlags, alphaTest); - else if(texturePtr) - { - // Texture, but no material. Make a default one. - writefln("WARNING: Making default material for %s", texture); - float[3] zero; - float[3] one; - zero[] = 0.0; - one[] = 1.0; - - ogre_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr, - 0.0, 1.0, texturePtr, alphaFlags, alphaTest); - } - - nomaterial: - - with(shape.data) - { - //writefln("Number of vertices: ", vertices.length); - - float *normalsPtr; - float *colorsPtr; - float *uvsPtr; - short *facesPtr; - - // Point pointers into the correct arrays, if they are present. If - // not, the pointers retain their values of null. - if(normals.length) normalsPtr = normals.ptr; - if(colors.length) colorsPtr = colors.ptr; - if(uvlist.length) uvsPtr = uvlist.ptr; - if(triangles.length) facesPtr = triangles.ptr; - - float - minX = float.infinity, - minY = float.infinity, - minZ = float.infinity, - maxX = -float.infinity, - maxY = -float.infinity, - maxZ = -float.infinity; - - // Calculate the bounding box. TODO: This is really a - // hack. IIRC the bounding box supplied by the NIF could not - // be trusted, but I can't remember why :/ - for( int i; i < vertices.length; i+=3 ) - { - if( vertices[i] < minX ) minX = vertices[i]; - if( vertices[i+1] < minY ) minY = vertices[i+1]; - if( vertices[i+2] < minZ) minZ = vertices[i+2]; - - if( vertices[i] > maxX) maxX = vertices[i]; - if( vertices[i+1] > maxY) maxY = vertices[i+1]; - if( vertices[i+2] > maxZ) maxZ = vertices[i+2]; - } - - // Get the node world transformation, needed to set up - // the collision shape properly. - float[3] trans; - float[9] matrix; - ogre_getWorldTransform(node, trans.ptr, matrix.ptr); - - // Next we must create the actual OGRE mesh and the collision - // objects, based on the flags we have been given. TODO: I - // guess the NIF bounding box is better than the one we have - // calculated ourselves? This code definitely doesn't work, - // but I haven't look into why yet. - assert(!bbcollide); - if(bbcollide) - // Insert the bounding box into the collision system - bullet_createBoxShape(minX, minY, minZ, maxX, maxY, maxZ, - trans.ptr, matrix.ptr); - - // Create a bullet collision shape from the trimesh. Pass - // along the world transformation as well, since we must - // transform the trimesh data manually. - else if(collide) - { - assert(facesPtr !is null, - "cannot create collision shape without a mesh"); - bullet_createTriShape(triangles.length, facesPtr, - vertices.length, vertices.ptr, - trans.ptr, matrix.ptr); - } - - // Create the ogre mesh, associate it with the node. Skip for - // hidden nodes. - if(!hidden) - ogre_createMesh(newName.ptr, vertices.length, vertices.ptr, - normalsPtr, colorsPtr, uvsPtr, triangles.length, - facesPtr, radius, material.ptr, minX, minY, minZ, - maxX, maxY, maxZ, node); - } - } -} diff --git a/old_d_version/openmw.d b/old_d_version/openmw.d deleted file mode 100644 index 095e8cdd82..0000000000 --- a/old_d_version/openmw.d +++ /dev/null @@ -1,471 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (openmw.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 openmw; - -import std.stdio; -import std.string; -import std.cstream; -import std.file; - -import ogre.ogre; -import ogre.bindings; -import gui.bindings; -import gui.gui; - -import bullet.bullet; - -import scene.celldata; -import scene.soundlist; -import scene.gamesettings; -import scene.player; - -import core.resource; -import core.memory; -import core.config; - -import monster.util.string; -import monster.vm.mclass; -import monster.vm.dbg; -import mscripts.setup; - -import sound.audio; - -import input.events; - -import terrain.terrain; - -// Set up exit handler -alias void function() c_func; -extern(C) int atexit(c_func); - -bool cleanExit = false; - -void exitHandler() -{ - // If we exit uncleanly, print the function stack. - if(!cleanExit) - writefln(dbg.getTrace()); -} - - -//* -import std.gc; -import gcstats; - -void poolSize() -{ - GCStats gc; - getStats(gc); - writefln("Pool size: ", comma(gc.poolsize)); - writefln("Used size: ", comma(gc.usedsize)); -} -//*/ - -void main(char[][] args) -{ - bool render = true; - bool help = false; - bool resetKeys = false; - bool showOgreFlag = false; - bool debugOut = false; - bool extTest = false; - bool doGen = false; - bool nextSave = false; - bool loadSave = false; - - // Some examples to try: - // - // "Abandoned Shipwreck, Upper Level"; - // "Gro-Bagrat Plantation"; - // "Abinabi"; - // "Abebaal Egg Mine"; - // "Ald-ruhn, Ald Skar Inn"; - // "Koal Cave"; - // "Ald-ruhn, Arobar Manor Bedrooms" - // "Sud"; - // "Vivec, The Lizard's Head"; - // "ToddTest"; - - // Cells to load - char[][] cells; - - // Savegame to load - char[] savefile; - - foreach(char[] a; args[1..$]) - if(a == "-n") render = false; - else if(a == "-ex") extTest = true; - else if(a == "-gen") doGen = true; - else if(a == "-h") help=true; - else if(a == "-rk") resetKeys = true; - else if(a == "-oc") showOgreFlag = true; - else if(a == "-ns") config.noSound = true; - else if(a == "-save") nextSave = true; - else if(a == "-debug") - { - // Enable Monster debug output - dbg.dbgOut = dout; - - // Tell OGRE to do the same later on - debugOut = true; - } - else if(nextSave) - { - savefile = a; - nextSave = false; - } - else cells ~= a; - - if(cells.length > 1) - { - writefln("More than one cell specified, rendering disabled"); - render=false; - } - - void showHelp() - { - writefln("Syntax: %s [options] cell-name [cell-name]", args[0]); - writefln(" Options:"); - writefln(" -n Only load, do not render"); - writefln(" -ex Test the terrain system"); - writefln(" -gen Generate landscape cache"); - writefln(" -rk Reset key bindings to default"); - writefln(" -oc Show the Ogre config dialogue"); - writefln(" -ns Completely disable sound"); - writefln(" -debug Print debug information"); - writefln(" -save Load cell/pos from savegame"); - writefln(" -h Show this help"); - writefln(""); - writefln("Specifying more than one cell implies -n"); - } - - if(help) - { - showHelp(); - return; - } - - initializeMemoryRegions(); - initMonsterScripts(); - - // This is getting increasingly hackish, but this entire engine - // design is now quickly outgrowing its usefulness, and a rewrite is - // coming soon anyway. - PlayerSaveInfo pi; - if(savefile != "") - { - if(cells.length) - { - writefln("Please don't specify both a savegame file (%s) and cell names (%s)", savefile, cells); - return; - } - - loadSave = true; - writefln("Loading savegame %s", savefile); - pi = importSavegame(savefile); - writefln(" Player name: %s", pi.playerName); - writefln(" Cell name: %s", pi.cellName); - writefln(" Pos: %s", pi.pos.position); - writefln(" Rot: %s", pi.pos.rotation); - - cells = [pi.cellName]; - } - - config.initialize(resetKeys); - scope(exit) config.writeConfig(); - - // Check if the data directory exists - if(!exists(config.dataDir) || !isdir(config.dataDir)) - { - writefln("Cannot find data directory '", config.dataDir, - "' - please edit openmw.ini."); - return; - } - - // If the -oc parameter is specified, we override any config - // setting. - if(showOgreFlag) config.finalOgreConfig = true; - - if(cells.length == 0) - if(config.defaultCell.length) - cells ~= config.defaultCell; - - if(cells.length == 1 && !loadSave) - config.defaultCell = cells[0]; - - if(cells.length == 0) - { - showHelp(); - return; - } - - if(!config.noSound) initializeSound(); - resources.initResources(); - - // Load all ESM and ESP files - loadTESFiles(config.gameFiles); - - scene.gamesettings.loadGameSettings(); - - CellData cd = cellList.get(); - - foreach(char[] cName; cells) - { - // Release the last cell data - cellList.release(cd); - - // Get a cell data holder and load an interior cell - cd = cellList.get(); - - try cd.loadIntCell(cName); - catch(Exception e) - { - writefln("\nUnable to load cell '%s'.", cName); - writefln("\nDetails: %s", e); - writefln(" -Perhaps this cell does not exist in your Morrowind language version? -Try specifying another cell name on the command line, or edit openmw.ini."); - return; - } - - // If we're loading from save, override the player position - if(loadSave) - *playerData.position = pi.pos; - } - - // Simple safety hack - NodePtr putObject(MeshIndex m, Placement *pos, float scale, - bool collide=false) - { - if(m is null) - writefln("WARNING: CANNOT PUT NULL OBJECT"); - else if(!m.isEmpty) - return placeObject(m, pos, scale, collide); - - //writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName); - return null; - } - - if(cd.inCell !is null) - // Set the name for the GUI (temporary hack) - gui_setCellName(cd.inCell.id.ptr); - - // Set up the exit handler - atexit(&exitHandler); - scope(exit) cleanExit = true; - - if(render) - { - // Warm up OGRE - setupOgre(debugOut); - scope(exit) cleanupOgre(); - - // Create the GUI system - initGUI(debugOut); - - // Set up Bullet - initBullet(); - scope(exit) cleanupBullet(); - - // Initialize the internal input and event manager. The - // lower-level input system (OIS) is initialized by the - // setupOgre() call further up. - initializeInput(); - - // Play some old tunes - if(extTest) - { - // Exterior cell - /* - Color c; - c.red = 180; - c.green = 180; - c.blue = 180; - setAmbient(c, c, c, 0); - - // Put in the water - ogre_createWater(cd.water); - - // Create an ugly sky - ogre_makeSky(); - */ - - initTerrain(doGen); - } - else - { - // Interior cell - assert(cd.inCell !is null); - setAmbient(cd.ambi.ambient, cd.ambi.sunlight, - cd.ambi.fog, cd.ambi.fogDensity); - - // Not all interior cells have water - if(cd.inCell.flags & CellFlags.HasWater) - ogre_createWater(cd.water); - - // Insert the meshes of statics into the scene - foreach(ref LiveStatic ls; cd.statics) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Inventory lights - foreach(ref LiveLight ls; cd.lights) - { - NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale()); - ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); - if(!config.noSound) - { - Sound *s = ls.m.sound; - if(s) - { - ls.loopSound = soundScene.insert(s, true); - if(ls.loopSound) - { - auto p = ls.getPos(); - ls.loopSound.setPos(p.position[0], - p.position[1], - p.position[2]); - } - } - } - } - // Static lights - foreach(ref LiveLight ls; cd.statLights) - { - NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); - if(!config.noSound) - { - Sound *s = ls.m.sound; - if(s) - { - ls.loopSound = soundScene.insert(s, true); - if(ls.loopSound) - { - auto p = ls.getPos(); - ls.loopSound.setPos(p.position[0], - p.position[1], - p.position[2]); - } - } - } - } - // Misc items - foreach(ref LiveMisc ls; cd.miscItems) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - /* - // NPCs (these are complicated, usually do not have normal meshes) - foreach(ref LiveNPC ls; cd.npcs) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - */ - // Containers - foreach(ref LiveContainer ls; cd.containers) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Doors - foreach(ref LiveDoor ls; cd.doors) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Activators (including beds etc) - foreach(ref LiveActivator ls; cd.activators) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Potions - foreach(ref LivePotion ls; cd.potions) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Apparatus - foreach(ref LiveApparatus ls; cd.appas) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Ingredients - foreach(ref LiveIngredient ls; cd.ingredients) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Armors - foreach(ref LiveArmor ls; cd.armors) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Weapons - foreach(ref LiveWeapon ls; cd.weapons) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Books - foreach(ref LiveBook ls; cd.books) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Clothes - foreach(ref LiveClothing ls; cd.clothes) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Tools - foreach(ref LiveTool ls; cd.tools) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Creatures (not displayed very well yet) - foreach(ref LiveCreature ls; cd.creatures) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - - // End of interior cell - } - - // Run GUI system - startGUI(); - - // Play some old tunes - if(!config.noSound) - Music.play(); - - // Run it until the user tells us to quit - startRendering(); - } - else if(debugOut) writefln("Skipping rendering"); - - if(!config.noSound) - { - soundScene.kill(); - shutdownSound(); - } - - if(debugOut) - { - writefln(); - writefln("%d statics", cd.statics.length); - writefln("%d misc items", cd.miscItems.length); - writefln("%d inventory lights", cd.lights.length); - writefln("%d static lights", cd.statLights.length); - writefln("%d NPCs", cd.npcs.length); - writefln("%d containers", cd.containers.length); - writefln("%d doors", cd.doors.length); - writefln("%d activators", cd.activators.length); - writefln("%d potions", cd.potions.length); - writefln("%d apparatuses", cd.appas.length); - writefln("%d ingredients", cd.ingredients.length); - writefln("%d armors", cd.armors.length); - writefln("%d weapons", cd.weapons.length); - writefln("%d books", cd.books.length); - writefln("%d tools", cd.tools.length); - writefln("%d clothes", cd.clothes.length); - writefln("%d creatures", cd.creatures.length); - writefln(); - } - - // This isn't necessary but it's here for testing purposes. - cellList.release(cd); - - // Write some statistics - if(debugOut) - { - poolSize(); - writefln(esmRegion); - writefln("Total objects: ", MonsterClass.getTotalObjects); - } -} diff --git a/old_d_version/terrain/archive.d b/old_d_version/terrain/archive.d deleted file mode 100644 index 5f5d5fd80f..0000000000 --- a/old_d_version/terrain/archive.d +++ /dev/null @@ -1,461 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (archive.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 terrain.archive; - -const float TEX_SCALE = 1.0/16; - -// This should be part of the generic cache system. -const int CACHE_MAGIC = 0x345AF815; - -import std.mmfile; -import std.string; -import std.stdio; -import terrain.myfile; - -version(Windows) - static int pageSize = 64*1024; -else - static int pageSize = 4*1024; - -extern(C) -{ - // Convert a texture index to string - char *d_terr_getTexName(int index) - { return g_archive.getString(index).ptr; } - - // Fill various hardware buffers from cache - void d_terr_fillVertexBuffer(MeshInfo *mi, float *buffer, ulong size) - { mi.fillVertexBuffer(buffer[0..size]); } - - void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer, ulong size) - { mi.fillIndexBuffer(buffer[0..size]); } - - void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer, ulong size) - { mi.fillAlphaBuffer(buffer[0..size]); } - - // Get a given alpha map struct belonging to a mesh - AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index) - { return mi.getAlphaInfo(index); } - - int d_terr_getAlphaSize() { return g_archive.alphaSize; } -} - -// Info about the entire quad. TODO: Some of this (such as the texture -// scale and probably the width and radius) can be generated at -// loadtime and is common for all quads on the same level. We could -// just make a QuadLevelInfo struct. -struct QuadInfo -{ - // Basic info - int cellX, cellY; - int level; - - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - float boundingRadius; - - // True if we should make the given child - bool hasChild[4]; - - // Number of mesh segments in this quad - int meshNum; - - // Location of this quad in the main archive file. The size includes - // everything related to this quad, including mesh data, alpha maps, - // etc. - size_t offset, size; -} - -// Info about an alpha map belonging to a mesh -struct AlphaInfo -{ - // Position of the actual image data - ulong bufSize, bufOffset; - - // The texture name for this layer. The actual string is stored in - // the archive's string buffer. - int texName = -1; - int alphaName = -1; - - // Fill the alpha texture buffer - void fillAlphaBuffer(ubyte abuf[]) - { - assert(abuf.length == bufSize); - g_archive.copy(abuf.ptr, bufOffset, bufSize); - } -} -static assert(AlphaInfo.sizeof == 6*4); - -// Info about each submesh -// If you change this struct please check whether align(1) still fits. -align(1) -struct MeshInfo -{ - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - - // Vertex and index numbers - int vertRows, vertCols; - - // Height offset to apply to all vertices - float heightOffset; - - // Size and offset of the vertex buffer - ulong vertBufSize, vertBufOffset; - - // Texture name. Index to the string table. - int texName = -1; - - // Number and offset of AlphaInfo blocks - int alphaNum; - ulong alphaOffset; - - // Fill the given vertex buffer - void fillVertexBuffer(float vdest[]) - { - // The height map and normals from the archive - byte *hmap = cast(byte*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr; - // The generic part, containing the x,y coordinates and the uv - // maps. - float *gmap = g_archive.getVertexBuffer(getLevel()).ptr; - - // Destination pointer - float *vbuf = vdest.ptr; - assert(vdest.length == vertRows*vertCols*8); - - // Merge the two data sets together into the output buffer. - float offset = heightOffset; - for(int y=0; y= 0); - assert(getLevel() == 1); - AlphaInfo *res = cast(AlphaInfo*)g_archive.getRelSlice - (alphaOffset, alphaNum*AlphaInfo.sizeof); - res += num; - return res; - } -} -static assert(MeshInfo.sizeof == 14*4); - -// The first part of the .index file -struct ArchiveHeader -{ - // "Magic" number to make sure we're actually reading an archive - // file - int magic; - - // Total number of quads in the archive - int quads; - - // Level of the 'root' quad. There will only be one quad on this - // level. - int rootLevel; - - // Size of the alpha maps, in pixels along one side. - int alphaSize; - - // Number of strings in the string table - int stringNum; - - // Size of the string buffer - size_t stringSize; -} - -TerrainArchive g_archive; - -// This class handles the cached terrain data. -struct TerrainArchive -{ - MeshInfo *curMesh; - QuadInfo *curQuad; - QuadInfo *rootQuad; - - void openFile(char[] name) - { - mmf = new MmFile(name, - MmFile.Mode.Read, - 0, null, pageSize); - - // Read the index file first - MyFile ifile = new MyFile(name ~ ".index"); - - ArchiveHeader head; - ifile.fill(head); - - // Reads data into an array. Would be better if this was part of - // the stream. - - // Sanity check - assert(head.magic == CACHE_MAGIC); - assert(head.quads > 0 && head.quads < 8192); - - // Store header info - alphaSize = head.alphaSize; - - // Read all the quads - quadList = new QuadInfo[head.quads]; - ifile.fillArray(quadList); - - // Create an index of all the quads - foreach(int index, qn; quadList) - { - int x = qn.cellX; - int y = qn.cellY; - int l = qn.level; - - assert(l >= 1); - - quadMap[l][x][y] = index; - assert(index == quadMap[l][x][y]); - - // Store the root quad - if(l == head.rootLevel) - { - assert(rootQuad == null); - rootQuad = &quadList[index]; - } - else - assert(l < head.rootLevel); - } - - // Make sure the root was set - assert(rootQuad !is null); - - // Next read the string table. First read the main string buffer. - stringBuf = new char[head.stringSize]; - ifile.fillArray(stringBuf); - - // Then read the string offsets - int[] offsets = new int[head.stringNum]; - ifile.fillArray(offsets); - - // Set up the string table - char *strptr = stringBuf.ptr; - strings.length = head.stringNum; - foreach(int i, ref str; strings) - { - // toString(char*) returns the string up to the zero - // terminator byte - str = toString(strptr + offsets[i]); - assert(str.ptr + str.length <= - stringBuf.ptr + stringBuf.length); - } - delete offsets; - - // Read the vertex buffer data - int bufNum = head.rootLevel; - assert(bufNum == 7); - vertBufData.length = bufNum; - - // Fill the vertex buffers. Start at level 1. - for(int i=1;i=1 && level= 0); - assert(index < strings.length); - - return strings[index]; - } - - void doMap(size_t offset, size_t size) - { - assert(mmf !is null); - assert(size); - mapped = cast(ubyte[])mmf[offset..offset+size]; - assert(mapped.length == size); - } - - // Get a slice of a given buffer within the mapped window. The - // offset is relative to the start of the window, and the size must - // fit inside the window. - ubyte[] getRelSlice(size_t offset, size_t size) - { - assert(mapped.length); - - return mapped[offset..offset+size]; - } - - // Copy a given buffer from the file. The buffer might be a - // compressed stream, so it's important that the buffers are written - // in the same block sizes as they are read. (Ie. you can't write a - // buffer as one operation and read it as two, or vice versa. Also, - // buffers cannot overlap.) The offset is relative to the current - // mapped file window. - void copy(void *dst, size_t offset, size_t inSize) - { - ubyte source[] = getRelSlice(offset, inSize); - - // Just copy it for now - ubyte* dest = cast(ubyte*)dst; - dest[0..source.length] = source[]; - } -} diff --git a/old_d_version/terrain/bindings.d b/old_d_version/terrain/bindings.d deleted file mode 100644 index 145d1a5934..0000000000 --- a/old_d_version/terrain/bindings.d +++ /dev/null @@ -1,27 +0,0 @@ -module terrain.bindings; - -alias void *SceneNode; -alias void *Bounds; -alias void *MeshObj; - -// These are all defined in cpp_terrain.cpp: -extern(C): - -SceneNode terr_createChildNode(float relX, float relY, SceneNode); -void terr_destroyNode(SceneNode); -Bounds terr_makeBounds(float minHeight, float maxHeight, float width, SceneNode); -void terr_killBounds(Bounds); -float terr_getSqCamDist(Bounds); -MeshObj terr_makeMesh(SceneNode,void*,int,float); -void terr_killMesh(MeshObj); - -void terr_genData(); -void terr_setupRendering(); - -void terr_makeLandMaterial(char*,float); -ubyte *terr_makeAlphaLayer(char*,int); -void terr_closeAlpha(char*,char*,float); -void terr_cleanupAlpha(char*,void*,int); - -void terr_resize(void*,void*,int,int); -void terr_saveImage(void*,int,char*); diff --git a/old_d_version/terrain/cachewriter.d b/old_d_version/terrain/cachewriter.d deleted file mode 100644 index 61a52addb1..0000000000 --- a/old_d_version/terrain/cachewriter.d +++ /dev/null @@ -1,337 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (cachewriter.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 terrain.cachewriter; - -import terrain.archive; - -import terrain.outbuffer; -import std.stdio, std.stream, std.string; -import terrain.myfile; -import std.math2; -import monster.util.string; -import monster.vm.dbg; - -// Helper structs -struct AlphaHolder -{ - AlphaInfo info; - - // Actual pixel buffer - ubyte[] buffer; -} - -struct MeshHolder -{ - MeshInfo info; - - // Actual buffers - byte[] vertexBuffer; - - // Alpha maps (if any) - AlphaHolder alphas[]; -} - -// A struct that gathers all the relevant quad data in one place. -struct QuadHolder -{ - QuadInfo info; - - MeshHolder meshes[]; -} - -struct CacheWriter -{ - // Opens the main archive file for output - void openFile(char[] fname) - { - mainFile = new File(fname, FileMode.OutNew); - iname = fname ~ ".index"; - - buf = new OutBuffer; - } - - void setParams(int mxLev, int alphSize) - { - maxLevel = mxLev; - alphaSize = alphSize; - - vertBuf.length = maxLevel; - } - - // Closes the main archive file and writes the index. - void finish() - { - mainFile.close(); - - // Write the index file - scope MyFile ofile = new MyFile(iname, FileMode.OutNew); - - // Header first - ArchiveHeader head; - head.magic = CACHE_MAGIC; - head.quads = quadList.length; - head.rootLevel = maxLevel; - head.alphaSize = alphaSize; - head.stringNum = stringList.length; - head.stringSize = totalStringLength; - ofile.dump(head); - - // Write the quads - ofile.dumpArray(quadList); - - // String table next. We need to sort it in order of the indices - // first. - char[][] strVector; - strVector.length = head.stringNum; - - foreach(char[] key, int value; stringList) - strVector[value] = key; - - // Next, write the strings to file while we fill in the offset - // list - int[] offsets = new int[head.stringNum]; - - size_t curOffs = 0; - for(int i=0; i level); - vertBuf[level] = buf; - } - - // Add a common index buffer - void setIndexBuffer(ushort[] buf) - { - indexBuf = buf; - } - - // Write a finished quad to the archive file. All the offsets and - // numbers in the *Info structs are filled in automatically based on - // the additional data in the Holder structs. - void writeQuad(ref QuadHolder qh) - { - scope auto _trc = new MTrace("writeQuad"); - - // Write the MeshInfo's first - int meshNum = qh.meshes.length; - - MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum); - - float minh = float.infinity; - float maxh = -float.infinity; - - // Then write the mesh data in approximately the order it's read - for(int i=0; igetFarClipDistance(); - // Recreate the mesh if the view distance has increased - if ( vd > mMeshDistance ) - { - destroyMesh(); - createMesh(); - } - - Ogre::Vector3 p = mCamera->getDerivedPosition(); - p.x -= ((int)p.x % CELL_WIDTH); - p.z -= ((int)p.z % CELL_WIDTH); - - float h = (p.y + 2048)*2.0/CELL_WIDTH; - h *= h; - - mNode->setPosition(p.x, -p.z, -32 -h); - } - -private: - void createMesh() - { - float vd = mCamera->getFarClipDistance(); - - mMeshDistance = vd; - - vd = vd/CELL_WIDTH * 32; - - mMat = Ogre::MaterialManager::getSingleton(). - create("BaseLandMat", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Ogre::TextureUnitState* us = mMat->getTechnique(0)->getPass(0)->createTextureUnitState("_land_default.dds"); - us->setTextureScale(1.0f/vd,1.0f/vd); - - mMat->getTechnique(0)->getPass(0)->setDepthBias(-1); - - mObject = mSceneMgr->createManualObject("BaseLand"); - mObject->begin("BaseLandMat", Ogre::RenderOperation::OT_TRIANGLE_LIST); - - vd = mMeshDistance; - - const int HEIGHT = -2048 - 10; - - mObject->position(-vd,vd,HEIGHT); - mObject->textureCoord(0, 1); - - mObject->position(-vd,-vd,HEIGHT); - mObject->textureCoord(0, 0); - - mObject->position(vd,-vd,HEIGHT); - mObject->textureCoord(1, 0); - - mObject->position(vd,vd,HEIGHT); - mObject->textureCoord(1, 1); - - mObject->quad(0,1,2,3); - - mObject->end(); - - mNode = g_rootTerrainNode->createChildSceneNode(); - mNode->attachObject(mObject); - } - - void destroyMesh() - { - mNode->detachAllObjects(); - mSceneMgr->destroyManualObject(mObject); - mNode->getParentSceneNode()->removeAndDestroyChild(mNode->getName()); - - mMat->getCreator()->remove(mMat->getHandle()); - mMat = Ogre::MaterialPtr(); - } - - ///the created mesh - Ogre::ManualObject* mObject; - - ///The material for the mesh - Ogre::MaterialPtr mMat; - - ///scene node for the mesh - Ogre::SceneNode* mNode; - - ///In essence, the farViewDistance of the camera last frame - Ogre::Real mMeshDistance; -}; diff --git a/old_d_version/terrain/cpp_mesh.cpp b/old_d_version/terrain/cpp_mesh.cpp deleted file mode 100644 index f5f6a26f57..0000000000 --- a/old_d_version/terrain/cpp_mesh.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// The Ogre renderable used to hold and display the terrain meshes. -class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject -{ -public: - - TerrainMesh(Ogre::SceneNode *parent, const MeshInfo &info, - int level, float scale) - : Ogre::Renderable(), - Ogre::MovableObject() - { - TRACE("TerrainMesh()"); - - mLevel = level; - - // This is a bit messy, with everything in one function. We could - // split it up later. - - // Use MW coordinates all the way - assert(info.worldWidth > 0); - assert(info.minHeight <= info.maxHeight); - mBounds.setExtents(0,0,info.minHeight, - info.worldWidth, info.worldWidth, - info.maxHeight); - mCenter = mBounds.getCenter(); - mBoundingRadius = mBounds.getHalfSize().length(); - - // TODO: VertexData has a clone() function. This probably means we - // can set this up once and then clone it, to get a completely - // unnoticable increase in performance :) - mVertices = new VertexData(); - mVertices->vertexStart = 0; - mVertices->vertexCount = info.vertRows*info.vertCols; - - VertexDeclaration* vertexDecl = mVertices->vertexDeclaration; - size_t currOffset = 0; - - vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); - - vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); - - vertexDecl->addElement(0, currOffset, VET_FLOAT2, - VES_TEXTURE_COORDINATES, 0); - currOffset += VertexElement::getTypeSize(VET_FLOAT2); - - assert(vertexDecl->getVertexSize(0) == currOffset); - - HardwareVertexBufferSharedPtr mMainBuffer; - mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer - ( - vertexDecl->getVertexSize(0), // size of one whole vertex - mVertices->vertexCount, // number of vertices - HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage - false); // no shadow buffer - - // Bind the data - mVertices->vertexBufferBinding->setBinding(0, mMainBuffer); - - // Fill the buffer - float* verts = static_cast - (mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); - info.fillVertexBuffer(verts,8*mVertices->vertexCount); - mMainBuffer->unlock(); - - // Create the index data holder - mIndices = new IndexData(); - mIndices->indexCount = 64*64*6; // TODO: Shouldn't be hard-coded - mIndices->indexBuffer = - HardwareBufferManager::getSingleton().createIndexBuffer - ( HardwareIndexBuffer::IT_16BIT, - mIndices->indexCount, - HardwareBuffer::HBU_STATIC_WRITE_ONLY, - false); - - // Fill the buffer with warm fuzzy archive data - unsigned short* indices = static_cast - (mIndices->indexBuffer->lock - (0, mIndices->indexBuffer->getSizeInBytes(), - HardwareBuffer::HBL_DISCARD)); - info.fillIndexBuffer(indices,mIndices->indexCount); - mIndices->indexBuffer->unlock(); - - // Finally, create the material - const std::string texName = info.getTexName(); - - // Set up the scene node. - mNode = parent->createChildSceneNode(); - mNode->attachObject(this); - - // Finally, create or retrieve the material - if(MaterialManager::getSingleton().resourceExists(texName)) - { - mMaterial = MaterialManager::getSingleton().getByName - (texName); - return; - } - - // No existing material. Create a new one. - mMaterial = MaterialManager::getSingleton().create - (texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Pass* pass = mMaterial->getTechnique(0)->getPass(0); - pass->setLightingEnabled(false); - - if(level > 1) - { - // This material just has a normal texture - pass->createTextureUnitState(texName) - //->setTextureAddressingMode(TextureUnitState::TAM_CLAMP) - ; - } - else - { - assert(level == 1); - - // Get the background texture. TODO: We should get this from - // somewhere, no file names should be hard coded. The texture - // might exist as a .tga in earlier versions of the game, and - // we might also want to specify a different background - // texture on some meshes. - //const char *bgTex = info.getBackgroundTex(); - - const char *bgTex = "_land_default.dds"; - pass->createTextureUnitState(bgTex) - ->setTextureScale(scale,scale); - - // Loop through all the textures in this mesh - for(int tnum=0; tnumgetBuffer(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - Ogre::uint8* pDest = static_cast(pixelBox.data); - - // Copy alpha data from file - alpha.fillAlphaBuffer(pDest,g_alphaSize*g_alphaSize); - - // Close the buffer - pixelBuffer->unlock(); - } - - pass = mMaterial->getTechnique(0)->createPass(); - pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); - pass->setLightingEnabled(false); - pass->setDepthFunction(Ogre::CMPF_EQUAL); - - Ogre::TextureUnitState* tus = pass->createTextureUnitState(alphaName); - //tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - - tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setIsAlpha(true); - - // Add the actual texture on top of the alpha map. - tus = pass->createTextureUnitState(tname); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_CURRENT); - - tus->setTextureScale(scale, scale); - } - } - } - - ~TerrainMesh() - { - assert(mNode); - mNode->detachAllObjects(); - mNode->getCreator()->destroySceneNode(mNode); - - // TODO: This still crashes on level1 meshes. Find out why! - if(mLevel!=1)delete mVertices; - delete mIndices; - } - - //----------------------------------------------------------------------- - // These are all Ogre functions that we have to override - //----------------------------------------------------------------------- - - // Internal Ogre function. We should call visitor->visit on all - // Renderables that are part of this object. In our case, this is - // only ourselves. - void visitRenderables(Renderable::Visitor* visitor, - bool debugRenderables = false) { - visitor->visit(this, 0, false); - } - - void getRenderOperation( Ogre::RenderOperation& op ) { - op.useIndexes = true; - op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; - op.vertexData = mVertices; - op.indexData = mIndices; - } - - void getWorldTransforms( Ogre::Matrix4* xform ) const { - *xform = mNode->_getFullTransform(); - } - - const Ogre::Quaternion& getWorldOrientation(void) const { - return mNode->_getDerivedOrientation(); - } - - const Ogre::Vector3& getWorldPosition(void) const { - return mNode->_getDerivedPosition(); - } - - Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const { - Ogre::Vector3 diff = mCenter - cam->getDerivedPosition(); - // Use squared length to avoid square root - return diff.squaredLength(); - } - - const Ogre::LightList& getLights(void) const { - if (mLightListDirty) { - getParentSceneNode()->getCreator()->_populateLightList - (mCenter, mBoundingRadius, mLightList); - mLightListDirty = false; - } - return mLightList; - } - virtual const Ogre::String& getMovableType( void ) const { - static Ogre::String t = "MW_TERRAIN"; - return t; - } - void _updateRenderQueue( Ogre::RenderQueue* queue ) { - mLightListDirty = true; - queue->addRenderable(this, mRenderQueueID); - } - const Ogre::AxisAlignedBox& getBoundingBox( void ) const - { - return mBounds; - } - - Ogre::Real getBoundingRadius(void) const { - return mBoundingRadius; - } - virtual const MaterialPtr& getMaterial(void) const - { return mMaterial; } - - //----------------------------------------------------------------------- - //----------------------------------------------------------------------- - -private: - - int mLevel; - - Ogre::SceneNode* mNode; - - Ogre::MaterialPtr mMaterial; - - Ogre::VertexData* mVertices; - Ogre::IndexData* mIndices; - - mutable bool mLightListDirty; - - Ogre::Real mBoundingRadius; - Ogre::AxisAlignedBox mBounds; - Ogre::Vector3 mCenter; -}; diff --git a/old_d_version/terrain/cpp_terrain.cpp b/old_d_version/terrain/cpp_terrain.cpp deleted file mode 100644 index ad27110361..0000000000 --- a/old_d_version/terrain/cpp_terrain.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Jacob Essex, Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (cpp_terrain.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/ . - -*/ - -const int CELL_WIDTH = 8192; - -SceneNode *g_rootTerrainNode; -int g_alphaSize; - -struct MeshInfo; -struct AlphaInfo; - -// D functions -extern "C" -{ - void d_terr_superman(); - void d_terr_terrainUpdate(); - - char *d_terr_getTexName(int32_t); - - void d_terr_fillVertexBuffer(const MeshInfo*,float*,uint64_t); - void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*,uint64_t); - AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t); - - void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*,uint64_t); - - int32_t d_terr_getAlphaSize(); -} - -// Info about a submesh. This is a clone of the struct defined in -// archive.d. TODO: Make sure the D and C++ structs are of the same -// size and alignment. -struct MeshInfo -{ - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - - // Vertex and index numbers - int32_t vertRows, vertCols; - - // Height offset to apply to all vertices - float heightOffset; - - // Size and offset of the vertex buffer - int64_t vertBufSize, vertBufOffset; - - // Texture name. Index to the string table. - int32_t texName; - - // Number and offset of AlphaInfo blocks - int32_t alphaNum; - uint64_t alphaOffset; - - inline void fillVertexBuffer(float *buffer, uint64_t size) const - { - d_terr_fillVertexBuffer(this, buffer, size); - } - - inline void fillIndexBuffer(uint16_t *buffer, uint64_t size) const - { - d_terr_fillIndexBuffer(this, buffer, size); - } - - inline char* getTexName() const - { - return d_terr_getTexName(texName); - } - - inline AlphaInfo *getAlphaInfo(int tnum) const - { - return d_terr_getAlphaInfo(this, tnum); - } -}; - -// Info about an alpha map belonging to a mesh -struct AlphaInfo -{ - // Position of the actual image data - uint64_t bufSize, bufOffset; - - // The texture name for this layer. The actual string is stored in - // the archive's string buffer. - int32_t texName; - int32_t alphaName; - - inline char* getTexName() const - { - return d_terr_getTexName(texName); - } - - inline char* getAlphaName() const - { - return d_terr_getTexName(alphaName); - } - - inline void fillAlphaBuffer(uint8_t *buffer, uint64_t size) const - { - return d_terr_fillAlphaBuffer(this, buffer, size); - } -}; - -#include "cpp_baseland.cpp" -#include "cpp_mesh.cpp" - -BaseLand *g_baseLand; - -class TerrainFrameListener : public FrameListener -{ -protected: - bool frameEnded(const FrameEvent& evt) - { - TRACE("Terrain frame"); - d_terr_terrainUpdate(); - if(g_baseLand) - g_baseLand->update(); - return true; - } -}; - -// Renders a material into a texture -Ogre::TexturePtr getRenderedTexture(Ogre::MaterialPtr mp, - const std::string& name, - int texSize, Ogre::PixelFormat tt) -{ - Ogre::CompositorPtr cp = Ogre::CompositorManager::getSingleton(). - create("Rtt_Comp", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass(); - Ogre::CompositionPass* cpass = ctp->createPass(); - cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD); - cpass->setMaterial(mp); - - // Create the destination texture - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton(). - createManual(name + "_T", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - texSize, - texSize, - 0, - tt, - Ogre::TU_RENDERTARGET - ); - - Ogre::RenderTexture* renderTexture = texture->getBuffer()->getRenderTarget(); - Ogre::Viewport* vp = renderTexture->addViewport(mCamera); - - Ogre::CompositorManager::getSingleton().addCompositor(vp, "Rtt_Comp"); - Ogre::CompositorManager::getSingleton().setCompositorEnabled(vp,"Rtt_Comp", true); - - renderTexture->update(); - - // Call the OGRE renderer. - Ogre::Root::getSingleton().renderOneFrame(); - - Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp"); - Ogre::CompositorManager::getSingleton().remove(cp->getHandle()); - - renderTexture->removeAllViewports(); - - return texture; -} - -// These are used between some functions below. Kinda messy. Since -// these are GLOBAL instances, they are terminated at program -// exit. However, OGRE itself is terminated before that, so we have to -// make sure we have no 'active' shared pointers after OGRE is -// finished (otherwise we get a segfault at exit.) -std::list createdResources; -Ogre::HardwarePixelBuffer *pixelBuffer; -MaterialPtr mat; - -// Functions called from D -extern "C" -{ - SceneNode* terr_createChildNode(float x, float y, - SceneNode *parent) - { - Ogre::Vector3 pos(x,y,0); - if(parent == NULL) - parent = g_rootTerrainNode; - - assert(parent); - return parent->createChildSceneNode(pos); - } - - void terr_destroyNode(SceneNode *node) - { - node->removeAndDestroyAllChildren(); - mSceneMgr->destroySceneNode(node); - } - - // TODO: We could make allocation a little more refined than new and - // delete. But that's true for everything here. A freelist based - // approach is best in most of these cases, as we have continuous - // allocation/deallocation of fixed-size structs. - Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight, - float width, SceneNode* node) - { - TRACE("terr_makeBounds"); - AxisAlignedBox *mBounds = new AxisAlignedBox; - - assert(maxHeight >= minHeight); - - mBounds->setExtents(0,0,minHeight, - width,width,maxHeight); - - // Transform the box to world coordinates, so it can be compared - // with the camera later. - mBounds->transformAffine(node->_getFullTransform()); - - return mBounds; - } - - void terr_killBounds(AxisAlignedBox *bounds) - { - TRACE("terr_killBounds"); - delete bounds; - } - - float terr_getSqCamDist(AxisAlignedBox *mBounds) - { - TRACE("terr_getSqCamDist"); - Ogre::Vector3 cpos = mCamera->getDerivedPosition(); - Ogre::Vector3 diff(0, 0, 0); - diff.makeFloor(cpos - mBounds->getMinimum() ); - diff.makeCeil(cpos - mBounds->getMaximum() ); - return diff.squaredLength(); - } - - TerrainMesh *terr_makeMesh(SceneNode *parent, - MeshInfo *info, - int level, float scale) - { - return new TerrainMesh(parent, *info, level, scale); - } - - void terr_killMesh(TerrainMesh *mesh) - { - TRACE("terr_killMesh"); - delete mesh; - } - - // Set up the rendering system - void terr_setupRendering() - { - TRACE("terr_setupRendering()"); - // Make sure the C++ sizes match the D sizes, since the structs - // are shared between the two. - assert(sizeof(MeshInfo) == 14*4); - assert(sizeof(AlphaInfo) == 6*4); - - // Add the terrain directory as a resource location. TODO: Get the - // name from D. - ResourceGroupManager::getSingleton(). - addResourceLocation("cache/terrain/", "FileSystem", "General"); - - // Enter superman mode - mCamera->setFarClipDistance(40*CELL_WIDTH); - //ogre_setFog(0.7, 0.7, 0.7, 200, 32*CELL_WIDTH); - d_terr_superman(); - - // Create a root scene node first. The 'root' node is rotated to - // match the MW coordinate system - g_rootTerrainNode = mwRoot->createChildSceneNode("TERRAIN_ROOT"); - - // Add the base land. This is the ground beneath the actual - // terrain mesh that makes the terrain look infinite. - //g_baseLand = new BaseLand(); - - g_alphaSize = d_terr_getAlphaSize(); - - // Add the frame listener - mRoot->addFrameListener(new TerrainFrameListener); - } - - // The next four functions are called in the function genLevel2Map() - // only. This is very top-down-programming-ish and a bit messy, but - // that's what I get for mixing C++ and D like this. - void terr_makeLandMaterial(const char* name, float scale) - { - // Get a new material - mat = Ogre::MaterialManager::getSingleton(). - create(name, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - // Put the default texture in the bottom 'layer', so that we don't - // end up seeing through the landscape. - Ogre::Pass* np = mat->getTechnique(0)->getPass(0); - np->setLightingEnabled(false); - np->createTextureUnitState("_land_default.dds") - ->setTextureScale(scale,scale); - } - - uint8_t *terr_makeAlphaLayer(const char* name, int32_t width) - { - // Create alpha map for this texture. - Ogre::TexturePtr texPtr = Ogre::TextureManager::getSingleton(). - createManual(name, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - width, width, - 1,0, // depth, mipmaps - Ogre::PF_A8, // One-channel alpha - Ogre::TU_STATIC_WRITE_ONLY); - - createdResources.push_back(texPtr); - - pixelBuffer = texPtr->getBuffer().get(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - return static_cast(pixelBox.data); - } - - void terr_closeAlpha(const char *alphaName, - const char *texName, float scale) - { - // Close the alpha pixel buffer opened in the previous function - pixelBuffer->unlock(); - - // Create a pass containing the alpha map - Pass *np = mat->getTechnique(0)->createPass(); - np->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); - np->setLightingEnabled(false); - np->setDepthFunction(Ogre::CMPF_EQUAL); - Ogre::TextureUnitState* tus = np->createTextureUnitState(alphaName); - tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - - // Set various blending options - tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setIsAlpha(true); - - // Add the terrain texture to the pass and scale it. - tus = np->createTextureUnitState(texName); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_CURRENT); - tus->setTextureScale(scale, scale); - } - - // Clean up after the above functions, render the material to - // texture and save the data in outdata and in the file outname. - void terr_cleanupAlpha(const char *outname, - void *outData, int32_t toSize) - { - TexturePtr tex1 = getRenderedTexture(mat,outname, - toSize,Ogre::PF_R8G8B8); - - // Blit the texture into the given memory buffer - PixelBox pb = PixelBox(toSize, toSize, 1, PF_R8G8B8); - pb.data = outData; - tex1->getBuffer()->blitToMemory(pb); - - // Clean up - TextureManager::getSingleton().remove(tex1->getHandle()); - const std::list::const_iterator iend = createdResources.end(); - for ( std::list::const_iterator itr = createdResources.begin(); - itr != iend; - ++itr) - (*itr)->getCreator()->remove((*itr)->getHandle()); - createdResources.clear(); - - MaterialManager::getSingleton().remove(mat->getHandle()); - mat.setNull(); - } - - void terr_resize(void* srcPtr, void* dstPtr, int32_t fromW, int32_t toW) - { - // Create pixelboxes - PixelBox src = PixelBox(fromW, fromW, 1, PF_R8G8B8); - PixelBox dst = PixelBox(toW, toW, 1, PF_R8G8B8); - - src.data = srcPtr; - dst.data = dstPtr; - - // Resize the image. The nearest neighbour filter makes sure - // there is no blurring. - Image::scale(src, dst, Ogre::Image::FILTER_NEAREST); - } - - void terr_saveImage(void *data, int32_t width, const char* name) - { - Image img; - img.loadDynamicImage((uchar*)data, width, width, PF_R8G8B8); - img.save(name); - } -} diff --git a/old_d_version/terrain/esmland.d b/old_d_version/terrain/esmland.d deleted file mode 100644 index 1ea169ade5..0000000000 --- a/old_d_version/terrain/esmland.d +++ /dev/null @@ -1,158 +0,0 @@ -module terrain.esmland; - -import esm.loadltex; -import esm.loadcell; -import util.regions; -import esm.filereader; - -import std.stdio; - -const int LAND_NUM_VERTS = 65*65; - -MWLand mwland; - -// Interface to the ESM landscape data -struct MWLand -{ - RegionManager reg; - - // These structs/types represent the way data is actually stored in - // the ESM files. - - // Heightmap - align(1) - struct VHGT - { - float heightOffset; - byte heightData[LAND_NUM_VERTS]; - short unknown1; - char unknown2; - } - - // Normals - typedef byte[LAND_NUM_VERTS*3] VNML; - - // Land textures. This is organized in 4x4 buffers of 4x4 squares - // each. This is how the original engine splits up the cell meshes, - // and it's probably a good idea for us to do the same. - typedef short[4][4][4][4] VTEX; - - static assert(VHGT.sizeof == 4232); - static assert(VNML.sizeof == 12675); - static assert(VTEX.sizeof == 512); - - // Landscape data for one cell - struct LandData - { - VHGT vhgt; - VNML normals; - } - - // Texture data for one cell - struct LTEXData - { - // Global list of land textures from the source ES file - LandTextureList.TextureList source; - - // Texture indices for this cell - VTEX vtex; - - // Get the texture x2,y2 from the sub map x1,x2 - char[] getTexture(int x1, int y1, int x2, int y2) - { - // Get the texture index relative to the current esm/esp file - short texID = vtex[y1][x1][y2][x2] - 1; - - if(texID == -1) - return null; - - // Return the 'new' texture name. This name was automatically - // been converted to .dds at load time if the .tga file was not - // found. - assert(source !is null); - assert(texID >= 0 && texID < source.length); - return source[texID].getNewName(); - } - - // Get a texture from the 16x16 grid in one cell - char[] getTexture(int x, int y) - { - return getTexture(x/4,y/4,x%4,y%4); - } - } - - // Get the maximum absolute coordinate value in any direction - int getMaxCoord() - { return cells.maxXY; } - - // Does the given cell exist and does it have land data? - bool hasData(int x, int y) - { - // Does the cell exist? - if(!cells.hasExt(x,y)) - return false; - - // And does it have terrain data? - auto ex = cells.getExt(x,y); - return ex.hasLand(); - } - - LandData *getLandData(int x, int y) - { - loadCell(x, y); - return ¤tLand; - } - - LTEXData *getLTEXData(int x, int y) - { - loadCell(x, y); - return ¤tLtex; - } - - private: - - int currentX = -1234; - int currentY = 4321; - - LandData currentLand; - LTEXData currentLtex; - - // Make sure the given cell is loaded - void loadCell(int x, int y) - { - // If the right cell is already loaded, don't do anything - if(x == currentX && y == currentY) - return; - - assert(hasData(x,y)); - - currentX = x; - currentY = y; - - // Get the file context for the terrain data. This can be used to - // skip to the right part of the ESM file. - auto cont = cells.getExt(x,y).land.context; - - // Get the land texture list from the file - currentLtex.source = landTextures.files[cont.filename]; - - // We should use an existing region later, or at least delete this - // once we're done with the gen process. - if(reg is null) - reg = new RegionManager(); - - // Open the ESM at this cell - esFile.restoreContext(cont, reg); - - // Store the cell-specific data - esFile.readHNExact(currentLand.normals.ptr, - currentLand.normals.length, "VNML"); - esFile.readHNExact(¤tLand.vhgt, VHGT.sizeof, "VHGT"); - - // These aren't used yet - if(esFile.isNextSub("WNAM")) esFile.skipHSubSize(81); - if(esFile.isNextSub("VCLR")) esFile.skipHSubSize(12675); - - esFile.readHNExact(¤tLtex.vtex, VTEX.sizeof, "VTEX"); - } -} diff --git a/old_d_version/terrain/generator.d b/old_d_version/terrain/generator.d deleted file mode 100644 index f1645c9ab9..0000000000 --- a/old_d_version/terrain/generator.d +++ /dev/null @@ -1,1114 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (generator.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 is responsible for generating the cache files. -module terrain.generator; - -import std.stdio; -import std.string; -import std.math2; -import std.c.string; - -import terrain.cachewriter; -import terrain.archive; -import terrain.esmland; -import terrain.terrain; -import terrain.bindings; - -import util.cachefile; - -import monster.util.aa; -import monster.util.string; -import monster.vm.dbg; - -int mCount; - -// Texture sizes for the various levels. For the most detailed level -// (level 1), this give the size of the alpha splatting textures rather -// than a final texture. -int[] texSizes; - -CacheWriter cache; - -void generate(char[] filename) -{ - makePath(cacheDir); - - cache.openFile(filename); - - // Find the maxiumum distance from (0,0) in any direction - int max = mwland.getMaxCoord(); - - // Round up to nearest power of 2 - int depth=1; - while(max) - { - max >>= 1; - depth++; - assert(depth <= 8); - } - max = 1 << depth-1; - - // We already know the answers - assert(max == 32); - assert(depth == 6); - - // Set the texture sizes. TODO: These should be config options, - // perhaps - or maybe a result of some higher-level detail setting. - texSizes.length = depth+1; - texSizes[6] = 1024; - texSizes[5] = 512; - texSizes[4] = 256; - texSizes[3] = 256; - texSizes[2] = 512; - texSizes[1] = 64; - - // Set some general parameters for the runtime - cache.setParams(depth+1, texSizes[1]); - - // Create some common data first - writefln("Generating common data"); - genDefaults(); - genIndexData(); - - writefln("Generating quad data"); - GenLevelResult gen; - - // Start at one level above the top, but don't generate data for it - genLevel(depth+1, -max, -max, gen, false); - - writefln("Writing index file"); - cache.finish(); - writefln("Pregeneration done. Results written to ", filename); -} - -struct GenLevelResult -{ - QuadHolder quad; - - bool hasMesh; - bool isAlpha; - - int width; - - ubyte[] data; - - void allocImage(int _width) - { - assert(isEmpty()); - - width = _width; - data.length = width*width*3; - quad.meshes.length = 1; - - assert(!hasAlpha()); - } - - void allocAlphas(int _width, int texNum) - { - assert(isEmpty() || hasMesh); - - width = _width; - data.length = width*width*texNum; - isAlpha = true; - - // Set up the alpha images. TODO: We have to split these over - // several meshes, but for now pretend that we're using only - // one. - assert(quad.meshes.length == 1); - quad.meshes[0].alphas.length = texNum; - assert(alphaNum() == texNum); - - int s = width*width; - for(int i=0;i 2); - genLevel2Map(null, defaults[2]); - - for(int i=3; i=0&&v>=0); - assert(u<=1&&v<=1); - - vertList[index++] = u; - vertList[index++] = v; - } - assert(index == vertList.length); - // Store the buffer - cache.addVertexBuffer(lev,vertList); - } - - // index stuff already ported -} - -void genLevel(int level, int X, int Y, ref GenLevelResult result, - bool makeData = true) -{ - scope auto _trc = new MTrace(format("genLevel(%s,%s,%s)",level,X,Y)); - result.quad.info.cellX = X; - result.quad.info.cellY = Y; - result.quad.info.level = level; - result.quad.info.worldWidth = 8192 << (level-1); - - assert(result.isEmpty); - - // Level 1 (most detailed) is handled differently from the - // other leves. - if(level == 1) - { - assert(makeData); - - if(!mwland.hasData(X,Y)) - // Oops, there's no data for this cell. Skip it. - return; - - // The mesh is generated in pieces rather than as one part. - genLevel1Meshes(result); - - // We also generate alpha maps instead of the actual textures. - genCellAlpha(result); - - if(!result.isEmpty()) - { - // Store the information we just created - assert(result.hasAlpha()); - cache.writeQuad(result.quad); - } - - return; - } - assert(level > 1); - - // Number of cells along one side in each sub-quad (not in this - // quad) - int cells = 1 << (level-2); - - // Call the sub-levels and store the result - GenLevelResult sub[4]; - genLevel(level-1, X, Y, sub[0]); // NW - genLevel(level-1, X+cells, Y, sub[1]); // NE - genLevel(level-1, X, Y+cells, sub[2]); // SW - genLevel(level-1, X+cells, Y+cells, sub[3]); // SE - - // Make sure we deallocate everything when the function exists - scope(exit) - { - foreach(ref s; sub) - s.kill(); - } - - // Mark the sub-quads that have data - bool anyUsed = false; - for(int i=0;i<4;i++) - { - bool used = !sub[i].isEmpty(); - result.quad.info.hasChild[i] = used; - anyUsed = anyUsed || used; - } - - if(!anyUsed) - { - // If our children are empty, then we are also empty. - assert(result.isEmpty()); - return; - } - - if(makeData) - { - if(level == 2) - // For level==2, generate a new texture from the alpha maps. - genLevel2Map(sub, result); - else - // Level 3+, merge the images from the previous levels - mergeMaps(sub, result); - - // Create the landscape mesh by merging the result from the - // children. - mergeMesh(sub, result); - } - - // Store the result in the cache file - cache.writeQuad(result.quad); -} - -// Generate mesh data for one cell -void genLevel1Meshes(ref GenLevelResult res) -{ - // Constants - int intervals = 64; - int vertNum = intervals+1; - int vertSep = 128; - - // Allocate the mesh buffer - res.allocMesh(vertNum); - - int cellX = res.quad.info.cellX; - int cellY = res.quad.info.cellY; - assert(res.quad.info.level==1); - - MeshHolder *mh = &res.quad.meshes[0]; - MeshInfo *mi = &mh.info; - - // Set some basic data - mi.worldWidth = vertSep*intervals; - assert(mi.worldWidth == 8192); - - auto land = mwland.getLandData(cellX, cellY); - - byte[] heightData = land.vhgt.heightData; - byte[] normals = land.normals; - mi.heightOffset = land.vhgt.heightOffset; - - float max=-1000000.0; - float min=1000000.0; - - byte[] verts = mh.vertexBuffer; - int index = 0; - - // Loop over all the vertices in the mesh - float rowheight = mi.heightOffset; - float height; - for(int y=0; y<65; y++) - for(int x=0; x<65; x++) - { - // Offset of this vertex within the source buffer - int offs=y*65+x; - - // The vertex data from the ESM - byte data = heightData[offs]; - - // Write the height value as a short (2 bytes) - *(cast(short*)&verts[index]) = data; - index+=2; - - // Calculate the height here, even though we don't store - // it. We use it to find the min and max values. - if(x == 0) - { - // Set the height to the row height - height = rowheight; - - // First value in each row adjusts the row height - rowheight += data; - } - // Adjust the accumulated height with the new data. - height += data; - - // Calculate the min and max - max = height > max ? height : max; - min = height < min ? height : min; - - // Store the normals - for(int k=0; k<3; k++) - verts[index++] = normals[offs*3+k]; - } - - // Make sure we wrote exactly the right amount of data - assert(index == verts.length); - - // Store the min/max values - mi.minHeight = min * 8; - mi.maxHeight = max * 8; -} - -// Generate the alpha splatting bitmap for one cell. -void genCellAlpha(ref GenLevelResult res) -{ - scope auto _trc = new MTrace("genCellAlpha"); - - int cellX = res.quad.info.cellX; - int cellY = res.quad.info.cellY; - assert(res.quad.info.level == 1); - - // Set the texture name - it's used internally as the material name - // at runtime. - assert(res.quad.meshes.length == 1); - res.setTexName("AMAT_"~toString(cellX)~"_"~toString(cellY)); - - // List of texture indices for this cell. A cell has 16x16 texture - // squares. - int ltex[16][16]; - - auto ltexData = mwland.getLTEXData(cellX, cellY); - - // A map from the global texture index to the local index for this - // cell. - int[int] textureMap; - - int texNum = 0; // Number of local indices - - // Loop through all the textures in the cell and get the indices - bool isDef = true; - for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++) - { - // Get the texture in a given cell - char[] textureName = ltexData.getTexture(tx,ty); - - // If the default texture is used, skip it. The background - // texture covers it (for now - we might change that later.) - if(textureName == "") - { - ltex[ty][tx] = -1; - continue; - } - - isDef = false; - - // Store the global index - int index = cache.addTexture(textureName); - ltex[ty][tx] = index; - - // Add the index to the map - if(!(index in textureMap)) - textureMap[index] = texNum++; - } - - assert(texNum == textureMap.length); - - // If we only found default textures, exit now. - if(isDef) - return; - - int imageRes = texSizes[1]; - int dataSize = imageRes*imageRes; - - // Number of alpha pixels per texture square - int pps = imageRes/16; - - // Make sure there are at least as many alpha pixels as there are - // textures - assert(imageRes >= 16); - assert(imageRes%16 == 0); - assert(pps >= 1); - assert(texNum >= 1); - - // Allocate the alpha images - res.allocAlphas(imageRes, texNum); - assert(res.hasAlpha() && !res.isEmpty()); - - // Write the indices to the result list - foreach(int global, int local; textureMap) - res.setAlphaTex(local, global); - - ubyte *uptr = res.data.ptr; - - // Loop over all textures again. This time, do alpha splatting. - for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++) - { - // Get the global texture index for this square, if any. - int index = ltex[ty][tx]; - if(index == -1) - continue; - - // Get the local index - index = textureMap[index]; - - // Get the offset of this square - long offs = index*dataSize + pps*(ty*imageRes + tx); - - // FIXME: Make real splatting later. This is just - // placeholder code. - - // Set alphas to full for this square - for(int y=0; y level); - assert(level > 2); - int fromSize = texSizes[level-1]; - int toSize = texSizes[level]; - - // Create a new image buffer large enough to hold the four - // sub textures - res.allocImage(fromSize*2); - - // Add the four sub-textures - for(int mi=0;mi<4;mi++) - { - ubyte[] src; - - // Use default texture if no source is present - if(maps.length == 0 || maps[mi].isEmpty()) - src = defaults[level-1].data; - else - src = maps[mi].data; - - assert(src.length == 3*fromSize*fromSize); - - // Find the sub-part of the destination buffer to write to - int x = (mi%2) * fromSize; - int y = (mi/2) * fromSize; - - // Copy the image into the new buffer - copyBox(src, res.data, fromSize, fromSize*2, x, y, 3); - } - - // Resize image if necessary - if(toSize != 2*fromSize) - res.resize(toSize); - - // Texture file name - char[] outname = res.getPNGName(maps.length == 0); - - // Save the image - res.save(outname); -} - -// Copy from one buffer into a sub-region of another buffer -void copyBox(ubyte[] src, ubyte[] dst, - int srcWidth, int dstWidth, - int dstX, int dstY, int pixSize) -{ - int fskip = srcWidth * pixSize; - int tskip = dstWidth * pixSize; - int rows = srcWidth; - int rowSize = srcWidth*pixSize; - - assert(src.length == pixSize*srcWidth*srcWidth); - assert(dst.length == pixSize*dstWidth*dstWidth); - assert(srcWidth <= dstWidth); - assert(dstX <= dstWidth-srcWidth && dstY <= dstWidth-srcWidth); - - // Source and destination pointers - ubyte *from = src.ptr; - ubyte *to = dst.ptr + dstY*tskip + dstX*pixSize; - - for(;rows>0;rows--) - { - memcpy(to, from, rowSize); - to += tskip; - from += fskip; - } -} - -// Create the mesh for this level, by merging the meshes from the -// previous levels. -void mergeMesh(GenLevelResult[] sub, ref GenLevelResult res) -{ - // How much to shift various numbers to the left at this level - // (ie. multiply by 2^shift). The height at each vertex is - // multiplied by 8 in each cell to get the final value. However, - // when we're merging two cells (in each direction), we have to - // scale down all the height values by 2 in order to fit the result - // in one byte. This means multiplying the factor by 2 for each - // level above the cell level. - int shift = res.quad.info.level - 1; - assert(shift >= 1); - assert(sub.length == 4); - - // Allocate the result buffer - res.allocMesh(65); - - MeshHolder *mh = &res.quad.meshes[0]; - MeshInfo *mi = &mh.info; - - // Basic info - mi.worldWidth = 8192 << shift; - - // Copy the mesh height from the top left mesh - mi.heightOffset = sub[0].getHeight(); - - // Output buffer - byte verts[] = mh.vertexBuffer; - - // Bytes per vertex - const int VSIZE = 5; - - // Used to calculate the max and min heights - float minh = 300000.0; - float maxh = -300000.0; - - foreach(si, s; sub) - { - int SX = si % 2; - int SY = si / 2; - - // Find the offset in the destination buffer - int dest = SX*32 + SY*65*32; - dest *= VSIZE; - - void putValue(int val) - { - assert(val >= short.min && val <= short.max); - *(cast(short*)&verts[dest]) = val; - dest += 2; - } - - if(s.hasMesh) - { - auto m = &s.quad.meshes[0]; - auto i = &m.info; - - minh = min(minh, i.minHeight); - maxh = max(maxh, i.maxHeight); - - byte[] source = m.vertexBuffer; - int src = 0; - - int getValue() - { - int s = *(cast(short*)&source[src]); - src += 2; - return s; - } - - // Loop through all the vertices in the mesh - for(int y=0;y<33;y++) - { - // Skip the first row in the mesh if there was a mesh - // above us. We assume that the previously written row - // already has the correct information. - if(y==0 && SY != 0) - { - src += 65*VSIZE; - dest += 65*VSIZE; - continue; - } - - // Handle the first vertex of the row outside the - // loop. - int height = getValue(); - - // If this isn't the very first row, sum up two row - // heights and skip the first row. - if(y!=0) - { - // Skip the rest of the row. - src += 64*VSIZE + 3; - - // Add the second height - height += getValue(); - } - - putValue(height); - - // Copy the normal - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - - // Loop through the remaining 64 vertices in this row, - // processing two at a time. - for(int x=0;x<32;x++) - { - height = getValue(); - - // Sum up the next two heights - src += 3; // Skip normal - height += getValue(); - - // Set the height - putValue(height); - - // Copy the normal - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - } - // Skip to the next row - dest += 32*VSIZE; - } - assert(src == source.length); - } - else - { - minh = min(minh, -2048); - maxh = max(maxh, -2048); - - // Set all the vertices to zero. - for(int y=0;y<33;y++) - { - if(y==0 && SY != 0) - { - dest += 65*VSIZE; - continue; - } - - for(int x=0;x<33;x++) - { - if(x==0 && SX != 0) - { - dest += VSIZE; - continue; - } - - // Zero height and vertical normal - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0x7f; - } - // Skip to the next row - dest += 32*VSIZE; - } - } - } - - mi.minHeight = minh; - mi.maxHeight = maxh; - assert(minh <= maxh); -} - -// ------- OLD CODE - use these snippets later ------- - -// About segments: -/* NOTES for the gen-phase: Was: -// This is pretty messy. Btw: 128*16 == 2048 == -// CELL_WIDTH/4 -// 65 points across one cell means 64 intervals, and 17 points - -// means 16=64/4 intervals. So IOW the number of verts when -// dividing by D is (65-1)/D + 1 = 64/D+1, which means that D -// should divide 64, that is, be a power of two < 64. - -addNewObject(Ogre::Vector3(x*16*128, 0, y*16*128), //pos -17, //size -false, //skirts -0.25f, float(x)/4.0f, float(y)/4.0f);//quad seg location -*/ - -/* This was also declared in the original code, you'll need it - when creating the cache data - - size_t vw = mWidth; // mWidth is 17 or 65 - if ( mUseSkirts ) vw += 2; // skirts are used for level 2 and up - vertCount=vw*vw; -*/ - -/** - * @brief fills the vertex buffer with data - * @todo I don't think tex co-ords are right - void calculateVertexValues() - { - int start = 0; - int end = mWidth; - - if ( mUseSkirts ) - { - --start; - ++end; - } - - for ( int y = start; y < end; y++ ) - for ( int x = start; x < end; x++ ) - { - if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) - { - // These are the skirt vertices. 'Skirts' are simply a - // wall at the edges of the mesh that goes straight down, - // cutting off the posibility that you might see 'gaps' - // between the meshes. Or at least I think that's the - // intention. - - assert(mUseSkirts); - - // 1st coordinate - if ( x < 0 ) - *verts++ = 0; - else if ( x > (mWidth-1) ) - *verts++ = (mWidth-1)*getVertexSeperation(); - else - *verts++ = x*getVertexSeperation(); - - // 2nd coordinate - *verts++ = -4096; //2048 below base sea floor height - - // 3rd coordinate - if ( y < 0 ) - *verts++ = 0; - else if ( y > (mWidth-1) ) - *verts++ = (mWidth-1)*getVertexSeperation(); - else - *verts++ = y*getVertexSeperation(); - - // No normals - for ( Ogre::uint i = 0; i < 3; i++ ) - *verts++ = 0; - - // It shouldn't matter if these go over 1 - float u = (float)(x) / (mWidth-1); - float v = (float)(y) / (mWidth-1); - *verts++ = u; - *verts++ = v; - } - else // Covered already - - void calculateIndexValues() - { - size_t vw = mWidth-1; - if ( mUseSkirts ) vw += 2; - - const size_t indexCount = (vw)*(vw)*6; - - //need to manage allocation if not null - assert(mIndices==0); - - // buffer was created here - - bool flag = false; - Ogre::uint indNum = 0; - for ( Ogre::uint y = 0; y < (vw); y+=1 ) { - for ( Ogre::uint x = 0; x < (vw); x+=1 ) { - - const int line1 = y * (vw+1) + x; - const int line2 = (y + 1) * (vw+1) + x; - - if ( flag ) { - *indices++ = line1; - *indices++ = line2; - *indices++ = line1 + 1; - - *indices++ = line1 + 1; - *indices++ = line2; - *indices++ = line2 + 1; - } else { - *indices++ = line1; - *indices++ = line2; - *indices++ = line2 + 1; - - *indices++ = line1; - *indices++ = line2 + 1; - *indices++ = line1 + 1; - } - flag = !flag; //flip tris for next time - - indNum+=6; - } - flag = !flag; //flip tries for next row - } - assert(indNum==indexCount); - //return mIndices; - } -*/ diff --git a/old_d_version/terrain/myfile.d b/old_d_version/terrain/myfile.d deleted file mode 100644 index 15239ac2eb..0000000000 --- a/old_d_version/terrain/myfile.d +++ /dev/null @@ -1,71 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (archive.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 terrain.myfile; - -import std.stream; -import std.stdio; - -// Add a couple of helper functions to the file stream -class MyFile : File -{ - this(string filename, FileMode mode = FileMode.In) - { - super(filename, mode); - } - - void fill(T)(ref T t) - { - readExact(&t, T.sizeof); - } - - void dump(T)(ref T t) - { - writeExact(&t, T.sizeof); - } - - void fillArray(T)(T[] t) - { - readExact(t.ptr, t.length*T.sizeof); - } - - void dumpArray(T)(T[] t) - { - writeExact(t.ptr, t.length*T.sizeof); - } - - void readArray(T)(ref T[] arr) - { - int size; - read(size); - assert(size < 1024*1024 && size > 0); - arr = new T[size]; - fillArray!(T)(arr); - } - - void writeArray(T)(T[] t) - { - int size = t.length; - write(size); - dumpArray!(T)(t); - } -} diff --git a/old_d_version/terrain/outbuffer.d b/old_d_version/terrain/outbuffer.d deleted file mode 100644 index c496a4787f..0000000000 --- a/old_d_version/terrain/outbuffer.d +++ /dev/null @@ -1,94 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (outbuffer.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 files provides a simple buffer class used for writing the cache - files. It lets you 'write' data to a growing memory buffer and - allows you to change the written data after the fact (since it's - retained in memory.) When you're done, you can write the entire - buffer to a stream in one operation. - */ - -module terrain.outbuffer; - -import util.regions; -import std.stream; - -class OutBuffer -{ - private: - RegionManager reg; - long used; - void[][] buffers; - - public: - this() - { - reg = new RegionManager("Outbuf", 200*1024); - } - - void reset() - { - if(buffers.length) - delete buffers; - - reg.freeAll(); - used = 0; - buffers = null; - } - - // Write everyting to a stream as one buffer - void writeTo(Stream str) - { - foreach(void[] v; buffers) - str.writeExact(v.ptr, v.length); - - reset(); - } - - // Get a pointer to a new block at least 'bytes' large, but don't - // add it to the list. - void[] reserve(size_t bytes) - { return reg.allocate(bytes); } - - // Get a new block which is 'bytes' size large. - void[] add(size_t bytes) - { - void[] p = reserve(bytes); - add(p); - return p; - } - - // Add an existing block to the write list - void add(void[] p) - { - buffers ~= p; - used += p.length; - } - - T[] write(T)(size_t num) - { - return cast(T[])add(num * T.sizeof); - } - - size_t size() { return used; } -} diff --git a/old_d_version/terrain/quad.d b/old_d_version/terrain/quad.d deleted file mode 100644 index c68afc1ca1..0000000000 --- a/old_d_version/terrain/quad.d +++ /dev/null @@ -1,286 +0,0 @@ -module terrain.quad; - -import terrain.archive; -import terrain.bindings; -import std.stdio; -import monster.vm.dbg; - -const int CELL_WIDTH = 8192; -const float SPLIT_FACTOR = 0.5; -const float UNSPLIT_FACTOR = 2.0; - -class Quad -{ - this(int cellX=0, int cellY=0, Quad parent = null) - { - scope auto _trc = new MTrace("Quad.this"); - - mCellX = cellX; - mCellY = cellY; - - // Do we have a parent? - if(parent !is null) - { - mLevel = parent.mLevel-1; - - // Coordinates relative to our parent - int relX = cellX - parent.mCellX; - int relY = cellY - parent.mCellY; - - // The coordinates give the top left corner of the quad, or our - // relative coordinates within that should always be positive. - assert(relX >= 0); - assert(relY >= 0); - - // Create a child scene node. The scene node position is given in - // world units, ie. CELL_WIDTH units per cell. - mNode = terr_createChildNode(relX*CELL_WIDTH, - relY*CELL_WIDTH, - parent.mNode); - - // Get the archive data for this quad. - mInfo = g_archive.getQuad(mCellX,mCellY,mLevel); - - // Set up the bounding box. Use MW coordinates all the - // way. - mBounds = terr_makeBounds(mInfo.minHeight, - mInfo.maxHeight, - mInfo.worldWidth, - mNode); - - float radius = mInfo.boundingRadius; - - mSplitDistance = radius * SPLIT_FACTOR; - mUnsplitDistance = radius * UNSPLIT_FACTOR; - - // Square the distances - mSplitDistance *= mSplitDistance; - mUnsplitDistance *= mUnsplitDistance; - - if(mLevel == 1) - { - // Create the terrain and leave it there. - buildTerrain(); - isStatic = true; - } - } - else - { - // No parent, this is the top-most quad. Get all the info from - // the archive. - mInfo = g_archive.rootQuad; - assert(mInfo); - - mLevel = mInfo.level; - cellX = mCellX = mInfo.cellX; - cellY = mCellY = mInfo.cellY; - - mNode = terr_createChildNode(cellX*CELL_WIDTH, - cellY*CELL_WIDTH, - null); - - // Split up - split(); - - // The root can never be unsplit - isStatic = true; - } - - assert(mLevel >= 1); - assert(mNode !is null); - - // Update the terrain. This will create the mesh or children if - // necessary. - update(); - } - - ~this() - { - scope auto _trc = new MTrace("Quad.~this"); - - // TODO: We might rewrite the code so that the quads are never - // actually destroyed, just 'inactivated' by hiding their scene - // node. We only call update on our children if we don't have a - // mesh ourselves. - if(hasMesh) - destroyTerrain(); - else if(hasChildren) - for (size_t i = 0; i < 4; i++) - delete mChildren[i]; - - terr_destroyNode(mNode); - if(mBounds !is null) - terr_killBounds(mBounds); - } - - // Remove the landscape for this quad, and create children. - void split() - { - scope auto _trc = new MTrace("split"); - // Never split a static quad or a quad that already has children. - assert(!isStatic); - assert(!hasChildren); - assert(mLevel > 1); - - if(hasMesh) - destroyTerrain(); - - // Find the cell width of our children - int cWidth = 1 << (mLevel-2); - - // Create children - for ( size_t i = 0; i < 4; ++i ) - { - if(!mInfo.hasChild[i]) - continue; - - // The cell coordinates for this child quad - int x = (i%2)*cWidth + mCellX; - int y = (i/2)*cWidth + mCellY; - - mChildren[i] = new Quad(x,y,this); - } - hasChildren = true; - } - - // Removes children and rebuilds terrain - void unsplit() - { - scope auto _trc = new MTrace("unsplit"); - // Never unsplit the root quad - assert(mLevel < g_archive.rootQuad.level); - // Never unsplit a static or quad that isn't split. - assert(!isStatic); - assert(hasChildren); - assert(!hasMesh); - - for( size_t i = 0; i < 4; i++ ) - { - delete mChildren[i]; - mChildren[i] = null; - } - - buildTerrain(); - - hasChildren = false; - } - - // Determines whether to split or unsplit the quad, and immediately - // does it. - void update() - { - scope auto _trc = new MTrace("Quad.update()"); - - // Static quads don't change - if(!isStatic) - { - assert(mUnsplitDistance > mSplitDistance); - - // Get (squared) camera distance. TODO: shouldn't this just - // be a simple vector difference from the mesh center? - assert(mBounds !is null); - float camDist = terr_getSqCamDist(mBounds); - - // No children? - if(!hasChildren) - { - // If we're close, split now. - if(camDist < mSplitDistance) - split(); - else - { - // We're not close, and don't have any children. Should we - // built terrain? - if(!hasMesh) - buildTerrain(); - return; - } - } - - // If we get here, we either had children when we entered, - // or we just performed a split. - assert(!hasMesh); - assert(hasChildren); - - // If the camera is too far away, kill the children. - if(camDist > mUnsplitDistance) - { - unsplit(); - return; - } - } - else if(!hasChildren) - return; - - // We have children and we're happy about it. Update them too. - for(int i; i < 4; ++i) - { - Quad q = mChildren[i]; - if(q !is null) q.update(); - } - } - - // Build the terrain for this quad - void buildTerrain() - { - scope auto _trc = new MTrace("buildTerrain"); - - assert(!hasMesh); - assert(!isStatic); - - // Map the terrain data into memory. - assert(mInfo); - g_archive.mapQuad(mInfo); - - // Create one mesh for each segment in the quad. TerrainMesh takes - // care of the loading. - meshList.length = mInfo.meshNum; - foreach(i, ref m; meshList) - { - MeshInfo *mi = g_archive.getMeshInfo(i); - m = terr_makeMesh(mNode, mi, mInfo.level, TEX_SCALE); - } - - hasMesh = true; - } - - void destroyTerrain() - { - scope auto _trc = new MTrace("destroyTerrain"); - - assert(hasMesh); - - foreach(m; meshList) - terr_killMesh(m); - - meshList[] = null; - hasMesh = false; - } - - private: - - // List of meshes, if any. The meshes are C++ objects. - MeshObj meshList[]; - - // Scene node. All child quads are added to this. - SceneNode mNode; - - // Bounding box, transformed to world coordinates. Used to calculate - // camera distance. - Bounds mBounds; - - float mSplitDistance,mUnsplitDistance; - - Quad mChildren[4]; - - // Contains the 'level' of this node. Level 1 is the closest and - // most detailed level - int mLevel; - int mCellX, mCellY; - - QuadInfo *mInfo; - - bool hasMesh; - bool hasChildren; - bool isStatic; // Static quads are never split or unsplit -} diff --git a/old_d_version/terrain/terrain.d b/old_d_version/terrain/terrain.d deleted file mode 100644 index 4bf03c87ba..0000000000 --- a/old_d_version/terrain/terrain.d +++ /dev/null @@ -1,103 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (terrain.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 terrain.terrain; - -import terrain.generator; -import terrain.archive; -import terrain.bindings; -import terrain.quad; -import std.file, std.stdio; - -char[] cacheDir = "cache/terrain/"; - -// Enable this to render single terrain meshes instead of the entire -// data set -//debug=singleMesh; - -void initTerrain(bool doGen) -{ - char[] fname = cacheDir ~ "landscape.cache"; - - if(!exists(fname)) - { - writefln("Cache file '%s' not found. Creating:", - fname); - doGen = true; - } - - if(doGen) - generate(fname); - - // Load the archive file - g_archive.openFile(fname); - - terr_setupRendering(); - - debug(singleMesh) - { - int X = 22; - int Y = 0; - bool next = false; - - void doQuad(int x, int y, int lev, int diffx, int diffy) - { - if(!g_archive.hasQuad(x,y,lev)) - return; - - diffx *= 8192; - diffy *= 8192; - - auto node = terr_createChildNode(20000+diffx,-60000+diffy,null); - auto info = g_archive.getQuad(x,y,lev); - g_archive.mapQuad(info); - auto mi = g_archive.getMeshInfo(0); - terr_makeMesh(node, mi, info.level, TEX_SCALE); - } - - doQuad(X,Y,1, 0,0); - doQuad(X+1,Y,1, 1,0); - doQuad(X,Y+1,1, 0,1); - doQuad(X+1,Y+1,1, 1,1); - - doQuad(X + (next?2:0),Y,2, 2,0); - - doQuad(20,Y,3, 0,2); - } - else - { - // Create the root quad - rootQuad = new Quad; - } -} - -extern(C) void d_terr_terrainUpdate() -{ - debug(singleMesh) return; - - // Update the root quad each frame. - assert(rootQuad !is null); - rootQuad.update(); -} - -Quad rootQuad;