Merge branch 'master' into occlusionquery

Conflicts:
	apps/openmw/CMakeLists.txt
	apps/openmw/mwrender/renderingmanager.cpp
	apps/openmw/mwrender/renderingmanager.hpp
	components/esm_store/cell_store.hpp
This commit is contained in:
scrawl 2012-03-30 14:39:42 +02:00
commit 4a6d034591
34 changed files with 846 additions and 34 deletions

View file

@ -201,6 +201,7 @@ if(APPLE)
"Plugin_ParticleFX") "Plugin_ParticleFX")
endif(APPLE) endif(APPLE)
add_subdirectory( files/)
add_subdirectory( files/mygui ) add_subdirectory( files/mygui )
# Specify build paths # Specify build paths

View file

@ -95,5 +95,5 @@ else()
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss") "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg") "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
endif() endif()

View file

@ -15,7 +15,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderingmanager debugging sky player animation npcanimation creatureanimation actors objects
renderinginterface localmap occlusionquery terrain terrainmaterial renderinginterface localmap occlusionquery terrain terrainmaterial water
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

View file

@ -315,7 +315,11 @@ void OMW::Engine::go()
// This has to be added BEFORE MyGUI is initialized, as it needs // This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here. // to find core.xml here.
//addResourcesDirectory(mResDir);
addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
// Create the window // Create the window
mOgre->createWindow("OpenMW"); mOgre->createWindow("OpenMW");

View file

@ -182,7 +182,9 @@ void HUD::setPlayerPos(const float x, const float y)
} }
MapWindow::MapWindow() MapWindow::MapWindow()
: Layout("openmw_map_window_layout.xml"), mGlobal(false) : Layout("openmw_map_window_layout.xml")
, mGlobal(false)
, mVisible(false)
{ {
setCoord(500,0,320,300); setCoord(500,0,320,300);
setText("WorldButton", "World"); setText("WorldButton", "World");
@ -272,6 +274,17 @@ void MapWindow::onWorldButtonClicked(MyGUI::Widget* _sender)
mButton->setCaption( mGlobal ? "Local" : "World" ); mButton->setCaption( mGlobal ? "Local" : "World" );
} }
LocalMapBase::LocalMapBase()
: mCurX(0)
, mCurY(0)
, mInterior(false)
, mLocalMap(NULL)
, mPrefix()
, mChanged(true)
, mLayout(NULL)
{
}
void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout) void LocalMapBase::init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout)
{ {
mLocalMap = widget; mLocalMap = widget;

View file

@ -34,6 +34,7 @@ namespace MWGui
class LocalMapBase class LocalMapBase
{ {
public: public:
LocalMapBase();
void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout); void init(MyGUI::ScrollView* widget, OEngine::GUI::Layout* layout);
void setCellPrefix(const std::string& prefix); void setCellPrefix(const std::string& prefix);
@ -85,6 +86,7 @@ namespace MWGui
{ {
public: public:
MapWindow(); MapWindow();
virtual ~MapWindow(){}
void setVisible(bool b); void setVisible(bool b);
void setPlayerPos(const float x, const float y); void setPlayerPos(const float x, const float y);

View file

@ -88,19 +88,66 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
NifOgre::NIFLoader::load(mesh); NifOgre::NIFLoader::load(mesh);
Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh); Ogre::Entity *ent = mRenderer.getScene()->createEntity(mesh);
/*
Ogre::Vector3 extents = ent->getBoundingBox().getSize();
extents *= insert->getScale();
// float size = std::max(std::max(extents.x, extents.y), extents.z);
bool small = (size < 250); /// \todo config value
// do not fade out doors. that will cause holes and look stupid
if (ptr.getTypeName().find("Door") != std::string::npos)
small = false;
*/
const bool small = false;
if (mBounds.find(ptr.getCell()) == mBounds.end())
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
Ogre::AxisAlignedBox bounds = ent->getBoundingBox();
bounds = Ogre::AxisAlignedBox(
insert->_getDerivedPosition() + bounds.getMinimum(),
insert->_getDerivedPosition() + bounds.getMaximum()
);
bounds.scale(insert->getScale());
mBounds[ptr.getCell()].merge(bounds);
if(!mIsStatic) if(!mIsStatic)
{ {
insert->attachObject(ent); insert->attachObject(ent);
ent->setRenderingDistance(small ? 2500 : 0); /// \todo config value
} }
else else
{ {
Ogre::StaticGeometry* sg = 0; Ogre::StaticGeometry* sg = 0;
if(mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
if (small)
{
if( mStaticGeometrySmall.find(ptr.getCell()) == mStaticGeometrySmall.end())
{ {
uniqueID = uniqueID +1; uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID)); sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
//Create the scenenode and put it in the map mStaticGeometrySmall[ptr.getCell()] = sg;
sg->setRenderingDistance(2500); /// \todo config value
}
else
sg = mStaticGeometrySmall[ptr.getCell()];
}
else
{
if( mStaticGeometry.find(ptr.getCell()) == mStaticGeometry.end())
{
uniqueID = uniqueID +1;
sg = mRenderer.getScene()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
mStaticGeometry[ptr.getCell()] = sg; mStaticGeometry[ptr.getCell()] = sg;
}
else
sg = mStaticGeometry[ptr.getCell()];
}
// This specifies the size of a single batch region. // This specifies the size of a single batch region.
// If it is set too high: // If it is set too high:
@ -110,16 +157,7 @@ void Objects::insertMesh (const MWWorld::Ptr& ptr, const std::string& mesh)
// - there will be too many batches. // - there will be too many batches.
sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500)); sg->setRegionDimensions(Ogre::Vector3(2500,2500,2500));
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
mBounds[ptr.getCell()].merge(ent->getBoundingBox());
}
else
{
sg = mStaticGeometry[ptr.getCell()];
}
sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale()); sg->addEntity(ent,insert->_getDerivedPosition(),insert->_getDerivedOrientation(),insert->_getDerivedScale());
mBounds[ptr.getCell()].merge(insert->_getDerivedPosition());
mRenderer.getScene()->destroyEntity(ent); mRenderer.getScene()->destroyEntity(ent);
} }
@ -206,6 +244,13 @@ void Objects::removeCell(MWWorld::Ptr::CellStore* store)
mRenderer.getScene()->destroyStaticGeometry (sg); mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0; sg = 0;
} }
if(mStaticGeometrySmall.find(store) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[store];
mStaticGeometrySmall.erase(store);
mRenderer.getScene()->destroyStaticGeometry (sg);
sg = 0;
}
if(mBounds.find(store) != mBounds.end()) if(mBounds.find(store) != mBounds.end())
mBounds.erase(store); mBounds.erase(store);
@ -218,6 +263,11 @@ void Objects::buildStaticGeometry(ESMS::CellStore<MWWorld::RefData>& cell)
Ogre::StaticGeometry* sg = mStaticGeometry[&cell]; Ogre::StaticGeometry* sg = mStaticGeometry[&cell];
sg->build(); sg->build();
} }
if(mStaticGeometrySmall.find(&cell) != mStaticGeometrySmall.end())
{
Ogre::StaticGeometry* sg = mStaticGeometrySmall[&cell];
sg->build();
}
} }
Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell) Ogre::AxisAlignedBox Objects::getDimensions(MWWorld::Ptr::CellStore* cell)

