diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e0986ae32..70979cca7 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -13,12 +13,15 @@ set(GAMEREND mwrender/mwscene.cpp mwrender/cellimp.cpp mwrender/interior.cpp + mwrender/exterior.cpp + mwrender/playerpos.cpp mwrender/sky.cpp) set(GAMEREND_HEADER mwrender/cell.hpp mwrender/cellimp.hpp mwrender/mwscene.hpp mwrender/interior.hpp + mwrender/exterior.hpp mwrender/playerpos.hpp mwrender/sky.hpp) source_group(apps\\openmw\\mwrender FILES ${GAMEREND} ${GAMEREND_HEADER}) diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 69a510803..6c96811d8 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -21,9 +21,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/apparatus.cpp b/apps/openmw/mwclass/apparatus.cpp index 007b2ca77..1d4afc096 100644 --- a/apps/openmw/mwclass/apparatus.cpp +++ b/apps/openmw/mwclass/apparatus.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 5c06077b2..1217d56a6 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/book.cpp b/apps/openmw/mwclass/book.cpp index 179a060c6..3679eb7ee 100644 --- a/apps/openmw/mwclass/book.cpp +++ b/apps/openmw/mwclass/book.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index c3bbfa9f0..07ded0264 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/container.cpp b/apps/openmw/mwclass/container.cpp index 9c1ed7a89..ea61508b8 100644 --- a/apps/openmw/mwclass/container.cpp +++ b/apps/openmw/mwclass/container.cpp @@ -21,9 +21,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index fc9515fff..2e16ac6a4 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -33,9 +33,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index c22b8005e..32b33e95a 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -29,9 +29,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 29d66d012..e4ea509f9 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index e57511452..de5c06a73 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -28,7 +28,7 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); @@ -40,7 +40,7 @@ namespace MWClass const float radius = float (ref->base->data.radius); cellRender.insertLight (r, g, b, radius); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/lockpick.cpp b/apps/openmw/mwclass/lockpick.cpp index c87d1e128..3b08ff199 100644 --- a/apps/openmw/mwclass/lockpick.cpp +++ b/apps/openmw/mwclass/lockpick.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 57dca3148..d4ffdce38 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0fdc77963..04ae468e4 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -40,7 +40,8 @@ namespace MWClass std::string headModel = "meshes\\" + environment.mWorld->getStore().bodyParts.find(headID)->model; - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); + cellRender.insertMesh (headModel); //TODO: define consts for each bodypart e.g. chest, foot, wrist... and put the parts in the @@ -51,7 +52,7 @@ namespace MWClass if (bodyPart) cellRender.insertMesh("meshes\\" + bodyPart->model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 3fe38542b..e163473f7 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/probe.cpp b/apps/openmw/mwclass/probe.cpp index bc6c4ba19..9188ae9ea 100644 --- a/apps/openmw/mwclass/probe.cpp +++ b/apps/openmw/mwclass/probe.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/repair.cpp b/apps/openmw/mwclass/repair.cpp index 253569afe..7b42bc95c 100644 --- a/apps/openmw/mwclass/repair.cpp +++ b/apps/openmw/mwclass/repair.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/static.cpp b/apps/openmw/mwclass/static.cpp index 211d239c6..33e3e0ed4 100644 --- a/apps/openmw/mwclass/static.cpp +++ b/apps/openmw/mwclass/static.cpp @@ -19,9 +19,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 5a941360a..4c14e58ea 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -24,9 +24,9 @@ namespace MWClass const std::string &model = ref->base->model; if (!model.empty()) { - cellRender.insertBegin (ref->ref); + MWRender::Rendering rendering (cellRender, ref->ref); cellRender.insertMesh ("meshes\\" + model); - ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); + ref->mData.setHandle (rendering.end (ref->mData.isEnabled())); } } diff --git a/apps/openmw/mwrender/cellimp.cpp b/apps/openmw/mwrender/cellimp.cpp index a5b4b51c7..887941f05 100644 --- a/apps/openmw/mwrender/cellimp.cpp +++ b/apps/openmw/mwrender/cellimp.cpp @@ -1,6 +1,8 @@ #include "cellimp.hpp" #include +#include +#include #include "../mwworld/class.hpp" #include "../mwworld/ptr.hpp" @@ -23,8 +25,16 @@ void insertCellRefList (CellRenderImp& cellRender, MWWorld::Environment& environ { MWWorld::Ptr ptr (&*it, &cell); - class_.insertObj (ptr, cellRender, environment); - class_.enable (ptr, environment); + try + { + class_.insertObj (ptr, cellRender, environment); + class_.enable (ptr, environment); + } + catch (const std::exception& e) + { + std::string error ("error during rendering: "); + std::cerr << error + e.what() << std::endl; + } } } } diff --git a/apps/openmw/mwrender/cellimp.hpp b/apps/openmw/mwrender/cellimp.hpp index fadba79a5..bc6f2874a 100644 --- a/apps/openmw/mwrender/cellimp.hpp +++ b/apps/openmw/mwrender/cellimp.hpp @@ -43,6 +43,38 @@ namespace MWRender void insertCell(ESMS::CellStore &cell, MWWorld::Environment& environment); }; + + /// Exception-safe rendering + class Rendering + { + CellRenderImp& mCellRender; + bool mEnd; + + // not implemented + Rendering (const Rendering&); + Rendering& operator= (const Rendering&); + + public: + + Rendering (CellRenderImp& cellRender, ESM::CellRef &ref) + : mCellRender (cellRender), mEnd (false) + { + mCellRender.insertBegin (ref); + } + + ~Rendering() + { + if (!mEnd) + mCellRender.insertEnd (false); + } + + std::string end (bool enable) + { + assert (!mEnd); + mEnd = true; + return mCellRender.insertEnd (enable); + } + }; } #endif diff --git a/apps/openmw/mwrender/exterior.cpp b/apps/openmw/mwrender/exterior.cpp new file mode 100644 index 000000000..58c783d72 --- /dev/null +++ b/apps/openmw/mwrender/exterior.cpp @@ -0,0 +1,241 @@ +#include "exterior.hpp" + +#include +#include +#include +#include +#include + +#include +#include "mwscene.hpp" + +using namespace MWRender; +using namespace Ogre; +using namespace ESMS; + +bool ExteriorCellRender::lightConst = false; +float ExteriorCellRender::lightConstValue = 0.0f; + +bool ExteriorCellRender::lightLinear = true; +int ExteriorCellRender::lightLinearMethod = 1; +float ExteriorCellRender::lightLinearValue = 3; +float ExteriorCellRender::lightLinearRadiusMult = 1; + +bool ExteriorCellRender::lightQuadratic = false; +int ExteriorCellRender::lightQuadraticMethod = 2; +float ExteriorCellRender::lightQuadraticValue = 16; +float ExteriorCellRender::lightQuadraticRadiusMult = 1; + +bool ExteriorCellRender::lightOutQuadInLin = false; + +// start inserting a new reference. + +void ExteriorCellRender::insertBegin (ESM::CellRef &ref) +{ + assert (!insert); + + // Create and place scene node for this object + insert = base->createChildSceneNode(); + + const float *f = ref.pos.pos; + insert->setPosition(f[0], f[1], f[2]); + insert->setScale(ref.scale, ref.scale, ref.scale); + + // Convert MW rotation to a quaternion: + f = ref.pos.rot; + + // Rotate around X axis + Quaternion xr(Radian(-f[0]), Vector3::UNIT_X); + + // Rotate around Y axis + Quaternion yr(Radian(-f[1]), Vector3::UNIT_Y); + + // Rotate around Z axis + Quaternion zr(Radian(-f[2]), Vector3::UNIT_Z); + + // Rotates first around z, then y, then x + insert->setOrientation(xr*yr*zr); +} + +// insert a mesh related to the most recent insertBegin call. + +void ExteriorCellRender::insertMesh(const std::string &mesh) +{ + assert (insert); + + NIFLoader::load(mesh); + MovableObject *ent = scene.getMgr()->createEntity(mesh); + insert->attachObject(ent); +} + +// insert a light related to the most recent insertBegin call. +void ExteriorCellRender::insertLight(float r, float g, float b, float radius) +{ + assert (insert); + + Ogre::Light *light = scene.getMgr()->createLight(); + light->setDiffuseColour (r, g, b); + + float cval=0.0f, lval=0.0f, qval=0.0f; + + if(lightConst) + cval = lightConstValue; + if(!lightOutQuadInLin) + { + if(lightLinear) + radius *= lightLinearRadiusMult; + if(lightQuadratic) + radius *= lightQuadraticRadiusMult; + + if(lightLinear) + lval = lightLinearValue / pow(radius, lightLinearMethod); + if(lightQuadratic) + qval = lightQuadraticValue / pow(radius, lightQuadraticMethod); + } + else + { + // FIXME: + // Do quadratic or linear, depending if we're in an exterior or interior + // cell, respectively. Ignore lightLinear and lightQuadratic. + } + + light->setAttenuation(10*radius, cval, lval, qval); + + insert->attachObject(light); +} + +// finish inserting a new reference and return a handle to it. + +std::string ExteriorCellRender::insertEnd (bool enable) +{ + assert (insert); + + std::string handle = insert->getName(); + + if (!enable) + insert->setVisible (false); + + insert = 0; + + return handle; +} + +// configure lighting according to cell + +void ExteriorCellRender::configureAmbient() +{ + ambientColor.setAsABGR (cell.cell->ambi.ambient); + setAmbientMode(); + + // Create a "sun" that shines light downwards. It doesn't look + // completely right, but leave it for now. + Ogre::Light *light = scene.getMgr()->createLight(); + Ogre::ColourValue colour; + colour.setAsABGR (cell.cell->ambi.sunlight); + light->setDiffuseColour (colour); + light->setType(Ogre::Light::LT_DIRECTIONAL); + light->setDirection(0,-1,0); +} + +// configure fog according to cell +void ExteriorCellRender::configureFog() +{ + Ogre::ColourValue color; + color.setAsABGR (cell.cell->ambi.fog); + + float high = 4500 + 9000 * (1-cell.cell->ambi.fogDensity); + float low = 200; + + scene.getMgr()->setFog (FOG_LINEAR, color, 0, low, high); + scene.getCamera()->setFarClipDistance (high + 10); + scene.getViewport()->setBackgroundColour (color); +} + +void ExteriorCellRender::setAmbientMode() +{ + switch (ambientMode) + { + case 0: + + scene.getMgr()->setAmbientLight(ambientColor); + break; + + case 1: + + scene.getMgr()->setAmbientLight(0.7f*ambientColor + 0.3f*ColourValue(1,1,1)); + break; + + case 2: + + scene.getMgr()->setAmbientLight(ColourValue(1,1,1)); + break; + } +} + +void ExteriorCellRender::show() +{ + base = scene.getRoot()->createChildSceneNode(); + + configureAmbient(); + configureFog(); + + insertCell(cell, mEnvironment); +} + +void ExteriorCellRender::hide() +{ + if(base) + base->setVisible(false); +} + +void ExteriorCellRender::destroy() +{ + if(base) + { + base->removeAndDestroyAllChildren(); + scene.getMgr()->destroySceneNode(base); + } + + base = NULL; +} + +// Switch through lighting modes. + +void ExteriorCellRender::toggleLight() +{ + if (ambientMode==2) + ambientMode = 0; + else + ++ambientMode; + + switch (ambientMode) + { + case 0: std::cout << "Setting lights to normal\n"; break; + case 1: std::cout << "Turning the lights up\n"; break; + case 2: std::cout << "Turning the lights to full\n"; break; + } + + setAmbientMode(); +} + +void ExteriorCellRender::enable (const std::string& handle) +{ + if (!handle.empty()) + scene.getMgr()->getSceneNode (handle)->setVisible (true); +} + +void ExteriorCellRender::disable (const std::string& handle) +{ + if (!handle.empty()) + scene.getMgr()->getSceneNode (handle)->setVisible (false); +} + +void ExteriorCellRender::deleteObject (const std::string& handle) +{ + if (!handle.empty()) + { + Ogre::SceneNode *node = scene.getMgr()->getSceneNode (handle); + node->removeAndDestroyAllChildren(); + scene.getMgr()->destroySceneNode (node); + } +} diff --git a/apps/openmw/mwrender/exterior.hpp b/apps/openmw/mwrender/exterior.hpp new file mode 100644 index 000000000..7de027672 --- /dev/null +++ b/apps/openmw/mwrender/exterior.hpp @@ -0,0 +1,114 @@ +#ifndef _GAME_RENDER_EXTERIOR_H +#define _GAME_RENDER_EXTERIOR_H + +#include "cell.hpp" +#include "cellimp.hpp" + +#include "OgreColourValue.h" + +namespace Ogre +{ + class SceneNode; +} + +namespace MWWorld +{ + class Environment; +} + +namespace MWRender +{ + class MWScene; + + /** + This class is responsible for inserting meshes and other + rendering objects from the given cell into the given rendering + scene. + */ + + class ExteriorCellRender : public CellRender, private CellRenderImp + { + + static bool lightConst; + static float lightConstValue; + + static bool lightLinear; + static int lightLinearMethod; + static float lightLinearValue; + static float lightLinearRadiusMult; + + static bool lightQuadratic; + static int lightQuadraticMethod; + static float lightQuadraticValue; + static float lightQuadraticRadiusMult; + + static bool lightOutQuadInLin; + + ESMS::CellStore &cell; + MWWorld::Environment &mEnvironment; + MWScene &scene; + + /// The scene node that contains all objects belonging to this + /// cell. + Ogre::SceneNode *base; + + Ogre::SceneNode *insert; + + // 0 normal, 1 more bright, 2 max + int ambientMode; + + Ogre::ColourValue ambientColor; + + /// start inserting a new reference. + virtual void insertBegin (ESM::CellRef &ref); + + /// insert a mesh related to the most recent insertBegin call. + virtual void insertMesh(const std::string &mesh); + + /// insert a light related to the most recent insertBegin call. + virtual void insertLight(float r, float g, float b, float radius); + + /// finish inserting a new reference and return a handle to it. + virtual std::string insertEnd (bool Enable); + + /// configure lighting according to cell + void configureAmbient(); + + /// configure fog according to cell + void configureFog(); + + void setAmbientMode(); + + public: + + ExteriorCellRender(ESMS::CellStore &_cell, MWWorld::Environment& environment, + MWScene &_scene) + : cell(_cell), mEnvironment (environment), scene(_scene), base(NULL), insert(NULL), ambientMode (0) {} + + virtual ~ExteriorCellRender() { destroy(); } + + /// Make the cell visible. Load the cell if necessary. + virtual void show(); + + /// Remove the cell from rendering, but don't remove it from + /// memory. + virtual void hide(); + + /// Destroy all rendering objects connected with this cell. + virtual void destroy(); // comment by Zini: shouldn't this go into the destructor? + + /// Switch through lighting modes. + void toggleLight(); + + /// Make the reference with the given handle visible. + virtual void enable (const std::string& handle); + + /// Make the reference with the given handle invisible. + virtual void disable (const std::string& handle); + + /// Remove the reference with the given handle permanently from the scene. + virtual void deleteObject (const std::string& handle); + }; +} + +#endif diff --git a/apps/openmw/mwrender/interior.hpp b/apps/openmw/mwrender/interior.hpp index 8d69ca061..ba5089eb5 100644 --- a/apps/openmw/mwrender/interior.hpp +++ b/apps/openmw/mwrender/interior.hpp @@ -24,8 +24,6 @@ namespace MWRender This class is responsible for inserting meshes and other rendering objects from the given cell into the given rendering scene. - - TODO FIXME: Doesn't do full cleanup yet. */ class InteriorCellRender : public CellRender, private CellRenderImp diff --git a/apps/openmw/mwrender/mwscene.cpp b/apps/openmw/mwrender/mwscene.cpp index 15163d7a2..133607797 100644 --- a/apps/openmw/mwrender/mwscene.cpp +++ b/apps/openmw/mwrender/mwscene.cpp @@ -9,6 +9,10 @@ #include "OgreCamera.h" #include "OgreTextureManager.h" +#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone +#include "../mwworld/ptr.hpp" +#include + using namespace MWRender; using namespace Ogre; @@ -36,7 +40,7 @@ MWScene::MWScene(OEngine::Render::OgreRenderer &_rend) mRaySceneQuery = rend.getScene()->createRayQuery(Ray()); } -std::pair MWScene::getFacedHandle() +std::pair MWScene::getFacedHandle (MWWorld::World& world) { std::string handle = ""; float distance = -1; @@ -57,10 +61,14 @@ std::pair MWScene::getFacedHandle() { // there seem to be omnipresent objects like the caelum sky dom, // the distance of these objects is always 0 so this if excludes these - // TODO: Check if the object can be focused (ignore walls etc.. - // in this state of openmw not possible) if ( itr->movable && itr->distance >= 0.1) { + // horrible hack to exclude statics. this must be removed as soon as a replacement for the + // AABB raycasting is implemented (we should not ignore statics) + MWWorld::Ptr ptr = world.getPtrViaHandle (itr->movable->getParentSceneNode()->getName()); + if (ptr.getType()==typeid (ESM::Static)) + break; + if ( nearest == result.end() ) //if no object is set { nearest = itr; @@ -80,4 +88,3 @@ std::pair MWScene::getFacedHandle() return std::pair(handle, distance); } - diff --git a/apps/openmw/mwrender/mwscene.hpp b/apps/openmw/mwrender/mwscene.hpp index ce04efc92..9d2a51bae 100644 --- a/apps/openmw/mwrender/mwscene.hpp +++ b/apps/openmw/mwrender/mwscene.hpp @@ -13,6 +13,11 @@ namespace Ogre class RaySceneQuery; } +namespace MWWorld +{ + class World; +} + namespace MWRender { /** Class responsible for Morrowind-specific interfaces to OGRE. @@ -43,7 +48,7 @@ namespace MWRender //pair //name is empty and distance = -1 if there is no object which //can be faced - std::pair getFacedHandle(); + std::pair getFacedHandle (MWWorld::World& world); }; } diff --git a/apps/openmw/mwrender/playerpos.cpp b/apps/openmw/mwrender/playerpos.cpp new file mode 100644 index 000000000..4a5e6628f --- /dev/null +++ b/apps/openmw/mwrender/playerpos.cpp @@ -0,0 +1,18 @@ + +#include "playerpos.hpp" + +#include "../mwworld/world.hpp" + +namespace MWRender +{ + void PlayerPos::setPos(float x, float y, float z, bool updateCamera) + { + mWorld.moveObject (getPlayer(), x, y, z); + + if (updateCamera) + camera->setPosition (Ogre::Vector3 ( + mPlayer.ref.pos.pos[0], + mPlayer.ref.pos.pos[2], + -mPlayer.ref.pos.pos[1])); + } +} diff --git a/apps/openmw/mwrender/playerpos.hpp b/apps/openmw/mwrender/playerpos.hpp index 48a4fd8d5..59a0e1b41 100644 --- a/apps/openmw/mwrender/playerpos.hpp +++ b/apps/openmw/mwrender/playerpos.hpp @@ -8,6 +8,11 @@ #include "../mwworld/refdata.hpp" #include "../mwworld/ptr.hpp" +namespace MWWorld +{ + class World; +} + namespace MWRender { // This class keeps track of the player position. It takes care of @@ -18,31 +23,19 @@ namespace MWRender ESMS::LiveCellRef mPlayer; MWWorld::Ptr::CellStore *mCellStore; Ogre::Camera *camera; + MWWorld::World& mWorld; public: - PlayerPos(Ogre::Camera *cam, const ESM::NPC *player) : - mCellStore (0), camera(cam) + PlayerPos(Ogre::Camera *cam, const ESM::NPC *player, MWWorld::World& world) : + mCellStore (0), camera(cam), mWorld (world) { mPlayer.base = player; mPlayer.ref.pos.pos[0] = mPlayer.ref.pos.pos[1] = mPlayer.ref.pos.pos[2] = 0; } // Set the player position. Uses Morrowind coordinates. - void setPos(float _x, float _y, float _z, bool updateCamera = false) - { - mPlayer.ref.pos.pos[0] = _x; - mPlayer.ref.pos.pos[1] = _y; - mPlayer.ref.pos.pos[2] = _z; + void setPos(float _x, float _y, float _z, bool updateCamera = false); - if (updateCamera) - camera->setPosition (Ogre::Vector3 ( - mPlayer.ref.pos.pos[0], - mPlayer.ref.pos.pos[2], - mPlayer.ref.pos.pos[1])); - - // TODO: Update sound listener - } - void setCell (MWWorld::Ptr::CellStore *cellStore) { mCellStore = cellStore; @@ -54,7 +47,6 @@ namespace MWRender // orientation. After the call, the new position is returned. void moveRel(float &relX, float &relY, float &relZ) { - // TODO: Update mPlayer state using namespace Ogre; // Move camera relative to its own direction @@ -75,7 +67,7 @@ namespace MWRender // Set the position setPos(relX, relY, relZ); } - + MWWorld::Ptr getPlayer() { MWWorld::Ptr ptr (&mPlayer, mCellStore); diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index 963cb5af8..7d633b59d 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -18,50 +18,79 @@ namespace MWScript class OpCellChanged : public Interpreter::Opcode0 { public: - + virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); - + runtime.push (context.getWorld().hasCellChanged() ? 1 : 0); - } + } }; class OpCOC : public Interpreter::Opcode0 { public: - + virtual void execute (Interpreter::Runtime& runtime) { InterpreterContext& context = static_cast (runtime.getContext()); - + std::string cell = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - + runtime.pop(); + ESM::Position pos; pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; context.getWorld().changeCell (cell, pos); - } + } + }; + + class OpCOE : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context + = static_cast (runtime.getContext()); + + Interpreter::Type_Integer x = runtime[0].mInteger; + runtime.pop(); + + Interpreter::Type_Integer y = runtime[0].mInteger; + runtime.pop(); + + ESM::Position pos; + + context.getWorld().indexToPosition (x, y, pos.pos[0], pos.pos[1]); + pos.pos[2] = 0; + + pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; + + context.getWorld().changeToExteriorCell (pos); + } }; - + const int opcodeCellChanged = 0x2000000; const int opcodeCOC = 0x2000026; - + const int opcodeCOE = 0x200007c; + void registerExtensions (Compiler::Extensions& extensions) { extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); extensions.registerInstruction ("coc", "S", opcodeCOC); extensions.registerInstruction ("centeroncell", "S", opcodeCOC); + extensions.registerInstruction ("coe", "ll", opcodeCOE); + extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); } - + void installOpcodes (Interpreter::Interpreter& interpreter) { interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged); interpreter.installSegment5 (opcodeCOC, new OpCOC); + interpreter.installSegment5 (opcodeCOE, new OpCOE); } } } - diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 0556450e6..5eee9dd55 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -88,6 +88,10 @@ op 0x2000078: GetItemCount op 0x2000079: GetItemCount, explicit reference op 0x200007a: RemoveItem op 0x200007b: RemoveItem, explicit reference +<<<<<<< HEAD:apps/openmw/mwscript/docs/vmformat.txt +op 0x200007c: COC +opcodes 0x200007d-0x3ffffff unused +======= op 0x200007c: GetAiPackageDone op 0x200007d: GetAiPackageDone, explicit reference op 0x200007e-0x2000084: Enable Controls @@ -95,3 +99,4 @@ op 0x2000085-0x200008b: Disable Controls op 0x200008c: Unlock op 0x200008d: Unlock, explicit reference opcodes 0x200008e-0x3ffffff unused +>>>>>>> master:apps/openmw/mwscript/docs/vmformat.txt diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index f757b70be..3db294818 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -13,6 +13,9 @@ namespace MWWorld void ActionTeleportPlayer::execute (Environment& environment) { - environment.mWorld->changeCell (mCellName, mPosition); + if (mCellName.empty()) + environment.mWorld->changeToExteriorCell (mPosition); + else + environment.mWorld->changeCell (mCellName, mPosition); } } diff --git a/apps/openmw/mwworld/actionteleport.hpp b/apps/openmw/mwworld/actionteleport.hpp index 78a55fba2..e4c972a9e 100644 --- a/apps/openmw/mwworld/actionteleport.hpp +++ b/apps/openmw/mwworld/actionteleport.hpp @@ -17,6 +17,7 @@ namespace MWWorld public: ActionTeleportPlayer (const std::string& cellName, const ESM::Position& position); + ///< If cellName is empty, an exterior cell is asumed. virtual void execute (Environment& environment); }; diff --git a/apps/openmw/mwworld/world.cpp b/apps/openmw/mwworld/world.cpp index 4ef20c7ff..496da44ef 100644 --- a/apps/openmw/mwworld/world.cpp +++ b/apps/openmw/mwworld/world.cpp @@ -8,6 +8,7 @@ #include "../mwrender/sky.hpp" #include "../mwrender/interior.hpp" +#include "../mwrender/exterior.hpp" #include "../mwmechanics/mechanicsmanager.hpp" @@ -249,6 +250,66 @@ namespace MWWorld throw std::runtime_error ("month out of range"); } + void World::removeScripts (Ptr::CellStore *cell) + { + ScriptList::iterator iter = mLocalScripts.begin(); + + while (iter!=mLocalScripts.end()) + { + if (iter->second.getCell()==cell) + mLocalScripts.erase (iter++); + else + ++iter; + } + } + + void World::unloadCell (CellRenderCollection::iterator iter) + { + removeScripts (iter->first); + mEnvironment.mMechanicsManager->dropActors (iter->first); + iter->second->destroy(); + mEnvironment.mSoundManager->stopSound (iter->first); + delete iter->second; + mActiveCells.erase (iter); + } + + void World::loadCell (Ptr::CellStore *cell, MWRender::CellRender *render) + { + // register local scripts + insertInteriorScripts (*cell); + + // This connects the cell data with the rendering scene. + std::pair result = + mActiveCells.insert (std::make_pair (cell, render)); + + if (result.second) + { + // Load the cell and insert it into the renderer + result.first->second->show(); + } + } + + void World::playerCellChange (Ptr::CellStore *cell, const ESM::Position& position) + { + mPlayerPos->setPos (position.pos[0], position.pos[1], position.pos[2], true); + mPlayerPos->setCell (cell); + // TODO orientation + + mEnvironment.mMechanicsManager->addActor (mPlayerPos->getPlayer()); + mEnvironment.mMechanicsManager->watchActor (mPlayerPos->getPlayer()); + } + + + void World::adjustSky() + { + if (mSky) + { + toggleSky(); + // TODO set weather + toggleSky(); + } + } + World::World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir, const std::string& master, bool newGame, Environment& environment) : mSkyManager (0), mScene (renderer), mPlayerPos (0), mCurrentCell (0), mGlobalVariables (0), @@ -263,7 +324,7 @@ namespace MWWorld mEsm.open (masterPath.file_string()); mStore.load (mEsm); - mPlayerPos = new MWRender::PlayerPos (mScene.getCamera(), mStore.npcs.find ("player")); + mPlayerPos = new MWRender::PlayerPos (mScene.getCamera(), mStore.npcs.find ("player"), *this); // global variables mGlobalVariables = new Globals (mStore); @@ -522,57 +583,114 @@ namespace MWWorld void World::changeCell (const std::string& cellName, const ESM::Position& position) { + // remove active + CellRenderCollection::iterator active = mActiveCells.begin(); + + while (active!=mActiveCells.end()) + { + unloadCell (active++); + } + // Load cell. mInteriors[cellName].loadInt (cellName, mStore, mEsm); Ptr::CellStore *cell = &mInteriors[cellName]; + loadCell (cell, new MWRender::InteriorCellRender (*cell, mEnvironment, mScene)); + + // adjust player + mCurrentCell = cell; + playerCellChange (cell, position); + + // Sky system + adjustSky(); + + mCellChanged = true; + } + + void World::changeCell (int X, int Y, const ESM::Position& position) + { // remove active CellRenderCollection::iterator active = mActiveCells.begin(); - if (active!=mActiveCells.end()) + while (active!=mActiveCells.end()) { - mEnvironment.mMechanicsManager->dropActors (active->first); - active->second->destroy(); - mEnvironment.mSoundManager->stopSound (active->first); - delete active->second; - mActiveCells.erase (active); + if (!(active->first->cell->data.flags & ESM::Cell::Interior)) + { + if (std::abs (X-active->first->cell->data.gridX)<=1 && + std::abs (Y-active->first->cell->data.gridY)<=1) + { + // keep cells within the new 3x3 grid + ++active; + continue; + } + } + + unloadCell (active++); } - // register local scripts - mLocalScripts.clear(); // FIXME won't work with exteriors - insertInteriorScripts (*cell); + // Load cells + for (int x=X-1; x<=X+1; ++x) + for (int y=Y-1; y<=Y+1; ++y) + { + CellRenderCollection::iterator iter = mActiveCells.begin(); - // adjust player - mPlayerPos->setPos (position.pos[0], position.pos[1], position.pos[2], true); - mPlayerPos->setCell (cell); - // TODO orientation + while (iter!=mActiveCells.end()) + { + assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); - // This connects the cell data with the rendering scene. - std::pair result = - mActiveCells.insert (std::make_pair (cell, - new MWRender::InteriorCellRender (*cell, mEnvironment, mScene))); + if (x==iter->first->cell->data.gridX && + y==iter->first->cell->data.gridY) + break; - if (result.second) + ++iter; + } + + if (iter==mActiveCells.end()) + { + mExteriors[std::make_pair (x, y)].loadExt (x, y, mStore, mEsm); + Ptr::CellStore *cell = &mExteriors[std::make_pair (x, y)]; + + loadCell (cell, new MWRender::ExteriorCellRender (*cell, mEnvironment, mScene)); + } + } + + // find current cell + CellRenderCollection::iterator iter = mActiveCells.begin(); + + while (iter!=mActiveCells.end()) { - // Load the cell and insert it into the renderer - result.first->second->show(); + assert (!(iter->first->cell->data.flags & ESM::Cell::Interior)); + + if (X==iter->first->cell->data.gridX && + Y==iter->first->cell->data.gridY) + break; + + ++iter; } - // Actors - mEnvironment.mMechanicsManager->addActor (mPlayerPos->getPlayer()); - mEnvironment.mMechanicsManager->watchActor (mPlayerPos->getPlayer()); + assert (iter!=mActiveCells.end()); + + mCurrentCell = iter->first; + + // adjust player + playerCellChange (&mExteriors[std::make_pair (X, Y)], position); // Sky system - if (mSky) - { - toggleSky(); - // TODO set weather - toggleSky(); - } + adjustSky(); mCellChanged = true; } + void World::changeToExteriorCell (const ESM::Position& position) + { + int x = 0; + int y = 0; + + positionToIndex (position.pos[0], position.pos[1], x, y); + + changeCell (x, y, position); + } + void World::markCellAsUnchanged() { mCellChanged = false; @@ -580,7 +698,7 @@ namespace MWWorld std::string World::getFacedHandle() { - std::pair result = mScene.getFacedHandle(); + std::pair result = mScene.getFacedHandle (*this); if (result.first.empty() || result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) @@ -608,4 +726,56 @@ namespace MWWorld } } } + + void World::moveObject (Ptr ptr, float x, float y, float z) + { + ptr.getCellRef().pos.pos[0] = x; + ptr.getCellRef().pos.pos[1] = y; + ptr.getCellRef().pos.pos[2] = z; + + if (ptr==mPlayerPos->getPlayer()) + { + if (mCurrentCell) + { + if (!(mCurrentCell->cell->data.flags & ESM::Cell::Interior)) + { + // exterior -> adjust loaded cells + int cellX = 0; + int cellY = 0; + + positionToIndex (x, y, cellX, cellY); + + if (mCurrentCell->cell->data.gridX!=cellX || mCurrentCell->cell->data.gridY!=cellY) + { + changeCell (cellX, cellY, mPlayerPos->getPlayer().getCellRef().pos); + } + } + } + } + + // TODO cell change for non-player ref + } + + void World::indexToPosition (int cellX, int cellY, float &x, float &y) const + { + const int cellSize = 8192; + + x = cellSize * cellX; + y = cellSize * cellY; + } + + void World::positionToIndex (float x, float y, int &cellX, int &cellY) const + { + const int cellSize = 8192; + + cellX = static_cast (x/cellSize); + + if (x<0) + --cellX; + + cellY = static_cast (y/cellSize); + + if (y<0) + --cellY; + } } diff --git a/apps/openmw/mwworld/world.hpp b/apps/openmw/mwworld/world.hpp index 2a7b8009a..2ff2d3ccc 100644 --- a/apps/openmw/mwworld/world.hpp +++ b/apps/openmw/mwworld/world.hpp @@ -41,7 +41,7 @@ namespace MWWorld { public: - typedef std::vector > ScriptList; + typedef std::list > ScriptList; private: @@ -56,6 +56,7 @@ namespace MWWorld ESM::ESMReader mEsm; ESMS::ESMStore mStore; std::map mInteriors; + std::map, Ptr::CellStore> mExteriors; ScriptList mLocalScripts; MWWorld::Globals *mGlobalVariables; bool mSky; @@ -76,6 +77,16 @@ namespace MWWorld int getDaysPerMonth (int month) const; + void removeScripts (Ptr::CellStore *cell); + + void unloadCell (CellRenderCollection::iterator iter); + + void loadCell (Ptr::CellStore *cell, MWRender::CellRender *render); + + void playerCellChange (Ptr::CellStore *cell, const ESM::Position& position); + + void adjustSky(); + public: World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& master, @@ -130,12 +141,24 @@ namespace MWWorld void changeCell (const std::string& cellName, const ESM::Position& position); ///< works only for interior cells currently. + void changeCell (int X, int Y, const ESM::Position& position); + + void changeToExteriorCell (const ESM::Position& position); + void markCellAsUnchanged(); std::string getFacedHandle(); ///< Return handle of the object the player is looking at void deleteObject (Ptr ptr); + + void moveObject (Ptr ptr, float x, float y, float z); + + void indexToPosition (int cellX, int cellY, float &x, float &y) const; + ///< Convert cell numbers to position. + + void positionToIndex (float x, float y, int &cellX, int &cellY) const; + ///< Convert position to cell numbers }; } diff --git a/components/esm_store/cell_store.hpp b/components/esm_store/cell_store.hpp index a150ec3d3..6acd81dce 100644 --- a/components/esm_store/cell_store.hpp +++ b/components/esm_store/cell_store.hpp @@ -123,7 +123,14 @@ namespace ESMS /** Ditto for exterior cell. */ void loadExt(int X, int Y, const ESMStore &store, ESMReader &esm) { + std::cout << "loading exterior cell '" << X << ", " << Y << "'\n"; + cell = store.cells.searchExt (X, Y); + + if(cell == NULL) + throw std::runtime_error("Exterior cell not found"); + + loadRefs(store, esm); } private: diff --git a/components/esm_store/reclists.hpp b/components/esm_store/reclists.hpp index 3cdb7f7b2..3711a20b1 100644 --- a/components/esm_store/reclists.hpp +++ b/components/esm_store/reclists.hpp @@ -194,6 +194,21 @@ namespace ESMS return it->second; } + const Cell *searchExt (int x, int y) const + { + ExtCells::const_iterator it = extCells.find (x); + + if (it==extCells.end()) + return 0; + + ExtCellsCol::const_iterator it2 = it->second.find (y); + + if (it2 == it->second.end()) + return 0; + + return it2->second; + } + void load(ESMReader &esm, const std::string &id) { using namespace std;