1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-03 16:39:41 +00:00
This commit is contained in:
Nicolay Korslund 2010-09-10 12:07:15 +02:00
commit 91e8590288
36 changed files with 786 additions and 112 deletions

View file

@ -13,12 +13,15 @@ set(GAMEREND
mwrender/mwscene.cpp mwrender/mwscene.cpp
mwrender/cellimp.cpp mwrender/cellimp.cpp
mwrender/interior.cpp mwrender/interior.cpp
mwrender/exterior.cpp
mwrender/playerpos.cpp
mwrender/sky.cpp) mwrender/sky.cpp)
set(GAMEREND_HEADER set(GAMEREND_HEADER
mwrender/cell.hpp mwrender/cell.hpp
mwrender/cellimp.hpp mwrender/cellimp.hpp
mwrender/mwscene.hpp mwrender/mwscene.hpp
mwrender/interior.hpp mwrender/interior.hpp
mwrender/exterior.hpp
mwrender/playerpos.hpp mwrender/playerpos.hpp
mwrender/sky.hpp) mwrender/sky.hpp)
source_group(apps\\openmw\\mwrender FILES ${GAMEREND} ${GAMEREND_HEADER}) source_group(apps\\openmw\\mwrender FILES ${GAMEREND} ${GAMEREND_HEADER})

View file

@ -21,9 +21,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -21,9 +21,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -33,9 +33,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -29,9 +29,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -28,7 +28,7 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
@ -40,7 +40,7 @@ namespace MWClass
const float radius = float (ref->base->data.radius); const float radius = float (ref->base->data.radius);
cellRender.insertLight (r, g, b, radius); cellRender.insertLight (r, g, b, radius);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -40,7 +40,8 @@ namespace MWClass
std::string headModel = "meshes\\" + std::string headModel = "meshes\\" +
environment.mWorld->getStore().bodyParts.find(headID)->model; environment.mWorld->getStore().bodyParts.find(headID)->model;
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh (headModel); cellRender.insertMesh (headModel);
//TODO: define consts for each bodypart e.g. chest, foot, wrist... and put the parts in the //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) if (bodyPart)
cellRender.insertMesh("meshes\\" + bodyPart->model); 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 void Npc::enable (const MWWorld::Ptr& ptr, MWWorld::Environment& environment) const

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -19,9 +19,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -24,9 +24,9 @@ namespace MWClass
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
if (!model.empty()) if (!model.empty())
{ {
cellRender.insertBegin (ref->ref); MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model); cellRender.insertMesh ("meshes\\" + model);
ref->mData.setHandle (cellRender.insertEnd (ref->mData.isEnabled())); ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
} }
} }

View file

@ -1,6 +1,8 @@
#include "cellimp.hpp" #include "cellimp.hpp"
#include <cassert> #include <cassert>
#include <iostream>
#include <exception>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
@ -23,8 +25,16 @@ void insertCellRefList (CellRenderImp& cellRender, MWWorld::Environment& environ
{ {
MWWorld::Ptr ptr (&*it, &cell); MWWorld::Ptr ptr (&*it, &cell);
class_.insertObj (ptr, cellRender, environment); try
class_.enable (ptr, environment); {
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;
}
} }
} }
} }

View file

@ -43,6 +43,38 @@ namespace MWRender
void insertCell(ESMS::CellStore<MWWorld::RefData> &cell, MWWorld::Environment& environment); void insertCell(ESMS::CellStore<MWWorld::RefData> &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 #endif

View file

@ -0,0 +1,241 @@
#include "exterior.hpp"
#include <OgreEntity.h>
#include <OgreLight.h>
#include <OgreSceneNode.h>
#include <OgreCamera.h>
#include <OgreSceneManager.h>
#include <components/nifogre/ogre_nif_loader.hpp>
#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);
}
}

View file

@ -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<MWWorld::RefData> &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<MWWorld::RefData> &_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

