diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 038535939..ef3063d83 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -120,6 +120,7 @@ namespace MWBase virtual bool toggleWater() = 0; virtual bool toggleWorld() = 0; + virtual bool toggleBorders() = 0; virtual void adjustSky() = 0; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 4fbcdc4d5..6e5b06038 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -181,6 +181,7 @@ namespace MWRender , mNightEyeFactor(0.f) , mFieldOfViewOverride(0.f) , mFieldOfViewOverridden(false) + , mBorders(false) { resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); @@ -219,9 +220,10 @@ namespace MWRender Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); if (distantTerrain) - mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); else - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); + mTerrain->setDefaultViewer(mViewer->getCamera()); mCamera.reset(new Camera(mViewer->getCamera())); @@ -441,6 +443,13 @@ namespace MWRender { mSky->setEnabled(enabled); } + + bool RenderingManager::toggleBorders() + { + mBorders = !mBorders; + mTerrain->setBordersVisible(mBorders); + return mBorders; + } bool RenderingManager::toggleRenderMode(RenderMode mode) { diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index f0087e43d..56839adce 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -205,6 +205,8 @@ namespace MWRender LandManager* getLandManager() const; + bool toggleBorders(); + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -257,6 +259,7 @@ namespace MWRender bool mFieldOfViewOverridden; float mFieldOfView; float mFirstPersonFieldOfView; + bool mBorders; void operator = (const RenderingManager&); RenderingManager(const RenderingManager&); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index e999097db..fed780be7 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -454,5 +454,6 @@ op 0x2000303: Fixme, explicit op 0x2000304: Show op 0x2000305: Show, explicit op 0x2000306: OnActivate, explicit +op 0x2000307: ToggleBorders, tb -opcodes 0x2000307-0x3ffffff unused +opcodes 0x2000308-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 1067b5536..632d9e97c 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -254,6 +254,20 @@ namespace MWScript } }; + class OpToggleBorders : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + bool enabled = + MWBase::Environment::get().getWorld()->toggleBorders(); + + runtime.getContext().report (enabled ? + "Border Rendering -> On" : "Border Rendering -> Off"); + } + }; + class OpTogglePathgrid : public Interpreter::Opcode0 { public: @@ -1374,6 +1388,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeRemoveFromLevItem, new OpRemoveFromLevItem); interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraph, new OpShowSceneGraph); interpreter.installSegment3 (Compiler::Misc::opcodeShowSceneGraphExplicit, new OpShowSceneGraph); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleBorders, new OpToggleBorders); } } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9c7fba9fa..af30b07d1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1919,6 +1919,11 @@ namespace MWWorld return mRendering->toggleRenderMode(MWRender::Render_Scene); } + bool World::toggleBorders() + { + return mRendering->toggleBorders(); + } + void World::PCDropped (const Ptr& item) { std::string script = item.getClass().getScript(item); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 7af7b2968..dc1ff4588 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -217,6 +217,7 @@ namespace MWWorld bool toggleWater() override; bool toggleWorld() override; + bool toggleBorders() override; void adjustSky() override; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index a0b426a16..d74f11fc2 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -120,7 +120,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode viewdata + storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode viewdata cellborder ) add_component_dir (loadinglistener diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index cd5bf7ef7..7638d0f78 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -318,6 +318,8 @@ namespace Compiler extensions.registerInstruction ("removefromlevcreature", "ccl", opcodeRemoveFromLevCreature); extensions.registerInstruction ("addtolevitem", "ccl", opcodeAddToLevItem); extensions.registerInstruction ("removefromlevitem", "ccl", opcodeRemoveFromLevItem); + extensions.registerInstruction ("tb", "", opcodeToggleBorders); + extensions.registerInstruction ("toggleborders", "", opcodeToggleBorders); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 6a6552467..aef92b311 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -295,6 +295,7 @@ namespace Compiler const int opcodeRemoveFromLevItem = 0x20002fe; const int opcodeShowSceneGraph = 0x2002f; const int opcodeShowSceneGraphExplicit = 0x20030; + const int opcodeToggleBorders = 0x2000307; } namespace Sky diff --git a/components/terrain/cellborder.cpp b/components/terrain/cellborder.cpp new file mode 100644 index 000000000..b74e2e9be --- /dev/null +++ b/components/terrain/cellborder.cpp @@ -0,0 +1,91 @@ +#include "cellborder.hpp" + +#include +#include +#include + +#include "world.hpp" + +namespace MWRender +{ + +CellBorder::CellBorder(Terrain::World *world, osg::Group *root): + mWorld(world), + mRoot(root), + mBorderRoot(0) +{ +} + +void CellBorder::createCellBorderGeometry(int x, int y) +{ + const int cellSize = 8192; + const int borderSegments = 40; + const float offset = 10.0; + + osg::Vec3 cellCorner = osg::Vec3(x * cellSize,y * cellSize,0); + + osg::ref_ptr vertices = new osg::Vec3Array; + osg::ref_ptr colors = new osg::Vec4Array; + osg::ref_ptr normals = new osg::Vec3Array; + + normals->push_back(osg::Vec3(0.0f,-1.0f, 0.0f)); + + float borderStep = cellSize / ((float) borderSegments); + + for (int i = 0; i <= 2 * borderSegments; ++i) + { + osg::Vec3f pos = i < borderSegments ? + osg::Vec3(i * borderStep,0.0f,0.0f) : + osg::Vec3(cellSize,(i - borderSegments) * borderStep,0.0f); + + pos += cellCorner; + pos += osg::Vec3f(0,0,mWorld->getHeightAt(pos) + offset); + + vertices->push_back(pos); + + osg::Vec4f col = i % 2 == 0 ? + osg::Vec4f(0,0,0,1) : + osg::Vec4f(1,1,0,1); + + colors->push_back(col); + } + + osg::ref_ptr border = new osg::Geometry; + border->setVertexArray(vertices.get()); + border->setNormalArray(normals.get()); + border->setNormalBinding(osg::Geometry::BIND_OVERALL); + border->setColorArray(colors.get()); + border->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + + border->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP,0,vertices->size())); + + osg::ref_ptr borderGeode = new osg::Geode; + borderGeode->addDrawable(border.get()); + + osg::StateSet *stateSet = borderGeode->getOrCreateStateSet(); + + osg::PolygonMode* polygonmode = new osg::PolygonMode; + polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); + stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON); + + mRoot->addChild(borderGeode); + + mBorderRoot = borderGeode; + + mCellBorderNodes[std::make_pair(x,y)] = borderGeode; +} + +void CellBorder::destroyCellBorderGeometry(int x, int y) +{ + CellGrid::iterator it = mCellBorderNodes.find(std::make_pair(x,y)); + + if (it == mCellBorderNodes.end()) + return; + + osg::ref_ptr borderNode = it->second; + mBorderRoot->removeChild(borderNode); + + mCellBorderNodes.erase(it); +} + +} diff --git a/components/terrain/cellborder.hpp b/components/terrain/cellborder.hpp new file mode 100644 index 000000000..6144f8c32 --- /dev/null +++ b/components/terrain/cellborder.hpp @@ -0,0 +1,36 @@ +#ifndef GAME_RENDER_CELLBORDER +#define GAME_RENDER_CELLBORDER + +#include +#include + +namespace Terrain +{ + class World; +} + +namespace MWRender +{ + /** + * @Brief Handles the debug cell borders. + */ + class CellBorder + { + public: + typedef std::map, osg::ref_ptr > CellGrid; + + CellBorder(Terrain::World *world, osg::Group *root); + + void createCellBorderGeometry(int x, int y); + void destroyCellBorderGeometry(int x, int y); + + protected: + Terrain::World *mWorld; + osg::Group *mRoot; + osg::Group *mBorderRoot; + + CellGrid mCellBorderNodes; + }; +} + +#endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index f31064805..f9bc7bb60 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -235,8 +235,8 @@ private: osg::ref_ptr mRootNode; }; -QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask) - : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask) +QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask) + : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index ef33f158e..c166a9cb1 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -19,7 +19,7 @@ namespace Terrain class QuadTreeWorld : public Terrain::World { public: - QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0); + QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0, int borderMask=0); ~QuadTreeWorld(); void accept(osg::NodeVisitor& nv); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 466cddddc..f5ab6f64b 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -17,8 +17,8 @@ public: virtual void reset(unsigned int frame) {} }; -TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask) - : Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask) +TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) + : Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask) , mNumSplits(4) { } @@ -75,6 +75,8 @@ void TerrainGrid::loadCell(int x, int y) if (!terrainNode) return; // no terrain defined + Terrain::World::loadCell(x,y); + mTerrainRoot->addChild(terrainNode); mGrid[std::make_pair(x,y)] = terrainNode; @@ -82,7 +84,9 @@ void TerrainGrid::loadCell(int x, int y) void TerrainGrid::unloadCell(int x, int y) { - Grid::iterator it = mGrid.find(std::make_pair(x,y)); + World::unloadCell(x,y); + + MWRender::CellBorder::CellGrid::iterator it = mGrid.find(std::make_pair(x,y)); if (it == mGrid.end()) return; diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 189fe7f63..f21dd39d3 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -7,6 +7,8 @@ #include "world.hpp" +#include "cellborder.hpp" + namespace Terrain { @@ -14,7 +16,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0); + TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0, int borderMask=0); ~TerrainGrid(); virtual void cacheCell(View* view, int x, int y); @@ -33,8 +35,7 @@ namespace Terrain // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; - typedef std::map, osg::ref_ptr > Grid; - Grid mGrid; + MWRender::CellBorder::CellGrid mGrid; }; } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 335bb496b..c08c02dc5 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -14,7 +14,7 @@ namespace Terrain { -World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask) +World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) : mStorage(storage) , mParent(parent) , mResourceSystem(resourceSystem) @@ -28,6 +28,12 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTerrainRoot->setName("Terrain Root"); + mBorderRoot = new osg::Switch; + mBorderRoot->setName("Border Root"); + mBorderRoot->setNodeMask(borderMask); + + mTerrainRoot->addChild(mBorderRoot); + osg::ref_ptr compositeCam = new osg::Camera; compositeCam->setRenderOrder(osg::Camera::PRE_RENDER, -1); compositeCam->setProjectionMatrix(osg::Matrix::identity()); @@ -39,7 +45,6 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst compileRoot->addChild(compositeCam); - mCompositeMapRenderer = new CompositeMapRenderer; compositeCam->addChild(mCompositeMapRenderer); @@ -48,8 +53,30 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); + mCellBorder.reset(new MWRender::CellBorder(this,mTerrainRoot.get())); + mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); + + setBordersVisible(false); +} + +void World::setBordersVisible(bool visible) +{ + if (visible) + mBorderRoot->setAllChildrenOn(); + else + mBorderRoot->setAllChildrenOff(); +} + +void World::loadCell(int x, int y) +{ + mCellBorder->createCellBorderGeometry(x,y); +} + +void World::unloadCell(int x, int y) +{ + mCellBorder->destroyCellBorderGeometry(x,y); } World::~World() diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e1c3828fc..0d043c035 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -4,11 +4,14 @@ #include #include #include +#include #include #include "defs.hpp" +#include "cellborder.hpp" + namespace osg { class Group; @@ -54,7 +57,7 @@ namespace Terrain /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root /// @param preCompileMask mask for pre compiling textures - World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask); + World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask); virtual ~World(); /// Apply the scene manager's texture filtering settings to all cached textures. @@ -73,16 +76,16 @@ namespace Terrain /// Load the cell into the scene graph. /// @note Not thread safe. - /// @note May be ignored by derived implementations that don't organize the terrain into cells. - virtual void loadCell(int x, int y) {} + virtual void loadCell(int x, int y); /// Remove the cell from the scene graph. /// @note Not thread safe. - /// @note May be ignored by derived implementations that don't organize the terrain into cells. - virtual void unloadCell(int x, int y) {} + virtual void unloadCell(int x, int y); virtual void enable(bool enabled) {} + void setBordersVisible(bool visible); + /// Create a View to use with preload feature. The caller is responsible for deleting the view. /// @note Thread safe. virtual View* createView() { return NULL; } @@ -98,10 +101,14 @@ namespace Terrain Storage* getStorage() { return mStorage; } protected: + void createCellBorderGeometry(int x, int y); + void destroyCellBorderGeometry(int x, int y); + Storage* mStorage; osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; + osg::ref_ptr mBorderRoot; osg::ref_ptr mCompositeMapCamera; osg::ref_ptr mCompositeMapRenderer; @@ -110,8 +117,9 @@ namespace Terrain std::unique_ptr mTextureManager; std::unique_ptr mChunkManager; - }; + std::unique_ptr mCellBorder; + }; } #endif