View file

@ -14,6 +14,7 @@ class Objects{
OEngine::Render::OgreRenderer &mRenderer; OEngine::Render::OgreRenderer &mRenderer;
std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes; std::map<MWWorld::Ptr::CellStore *, Ogre::SceneNode *> mCellSceneNodes;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry; std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometry;
std::map<MWWorld::Ptr::CellStore *, Ogre::StaticGeometry*> mStaticGeometrySmall;
std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds; std::map<MWWorld::Ptr::CellStore *, Ogre::AxisAlignedBox> mBounds;
Ogre::SceneNode* mMwRoot; Ogre::SceneNode* mMwRoot;
bool mIsStatic; bool mIsStatic;

View file

@ -58,6 +58,8 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mWater = 0;
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0; mSun = 0;
@ -95,13 +97,28 @@ OEngine::Render::Fader* RenderingManager::getFader()
return mRendering.getFader(); return mRendering.getFader();
} }
void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store){ void RenderingManager::removeCell (MWWorld::Ptr::CellStore *store)
{
mObjects.removeCell(store); mObjects.removeCell(store);
mActors.removeCell(store); mActors.removeCell(store);
if (store->cell->isExterior()) if (store->cell->isExterior())
mTerrainManager->cellRemoved(store); mTerrainManager->cellRemoved(store);
} }
void RenderingManager::removeWater ()
{
if(mWater){
delete mWater;
mWater = 0;
}
}
void RenderingManager::toggleWater()
{
if (mWater)
mWater->toggle();
}
void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store) void RenderingManager::cellAdded (MWWorld::Ptr::CellStore *store)
{ {
mObjects.buildStaticGeometry (*store); mObjects.buildStaticGeometry (*store);
@ -157,6 +174,27 @@ void RenderingManager::update (float duration){
mRendering.update(duration); mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );
checkUnderwater();
}
void RenderingManager::waterAdded (MWWorld::Ptr::CellStore *store){
if(store->cell->data.flags & store->cell->HasWater){
if(mWater == 0)
mWater = new MWRender::Water(mRendering.getCamera(), store->cell);
else
mWater->changeCell(store->cell);
//else
}
else
removeWater();
}
void RenderingManager::setWaterHeight(const float height)
{
if (mWater)
mWater->setHeight(height);
} }
void RenderingManager::skyEnable () void RenderingManager::skyEnable ()
@ -301,6 +339,11 @@ void RenderingManager::toggleLight()
setAmbientMode(); setAmbientMode();
} }
void RenderingManager::checkUnderwater(){
if(mWater){
mWater->checkUnderwater( mRendering.getCamera()->getRealPosition().y );
}
}
void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName, void RenderingManager::playAnimationGroup (const MWWorld::Ptr& ptr, const std::string& groupName,
int mode, int number) int mode, int number)