View file

@ -24,8 +24,6 @@ namespace MWRender
This class is responsible for inserting meshes and other This class is responsible for inserting meshes and other
rendering objects from the given cell into the given rendering rendering objects from the given cell into the given rendering
scene. scene.
TODO FIXME: Doesn't do full cleanup yet.
*/ */
class InteriorCellRender : public CellRender, private CellRenderImp class InteriorCellRender : public CellRender, private CellRenderImp

View file

@ -9,6 +9,10 @@
#include "OgreCamera.h" #include "OgreCamera.h"
#include "OgreTextureManager.h" #include "OgreTextureManager.h"
#include "../mwworld/world.hpp" // these includes can be removed once the static-hack is gone
#include "../mwworld/ptr.hpp"
#include <components/esm/loadstat.hpp>
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -36,7 +40,7 @@ MWScene::MWScene(OEngine::Render::OgreRenderer &_rend)
mRaySceneQuery = rend.getScene()->createRayQuery(Ray()); mRaySceneQuery = rend.getScene()->createRayQuery(Ray());
} }
std::pair<std::string, float> MWScene::getFacedHandle() std::pair<std::string, float> MWScene::getFacedHandle (MWWorld::World& world)
{ {
std::string handle = ""; std::string handle = "";
float distance = -1; float distance = -1;
@ -57,10 +61,14 @@ std::pair<std::string, float> MWScene::getFacedHandle()
{ {
// there seem to be omnipresent objects like the caelum sky dom, // there seem to be omnipresent objects like the caelum sky dom,
// the distance of these objects is always 0 so this if excludes these // 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) 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 if ( nearest == result.end() ) //if no object is set
{ {
nearest = itr; nearest = itr;
@ -80,4 +88,3 @@ std::pair<std::string, float> MWScene::getFacedHandle()
return std::pair<std::string, float>(handle, distance); return std::pair<std::string, float>(handle, distance);
} }

View file

@ -13,6 +13,11 @@ namespace Ogre
class RaySceneQuery; class RaySceneQuery;
} }
namespace MWWorld
{
class World;
}
namespace MWRender namespace MWRender
{ {
/** Class responsible for Morrowind-specific interfaces to OGRE. /** Class responsible for Morrowind-specific interfaces to OGRE.
@ -43,7 +48,7 @@ namespace MWRender
//pair<name, distance> //pair<name, distance>
//name is empty and distance = -1 if there is no object which //name is empty and distance = -1 if there is no object which
//can be faced //can be faced
std::pair<std::string, float> getFacedHandle(); std::pair<std::string, float> getFacedHandle (MWWorld::World& world);
}; };
} }

View file

@ -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]));
}
}

View file

@ -8,6 +8,11 @@
#include "../mwworld/refdata.hpp" #include "../mwworld/refdata.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWWorld
{
class World;
}
namespace MWRender namespace MWRender
{ {
// This class keeps track of the player position. It takes care of // This class keeps track of the player position. It takes care of
@ -18,31 +23,19 @@ namespace MWRender
ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> mPlayer; ESMS::LiveCellRef<ESM::NPC, MWWorld::RefData> mPlayer;
MWWorld::Ptr::CellStore *mCellStore; MWWorld::Ptr::CellStore *mCellStore;
Ogre::Camera *camera; Ogre::Camera *camera;
MWWorld::World& mWorld;
public: public:
PlayerPos(Ogre::Camera *cam, const ESM::NPC *player) : PlayerPos(Ogre::Camera *cam, const ESM::NPC *player, MWWorld::World& world) :
mCellStore (0), camera(cam) mCellStore (0), camera(cam), mWorld (world)
{ {
mPlayer.base = player; mPlayer.base = player;
mPlayer.ref.pos.pos[0] = mPlayer.ref.pos.pos[1] = mPlayer.ref.pos.pos[2] = 0; mPlayer.ref.pos.pos[0] = mPlayer.ref.pos.pos[1] = mPlayer.ref.pos.pos[2] = 0;
} }
// Set the player position. Uses Morrowind coordinates. // Set the player position. Uses Morrowind coordinates.
void setPos(float _x, float _y, float _z, bool updateCamera = false) 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;
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) void setCell (MWWorld::Ptr::CellStore *cellStore)
{ {
mCellStore = cellStore; mCellStore = cellStore;
@ -54,7 +47,6 @@ namespace MWRender
// orientation. After the call, the new position is returned. // orientation. After the call, the new position is returned.
void moveRel(float &relX, float &relY, float &relZ) void moveRel(float &relX, float &relY, float &relZ)
{ {
// TODO: Update mPlayer state
using namespace Ogre; using namespace Ogre;
// Move camera relative to its own direction // Move camera relative to its own direction
@ -75,7 +67,7 @@ namespace MWRender
// Set the position // Set the position
setPos(relX, relY, relZ); setPos(relX, relY, relZ);
} }
MWWorld::Ptr getPlayer() MWWorld::Ptr getPlayer()
{ {
MWWorld::Ptr ptr (&mPlayer, mCellStore); MWWorld::Ptr ptr (&mPlayer, mCellStore);

View file

@ -18,50 +18,79 @@ namespace MWScript
class OpCellChanged : public Interpreter::Opcode0 class OpCellChanged : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext()); = static_cast<InterpreterContext&> (runtime.getContext());
runtime.push (context.getWorld().hasCellChanged() ? 1 : 0); runtime.push (context.getWorld().hasCellChanged() ? 1 : 0);
} }
}; };
class OpCOC : public Interpreter::Opcode0 class OpCOC : public Interpreter::Opcode0
{ {
public: public:
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
InterpreterContext& context InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext()); = static_cast<InterpreterContext&> (runtime.getContext());
std::string cell = runtime.getStringLiteral (runtime[0].mInteger); std::string cell = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
ESM::Position pos; ESM::Position pos;
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0; pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.rot[2] = 0; pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;
context.getWorld().changeCell (cell, pos); context.getWorld().changeCell (cell, pos);
} }
}; };
class OpCOE : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (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 opcodeCellChanged = 0x2000000;
const int opcodeCOC = 0x2000026; const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x200007c;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged); extensions.registerFunction ("cellchanged", 'l', "", opcodeCellChanged);
extensions.registerInstruction ("coc", "S", opcodeCOC); extensions.registerInstruction ("coc", "S", opcodeCOC);
extensions.registerInstruction ("centeroncell", "S", opcodeCOC); extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
extensions.registerInstruction ("coe", "ll", opcodeCOE);
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged); interpreter.installSegment5 (opcodeCellChanged, new OpCellChanged);
interpreter.installSegment5 (opcodeCOC, new OpCOC); interpreter.installSegment5 (opcodeCOC, new OpCOC);
interpreter.installSegment5 (opcodeCOE, new OpCOE);
} }
} }
} }

View file

@ -88,6 +88,10 @@ op 0x2000078: GetItemCount
op 0x2000079: GetItemCount, explicit reference op 0x2000079: GetItemCount, explicit reference
op 0x200007a: RemoveItem op 0x200007a: RemoveItem
op 0x200007b: RemoveItem, explicit reference op 0x200007b: RemoveItem, explicit reference
<<<<<<< HEAD:apps/openmw/mwscript/docs/vmformat.txt
op 0x200007c: COC
opcodes 0x200007d-0x3ffffff unused
=======
op 0x200007c: GetAiPackageDone op 0x200007c: GetAiPackageDone
op 0x200007d: GetAiPackageDone, explicit reference op 0x200007d: GetAiPackageDone, explicit reference
op 0x200007e-0x2000084: Enable Controls op 0x200007e-0x2000084: Enable Controls
@ -95,3 +99,4 @@ op 0x2000085-0x200008b: Disable Controls
op 0x200008c: Unlock op 0x200008c: Unlock
op 0x200008d: Unlock, explicit reference op 0x200008d: Unlock, explicit reference
opcodes 0x200008e-0x3ffffff unused opcodes 0x200008e-0x3ffffff unused
>>>>>>> master:apps/openmw/mwscript/docs/vmformat.txt

View file

@ -13,6 +13,9 @@ namespace MWWorld
void ActionTeleportPlayer::execute (Environment& environment) void ActionTeleportPlayer::execute (Environment& environment)
{ {
environment.mWorld->changeCell (mCellName, mPosition); if (mCellName.empty())
environment.mWorld->changeToExteriorCell (mPosition);
else
environment.mWorld->changeCell (mCellName, mPosition);
} }
} }

View file

@ -17,6 +17,7 @@ namespace MWWorld
public: public:
ActionTeleportPlayer (const std::string& cellName, const ESM::Position& position); ActionTeleportPlayer (const std::string& cellName, const ESM::Position& position);
///< If cellName is empty, an exterior cell is asumed.
virtual void execute (Environment& environment); virtual void execute (Environment& environment);
}; };

View file

@ -8,6 +8,7 @@
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/interior.hpp" #include "../mwrender/interior.hpp"
#include "../mwrender/exterior.hpp"
#include "../mwmechanics/mechanicsmanager.hpp" #include "../mwmechanics/mechanicsmanager.hpp"
@ -249,6 +250,66 @@ namespace MWWorld
throw std::runtime_error ("month out of range"); 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<CellRenderCollection::iterator, bool> 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, World::World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& dataDir,
const std::string& master, bool newGame, Environment& environment) const std::string& master, bool newGame, Environment& environment)
: mSkyManager (0), mScene (renderer), mPlayerPos (0), mCurrentCell (0), mGlobalVariables (0), : mSkyManager (0), mScene (renderer), mPlayerPos (0), mCurrentCell (0), mGlobalVariables (0),
@ -263,7 +324,7 @@ namespace MWWorld
mEsm.open (masterPath.file_string()); mEsm.open (masterPath.file_string());
mStore.load (mEsm); 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 // global variables
mGlobalVariables = new Globals (mStore); mGlobalVariables = new Globals (mStore);
@ -522,57 +583,114 @@ namespace MWWorld
void World::changeCell (const std::string& cellName, const ESM::Position& position) 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. // Load cell.
mInteriors[cellName].loadInt (cellName, mStore, mEsm); mInteriors[cellName].loadInt (cellName, mStore, mEsm);
Ptr::CellStore *cell = &mInteriors[cellName]; 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 // remove active
CellRenderCollection::iterator active = mActiveCells.begin(); CellRenderCollection::iterator active = mActiveCells.begin();
if (active!=mActiveCells.end()) while (active!=mActiveCells.end())
{ {
mEnvironment.mMechanicsManager->dropActors (active->first); if (!(active->first->cell->data.flags & ESM::Cell::Interior))
active->second->destroy(); {
mEnvironment.mSoundManager->stopSound (active->first); if (std::abs (X-active->first->cell->data.gridX)<=1 &&
delete active->second; std::abs (Y-active->first->cell->data.gridY)<=1)
mActiveCells.erase (active); {
// keep cells within the new 3x3 grid
++active;
continue;
}
}
unloadCell (active++);
} }
// register local scripts // Load cells
mLocalScripts.clear(); // FIXME won't work with exteriors for (int x=X-1; x<=X+1; ++x)
insertInteriorScripts (*cell); for (int y=Y-1; y<=Y+1; ++y)
{
CellRenderCollection::iterator iter = mActiveCells.begin();
while (iter!=mActiveCells.end())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (x==iter->first->cell->data.gridX &&
y==iter->first->cell->data.gridY)
break;
++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())
{
assert (!(iter->first->cell->data.flags & ESM::Cell::Interior));
if (X==iter->first->cell->data.gridX &&
Y==iter->first->cell->data.gridY)
break;
++iter;
}
assert (iter!=mActiveCells.end());
mCurrentCell = iter->first;
// adjust player // adjust player
mPlayerPos->setPos (position.pos[0], position.pos[1], position.pos[2], true); playerCellChange (&mExteriors[std::make_pair (X, Y)], position);
mPlayerPos->setCell (cell);
// TODO orientation
// This connects the cell data with the rendering scene.
std::pair<CellRenderCollection::iterator, bool> result =
mActiveCells.insert (std::make_pair (cell,
new MWRender::InteriorCellRender (*cell, mEnvironment, mScene)));
if (result.second)
{
// Load the cell and insert it into the renderer
result.first->second->show();
}
// Actors
mEnvironment.mMechanicsManager->addActor (mPlayerPos->getPlayer());
mEnvironment.mMechanicsManager->watchActor (mPlayerPos->getPlayer());
// Sky system // Sky system
if (mSky) adjustSky();
{
toggleSky();
// TODO set weather
toggleSky();
}
mCellChanged = true; 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() void World::markCellAsUnchanged()
{ {
mCellChanged = false; mCellChanged = false;
@ -580,7 +698,7 @@ namespace MWWorld
std::string World::getFacedHandle() std::string World::getFacedHandle()
{ {
std::pair<std::string, float> result = mScene.getFacedHandle(); std::pair<std::string, float> result = mScene.getFacedHandle (*this);
if (result.first.empty() || if (result.first.empty() ||
result.second>getStore().gameSettings.find ("iMaxActivateDist")->i) 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<int> (x/cellSize);
if (x<0)
--cellX;
cellY = static_cast<int> (y/cellSize);
if (y<0)
--cellY;
}
} }

View file

@ -41,7 +41,7 @@ namespace MWWorld
{ {
public: public:
typedef std::vector<std::pair<std::string, Ptr> > ScriptList; typedef std::list<std::pair<std::string, Ptr> > ScriptList;
private: private:
@ -56,6 +56,7 @@ namespace MWWorld
ESM::ESMReader mEsm; ESM::ESMReader mEsm;
ESMS::ESMStore mStore; ESMS::ESMStore mStore;
std::map<std::string, Ptr::CellStore> mInteriors; std::map<std::string, Ptr::CellStore> mInteriors;
std::map<std::pair<int, int>, Ptr::CellStore> mExteriors;
ScriptList mLocalScripts; ScriptList mLocalScripts;
MWWorld::Globals *mGlobalVariables; MWWorld::Globals *mGlobalVariables;
bool mSky; bool mSky;
@ -76,6 +77,16 @@ namespace MWWorld
int getDaysPerMonth (int month) const; 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: public:
World (OEngine::Render::OgreRenderer& renderer, const boost::filesystem::path& master, 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); void changeCell (const std::string& cellName, const ESM::Position& position);
///< works only for interior cells currently. ///< works only for interior cells currently.
void changeCell (int X, int Y, const ESM::Position& position);
void changeToExteriorCell (const ESM::Position& position);
void markCellAsUnchanged(); void markCellAsUnchanged();
std::string getFacedHandle(); std::string getFacedHandle();
///< Return handle of the object the player is looking at ///< Return handle of the object the player is looking at
void deleteObject (Ptr ptr); 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
}; };
} }

View file

@ -123,7 +123,14 @@ namespace ESMS
/** Ditto for exterior cell. */ /** Ditto for exterior cell. */
void loadExt(int X, int Y, const ESMStore &store, ESMReader &esm) 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: private:

View file

@ -194,6 +194,21 @@ namespace ESMS
return it->second; 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) void load(ESMReader &esm, const std::string &id)
{ {
using namespace std; using namespace std;