View file

@ -25,6 +25,7 @@
#include "objects.hpp" #include "objects.hpp"
#include "actors.hpp" #include "actors.hpp"
#include "player.hpp" #include "player.hpp"
#include "water.hpp"
#include "localmap.hpp" #include "localmap.hpp"
#include "occlusionquery.hpp" #include "occlusionquery.hpp"
@ -61,6 +62,8 @@ class RenderingManager: private RenderingInterface {
RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment); RenderingManager(OEngine::Render::OgreRenderer& _rend, const boost::filesystem::path& resDir, OEngine::Physic::PhysicEngine* engine, MWWorld::Environment& environment);
virtual ~RenderingManager(); virtual ~RenderingManager();
virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as virtual MWRender::Player& getPlayer(); /// \todo move this to private again as soon as
/// MWWorld::Player has been rewritten to not need access /// MWWorld::Player has been rewritten to not need access
/// to internal details of the rendering system anymore /// to internal details of the rendering system anymore
@ -77,6 +80,9 @@ class RenderingManager: private RenderingInterface {
/// \todo this function should be removed later. Instead the rendering subsystems should track /// \todo this function should be removed later. Instead the rendering subsystems should track
/// when rebatching is needed and update automatically at the end of each frame. /// when rebatching is needed and update automatically at the end of each frame.
void cellAdded (MWWorld::Ptr::CellStore *store); void cellAdded (MWWorld::Ptr::CellStore *store);
void waterAdded(MWWorld::Ptr::CellStore *store);
void removeWater();
void preCellChange (MWWorld::Ptr::CellStore* store); void preCellChange (MWWorld::Ptr::CellStore* store);
///< this event is fired immediately before changing cell ///< this event is fired immediately before changing cell
@ -88,6 +94,10 @@ class RenderingManager: private RenderingInterface {
void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale); void scaleObject (const MWWorld::Ptr& ptr, const Ogre::Vector3& scale);
void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation); void rotateObject (const MWWorld::Ptr& ptr, const::Ogre::Quaternion& orientation);
void checkUnderwater();
void setWaterHeight(const float height);
void toggleWater();
/// \param store Cell the object was in previously (\a ptr has already been updated to the new cell). /// \param store Cell the object was in previously (\a ptr has already been updated to the new cell).
void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store); void moveObjectToCell (const MWWorld::Ptr& ptr, const Ogre::Vector3& position, MWWorld::Ptr::CellStore *store);
@ -143,6 +153,8 @@ class RenderingManager: private RenderingInterface {
TerrainManager* mTerrainManager; TerrainManager* mTerrainManager;
MWRender::Water *mWater;
OEngine::Render::OgreRenderer &mRendering; OEngine::Render::OgreRenderer &mRendering;
MWRender::Objects mObjects; MWRender::Objects mObjects;

View file

@ -0,0 +1,95 @@
#include "water.hpp"
namespace MWRender
{
Water::Water (Ogre::Camera *camera, const ESM::Cell* cell) :
mCamera (camera), mViewport (camera->getViewport()), mSceneManager (camera->getSceneManager()),
mIsUnderwater(false)
{
try
{
Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Water", -1);
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mTop = cell->water;
mIsUnderwater = false;
mWaterPlane = Ogre::Plane(Ogre::Vector3::UNIT_Y, 0);
Ogre::MeshManager::getSingleton().createPlane("water", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, mWaterPlane, CELL_SIZE*5, CELL_SIZE * 5, 10, 10, true, 1, 3,5, Ogre::Vector3::UNIT_Z);
mWater = mSceneManager->createEntity("water");
mWater->setMaterialName("Examples/Water0");
mWaterNode = mSceneManager->getRootSceneNode()->createChildSceneNode();
mWaterNode->setPosition(0, mTop, 0);
if(!(cell->data.flags & cell->Interior))
{
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
}
mWaterNode->attachObject(mWater);
}
Water::~Water()
{
Ogre::MeshManager::getSingleton().remove("water");
mWaterNode->detachObject(mWater);
mSceneManager->destroyEntity(mWater);
mSceneManager->destroySceneNode(mWaterNode);
Ogre::CompositorManager::getSingleton().removeCompositorChain(mViewport);
}
void Water::changeCell(const ESM::Cell* cell)
{
mTop = cell->water;
if(!(cell->data.flags & cell->Interior))
mWaterNode->setPosition(getSceneNodeCoordinates(cell->data.gridX, cell->data.gridY));
else
setHeight(mTop);
}
void Water::setHeight(const float height)
{
mTop = height;
mWaterNode->setPosition(0, height, 0);
}
void Water::toggle()
{
mWater->setVisible(!mWater->getVisible());
}
void Water::checkUnderwater(float y)
{
if ((mIsUnderwater && y > mTop) || !mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", false);
} catch(...) {}
mIsUnderwater = false;
}
if (!mIsUnderwater && y < mTop && mWater->isVisible())
{
try {
Ogre::CompositorManager::getSingleton().setCompositorEnabled(mViewport, "Water", true);
} catch(...) {}
mIsUnderwater = true;
}
}
Ogre::Vector3 Water::getSceneNodeCoordinates(int gridX, int gridY)
{
return Ogre::Vector3(gridX * CELL_SIZE + (CELL_SIZE / 2), mTop, -gridY * CELL_SIZE - (CELL_SIZE / 2));
}
} // namespace

View file

@ -0,0 +1,40 @@
#ifndef GAME_MWRENDER_WATER_H
#define GAME_MWRENDER_WATER_H
#include <Ogre.h>
#include <components/esm/loadcell.hpp>
namespace MWRender {
/// Water rendering
class Water : Ogre::RenderTargetListener, Ogre::Camera::Listener
{
static const int CELL_SIZE = 8192;
Ogre::Camera *mCamera;
Ogre::SceneManager *mSceneManager;
Ogre::Viewport *mViewport;
Ogre::Plane mWaterPlane;
Ogre::SceneNode *mWaterNode;
Ogre::Entity *mWater;
bool mIsUnderwater;
int mTop;
Ogre::Vector3 getSceneNodeCoordinates(int gridX, int gridY);
public:
Water (Ogre::Camera *camera, const ESM::Cell* cell);
~Water();
void toggle();
void checkUnderwater(float y);
void changeCell(const ESM::Cell* cell);
void setHeight(const float height);
};
}
#endif

View file

@ -133,11 +133,70 @@ namespace MWScript
} }
}; };
class OpGetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
runtime.push (cell->mWaterLevel);
}
};
class OpSetWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel = level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
class OpModWaterLevel : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context
= static_cast<InterpreterContext&> (runtime.getContext());
Interpreter::Type_Float level = runtime[0].mFloat;
MWWorld::Ptr::CellStore *cell = context.getWorld().getPlayer().getPlayer().getCell();
if (!(cell->cell->data.flags & ESM::Cell::Interior))
throw std::runtime_error("Can't set water level in exterior cell");
cell->mWaterLevel +=level;
context.getEnvironment().mWorld->setWaterHeight(cell->mWaterLevel);
}
};
const int opcodeCellChanged = 0x2000000; const int opcodeCellChanged = 0x2000000;
const int opcodeCOC = 0x2000026; const int opcodeCOC = 0x2000026;
const int opcodeCOE = 0x200008e; const int opcodeCOE = 0x200008e;
const int opcodeGetInterior = 0x2000131; const int opcodeGetInterior = 0x2000131;
const int opcodeGetPCCell = 0x2000136; const int opcodeGetPCCell = 0x2000136;
const int opcodeGetWaterLevel = 0x2000141;
const int opcodeSetWaterLevel = 0x2000142;
const int opcodeModWaterLevel = 0x2000143;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
@ -146,8 +205,11 @@ namespace MWScript
extensions.registerInstruction ("centeroncell", "S", opcodeCOC); extensions.registerInstruction ("centeroncell", "S", opcodeCOC);
extensions.registerInstruction ("coe", "ll", opcodeCOE); extensions.registerInstruction ("coe", "ll", opcodeCOE);
extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE); extensions.registerInstruction ("centeronexterior", "ll", opcodeCOE);
extensions.registerInstruction ("setwaterlevel", "f", opcodeSetWaterLevel);
extensions.registerInstruction ("modwaterlevel", "f", opcodeModWaterLevel);
extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior); extensions.registerFunction ("getinterior", 'l', "", opcodeGetInterior);
extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell); extensions.registerFunction ("getpccell", 'l', "c", opcodeGetPCCell);
extensions.registerFunction ("getwaterlevel", 'f', "", opcodeGetWaterLevel);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -157,6 +219,9 @@ namespace MWScript
interpreter.installSegment5 (opcodeCOE, new OpCOE); interpreter.installSegment5 (opcodeCOE, new OpCOE);
interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior); interpreter.installSegment5 (opcodeGetInterior, new OpGetInterior);
interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell); interpreter.installSegment5 (opcodeGetPCCell, new OpGetPCCell);
interpreter.installSegment5 (opcodeGetWaterLevel, new OpGetWaterLevel);
interpreter.installSegment5 (opcodeSetWaterLevel, new OpSetWaterLevel);
interpreter.installSegment5 (opcodeModWaterLevel, new OpModWaterLevel);
} }
} }
} }

View file

@ -123,4 +123,8 @@ op 0x200013d: FadeOut
op 0x200013e: FadeTo op 0x200013e: FadeTo
op 0x200013f: GetCurrentWeather op 0x200013f: GetCurrentWeather
op 0x2000140: ChangeWeather op 0x2000140: ChangeWeather
opcodes 0x2000141-0x3ffffff unused op 0x2000141: GetWaterLevel
op 0x2000142: SetWaterLevel
op 0x2000143: ModWaterLevel
op 0x2000144: ToggleWater, twa
opcodes 0x2000145-0x3ffffff unused

View file

@ -175,6 +175,19 @@ namespace MWScript
} }
}; };
class OpToggleWater : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
context.getWorld().toggleWater();
}
};
const int opcodeXBox = 0x200000c; const int opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d; const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075; const int opcodeActivate = 0x2000075;
@ -187,6 +200,7 @@ namespace MWScript
const int opcodeFadeIn = 0x200013c; const int opcodeFadeIn = 0x200013c;
const int opcodeFadeOut = 0x200013d; const int opcodeFadeOut = 0x200013d;
const int opcodeFadeTo = 0x200013e; const int opcodeFadeTo = 0x200013e;
const int opcodeToggleWater = 0x2000144;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
@ -204,6 +218,8 @@ namespace MWScript
extensions.registerInstruction ("fadein", "f", opcodeFadeIn); extensions.registerInstruction ("fadein", "f", opcodeFadeIn);
extensions.registerInstruction ("fadeout", "f", opcodeFadeOut); extensions.registerInstruction ("fadeout", "f", opcodeFadeOut);
extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo); extensions.registerInstruction ("fadeto", "ff", opcodeFadeTo);
extensions.registerInstruction ("togglewater", "", opcodeToggleWater);
extensions.registerInstruction ("twa", "", opcodeToggleWater);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -220,6 +236,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn); interpreter.installSegment5 (opcodeFadeIn, new OpFadeIn);
interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut); interpreter.installSegment5 (opcodeFadeOut, new OpFadeOut);
interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo); interpreter.installSegment5 (opcodeFadeTo, new OpFadeTo);
interpreter.installSegment5 (opcodeToggleWater, new OpToggleWater);
} }
} }
} }

View file

@ -441,6 +441,27 @@ void OpenAL_Output::init(const std::string &devname)
try try
{ {
ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256); ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
if (maxtotal == 0) // workaround for broken implementations
{
maxtotal = 256;
bool stop = false;
for(size_t i = 0;i < maxtotal && !stop;i++) // generate source until error returned
{
ALuint src = 0;
alGenSources(1, &src);
ALenum err = alGetError();
if(err != AL_NO_ERROR)
{
stop = true;
}
else
{
mFreeSources.push_back(src);
}
}
}
else // normal case
{
for(size_t i = 0;i < maxtotal;i++) for(size_t i = 0;i < maxtotal;i++)
{ {
ALuint src = 0; ALuint src = 0;
@ -449,6 +470,7 @@ void OpenAL_Output::init(const std::string &devname)
mFreeSources.push_back(src); mFreeSources.push_back(src);
} }
} }
}
catch(std::exception &e) catch(std::exception &e)
{ {
std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl; std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl;

View file

@ -54,9 +54,11 @@ void insertCellRefList(MWRender::RenderingManager& rendering, MWWorld::Environme
namespace MWWorld namespace MWWorld
{ {
void Scene::update (float duration){ void Scene::update (float duration){
mRendering.update (duration); mRendering.update (duration);
} }
void Scene::unloadCell (CellStoreCollection::iterator iter) void Scene::unloadCell (CellStoreCollection::iterator iter)
{ {
std::cout << "Unloading cell\n"; std::cout << "Unloading cell\n";
@ -79,6 +81,7 @@ namespace MWWorld
mPhysics->removeObject (node->getName()); mPhysics->removeObject (node->getName());
} }
} }
mRendering.removeCell(*iter); mRendering.removeCell(*iter);
//mPhysics->removeObject("Unnamed_43"); //mPhysics->removeObject("Unnamed_43");
@ -88,6 +91,7 @@ namespace MWWorld
mActiveCells.erase(*iter); mActiveCells.erase(*iter);
} }
void Scene::loadCell (Ptr::CellStore *cell) void Scene::loadCell (Ptr::CellStore *cell)
@ -192,6 +196,7 @@ namespace MWWorld
mCurrentCell = *iter; mCurrentCell = *iter;
// adjust player // adjust player
playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos); playerCellChange (mWorld->getExterior(X, Y), position, adjustPlayerPos);
@ -199,6 +204,7 @@ namespace MWWorld
mWorld->adjustSky(); mWorld->adjustSky();
mCellChanged = true; mCellChanged = true;
mRendering.waterAdded(mCurrentCell);
} }
//We need the ogre renderer and a scene node. //We need the ogre renderer and a scene node.
@ -239,6 +245,7 @@ namespace MWWorld
loadCell (cell); loadCell (cell);
// adjust player // adjust player
mCurrentCell = cell; mCurrentCell = cell;
playerCellChange (cell, position); playerCellChange (cell, position);
@ -250,6 +257,8 @@ namespace MWWorld
mWorld->adjustSky(); mWorld->adjustSky();
mCellChanged = true; mCellChanged = true;
mRendering.waterAdded(cell);
} }
void Scene::changeToExteriorCell (const ESM::Position& position) void Scene::changeToExteriorCell (const ESM::Position& position)

View file

@ -540,9 +540,10 @@ namespace MWWorld
ptr.getRefData().getPosition().pos[0] = x; ptr.getRefData().getPosition().pos[0] = x;
ptr.getRefData().getPosition().pos[1] = y; ptr.getRefData().getPosition().pos[1] = y;
ptr.getRefData().getPosition().pos[2] = z; ptr.getRefData().getPosition().pos[2] = z;
if (ptr==mPlayer->getPlayer()) if (ptr==mPlayer->getPlayer())
{ {
//std::cout << "X:" << ptr.getRefData().getPosition().pos[0] << " Z: " << ptr.getRefData().getPosition().pos[1] << "\n";
Ptr::CellStore *currentCell = mWorldScene->getCurrentCell(); Ptr::CellStore *currentCell = mWorldScene->getCurrentCell();
if (currentCell) if (currentCell)
{ {
@ -832,4 +833,15 @@ namespace MWWorld
{ {
return mRendering->getFader(); return mRendering->getFader();
} }
void World::setWaterHeight(const float height)
{
mRendering->setWaterHeight(height);
}
void World::toggleWater()
{
mRendering->toggleWater();
}
} }

View file

@ -119,6 +119,9 @@ namespace MWWorld
Ptr::CellStore *getInterior (const std::string& name); Ptr::CellStore *getInterior (const std::string& name);
void setWaterHeight(const float height);
void toggleWater();
void adjustSky(); void adjustSky();
MWWorld::Player& getPlayer(); MWWorld::Player& getPlayer();

View file

@ -21,8 +21,13 @@ void Cell::load(ESMReader &esm)
if (data.flags & Interior) if (data.flags & Interior)
{ {
// Interior cells // Interior cells
if (esm.isNextSub("INTV"))
if (esm.isNextSub("INTV") || esm.isNextSub("WHGT")) {
int waterl;
esm.getHT(waterl);
water = (float) waterl;
}
else if (esm.isNextSub("WHGT"))
esm.getHT(water); esm.getHT(water);
// Quasi-exterior cells have a region (which determines the // Quasi-exterior cells have a region (which determines the

View file

@ -114,7 +114,7 @@ struct Cell
ESM_Context context; // File position ESM_Context context; // File position
DATAstruct data; DATAstruct data;
AMBIstruct ambi; AMBIstruct ambi;
int water; // Water level float water; // Water level
int mapColor; int mapColor;
void load(ESMReader &esm); void load(ESMReader &esm);

View file

@ -96,12 +96,16 @@ namespace ESMS
}; };
CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded) CellStore (const ESM::Cell *cell_) : cell (cell_), mState (State_Unloaded)
{} {
mWaterLevel = cell->water;
}
const ESM::Cell *cell; const ESM::Cell *cell;
State mState; State mState;
std::vector<std::string> mIds; std::vector<std::string> mIds;
float mWaterLevel;
// Lists for each individual object type // Lists for each individual object type
CellRefList<Activator, D> activators; CellRefList<Activator, D> activators;
CellRefList<Potion, D> potions; CellRefList<Potion, D> potions;

13
files/CMakeLists.txt Normal file
View file

@ -0,0 +1,13 @@
project(resources)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/caustic_0.png "${OpenMW_BINARY_DIR}/resources/water/caustic_0.png" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_Fresnel.cg "${OpenMW_BINARY_DIR}/resources/water/Example_Fresnel.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Example_FresnelPS.asm "${OpenMW_BINARY_DIR}/resources/water/Example_FresnelPS.asm" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassFP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassFP.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/GlassVP.cg "${OpenMW_BINARY_DIR}/resources/water/GlassVP.cg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/perlinvolume.dds "${OpenMW_BINARY_DIR}/resources/water/perlinvolume.dds" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Water02.jpg "${OpenMW_BINARY_DIR}/resources/water/Water02.jpg" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/water.compositor "${OpenMW_BINARY_DIR}/resources/water/water.compositor" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/waves2.dds "${OpenMW_BINARY_DIR}/resources/water/waves2.dds" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/Examples-Water.material "${OpenMW_BINARY_DIR}/resources/water/Examples-Water.material" COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/water/WaterNormal1.tga "${OpenMW_BINARY_DIR}/resources/water/WaterNormal1.tga" COPYONLY)

View file

@ -0,0 +1,116 @@
// Vertex program for fresnel reflections / refractions
void main_vp(
float4 pos : POSITION,
float4 normal : NORMAL,
float2 tex : TEXCOORD0,
out float4 oPos : POSITION,
out float3 noiseCoord : TEXCOORD0,
out float4 projectionCoord : TEXCOORD1,
out float3 oEyeDir : TEXCOORD2,
out float3 oNormal : TEXCOORD3,
uniform float4x4 worldViewProjMatrix,
uniform float3 eyePosition, // object space
uniform float timeVal,
uniform float scale, // the amount to scale the noise texture by
uniform float scroll, // the amount by which to scroll the noise
uniform float noise // the noise perturb as a factor of the time
)
{
oPos = mul(worldViewProjMatrix, pos);
// Projective texture coordinates, adjust for mapping
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
0,-0.5, 0, 0.5,
0, 0, 0.5, 0.5,
0, 0, 0, 1);
projectionCoord = mul(scalemat, oPos);
// Noise map coords
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
noiseCoord.z = noise * timeVal;
oEyeDir = normalize(pos.xyz - eyePosition);
oNormal = normal.rgb;
}
// Fragment program for distorting a texture using a 3D noise texture
void main_fp(
float3 noiseCoord : TEXCOORD0,
float4 projectionCoord : TEXCOORD1,
float3 eyeDir : TEXCOORD2,
float3 normal : TEXCOORD3,
out float4 col : COLOR,
uniform float4 tintColour,
uniform float noiseScale,
uniform float fresnelBias,
uniform float fresnelScale,
uniform float fresnelPower,
uniform sampler2D waterTex : register(s0),
uniform sampler2D noiseMap : register(s1),
uniform sampler2D reflectMap : register(s2),
uniform sampler2D refractMap : register(s3)
)
{
// Do the tex projection manually so we can distort _after_
float2 final = projectionCoord.xy / projectionCoord.w;
// Noise
float3 noiseNormal = (tex2D(noiseMap, (noiseCoord.xy / 5)).rgb - 0.5).rbg * noiseScale;
final += noiseNormal.xz;
// Fresnel
//normal = normalize(normal + noiseNormal.xz);
float fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
// Reflection / refraction
float4 reflectionColour = tex2D(reflectMap, final);
float4 refractionColour = tex2D(refractMap, final) + tintColour;
// Final colour
col = lerp(refractionColour, reflectionColour, fresnel) * tex2D(waterTex, noiseNormal) / 3 ;
}
// Old version to match ATI PS 1.3 implementation
void main_vp_old(
float4 pos : POSITION,
float4 normal : NORMAL,
float2 tex : TEXCOORD0,
out float4 oPos : POSITION,
out float fresnel : COLOR,
out float3 noiseCoord : TEXCOORD0,
out float4 projectionCoord : TEXCOORD1,
uniform float4x4 worldViewProjMatrix,
uniform float3 eyePosition, // object space
uniform float fresnelBias,
uniform float fresnelScale,
uniform float fresnelPower,
uniform float timeVal,
uniform float scale, // the amount to scale the noise texture by
uniform float scroll, // the amount by which to scroll the noise
uniform float noise // the noise perturb as a factor of the time
)
{
oPos = mul(worldViewProjMatrix, pos);
// Projective texture coordinates, adjust for mapping
float4x4 scalemat = float4x4(0.5, 0, 0, 0.5,
0,-0.5, 0, 0.5,
0, 0, 0.5, 0.5,
0, 0, 0, 1);
projectionCoord = mul(scalemat, oPos);
// Noise map coords
noiseCoord.xy = (tex + (timeVal * scroll)) * scale;
noiseCoord.z = noise * timeVal;
// calc fresnel factor (reflection coefficient)
float3 eyeDir = normalize(pos.xyz - eyePosition);
fresnel = fresnelBias + fresnelScale * pow(1 + dot(eyeDir, normal), fresnelPower);
}

View file

@ -0,0 +1,72 @@
ps.1.4
// conversion from Cg generated ARB_fragment_program to ps.1.4 by NFZ
// command line args: -profile arbfp1 -entry main_fp
// program main_fp
// c0 : distortionRange
// c1 : tintColour
// testure 0 : noiseMap
// texture 1 : reflectMap
// texture 2 : refractMap
// v0.x : fresnel
// t0.xyz : noiseCoord
// t1.xyw : projectionCoord
def c2, 2, 1, 0, 0
// Cg: distort.x = tex3D(noiseMap, noiseCoord).x;
// arbfp1: TEX R0.x, fragment.texcoord[0], texture[0], 3D;
// sample noise map using noiseCoord in TEX unit 0
texld r0, t0.xyz
// get projected texture coordinates from TEX coord 1
// will be used in phase 2
texcrd r1.xy, t1_dw.xyw
mov r1.z, c2.y
// Cg: distort.y = tex3D(noiseMap, noiseCoord + yoffset).x;
// arbfp1: ADD R1.xyz, fragment.texcoord[0], c1;
// arbfp1: TEX R1.x, R1, texture[0], 3D;
// arbfp1: MOV R0.y, R1.x;
// Cg: distort = (distort * 2 - 1) * distortionRange;
// arbfp1: MAD R0.xy, R0, c0.x, -c0.y;
// arbfp1: MUL R0.xy, R0, u0.x;
// (distort * 2 - 1) same as 2*(distort -.5) so use _bx2
// Cg: final = projectionCoord.xy / projectionCoord.w;
// Cg: final += distort;
// arbfp1: RCP R0.w, fragment.texcoord[1].w;
// arbfp1: MAD R0.xy, fragment.texcoord[1], R0.w, R0;
// final = (distort * projectionCoord.w) + projectionCoord.xy
// for ps.1.4 have to re-arrange things a bit to perturb projected texture coordinates
mad r0.xyz, r0_bx2, c0.x, r1
phase
// do dependant texture reads
// Cg: reflectionColour = tex2D(reflectMap, final);
// arbfp1: TEX R0, R0, texture[1], 2D;
// sampe reflectMap using dependant read : texunit 1
texld r1, r0.xyz
// Cg: refractionColour = tex2D(refractMap, final) + tintColour;
// arbfp1: TEX R1, R0, texture[2], 2D;
// sample refractMap : texunit 2
texld r2, r0.xyz
// adding tintColour that is in global c1
// arbfp1: ADD R1, R1, u1;
add r2, r2, c1
// Cg: col = lerp(refractionColour, reflectionColour, fresnel);
// arbfp1: ADD R0, R0, -R1;
// arbfp1: MAD result.color, fragment.color.primary.x, R0, R1;
lrp r0, v0.x, r1, r2

View file

@ -0,0 +1,149 @@
vertex_program Water/GlassVP cg
{
source GlassVP.cg
entry_point glass_vp
profiles vs_1_1 arbvp1
default_params
{
param_named_auto worldViewProj worldviewproj_matrix
}
}
fragment_program Water/GlassFP cg
{
source GlassFP.cg
entry_point main_ps
profiles ps_2_0 arbfp1
}
material Water/Compositor
{
technique
{
pass
{
depth_check off
vertex_program_ref Water/GlassVP
{
param_named_auto timeVal time 0.25
param_named scale float 0.1
}
fragment_program_ref Water/GlassFP
{
param_named tintColour float4 0 0.35 0.35 1
}
texture_unit RT
{
tex_coord_set 0
tex_address_mode clamp
filtering linear linear linear
}
texture_unit
{
texture WaterNormal1.tga 2d
tex_coord_set 1
//tex_address_mode clamp
filtering linear linear linear
}
texture_unit
{
texture caustic_0.png 2d
tex_coord_set 2
//tex_address_mode clamp
filtering linear linear linear
}
}
}
}
vertex_program Water/RefractReflectVP cg
{
source Example_Fresnel.cg
entry_point main_vp
profiles vs_1_1 arbvp1
}
vertex_program Water/RefractReflectVPold cg
{
source Example_Fresnel.cg
entry_point main_vp_old
profiles vs_1_1 arbvp1
}
fragment_program Water/RefractReflectFP cg
{
source Example_Fresnel.cg
entry_point main_fp
// sorry, ps_1_1 and fp20 can't do this
profiles ps_2_0 arbfp1
}
fragment_program Water/RefractReflectPS asm
{
source Example_FresnelPS.asm
// sorry, only for ps_1_4 :)
syntax ps_1_4
}
material Examples/Water0
{
technique
{
pass
{
//
depth_write off
vertex_program_ref Water/RefractReflectVP
{
param_named_auto worldViewProjMatrix worldviewproj_matrix
param_named_auto eyePosition camera_position_object_space
param_named_auto timeVal time 0.15
param_named scroll float 1
param_named scale float 1
param_named noise float 1
// scroll and noisePos will need updating per frame
}
fragment_program_ref Water/RefractReflectFP
{
param_named fresnelBias float -0.1
param_named fresnelScale float 0.8
param_named fresnelPower float 20
param_named tintColour float4 1 1 1 1
param_named noiseScale float 0.05
}
// Water
scene_blend alpha_blend
texture_unit
{
// Water texture
texture Water02.jpg
// min / mag filtering, no mip
filtering linear linear none
alpha_op_ex source1 src_manual src_current 0.9
}
// Noise
texture_unit
{
alpha_op_ex source1 src_manual src_current 0.9
// Perlin noise volume
texture waves2.dds
// min / mag filtering, no mip
filtering linear linear none
}
}
}
}

15
files/water/GlassFP.cg Normal file
View file

@ -0,0 +1,15 @@
sampler RT : register(s0);
sampler NormalMap : register(s1);
sampler CausticMap : register(s2);
float4 main_ps(float2 iTexCoord : TEXCOORD0,
float3 noiseCoord : TEXCOORD1,
uniform float4 tintColour) : COLOR
{
float4 normal = tex2D(NormalMap, noiseCoord);
return tex2D(RT, iTexCoord + normal.xy * 0.05) +
(tex2D(CausticMap, noiseCoord) / 5) +
tintColour ;
}

24
files/water/GlassVP.cg Normal file
View file

@ -0,0 +1,24 @@
void glass_vp
(
in float4 inPos : POSITION,
out float4 pos : POSITION,
out float2 uv0 : TEXCOORD0,
out float4 noiseCoord : TEXCOORD1,
uniform float4x4 worldViewProj,
uniform float timeVal,
uniform float scale
)
{
// Use standardise transform, so work accord with render system specific (RS depth, requires texture flipping, etc)
pos = mul(worldViewProj, inPos);
// The input positions adjusted by texel offsets, so clean up inaccuracies
inPos.xy = sign(inPos.xy);
// Convert to image-space
uv0 = (float2(inPos.x, -inPos.y) + 1.0f) * 0.5f;
noiseCoord = (pos + timeVal) * scale;
}

BIN
files/water/Water02.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
files/water/caustic_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

View file

@ -0,0 +1,21 @@
compositor Water
{
technique
{
texture rt0 target_width target_height PF_R8G8B8
target rt0 { input previous }
target_output
{
// Start with clear output
input none
pass render_quad
{
material Water/Compositor
input 0 rt0
}
}
}
}

BIN
files/water/waves2.dds Normal file

Binary file not shown.