From c1fe9f2a89718b8027ff7d212cf1ff450fce93a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 00:21:49 +0100 Subject: [PATCH 01/88] Avoid warping the mouse cursor more than necessary Apparently, the SDL_WarpMouseInWindow can be very expensive (anywhere from 0.1-5ms) due to XSync() in the implementation. This was causing no-grab=1 configurations to suffer from terrible stuttering when turning the view. --- components/sdlutil/sdlinputwrapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/sdlutil/sdlinputwrapper.cpp b/components/sdlutil/sdlinputwrapper.cpp index 204680b44..c88210b0a 100644 --- a/components/sdlutil/sdlinputwrapper.cpp +++ b/components/sdlutil/sdlinputwrapper.cpp @@ -329,8 +329,8 @@ InputWrapper::InputWrapper(SDL_Window* window, osg::ref_ptr v SDL_GetWindowSize(mSDLWindow, &width, &height); - const int FUDGE_FACTOR_X = width; - const int FUDGE_FACTOR_Y = height; + const int FUDGE_FACTOR_X = width/4; + const int FUDGE_FACTOR_Y = height/4; //warp the mouse if it's about to go outside the window if(evt.x - FUDGE_FACTOR_X < 0 || evt.x + FUDGE_FACTOR_X > width From 4c9bbce1e27559eb6d2bdd3325f4514f52c2851e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 01:01:50 +0100 Subject: [PATCH 02/88] Fix node mask being reset --- apps/openmw/mwrender/sky.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 13fbbc99c..09c7af6a2 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -428,10 +428,12 @@ private: class CelestialBody { public: - CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets) + CelestialBody(osg::Group* parentNode, float scaleFactor, int numUvSets, unsigned int visibleMask=~0) + : mVisibleMask(visibleMask) { mGeom = createTexturedQuad(numUvSets); mTransform = new osg::PositionAttitudeTransform; + mTransform->setNodeMask(mVisibleMask); mTransform->setScale(osg::Vec3f(450,450,450) * scaleFactor); mTransform->addChild(mGeom); @@ -444,10 +446,11 @@ public: void setVisible(bool visible) { - mTransform->setNodeMask(visible ? ~0 : 0); + mTransform->setNodeMask(visible ? mVisibleMask : 0); } protected: + unsigned int mVisibleMask; static const float mDistance; osg::ref_ptr mTransform; osg::ref_ptr mGeom; @@ -459,11 +462,10 @@ class Sun : public CelestialBody { public: Sun(osg::Group* parentNode, Resource::ImageManager& imageManager) - : CelestialBody(parentNode, 1.0f, 1) + : CelestialBody(parentNode, 1.0f, 1, Mask_Sun) , mUpdater(new Updater) { mTransform->addUpdateCallback(mUpdater); - mTransform->setNodeMask(Mask_Sun); osg::ref_ptr sunTex (new osg::Texture2D(imageManager.getImage("textures/tx_sun_05.dds"))); sunTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); From 37c71c15f2c9f90c9bda36d5f8a92c429c91d122 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 04:57:36 +0100 Subject: [PATCH 03/88] Remove redundant state --- apps/openmw/mwrender/localmap.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0e854d5dc..0f77efafe 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -174,9 +174,7 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f camera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr stateset = new osg::StateSet; - stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); - stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON); - stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON); + // assign large value to effectively turn off fog // shaders don't respect glDisable(GL_FOG) osg::ref_ptr fog (new osg::Fog); From fe439e7bbf19e9883fc52203932f0042019110a8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 05:02:31 +0100 Subject: [PATCH 04/88] Add missing default material state for character preview --- apps/openmw/mwrender/characterpreview.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index 84d7932d4..9dc1d6e77 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -142,6 +143,13 @@ namespace MWRender stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON); stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON); stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON); + osg::ref_ptr defaultMat (new osg::Material); + defaultMat->setColorMode(osg::Material::OFF); + defaultMat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1)); + defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); + stateset->setAttribute(defaultMat); + // assign large value to effectively turn off fog // shaders don't respect glDisable(GL_FOG) osg::ref_ptr fog (new osg::Fog); From ccfebdd2c330720513de334bc9e0df563d358149 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 19:27:44 +0100 Subject: [PATCH 05/88] Set the underwater fog relative to default view distance --- apps/openmw/mwrender/renderingmanager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 5879c14c6..69aa30702 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -493,9 +493,11 @@ namespace MWRender mCurrentCameraPos = cameraPos; if (mWater->isUnderwater(cameraPos)) { + float viewDistance = mViewDistance; + viewDistance = std::min(viewDistance, 6666.f); setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight)); - mStateUpdater->setFogStart(mViewDistance * (1 - mUnderwaterFog)); - mStateUpdater->setFogEnd(mViewDistance); + mStateUpdater->setFogStart(viewDistance * (1 - mUnderwaterFog)); + mStateUpdater->setFogEnd(viewDistance); } else { From b78a9f89af54b24151d8b8a3a36b075a12666d81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Mar 2017 17:42:38 +0100 Subject: [PATCH 06/88] Refactor LightListCallback to allow for integration in custom Drawables. --- components/sceneutil/lightmanager.cpp | 36 +++++++++++++-------------- components/sceneutil/lightmanager.hpp | 8 ++++++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index bd8ef8af4..b421f87cf 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -381,21 +381,23 @@ namespace SceneUtil { osgUtil::CullVisitor* cv = static_cast(nv); + bool pushedState = pushLightState(node, cv); + traverse(node, nv); + if (pushedState) + cv->popStateSet(); + } + + bool LightListCallback::pushLightState(osg::Node *node, osgUtil::CullVisitor *cv) + { if (!mLightManager) { - mLightManager = findLightManager(nv->getNodePath()); + mLightManager = findLightManager(cv->getNodePath()); if (!mLightManager) - { - traverse(node, nv); - return; - } + return false; } if (!(cv->getCurrentCamera()->getCullMask() & mLightManager->getLightingMask())) - { - traverse(node, nv); - return; - } + return false; // Possible optimizations: // - cull list of lights by the camera frustum @@ -404,9 +406,9 @@ namespace SceneUtil // update light list if necessary // makes sure we don't update it more than once per frame when rendering with multiple cameras - if (mLastFrameNumber != nv->getTraversalNumber()) + if (mLastFrameNumber != cv->getTraversalNumber()) { - mLastFrameNumber = nv->getTraversalNumber(); + mLastFrameNumber = cv->getTraversalNumber(); // Don't use Camera::getViewMatrix, that one might be relative to another camera! const osg::RefMatrix* viewMatrix = cv->getCurrentRenderStage()->getInitialViewMatrix(); @@ -469,20 +471,16 @@ namespace SceneUtil while (lightList.size() > maxLights) lightList.pop_back(); } - stateset = mLightManager->getLightListStateSet(lightList, nv->getTraversalNumber()); + stateset = mLightManager->getLightListStateSet(lightList, cv->getTraversalNumber()); } else - stateset = mLightManager->getLightListStateSet(mLightList, nv->getTraversalNumber()); + stateset = mLightManager->getLightListStateSet(mLightList, cv->getTraversalNumber()); cv->pushStateSet(stateset); - - traverse(node, nv); - - cv->popStateSet(); + return true; } - else - traverse(node, nv); + return false; } } diff --git a/components/sceneutil/lightmanager.hpp b/components/sceneutil/lightmanager.hpp index 2c7a6a3d8..55d0c69cd 100644 --- a/components/sceneutil/lightmanager.hpp +++ b/components/sceneutil/lightmanager.hpp @@ -9,6 +9,11 @@ #include #include +namespace osgUtil +{ + class CullVisitor; +} + namespace SceneUtil { @@ -148,6 +153,7 @@ namespace SceneUtil /// rendering when the size of a light list exceeds the OpenGL limit on the number of concurrent lights (8). A good /// starting point is to attach a LightListCallback to each game object's base node. /// @note Not thread safe for CullThreadPerCamera threading mode. + /// @note Due to lack of OSG support, the callback does not work on Drawables. class LightListCallback : public osg::NodeCallback { public: @@ -166,6 +172,8 @@ namespace SceneUtil void operator()(osg::Node* node, osg::NodeVisitor* nv); + bool pushLightState(osg::Node* node, osgUtil::CullVisitor* nv); + std::set& getIgnoredLightSources() { return mIgnoredLightSources; } private: From 34130fc5ccfb36c9f65d705b0dd0a2587385204d Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Mar 2017 18:20:47 +0100 Subject: [PATCH 07/88] Fix handling in LightListCallback when the node is not a Group --- components/sceneutil/lightmanager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/sceneutil/lightmanager.cpp b/components/sceneutil/lightmanager.cpp index b421f87cf..341bc8034 100644 --- a/components/sceneutil/lightmanager.cpp +++ b/components/sceneutil/lightmanager.cpp @@ -417,12 +417,14 @@ namespace SceneUtil // get the node bounds in view space // NB do not node->getBound() * modelView, that would apply the node's transformation twice osg::BoundingSphere nodeBound; - osg::Group* group = node->asGroup(); - if (group) + osg::Transform* transform = node->asTransform(); + if (transform) { - for (unsigned int i=0; igetNumChildren(); ++i) - nodeBound.expandBy(group->getChild(i)->getBound()); + for (unsigned int i=0; igetNumChildren(); ++i) + nodeBound.expandBy(transform->getChild(i)->getBound()); } + else + nodeBound = node->getBound(); osg::Matrixf mat = *cv->getModelViewMatrix(); transformBoundingSphere(mat, nodeBound); From eef63a880a26b1650846226d431399682acb8d05 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 3 Mar 2017 18:26:40 +0100 Subject: [PATCH 08/88] terrain: use a custom drawable for multi-pass render instead of osgFX::Effect osgFX::Effect is awkward to use because of the lazy-definition of passes, no support for compileGLObjects, useless 'Technique' abstraction and having to define silly methods like 'effectAuthor()' Handling the multi-pass rendering inside the Drawable also avoids redundant culling tests against the same bounding box for each pass. --- components/CMakeLists.txt | 3 +- components/terrain/material.cpp | 171 +++++++++---------------- components/terrain/material.hpp | 61 +-------- components/terrain/terraindrawable.cpp | 85 ++++++++++++ components/terrain/terraindrawable.hpp | 51 ++++++++ components/terrain/terraingrid.cpp | 34 ++--- 6 files changed, 213 insertions(+), 192 deletions(-) create mode 100644 components/terrain/terraindrawable.cpp create mode 100644 components/terrain/terraindrawable.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 1035558af..9220f91ad 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material + storage world buffercache defs terraingrid material terraindrawable ) add_component_dir (loadinglistener @@ -204,7 +204,6 @@ target_link_libraries(components ${OSGVIEWER_LIBRARIES} ${OSGTEXT_LIBRARIES} ${OSGGA_LIBRARIES} - ${OSGFX_LIBRARIES} ${OSGANIMATION_LIBRARIES} ${Bullet_LIBRARIES} ${SDL2_LIBRARIES} diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 323f41798..496a2cc75 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -58,11 +58,13 @@ namespace Terrain return depth; } - FixedFunctionTechnique::FixedFunctionTechnique(const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize) + std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { + std::vector > passes; + bool firstLayer = true; - int i=0; + unsigned int blendmapIndex = 0; + unsigned int passIndex = 0; for (std::vector::const_iterator it = layers.begin(); it != layers.end(); ++it) { osg::ref_ptr stateset (new osg::StateSet); @@ -70,138 +72,91 @@ namespace Terrain if (!firstLayer) { stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setAttributeAndModes(getEqualDepth(), osg::StateAttribute::ON); } int texunit = 0; - if(!firstLayer) + + if (useShaders) { - osg::ref_ptr blendmap = blendmaps.at(i++); + stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap); - stateset->setTextureAttributeAndModes(texunit, blendmap.get()); + stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); - // This is to map corner vertices directly to the center of a blendmap texel. - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); + stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); - static osg::ref_ptr texEnvCombine; - if (!texEnvCombine) + if(!firstLayer) { - texEnvCombine = new osg::TexEnvCombine; - texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); - texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); - } - - stateset->setTextureAttributeAndModes(texunit, texEnvCombine, osg::StateAttribute::ON); - - ++texunit; - } - - // Add the actual layer texture multiplied by the alpha map. - osg::ref_ptr tex = it->mDiffuseMap; - stateset->setTextureAttributeAndModes(texunit, tex.get()); - - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + ++texunit; + osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); - firstLayer = false; + stateset->setTextureAttributeAndModes(texunit, blendmap.get()); - addPass(stateset); - } - } + stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); + stateset->addUniform(new osg::Uniform("blendMap", texunit)); + } - ShaderTechnique::ShaderTechnique(Shader::ShaderManager& shaderManager, bool forcePerPixelLighting, bool clampLighting, const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize) - { - bool firstLayer = true; - int i=0; - for (std::vector::const_iterator it = layers.begin(); it != layers.end(); ++it) - { - osg::ref_ptr stateset (new osg::StateSet); + if (it->mNormalMap) + { + ++texunit; + stateset->setTextureAttributeAndModes(texunit, it->mNormalMap); + stateset->addUniform(new osg::Uniform("normalMap", texunit)); + } - if (!firstLayer) - { - stateset->setMode(GL_BLEND, osg::StateAttribute::ON); - stateset->setAttributeAndModes(getEqualDepth(), osg::StateAttribute::ON); + Shader::ShaderManager::DefineMap defineMap; + defineMap["forcePPL"] = forcePerPixelLighting ? "1" : "0"; + defineMap["clamp"] = clampLighting ? "1" : "0"; + defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; + defineMap["blendMap"] = !firstLayer ? "1" : "0"; + defineMap["colorMode"] = "2"; + defineMap["specularMap"] = it->mSpecular ? "1" : "0"; + defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0"; + + osg::ref_ptr vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX); + osg::ref_ptr fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT); + if (!vertexShader || !fragmentShader) + throw std::runtime_error("Unable to create shader"); + + stateset->setAttributeAndModes(shaderManager->getProgram(vertexShader, fragmentShader)); } + else + { + if(!firstLayer) + { + osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); - int texunit = 0; - - stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap); + stateset->setTextureAttributeAndModes(texunit, blendmap.get()); - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + // This is to map corner vertices directly to the center of a blendmap texel. + stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); - stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); + static osg::ref_ptr texEnvCombine; + if (!texEnvCombine) + { + texEnvCombine = new osg::TexEnvCombine; + texEnvCombine->setCombine_RGB(osg::TexEnvCombine::REPLACE); + texEnvCombine->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); + } - if(!firstLayer) - { - ++texunit; - osg::ref_ptr blendmap = blendmaps.at(i++); + stateset->setTextureAttributeAndModes(texunit, texEnvCombine, osg::StateAttribute::ON); - stateset->setTextureAttributeAndModes(texunit, blendmap.get()); + ++texunit; + } - stateset->setTextureAttributeAndModes(texunit, getBlendmapTexMat(blendmapScale)); - stateset->addUniform(new osg::Uniform("blendMap", texunit)); - } + // Add the actual layer texture multiplied by the alpha map. + osg::ref_ptr tex = it->mDiffuseMap; + stateset->setTextureAttributeAndModes(texunit, tex.get()); - if (it->mNormalMap) - { - ++texunit; - stateset->setTextureAttributeAndModes(texunit, it->mNormalMap); - stateset->addUniform(new osg::Uniform("normalMap", texunit)); + stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); } - Shader::ShaderManager::DefineMap defineMap; - defineMap["forcePPL"] = forcePerPixelLighting ? "1" : "0"; - defineMap["clamp"] = clampLighting ? "1" : "0"; - defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; - defineMap["blendMap"] = !firstLayer ? "1" : "0"; - defineMap["colorMode"] = "2"; - defineMap["specularMap"] = it->mSpecular ? "1" : "0"; - defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0"; - - osg::ref_ptr vertexShader = shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX); - osg::ref_ptr fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT); - if (!vertexShader || !fragmentShader) - throw std::runtime_error("Unable to create shader"); - - stateset->setAttributeAndModes(shaderManager.getProgram(vertexShader, fragmentShader)); - firstLayer = false; - addPass(stateset); - } - } - - Effect::Effect(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, - int blendmapScale, float layerTileSize) - : mShaderManager(shaderManager) - , mUseShaders(useShaders) - , mForcePerPixelLighting(forcePerPixelLighting) - , mClampLighting(clampLighting) - , mLayers(layers) - , mBlendmaps(blendmaps) - , mBlendmapScale(blendmapScale) - , mLayerTileSize(layerTileSize) - { - selectTechnique(0); - } + stateset->setRenderBinDetails(passIndex++, "RenderBin"); - bool Effect::define_techniques() - { - try - { - if (mUseShaders && mShaderManager) - addTechnique(new ShaderTechnique(*mShaderManager, mForcePerPixelLighting, mClampLighting, mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize)); - else - addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize)); + passes.push_back(stateset); } - catch (std::exception& e) - { - std::cerr << "Error: " << e.what() << std::endl; - addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps, mBlendmapScale, mLayerTileSize)); - } - - return true; + return passes; } } diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index c13b21e8f..0ffd4db54 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -1,8 +1,7 @@ #ifndef COMPONENTS_TERRAIN_MATERIAL_H #define COMPONENTS_TERRAIN_MATERIAL_H -#include -#include +#include #include "defs.hpp" @@ -27,61 +26,9 @@ namespace Terrain bool mSpecular; }; - class FixedFunctionTechnique : public osgFX::Technique - { - public: - FixedFunctionTechnique( - const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize); - - protected: - virtual void define_passes() {} - }; - - class ShaderTechnique : public osgFX::Technique - { - public: - ShaderTechnique(Shader::ShaderManager& shaderManager, bool forcePerPixelLighting, bool clampLighting, - const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize); - - protected: - virtual void define_passes() {} - }; - - class Effect : public osgFX::Effect - { - public: - Effect(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, - const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize); - - virtual bool define_techniques(); - - virtual const char *effectName() const - { - return NULL; - } - virtual const char *effectDescription() const - { - return NULL; - } - virtual const char *effectAuthor() const - { - return NULL; - } - - private: - Shader::ShaderManager* mShaderManager; - bool mUseShaders; - bool mForcePerPixelLighting; - bool mClampLighting; - std::vector mLayers; - std::vector > mBlendmaps; - int mBlendmapScale; - float mLayerTileSize; - }; - + std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, + const std::vector& layers, + const std::vector >& blendmaps, int blendmapScale, float layerTileSize); } #endif diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp new file mode 100644 index 000000000..b5934eb12 --- /dev/null +++ b/components/terrain/terraindrawable.cpp @@ -0,0 +1,85 @@ +#include "terraindrawable.hpp" + +#include + +#include + +namespace Terrain +{ + +TerrainDrawable::TerrainDrawable() +{ + mLightListCallback = new SceneUtil::LightListCallback; +} + +TerrainDrawable::TerrainDrawable(const TerrainDrawable ©, const osg::CopyOp ©op) + : osg::Geometry(copy, copyop) + , mPasses(copy.mPasses) + , mLightListCallback(copy.mLightListCallback) +{ + +} + +void TerrainDrawable::accept(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) + { + osg::Geometry::accept(nv); + } + else if (nv.validNodeMask(*this)) + { + nv.pushOntoNodePath(this); + cull(static_cast(&nv)); + nv.popFromNodePath(); + } +} + +inline float distance(const osg::Vec3& coord,const osg::Matrix& matrix) +{ + return -((float)coord[0]*(float)matrix(0,2)+(float)coord[1]*(float)matrix(1,2)+(float)coord[2]*(float)matrix(2,2)+matrix(3,2)); +} + +void TerrainDrawable::cull(osgUtil::CullVisitor *cv) +{ + const osg::BoundingBox& bb = getBoundingBox(); + + if (cv->isCulled(getBoundingBox())) + return; + + osg::RefMatrix& matrix = *cv->getModelViewMatrix(); + + float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f; + if (osg::isNaN(depth)) + return; + + bool pushedLight = mLightListCallback->pushLightState(this, cv); + + for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) + { + cv->pushStateSet(*it); + cv->addDrawableAndDepth(this, &matrix, depth); + cv->popStateSet(); + } + + if (pushedLight) + cv->popStateSet(); +} + +void TerrainDrawable::setPasses(const TerrainDrawable::PassVector &passes) +{ + mPasses = passes; +} + +void TerrainDrawable::compileGLObjects(osg::RenderInfo &renderInfo) const +{ + for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) + { + osg::StateSet* stateset = *it; + stateset->compileGLObjects(*renderInfo.getState()); + } + + osg::Geometry::compileGLObjects(renderInfo); +} + +} + diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp new file mode 100644 index 000000000..d7c2bfa72 --- /dev/null +++ b/components/terrain/terraindrawable.hpp @@ -0,0 +1,51 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_DRAWABLE_H +#define OPENMW_COMPONENTS_TERRAIN_DRAWABLE_H + +#include + +namespace osgUtil +{ + class CullVisitor; +} + +namespace SceneUtil +{ + class LightListCallback; +} + +namespace Terrain +{ + + /** + * Subclass of Geometry that supports built in multi-pass rendering and built in LightListCallback. + */ + class TerrainDrawable : public osg::Geometry + { + public: + virtual osg::Object* cloneType() const { return new TerrainDrawable (); } + virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new TerrainDrawable (*this,copyop); } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual const char* className() const { return "TerrainDrawable"; } + virtual const char* libraryName() const { return "Terrain"; } + + TerrainDrawable(); + TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop); + + virtual void accept(osg::NodeVisitor &nv); + void cull(osgUtil::CullVisitor* cv); + + typedef std::vector > PassVector; + void setPasses (const PassVector& passes); + + virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; + + private: + PassVector mPasses; + + osg::ref_ptr mLightListCallback; + }; + +} + + +#endif diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 6113178d3..a542c8cee 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -3,6 +3,9 @@ #include #include +#include + +#include #include @@ -16,15 +19,10 @@ #include -#include -#include - -#include - -#include #include "material.hpp" #include "storage.hpp" +#include "terraindrawable.hpp" namespace { @@ -124,7 +122,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu mStorage->fillVertexBuffers(0, chunkSize, chunkCenter, positions, normals, colors); - osg::ref_ptr geometry (new osg::Geometry); + osg::ref_ptr geometry (new TerrainDrawable); geometry->setVertexArray(positions); geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); @@ -147,10 +145,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu std::vector > blendmaps; mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); - // For compiling textures, I don't think the osgFX::Effect does it correctly - osg::ref_ptr textureCompileDummy (new osg::Node); - unsigned int dummyTextureCounter = 0; - bool useShaders = mResourceSystem->getSceneManager()->getForceShaders(); if (!mResourceSystem->getSceneManager()->getClampLighting()) useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps @@ -172,7 +166,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu mTextureCache[it->mDiffuseMap] = texture; } textureLayer.mDiffuseMap = texture; - textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, texture); if (!it->mNormalMap.empty()) { @@ -185,7 +178,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu mResourceSystem->getSceneManager()->applyFilterSettings(texture); mTextureCache[it->mNormalMap] = texture; } - textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, texture); textureLayer.mNormalMap = texture; } @@ -205,8 +197,6 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); texture->setResizeNonPowerOfTwoHint(false); blendmapTextures.push_back(texture); - - textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(dummyTextureCounter++, blendmapTextures.back()); } // use texture coordinates for both texture units, the layer texture and blend texture @@ -214,21 +204,15 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu geometry->setTexCoordArray(i, mCache.getUVBuffer()); float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize; - osg::ref_ptr effect (new Terrain::Effect(mShaderManager ? useShaders : false, mResourceSystem->getSceneManager()->getForcePerPixelLighting(), mResourceSystem->getSceneManager()->getClampLighting(), - mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); - - effect->addCullCallback(new SceneUtil::LightListCallback); - - transform->addChild(effect); - osg::Node* toAttach = geometry.get(); + geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mResourceSystem->getSceneManager()->getForcePerPixelLighting(), + mResourceSystem->getSceneManager()->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); - effect->addChild(toAttach); + transform->addChild(geometry); if (mIncrementalCompileOperation) { - mIncrementalCompileOperation->add(toAttach); - mIncrementalCompileOperation->add(textureCompileDummy); + mIncrementalCompileOperation->add(geometry); } return transform; From 9d72d9f0c93555a2f902d8e9a9e7b0ca490cfd1e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 16:07:03 +0100 Subject: [PATCH 09/88] Change order of operations in SceneManager::updateCache to allow deleting of StateSets that just got unreferenced by the scene --- components/resource/scenemanager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index 698060597..fc31b00a0 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -689,13 +689,13 @@ namespace Resource void SceneManager::updateCache(double referenceTime) { - mSharedStateMutex.lock(); - mSharedStateManager->prune(); - mSharedStateMutex.unlock(); - ResourceManager::updateCache(referenceTime); mInstanceCache->removeUnreferencedObjectsInCache(); + + mSharedStateMutex.lock(); + mSharedStateManager->prune(); + mSharedStateMutex.unlock(); } void SceneManager::reportStats(unsigned int frameNumber, osg::Stats *stats) From 4cd4457d21744e8f4b39eed2e88b1ce45df3d711 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 16:19:32 +0100 Subject: [PATCH 10/88] Add support for Functors to ObjectCache --- components/resource/objectcache.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/resource/objectcache.hpp b/components/resource/objectcache.hpp index e1caa4123..72db835e8 100644 --- a/components/resource/objectcache.hpp +++ b/components/resource/objectcache.hpp @@ -73,6 +73,15 @@ class ObjectCache : public osg::Referenced /** call node->accept(nv); for all nodes in the objectCache. */ void accept(osg::NodeVisitor& nv); + /** call operator()(osg::Object*) for each object in the cache. */ + template + void call(Functor& f) + { + OpenThreads::ScopedLock lock(_objectCacheMutex); + for (ObjectCacheMap::iterator it = _objectCache.begin(); it != _objectCache.end(); ++it) + f(it->second.first.get()); + } + /** Get the number of objects in the cache. */ unsigned int getCacheSize() const; From 804f873649fa7363c8eade47f981f7b3e4046fba Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 16:32:56 +0100 Subject: [PATCH 11/88] terrain: factor out texture caching into a separate class --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- components/CMakeLists.txt | 2 +- components/terrain/terraingrid.cpp | 46 +++---------------- components/terrain/terraingrid.hpp | 6 +-- components/terrain/texturemanager.cpp | 64 +++++++++++++++++++++++++++ components/terrain/texturemanager.hpp | 39 ++++++++++++++++ components/terrain/world.cpp | 9 ++++ components/terrain/world.hpp | 8 +++- 8 files changed, 128 insertions(+), 48 deletions(-) create mode 100644 components/terrain/texturemanager.cpp create mode 100644 components/terrain/texturemanager.hpp diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 278efa53d..c870369c9 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -174,7 +174,7 @@ namespace MWWorld { mResourceSystem->updateCache(mReferenceTime); - mTerrain->updateCache(); + mTerrain->updateCache(mReferenceTime); } private: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 9220f91ad..c1b329942 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable + storage world buffercache defs terraingrid material terraindrawable texturemanager ) add_component_dir (loadinglistener diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a542c8cee..ffb30c04a 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -23,6 +23,7 @@ #include "material.hpp" #include "storage.hpp" #include "terraindrawable.hpp" +#include "texturemanager.hpp" namespace { @@ -150,35 +151,17 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps std::vector layers; { - OpenThreads::ScopedLock lock(mTextureCacheMutex); for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) { TextureLayer textureLayer; textureLayer.mParallax = it->mParallax; textureLayer.mSpecular = it->mSpecular; - osg::ref_ptr texture = mTextureCache[it->mDiffuseMap]; - if (!texture) - { - texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mDiffuseMap)); - texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); - mResourceSystem->getSceneManager()->applyFilterSettings(texture); - mTextureCache[it->mDiffuseMap] = texture; - } - textureLayer.mDiffuseMap = texture; + + textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap); if (!it->mNormalMap.empty()) { - texture = mTextureCache[it->mNormalMap]; - if (!texture) - { - texture = new osg::Texture2D(mResourceSystem->getImageManager()->getImage(it->mNormalMap)); - texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); - texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); - mResourceSystem->getSceneManager()->applyFilterSettings(texture); - mTextureCache[it->mNormalMap] = texture; - } - textureLayer.mNormalMap = texture; + textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap); } if (it->requiresShaders()) @@ -266,7 +249,7 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } -void TerrainGrid::updateCache() +void TerrainGrid::updateCache(double referenceTime) { { OpenThreads::ScopedLock lock(mGridCacheMutex); @@ -278,24 +261,11 @@ void TerrainGrid::updateCache() ++it; } } - - { - OpenThreads::ScopedLock lock(mTextureCacheMutex); - for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end();) - { - if (it->second->referenceCount() <= 1) - mTextureCache.erase(it++); - else - ++it; - } - } } void TerrainGrid::updateTextureFiltering() { - OpenThreads::ScopedLock lock(mTextureCacheMutex); - for (TextureCache::iterator it = mTextureCache.begin(); it != mTextureCache.end(); ++it) - mResourceSystem->getSceneManager()->applyFilterSettings(it->second); + mTextureManager->updateTextureFiltering(); } void TerrainGrid::reportStats(unsigned int frameNumber, osg::Stats *stats) @@ -304,10 +274,6 @@ void TerrainGrid::reportStats(unsigned int frameNumber, osg::Stats *stats) OpenThreads::ScopedLock lock(mGridCacheMutex); stats->setAttribute(frameNumber, "Terrain Cell", mGridCache.size()); } - { - OpenThreads::ScopedLock lock(mTextureCacheMutex); - stats->setAttribute(frameNumber, "Terrain Texture", mTextureCache.size()); - } } } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index d619ed8f7..7f6ea2d30 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -43,7 +43,7 @@ namespace Terrain /// Clear cached objects that are no longer referenced /// @note Thread safe. - void updateCache(); + void updateCache(double referenceTime); /// Apply the scene manager's texture filtering settings to all cached textures. /// @note Thread safe. @@ -57,10 +57,6 @@ namespace Terrain // split each ESM::Cell into mNumSplits*mNumSplits terrain chunks unsigned int mNumSplits; - typedef std::map > TextureCache; - TextureCache mTextureCache; - OpenThreads::Mutex mTextureCacheMutex; - typedef std::map, osg::ref_ptr > Grid; Grid mGrid; diff --git a/components/terrain/texturemanager.cpp b/components/terrain/texturemanager.cpp new file mode 100644 index 000000000..dd1e87bf6 --- /dev/null +++ b/components/terrain/texturemanager.cpp @@ -0,0 +1,64 @@ +#include "texturemanager.hpp" + +#include +#include + +#include +#include +#include + +namespace Terrain +{ + +TextureManager::TextureManager(Resource::SceneManager *sceneMgr) + : ResourceManager(sceneMgr->getVFS()) + , mSceneManager(sceneMgr) +{ + +} + +struct UpdateTextureFilteringFunctor +{ + UpdateTextureFilteringFunctor(Resource::SceneManager* sceneMgr) + : mSceneManager(sceneMgr) + { + } + Resource::SceneManager* mSceneManager; + + void operator()(osg::Object* obj) + { + mSceneManager->applyFilterSettings(static_cast(obj)); + } +}; + +void TextureManager::updateTextureFiltering() +{ + UpdateTextureFilteringFunctor f(mSceneManager); + mCache->call(f); +} + +osg::ref_ptr TextureManager::getTexture(const std::string &name) +{ + // don't bother with case folding, since there is only one way of referring to terrain textures we can assume the case is always the same + osg::ref_ptr obj = mCache->getRefFromObjectCache(name); + if (obj) + return static_cast(obj.get()); + else + { + osg::ref_ptr texture (new osg::Texture2D(mSceneManager->getImageManager()->getImage(name))); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT); + mSceneManager->applyFilterSettings(texture); + mCache->addEntryToObjectCache(name, texture.get()); + return texture; + } +} + +void TextureManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +{ + stats->setAttribute(frameNumber, "Terrain Texture", mCache->getCacheSize()); +} + + + +} diff --git a/components/terrain/texturemanager.hpp b/components/terrain/texturemanager.hpp new file mode 100644 index 000000000..9aba2f092 --- /dev/null +++ b/components/terrain/texturemanager.hpp @@ -0,0 +1,39 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H +#define OPENMW_COMPONENTS_TERRAIN_TEXTUREMANAGER_H + +#include + +#include + +namespace Resource +{ + class SceneManager; +} + +namespace osg +{ + class Texture2D; +} + +namespace Terrain +{ + + class TextureManager : public Resource::ResourceManager + { + public: + TextureManager(Resource::SceneManager* sceneMgr); + + void updateTextureFiltering(); + + osg::ref_ptr getTexture(const std::string& name); + + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + + private: + Resource::SceneManager* mSceneManager; + + }; + +} + +#endif diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index f699cdc75..329ed5082 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -3,7 +3,10 @@ #include #include +#include + #include "storage.hpp" +#include "texturemanager.hpp" namespace Terrain { @@ -21,10 +24,16 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt mTerrainRoot->setName("Terrain Root"); mParent->addChild(mTerrainRoot); + + mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); + + mResourceSystem->addResourceManager(mTextureManager.get()); } World::~World() { + mResourceSystem->removeResourceManager(mTextureManager.get()); + mParent->removeChild(mTerrainRoot); delete mStorage; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index cc2285e45..30d765e2d 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -3,6 +3,8 @@ #include +#include + #include "defs.hpp" #include "buffercache.hpp" @@ -26,6 +28,8 @@ namespace Terrain { class Storage; + class TextureManager; + /** * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed * is up to the implementation. @@ -42,7 +46,7 @@ namespace Terrain virtual void updateTextureFiltering() {} - virtual void updateCache() {} + virtual void updateCache(double referenceTime) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} @@ -65,6 +69,8 @@ namespace Terrain Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mIncrementalCompileOperation; + + std::auto_ptr mTextureManager; }; } From 5f763178079daec7c966584cbedc66daaf516c76 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 18:14:43 +0100 Subject: [PATCH 12/88] Wait for completion of CreateMapItem on exit to avoid potential threading issue --- apps/openmw/mwrender/globalmap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwrender/globalmap.cpp b/apps/openmw/mwrender/globalmap.cpp index 2536818ef..24f6de6ce 100644 --- a/apps/openmw/mwrender/globalmap.cpp +++ b/apps/openmw/mwrender/globalmap.cpp @@ -238,6 +238,9 @@ namespace MWRender removeCamera(*it); for (CameraVector::iterator it = mActiveCameras.begin(); it != mActiveCameras.end(); ++it) removeCamera(*it); + + if (mWorkItem) + mWorkItem->waitTillDone(); } void GlobalMap::render () From b898315962bec435cb3f46db6bd9e6a469b0a04a Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 18:15:53 +0100 Subject: [PATCH 13/88] cellpreloader: abort all tasks first before waiting --- apps/openmw/mwworld/cellpreloader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index c870369c9..60c1f9ca0 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -198,10 +198,11 @@ namespace MWWorld CellPreloader::~CellPreloader() { for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) - { it->second.mWorkItem->abort(); + + for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) it->second.mWorkItem->waitTillDone(); - } + mPreloadCells.clear(); } From 9a3a64f0c4972095ae08c9909d883b41cb672e83 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 19:04:17 +0100 Subject: [PATCH 14/88] Add resource manager for ESM::Land to allow data to be unloaded when no longer required --- apps/opencs/view/render/terrainstorage.cpp | 4 +- apps/opencs/view/render/terrainstorage.hpp | 2 +- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwphysics/physicssystem.cpp | 9 +- apps/openmw/mwphysics/physicssystem.hpp | 3 +- apps/openmw/mwrender/landmanager.cpp | 48 +++++++++++ apps/openmw/mwrender/landmanager.hpp | 33 ++++++++ apps/openmw/mwrender/renderingmanager.cpp | 16 +++- apps/openmw/mwrender/renderingmanager.hpp | 5 ++ apps/openmw/mwrender/terrainstorage.cpp | 35 ++++---- apps/openmw/mwrender/terrainstorage.hpp | 22 ++++- apps/openmw/mwworld/cellpreloader.cpp | 12 ++- apps/openmw/mwworld/cellpreloader.hpp | 8 +- apps/openmw/mwworld/scene.cpp | 26 +++--- components/esmterrain/storage.cpp | 96 ++++++++++++++++------ components/esmterrain/storage.hpp | 35 +++++--- components/resource/stats.cpp | 2 +- 17 files changed, 266 insertions(+), 92 deletions(-) create mode 100644 apps/openmw/mwrender/landmanager.cpp create mode 100644 apps/openmw/mwrender/landmanager.hpp diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 2be4efd73..9894ce17c 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -9,7 +9,7 @@ namespace CSVRender { } - const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) { std::ostringstream stream; stream << "#" << cellX << " " << cellY; @@ -23,7 +23,7 @@ namespace CSVRender const ESM::Land& land = mData.getLand().getRecord(index).get(); int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; land.loadData (mask); - return &land; + return new ESMTerrain::LandObject(&land, 0); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) diff --git a/apps/opencs/view/render/terrainstorage.hpp b/apps/opencs/view/render/terrainstorage.hpp index 16b0f3ec7..949311248 100644 --- a/apps/opencs/view/render/terrainstorage.hpp +++ b/apps/opencs/view/render/terrainstorage.hpp @@ -18,7 +18,7 @@ namespace CSVRender private: const CSMWorld::Data& mData; - virtual const ESM::Land* getLand (int cellX, int cellY); + virtual osg::ref_ptr getLand (int cellX, int cellY); virtual const ESM::LandTexture* getLandTexture(int index, short plugin); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index baa523654..a06678488 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -23,7 +23,7 @@ add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation - renderbin actoranimation + renderbin actoranimation landmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 9844667ae..3193e71a4 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -516,7 +516,7 @@ namespace MWPhysics class HeightField { public: - HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts) + HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) { // find the minimum and maximum heights (needed for bullet) float minh = heights[0]; @@ -544,6 +544,8 @@ namespace MWPhysics mCollisionObject = new btCollisionObject; mCollisionObject->setCollisionShape(mShape); mCollisionObject->setWorldTransform(transform); + + mHoldObject = holdObject; } ~HeightField() { @@ -558,6 +560,7 @@ namespace MWPhysics private: btHeightfieldTerrainShape* mShape; btCollisionObject* mCollisionObject; + osg::ref_ptr mHoldObject; void operator=(const HeightField&); HeightField(const HeightField&); @@ -1140,9 +1143,9 @@ namespace MWPhysics return MovementSolver::traceDown(ptr, position, found->second, mCollisionWorld, maxHeight); } - void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts) + void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) { - HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts); + HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts, holdObject); mHeightFields[std::make_pair(x,y)] = heightfield; mCollisionWorld->addCollisionObject(heightfield->getCollisionObject(), CollisionType_HeightMap, diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index 9730cda74..ae585281b 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -15,6 +15,7 @@ namespace osg { class Group; + class Object; } namespace MWRender @@ -80,7 +81,7 @@ namespace MWPhysics void updatePosition (const MWWorld::Ptr& ptr); - void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts); + void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject); void removeHeightField (int x, int y); diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp new file mode 100644 index 000000000..082a136d5 --- /dev/null +++ b/apps/openmw/mwrender/landmanager.cpp @@ -0,0 +1,48 @@ +#include "landmanager.hpp" + +#include + +#include + +#include + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" + +namespace MWRender +{ + +LandManager::LandManager(int loadFlags) + : ResourceManager(NULL) + , mLoadFlags(loadFlags) +{ +} + +osg::ref_ptr LandManager::getLand(int x, int y) +{ + std::ostringstream id; + id << x << " " << y; + std::string idstr = id.str(); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(idstr); + if (obj) + return static_cast(obj.get()); + else + { + const ESM::Land* land = MWBase::Environment::get().getWorld()->getStore().get().search(x,y); + if (!land) + return NULL; + osg::ref_ptr landObj (new ESMTerrain::LandObject(land, mLoadFlags)); + mCache->addEntryToObjectCache(idstr, landObj.get()); + return landObj; + } +} + +void LandManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +{ + stats->setAttribute(frameNumber, "Land", mCache->getCacheSize()); +} + + +} diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp new file mode 100644 index 000000000..4fd05f064 --- /dev/null +++ b/apps/openmw/mwrender/landmanager.hpp @@ -0,0 +1,33 @@ +#ifndef OPENMW_COMPONENTS_ESMTERRAIN_LANDMANAGER_H +#define OPENMW_COMPONENTS_ESMTERRAIN_LANDMANAGER_H + +#include + +#include +#include + +namespace ESM +{ + struct Land; +} + +namespace MWRender +{ + + class LandManager : public Resource::ResourceManager + { + public: + LandManager(int loadFlags); + + /// @note Will return NULL if not found. + osg::ref_ptr getLand(int x, int y); + + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + + private: + int mLoadFlags; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 69aa30702..886673caf 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -212,11 +212,13 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); + + + mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), + Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - new TerrainStorage(mResourceSystem->getVFS(), Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), - Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), - Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")), - Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); + mTerrainStorage, Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); mCamera.reset(new Camera(mViewer->getCamera())); @@ -1055,4 +1057,10 @@ namespace MWRender SceneUtil::writeScene(node, filename, format); } + LandManager *RenderingManager::getLandManager() const + { + return mTerrainStorage->getLandManager(); + } + + } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b1bb9fe0d..a1c962ff0 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -59,6 +59,8 @@ namespace MWRender class Pathgrid; class Camera; class Water; + class TerrainStorage; + class LandManager; class RenderingManager : public MWRender::RenderingInterface { @@ -190,6 +192,8 @@ namespace MWRender void exportSceneGraph(const MWWorld::Ptr& ptr, const std::string& filename, const std::string& format); + LandManager* getLandManager() const; + private: void updateProjectionMatrix(); void updateTextureFiltering(); @@ -212,6 +216,7 @@ namespace MWRender std::auto_ptr mObjects; std::auto_ptr mWater; std::auto_ptr mTerrain; + TerrainStorage* mTerrainStorage; std::auto_ptr mSky; std::auto_ptr mEffectManager; osg::ref_ptr mPlayerAnimation; diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 311eb1621..9d229c96c 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -6,12 +6,22 @@ #include "../mwbase/environment.hpp" #include "../mwworld/esmstore.hpp" +#include "landmanager.hpp" + namespace MWRender { - TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) - : ESMTerrain::Storage(vfs, normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) + TerrainStorage::TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) + : ESMTerrain::Storage(resourceSystem->getVFS(), normalMapPattern, normalHeightMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps) + , mLandManager(new LandManager(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX)) + , mResourceSystem(resourceSystem) + { + mResourceSystem->addResourceManager(mLandManager.get()); + } + + TerrainStorage::~TerrainStorage() { + mResourceSystem->removeResourceManager(mLandManager.get()); } void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY) @@ -39,22 +49,14 @@ namespace MWRender maxY += 1; } - const ESM::Land* TerrainStorage::getLand(int cellX, int cellY) + LandManager *TerrainStorage::getLandManager() const { - const MWWorld::ESMStore &esmStore = - MWBase::Environment::get().getWorld()->getStore(); - - const ESM::Land* land = esmStore.get().search(cellX, cellY); - if (!land) - return NULL; - - const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; - if (!land->isDataLoaded(flags)) - land->loadData(flags); - - // TODO: unload land data when it's no longer needed + return mLandManager.get(); + } - return land; + osg::ref_ptr TerrainStorage::getLand(int cellX, int cellY) + { + return mLandManager->getLand(cellX, cellY); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) @@ -64,4 +66,5 @@ namespace MWRender return esmStore.get().search(index, plugin); } + } diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 6fa8b98ce..cf4011d45 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -1,23 +1,37 @@ #ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H +#include + #include +#include + namespace MWRender { + class LandManager; + /// @brief Connects the ESM Store used in OpenMW with the ESMTerrain storage. class TerrainStorage : public ESMTerrain::Storage { - private: - virtual const ESM::Land* getLand (int cellX, int cellY); - virtual const ESM::LandTexture* getLandTexture(int index, short plugin); public: - TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + TerrainStorage(Resource::ResourceSystem* resourceSystem, const std::string& normalMapPattern = "", const std::string& normalHeightMapPatteern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); + ~TerrainStorage(); + + virtual osg::ref_ptr getLand (int cellX, int cellY); + virtual const ESM::LandTexture* getLandTexture(int index, short plugin); /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); + + LandManager* getLandManager() const; + + private: + std::auto_ptr mLandManager; + + Resource::ResourceSystem* mResourceSystem; }; } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60c1f9ca0..db3ec5018 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "../mwbase/environment.hpp" @@ -16,6 +17,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" +#include "../mwrender/landmanager.hpp" #include "cellstore.hpp" #include "manualref.hpp" @@ -46,7 +48,7 @@ namespace MWWorld { public: /// Constructor to be called from the main thread. - PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain, bool preloadInstances) + PreloadItem(MWWorld::CellStore* cell, Resource::SceneManager* sceneManager, Resource::BulletShapeManager* bulletShapeManager, Resource::KeyframeManager* keyframeManager, Terrain::World* terrain, MWRender::LandManager* landManager, bool preloadInstances) : mIsExterior(cell->getCell()->isExterior()) , mX(cell->getCell()->getGridX()) , mY(cell->getCell()->getGridY()) @@ -54,6 +56,7 @@ namespace MWWorld , mBulletShapeManager(bulletShapeManager) , mKeyframeManager(keyframeManager) , mTerrain(terrain) + , mLandManager(landManager) , mPreloadInstances(preloadInstances) , mAbort(false) { @@ -90,6 +93,7 @@ namespace MWWorld try { mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); + mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); } catch(std::exception& e) { @@ -151,6 +155,7 @@ namespace MWWorld Resource::BulletShapeManager* mBulletShapeManager; Resource::KeyframeManager* mKeyframeManager; Terrain::World* mTerrain; + MWRender::LandManager* mLandManager; bool mPreloadInstances; volatile bool mAbort; @@ -183,10 +188,11 @@ namespace MWWorld Terrain::World* mTerrain; }; - CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain) + CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager) : mResourceSystem(resourceSystem) , mBulletShapeManager(bulletShapeManager) , mTerrain(terrain) + , mLandManager(landManager) , mExpiryDelay(0.0) , mMinCacheSize(0) , mMaxCacheSize(0) @@ -251,7 +257,7 @@ namespace MWWorld return; } - osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain, mPreloadInstances)); + osg::ref_ptr item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain, mLandManager, mPreloadInstances)); mWorkQueue->addWorkItem(item); mPreloadCells[cell] = PreloadEntry(timestamp, item); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index e8e59a3a2..ee73a4ea1 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -21,6 +21,11 @@ namespace SceneUtil class UnrefQueue; } +namespace MWRender +{ + class LandManager; +} + namespace MWWorld { class CellStore; @@ -28,7 +33,7 @@ namespace MWWorld class CellPreloader { public: - CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain); + CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager); ~CellPreloader(); /// Ask a background thread to preload rendering meshes and collision shapes for objects in this cell. @@ -64,6 +69,7 @@ namespace MWWorld Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; Terrain::World* mTerrain; + MWRender::LandManager* mLandManager; osg::ref_ptr mWorkQueue; osg::ref_ptr mUnrefQueue; double mExpiryDelay; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index dadada7b2..13546eb3f 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -16,6 +16,7 @@ #include "../mwbase/windowmanager.hpp" #include "../mwrender/renderingmanager.hpp" +#include "../mwrender/landmanager.hpp" #include "../mwphysics/physicssystem.hpp" @@ -271,26 +272,19 @@ namespace MWWorld // Load terrain physics first... if (cell->getCell()->isExterior()) { - const ESM::Land* land = - MWBase::Environment::get().getWorld()->getStore().get().search( - cell->getCell()->getGridX(), - cell->getCell()->getGridY() - ); - if (land && land->mDataTypes&ESM::Land::DATA_VHGT) { - // Actually only VHGT is needed here, but we'll need the rest for rendering anyway. - // Load everything now to reduce IO overhead. - const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; - - const ESM::Land::LandData *data = land->getLandData (flags); - mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(), - worldsize / (verts-1), verts); + int cellX = cell->getCell()->getGridX(); + int cellY = cell->getCell()->getGridY(); + osg::ref_ptr land = mRendering.getLandManager()->getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + if (data) + { + mPhysics->addHeightField (data->mHeights, cellX, cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); } else { static std::vector defaultHeight; defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); - mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), - worldsize / (verts-1), verts); + mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); } } @@ -487,7 +481,7 @@ namespace MWWorld , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) { - mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain())); + mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); mPreloader->setUnrefQueue(rendering.getUnrefQueue()); diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index d1ccbeab5..e48c8aeb2 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,6 +16,39 @@ namespace ESMTerrain { + + LandObject::LandObject() + { + } + + LandObject::LandObject(const ESM::Land *land, int loadFlags) + : mLand(land) + , mLoadFlags(loadFlags) + { + mLand->loadData(mLoadFlags); + } + + LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op) + { + } + + LandObject::~LandObject() + { + if (mLand && mLoadFlags) // only unload if we were responsible for loading to begin with. + mLand->unloadData(); + } + + const ESM::Land::LandData *LandObject::getData(int flags) const + { + return mLand->getLandData(flags); + } + + int LandObject::getPlugin() const + { + return mLand->mPlugin; + } + + const float defaultHeight = ESM::Land::DEFAULT_HEIGHT; Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, const std::string& normalHeightMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps) @@ -28,14 +61,6 @@ namespace ESMTerrain { } - const ESM::Land::LandData *Storage::getLandData (int cellX, int cellY, int flags) - { - if (const ESM::Land *land = getLand (cellX, cellY)) - return land->getLandData (flags); - - return 0; - } - bool Storage::getMinMaxHeights(float size, const osg::Vec2f ¢er, float &min, float &max) { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); @@ -53,7 +78,9 @@ namespace ESMTerrain int endRow = startRow + size * (ESM::Land::LAND_SIZE-1) + 1; int endColumn = startColumn + size * (ESM::Land::LAND_SIZE-1) + 1; - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VHGT)) + osg::ref_ptr land = getLand (cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; + if (data) { min = std::numeric_limits::max(); max = -std::numeric_limits::max(); @@ -99,7 +126,9 @@ namespace ESMTerrain row += ESM::Land::LAND_SIZE-1; } - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VNML)) + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; + if (data) { normal.x() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; normal.y() = data->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; @@ -134,7 +163,9 @@ namespace ESMTerrain row = 0; } - if (const ESM::Land::LandData *data = getLandData (cellX, cellY, ESM::Land::DATA_VCLR)) + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; + if (data) { color.r() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; color.g() = data->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; @@ -146,7 +177,6 @@ namespace ESMTerrain color.g() = 1; color.b() = 1; } - } void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, @@ -180,9 +210,16 @@ namespace ESMTerrain float vertX_ = 0; // of current cell corner for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) { - const ESM::Land::LandData *heightData = getLandData (cellX, cellY, ESM::Land::DATA_VHGT); - const ESM::Land::LandData *normalData = getLandData (cellX, cellY, ESM::Land::DATA_VNML); - const ESM::Land::LandData *colourData = getLandData (cellX, cellY, ESM::Land::DATA_VCLR); + osg::ref_ptr land = getLand(cellX, cellY); + const ESM::Land::LandData *heightData = 0; + const ESM::Land::LandData *normalData = 0; + const ESM::Land::LandData *colourData = 0; + if (land) + { + heightData = land->getData(ESM::Land::DATA_VHGT); + normalData = land->getData(ESM::Land::DATA_VNML); + colourData = land->getData(ESM::Land::DATA_VCLR); + } int rowStart = 0; int colStart = 0; @@ -298,15 +335,16 @@ namespace ESMTerrain assert(x land = getLand(cellX, cellY); + const ESM::Land::LandData *data = land ? land->getData(ESM::Land::DATA_VTEX) : 0; + if (data) { int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x]; if (tex == 0) return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin - return std::make_pair(tex, getLand (cellX, cellY)->mPlugin); + return std::make_pair(tex, land->getPlugin()); } - else - return std::make_pair(0,0); + return std::make_pair(0,0); } std::string Storage::getTextureName(UniqueTextureId id) @@ -421,8 +459,12 @@ namespace ESMTerrain int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); - const ESM::Land* land = getLand(cellX, cellY); - if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) + osg::ref_ptr land = getLand(cellX, cellY); + if (!land) + return defaultHeight; + + const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT); + if (!data) return defaultHeight; // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition @@ -461,10 +503,10 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); - osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); - osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); - osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(data, startX, startY) / 8192.f); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(data, endX, startY) / 8192.f); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(data, endX, endY) / 8192.f); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(data, startX, endY) / 8192.f); // define this plane in terrain space osg::Plane plane; // FIXME: deal with differing triangle alignment @@ -496,11 +538,11 @@ namespace ESMTerrain } - float Storage::getVertexHeight(const ESM::Land *land, int x, int y) + float Storage::getVertexHeight(const ESM::Land::LandData* data, int x, int y) { assert(x < ESM::Land::LAND_SIZE); assert(y < ESM::Land::LAND_SIZE); - return land->getLandData()->mHeights[y * ESM::Land::LAND_SIZE + x]; + return data->mHeights[y * ESM::Land::LAND_SIZE + x]; } Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 092998b2e..9204d4059 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -16,25 +16,36 @@ namespace VFS namespace ESMTerrain { - /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) - /// into the terrain component, converting it on the fly as needed. - class Storage : public Terrain::Storage + /// @brief Wrapper around ESM::Land with reference counting. The wrapper needs to be held as long as the data is still in use + /// Data will be unloaded when wrapper object is deleted + class LandObject : public osg::Object { private: + const ESM::Land* mLand; + int mLoadFlags; - // Not implemented in this class, because we need different Store implementations for game and editor - virtual const ESM::Land* getLand (int cellX, int cellY)= 0; - virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; + public: + META_Object(ESMTerrain, LandObject) + const ESM::Land::LandData* getData(int flags) const; + int getPlugin() const; + + LandObject(); + LandObject(const ESM::Land* land, int loadFlags); + LandObject(const LandObject& copy, const osg::CopyOp& copyop); + virtual ~LandObject(); + }; + + /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) + /// into the terrain component, converting it on the fly as needed. + class Storage : public Terrain::Storage + { public: Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", const std::string& normalHeightMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false); - /// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for - /// any of the data types specified via \a flags. Will also return a 0-pointer if there - /// is no land record for the coordinates \a cellX / \a cellY. - const ESM::Land::LandData *getLandData (int cellX, int cellY, int flags); - // Not implemented in this class, because we need different Store implementations for game and editor + virtual osg::ref_ptr getLand (int cellX, int cellY)= 0; + virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; @@ -95,7 +106,7 @@ namespace ESMTerrain void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row); void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); - float getVertexHeight (const ESM::Land* land, int x, int y); + float getVertexHeight (const ESM::Land::LandData* data, int x, int y); // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index d696c9ecc..3cc2d3b01 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _resourceStatsChildNum = _switch->getNumChildren(); _switch->addChild(group, false); - const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "", "UnrefQueue"}; + const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "Land", "", "UnrefQueue"}; int numLines = sizeof(statNames) / sizeof(statNames[0]); From 274690f79098763dcbb5a4849f8fefa269313327 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 20:22:30 +0100 Subject: [PATCH 15/88] Refactor BufferCache to allow caching buffers of different sizes --- components/terrain/buffercache.cpp | 34 +++++++++++++++--------------- components/terrain/buffercache.hpp | 10 +++------ components/terrain/terraingrid.cpp | 7 +++--- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index 3bf5d5651..470655539 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -178,56 +178,56 @@ osg::ref_ptr createIndexBuffer(unsigned int flags, unsigned int namespace Terrain { - osg::ref_ptr BufferCache::getUVBuffer() + osg::ref_ptr BufferCache::getUVBuffer(unsigned int numVerts) { OpenThreads::ScopedLock lock(mUvBufferMutex); - if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) + if (mUvBufferMap.find(numVerts) != mUvBufferMap.end()) { - return mUvBufferMap[mNumVerts]; + return mUvBufferMap[numVerts]; } - int vertexCount = mNumVerts * mNumVerts; + int vertexCount = numVerts * numVerts; osg::ref_ptr uvs (new osg::Vec2Array); uvs->reserve(vertexCount); - for (unsigned int col = 0; col < mNumVerts; ++col) + for (unsigned int col = 0; col < numVerts; ++col) { - for (unsigned int row = 0; row < mNumVerts; ++row) + for (unsigned int row = 0; row < numVerts; ++row) { - uvs->push_back(osg::Vec2f(col / static_cast(mNumVerts-1), - ((mNumVerts-1) - row) / static_cast(mNumVerts-1))); + uvs->push_back(osg::Vec2f(col / static_cast(numVerts-1), + ((numVerts-1) - row) / static_cast(numVerts-1))); } } // Assign a VBO here to enable state sharing between different Geometries. uvs->setVertexBufferObject(new osg::VertexBufferObject); - mUvBufferMap[mNumVerts] = uvs; + mUvBufferMap[numVerts] = uvs; return uvs; } - osg::ref_ptr BufferCache::getIndexBuffer(unsigned int flags) + osg::ref_ptr BufferCache::getIndexBuffer(unsigned int numVerts, unsigned int flags) { + std::pair id = std::make_pair(numVerts, flags); OpenThreads::ScopedLock lock(mIndexBufferMutex); - unsigned int verts = mNumVerts; - if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) + if (mIndexBufferMap.find(id) != mIndexBufferMap.end()) { - return mIndexBufferMap[flags]; + return mIndexBufferMap[id]; } osg::ref_ptr buffer; - if (verts*verts <= (0xffffu)) - buffer = createIndexBuffer(flags, verts); + if (numVerts*numVerts <= (0xffffu)) + buffer = createIndexBuffer(flags, numVerts); else - buffer = createIndexBuffer(flags, verts); + buffer = createIndexBuffer(flags, numVerts); // Assign a EBO here to enable state sharing between different Geometries. buffer->setElementBufferObject(new osg::ElementBufferObject); - mIndexBufferMap[flags] = buffer; + mIndexBufferMap[id] = buffer; return buffer; } diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp index 172b9d672..e8963354b 100644 --- a/components/terrain/buffercache.hpp +++ b/components/terrain/buffercache.hpp @@ -14,28 +14,24 @@ namespace Terrain class BufferCache { public: - BufferCache(unsigned int numVerts) : mNumVerts(numVerts) {} - /// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each) /// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices) /// @note Thread safe. - osg::ref_ptr getIndexBuffer (unsigned int flags); + osg::ref_ptr getIndexBuffer (unsigned int numVerts, unsigned int flags); /// @note Thread safe. - osg::ref_ptr getUVBuffer(); + osg::ref_ptr getUVBuffer(unsigned int numVerts); // TODO: add releaseGLObjects() for our vertex/element buffer objects private: // Index buffers are shared across terrain batches where possible. There is one index buffer for each // combination of LOD deltas and index buffer LOD we may need. - std::map > mIndexBufferMap; + std::map, osg::ref_ptr > mIndexBufferMap; OpenThreads::Mutex mIndexBufferMutex; std::map > mUvBufferMap; OpenThreads::Mutex mUvBufferMutex; - - unsigned int mNumVerts; }; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index ffb30c04a..7a8307043 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -51,7 +51,6 @@ namespace Terrain TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager, SceneUtil::UnrefQueue* unrefQueue) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) , mNumSplits(4) - , mCache((storage->getCellVertices()-1)/static_cast(mNumSplits) + 1) , mUnrefQueue(unrefQueue) , mShaderManager(shaderManager) { @@ -130,7 +129,9 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(true); - geometry->addPrimitiveSet(mCache.getIndexBuffer(0)); + unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize + 1; + + geometry->addPrimitiveSet(mCache.getIndexBuffer(numVerts, 0)); // we already know the bounding box, so no need to let OSG compute it. osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize, @@ -184,7 +185,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu // use texture coordinates for both texture units, the layer texture and blend texture for (unsigned int i=0; i<2; ++i) - geometry->setTexCoordArray(i, mCache.getUVBuffer()); + geometry->setTexCoordArray(i, mCache.getUVBuffer(numVerts)); float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize; From 35d53acc65d66779fed914e626e3e63e4c15f938 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 20:41:02 +0100 Subject: [PATCH 16/88] Factor out terrain chunk loading/caching into a new resource manager --- apps/opencs/view/render/cell.hpp | 1 + apps/openmw/mwrender/renderingmanager.cpp | 2 - apps/openmw/mwworld/cellpreloader.cpp | 8 +- components/CMakeLists.txt | 2 +- components/esmterrain/storage.cpp | 5 + components/esmterrain/storage.hpp | 2 + components/resource/scenemanager.cpp | 5 + components/resource/scenemanager.hpp | 2 + components/resource/stats.cpp | 2 +- components/terrain/chunkmanager.cpp | 180 +++++++++++++++++++ components/terrain/chunkmanager.hpp | 59 +++++++ components/terrain/storage.hpp | 2 + components/terrain/terraingrid.cpp | 202 ++-------------------- components/terrain/terraingrid.hpp | 13 +- components/terrain/world.cpp | 6 +- components/terrain/world.hpp | 9 +- 16 files changed, 285 insertions(+), 215 deletions(-) create mode 100644 components/terrain/chunkmanager.cpp create mode 100644 components/terrain/chunkmanager.hpp diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 8f68e9f53..c89c1517f 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 886673caf..090eddd0d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -914,8 +914,6 @@ namespace MWRender if (stats->collectStats("resource")) { stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems()); - - mTerrain->reportStats(frameNumber, stats); } } diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index db3ec5018..e45cf55bc 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -168,24 +168,20 @@ namespace MWWorld class UpdateCacheItem : public SceneUtil::WorkItem { public: - UpdateCacheItem(Resource::ResourceSystem* resourceSystem, Terrain::World* terrain, double referenceTime) + UpdateCacheItem(Resource::ResourceSystem* resourceSystem, double referenceTime) : mReferenceTime(referenceTime) , mResourceSystem(resourceSystem) - , mTerrain(terrain) { } virtual void doWork() { mResourceSystem->updateCache(mReferenceTime); - - mTerrain->updateCache(mReferenceTime); } private: double mReferenceTime; Resource::ResourceSystem* mResourceSystem; - Terrain::World* mTerrain; }; CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager) @@ -313,7 +309,7 @@ namespace MWWorld if (timestamp - mLastResourceCacheUpdate > 1.0) { // the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations - mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, mTerrain, timestamp), true); + mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp), true); mLastResourceCacheUpdate = timestamp; } } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index c1b329942..37c19f527 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable texturemanager + storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager ) add_component_dir (loadinglistener diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index e48c8aeb2..5d1591e79 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -612,4 +612,9 @@ namespace ESMTerrain return ESM::Land::LAND_SIZE; } + int Storage::getBlendmapScale(float chunkSize) + { + return ESM::Land::LAND_TEXTURE_SIZE*chunkSize; + } + } diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 9204d4059..7502f44cd 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -99,6 +99,8 @@ namespace ESMTerrain /// Get the number of vertices on one side for each cell. Should be (power of two)+1 virtual int getCellVertices(); + virtual int getBlendmapScale(float chunkSize); + private: const VFS::Manager* mVFS; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index fc31b00a0..da8ba180e 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -621,6 +621,11 @@ namespace Resource mIncrementalCompileOperation = ico; } + osgUtil::IncrementalCompileOperation *SceneManager::getIncrementalCompileOperation() + { + return mIncrementalCompileOperation.get(); + } + Resource::ImageManager* SceneManager::getImageManager() { return mImageManager; diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index fdd1d6583..3ed7d1bd3 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -121,6 +121,8 @@ namespace Resource /// Set up an IncrementalCompileOperation for background compiling of loaded scenes. void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico); + osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation(); + Resource::ImageManager* getImageManager(); /// @param mask The node mask to apply to loaded particle system nodes. diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 3cc2d3b01..e2a91749e 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _resourceStatsChildNum = _switch->getNumChildren(); _switch->addChild(group, false); - const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "Land", "", "UnrefQueue"}; + const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Chunk", "Terrain Texture", "Land", "", "UnrefQueue"}; int numLines = sizeof(statNames) / sizeof(statNames[0]); diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp new file mode 100644 index 000000000..96bea517b --- /dev/null +++ b/components/terrain/chunkmanager.cpp @@ -0,0 +1,180 @@ +#include "chunkmanager.hpp" + +#include + +#include + +#include + +#include +#include + +#include + +#include "terraindrawable.hpp" +#include "material.hpp" +#include "storage.hpp" +#include "texturemanager.hpp" + +namespace +{ + class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback + { + public: + StaticBoundingBoxCallback(const osg::BoundingBox& bounds) + : mBoundingBox(bounds) + { + } + + virtual osg::BoundingBox computeBound(const osg::Drawable&) const + { + return mBoundingBox; + } + + private: + osg::BoundingBox mBoundingBox; + }; +} + +namespace Terrain +{ + +ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager) + : ResourceManager(NULL) + , mStorage(storage) + , mSceneManager(sceneMgr) + , mTextureManager(textureManager) + , mShaderManager(NULL) +{ + +} + +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er) +{ + std::ostringstream stream; + stream << size << " " << center.x() << " " << center.y(); + std::string id = stream.str(); + + osg::ref_ptr obj = mCache->getRefFromObjectCache(id); + if (obj) + return obj->asNode(); + else + { + osg::ref_ptr node = createChunk(size, center); + mCache->addEntryToObjectCache(id, node.get()); + return node; + } +} + +void ChunkManager::setShaderManager(Shader::ShaderManager *shaderManager) +{ + mShaderManager = shaderManager; +} + +void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +{ + stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); +} + +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter) +{ + float minH, maxH; + if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH)) + return NULL; // no terrain defined + + osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); + osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); + transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); + + osg::ref_ptr positions (new osg::Vec3Array); + osg::ref_ptr normals (new osg::Vec3Array); + osg::ref_ptr colors (new osg::Vec4Array); + + osg::ref_ptr vbo (new osg::VertexBufferObject); + positions->setVertexBufferObject(vbo); + normals->setVertexBufferObject(vbo); + colors->setVertexBufferObject(vbo); + + unsigned int lod = 0; + + mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors); + + osg::ref_ptr geometry (new TerrainDrawable); + geometry->setVertexArray(positions); + geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); + geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geometry->setUseDisplayList(false); + geometry->setUseVertexBufferObjects(true); + + unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; + + geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0)); + + + // we already know the bounding box, so no need to let OSG compute it. + osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize, + -0.5f*mStorage->getCellWorldSize()*chunkSize, + minH); + osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize, + 0.5f*mStorage->getCellWorldSize()*chunkSize, + maxH); + osg::BoundingBox bounds(min, max); + geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); + + std::vector layerList; + std::vector > blendmaps; + mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); + + bool useShaders = mSceneManager->getForceShaders(); + if (!mSceneManager->getClampLighting()) + useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps + std::vector layers; + { + for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) + { + TextureLayer textureLayer; + textureLayer.mParallax = it->mParallax; + textureLayer.mSpecular = it->mSpecular; + + textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap); + + if (!it->mNormalMap.empty()) + textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap); + + if (it->requiresShaders()) + useShaders = true; + + layers.push_back(textureLayer); + } + } + + std::vector > blendmapTextures; + for (std::vector >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) + { + osg::ref_ptr texture (new osg::Texture2D); + texture->setImage(*it); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + texture->setResizeNonPowerOfTwoHint(false); + blendmapTextures.push_back(texture); + } + + // use texture coordinates for both texture units, the layer texture and blend texture + for (unsigned int i=0; i<2; ++i) + geometry->setTexCoordArray(i, mBufferCache.getUVBuffer(numVerts)); + + float blendmapScale = mStorage->getBlendmapScale(chunkSize); + + geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); + + transform->addChild(geometry); + + if (mSceneManager->getIncrementalCompileOperation()) + { + mSceneManager->getIncrementalCompileOperation()->add(geometry); + } + return transform; +} + +} diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp new file mode 100644 index 000000000..4bb4c5432 --- /dev/null +++ b/components/terrain/chunkmanager.hpp @@ -0,0 +1,59 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H +#define OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H + +#include + +#include "buffercache.hpp" + +namespace osg +{ + class Geometry; +} + +namespace SceneUtil +{ + class PositionAttitudeTransform; +} + +namespace Resource +{ + class SceneManager; +} + +namespace Shader +{ + class ShaderManager; +} + +namespace Terrain +{ + + class TextureManager; + class Storage; + + /// @brief Handles loading and caching of terrain chunks + class ChunkManager : public Resource::ResourceManager + { + public: + ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager); + + osg::ref_ptr getChunk(float size, const osg::Vec2f& center); + + // Optional + void setShaderManager(Shader::ShaderManager* shaderManager); + + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + + private: + osg::ref_ptr createChunk(float size, const osg::Vec2f& center); + + Terrain::Storage* mStorage; + Resource::SceneManager* mSceneManager; + TextureManager* mTextureManager; + Shader::ShaderManager* mShaderManager; + BufferCache mBufferCache; + }; + +} + +#endif diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index bd5706b25..efe8fa037 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -78,6 +78,8 @@ namespace Terrain /// Get the number of vertices on one side for each cell. Should be (power of two)+1 virtual int getCellVertices() = 0; + + virtual int getBlendmapScale(float chunkSize) = 0; }; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 7a8307043..fa7b93684 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -3,47 +3,12 @@ #include #include -#include +#include -#include - -#include - -#include -#include -#include - -#include -#include #include -#include - - -#include "material.hpp" -#include "storage.hpp" -#include "terraindrawable.hpp" #include "texturemanager.hpp" - -namespace -{ - class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback - { - public: - StaticBoundingBoxCallback(const osg::BoundingBox& bounds) - : mBoundingBox(bounds) - { - } - - virtual osg::BoundingBox computeBound(const osg::Drawable&) const - { - return mBoundingBox; - } - - private: - osg::BoundingBox mBoundingBox; - }; -} +#include "chunkmanager.hpp" namespace Terrain { @@ -57,6 +22,8 @@ TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceS osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); + + mChunkManager->setShaderManager(mShaderManager); } TerrainGrid::~TerrainGrid() @@ -69,17 +36,8 @@ TerrainGrid::~TerrainGrid() osg::ref_ptr TerrainGrid::cacheCell(int x, int y) { - { - OpenThreads::ScopedLock lock(mGridCacheMutex); - Grid::iterator found = mGridCache.find(std::make_pair(x,y)); - if (found != mGridCache.end()) - return found->second; - } - osg::ref_ptr node = buildTerrain(NULL, 1.f, osg::Vec2f(x+0.5, y+0.5)); - - OpenThreads::ScopedLock lock(mGridCacheMutex); - mGridCache.insert(std::make_pair(std::make_pair(x,y), node)); - return node; + osg::Vec2f center(x+0.5f, y+0.5f); + return buildTerrain(NULL, 1.f, center); } osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) @@ -100,106 +58,13 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - float minH, maxH; - if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH)) - return NULL; // no terrain defined - - osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); - osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); - transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); - + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter); + if (!node) + return NULL; if (parent) - parent->addChild(transform); - - osg::ref_ptr positions (new osg::Vec3Array); - osg::ref_ptr normals (new osg::Vec3Array); - osg::ref_ptr colors (new osg::Vec4Array); - - osg::ref_ptr vbo (new osg::VertexBufferObject); - positions->setVertexBufferObject(vbo); - normals->setVertexBufferObject(vbo); - colors->setVertexBufferObject(vbo); - - mStorage->fillVertexBuffers(0, chunkSize, chunkCenter, positions, normals, colors); - - osg::ref_ptr geometry (new TerrainDrawable); - geometry->setVertexArray(positions); - geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); - geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - geometry->setUseDisplayList(false); - geometry->setUseVertexBufferObjects(true); - - unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize + 1; - - geometry->addPrimitiveSet(mCache.getIndexBuffer(numVerts, 0)); - - // we already know the bounding box, so no need to let OSG compute it. - osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize, - -0.5f*mStorage->getCellWorldSize()*chunkSize, - minH); - osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize, - 0.5f*mStorage->getCellWorldSize()*chunkSize, - maxH); - osg::BoundingBox bounds(min, max); - geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); - - std::vector layerList; - std::vector > blendmaps; - mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); - - bool useShaders = mResourceSystem->getSceneManager()->getForceShaders(); - if (!mResourceSystem->getSceneManager()->getClampLighting()) - useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps - std::vector layers; - { - for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) - { - TextureLayer textureLayer; - textureLayer.mParallax = it->mParallax; - textureLayer.mSpecular = it->mSpecular; + parent->addChild(node); - textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap); - - if (!it->mNormalMap.empty()) - { - textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap); - } - - if (it->requiresShaders()) - useShaders = true; - - layers.push_back(textureLayer); - } - } - - std::vector > blendmapTextures; - for (std::vector >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) - { - osg::ref_ptr texture (new osg::Texture2D); - texture->setImage(*it); - texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); - texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - texture->setResizeNonPowerOfTwoHint(false); - blendmapTextures.push_back(texture); - } - - // use texture coordinates for both texture units, the layer texture and blend texture - for (unsigned int i=0; i<2; ++i) - geometry->setTexCoordArray(i, mCache.getUVBuffer(numVerts)); - - float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize; - - geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mResourceSystem->getSceneManager()->getForcePerPixelLighting(), - mResourceSystem->getSceneManager()->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); - - transform->addChild(geometry); - - if (mIncrementalCompileOperation) - { - mIncrementalCompileOperation->add(geometry); - } - - return transform; + return node; } } @@ -208,27 +73,10 @@ void TerrainGrid::loadCell(int x, int y) if (mGrid.find(std::make_pair(x, y)) != mGrid.end()) return; // already loaded - // try to get it from the cache - osg::ref_ptr terrainNode; - { - OpenThreads::ScopedLock lock(mGridCacheMutex); - Grid::const_iterator found = mGridCache.find(std::make_pair(x,y)); - if (found != mGridCache.end()) - { - terrainNode = found->second; - if (!terrainNode) - return; // no terrain defined - } - } - - // didn't find in cache, build it + osg::Vec2f center(x+0.5f, y+0.5f); + osg::ref_ptr terrainNode = buildTerrain(NULL, 1.f, center); if (!terrainNode) - { - osg::Vec2f center(x+0.5f, y+0.5f); - terrainNode = buildTerrain(NULL, 1.f, center); - if (!terrainNode) - return; // no terrain defined - } + return; // no terrain defined mTerrainRoot->addChild(terrainNode); @@ -250,31 +98,9 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } -void TerrainGrid::updateCache(double referenceTime) -{ - { - OpenThreads::ScopedLock lock(mGridCacheMutex); - for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();) - { - if (it->second->referenceCount() <= 1) - mGridCache.erase(it++); - else - ++it; - } - } -} - void TerrainGrid::updateTextureFiltering() { mTextureManager->updateTextureFiltering(); } -void TerrainGrid::reportStats(unsigned int frameNumber, osg::Stats *stats) -{ - { - OpenThreads::ScopedLock lock(mGridCacheMutex); - stats->setAttribute(frameNumber, "Terrain Cell", mGridCache.size()); - } -} - } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 7f6ea2d30..59d93dc6f 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -1,6 +1,8 @@ #ifndef COMPONENTS_TERRAIN_TERRAINGRID_H #define COMPONENTS_TERRAIN_TERRAINGRID_H +#include + #include #include "world.hpp" @@ -41,16 +43,10 @@ namespace Terrain /// @note Not thread safe. virtual void unloadCell(int x, int y); - /// Clear cached objects that are no longer referenced - /// @note Thread safe. - void updateCache(double referenceTime); - /// Apply the scene manager's texture filtering settings to all cached textures. /// @note Thread safe. void updateTextureFiltering(); - void reportStats(unsigned int frameNumber, osg::Stats *stats); - private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); @@ -60,11 +56,6 @@ namespace Terrain typedef std::map, osg::ref_ptr > Grid; Grid mGrid; - Grid mGridCache; - OpenThreads::Mutex mGridCacheMutex; - - BufferCache mCache; - osg::ref_ptr mUnrefQueue; Shader::ShaderManager* mShaderManager; diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 329ed5082..007303243 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -7,6 +7,7 @@ #include "storage.hpp" #include "texturemanager.hpp" +#include "chunkmanager.hpp" namespace Terrain { @@ -26,13 +27,16 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt mParent->addChild(mTerrainRoot); mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); - mResourceSystem->addResourceManager(mTextureManager.get()); + + mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get())); + mResourceSystem->addResourceManager(mChunkManager.get()); } World::~World() { mResourceSystem->removeResourceManager(mTextureManager.get()); + mResourceSystem->removeResourceManager(mChunkManager.get()); mParent->removeChild(mTerrainRoot); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 30d765e2d..a8cc0f0db 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -2,16 +2,17 @@ #define COMPONENTS_TERRAIN_WORLD_H #include +#include #include #include "defs.hpp" -#include "buffercache.hpp" namespace osg { class Group; class Stats; + class Node; } namespace osgUtil @@ -29,6 +30,7 @@ namespace Terrain class Storage; class TextureManager; + class ChunkManager; /** * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed @@ -46,10 +48,6 @@ namespace Terrain virtual void updateTextureFiltering() {} - virtual void updateCache(double referenceTime) {} - - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} - float getHeightAt (const osg::Vec3f& worldPos); virtual osg::ref_ptr cacheCell(int x, int y) {return NULL;} @@ -71,6 +69,7 @@ namespace Terrain osg::ref_ptr mIncrementalCompileOperation; std::auto_ptr mTextureManager; + std::auto_ptr mChunkManager; }; } From 2c68ed4fb482a1771c7c838d6d9c940c5287c4b5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 20:42:19 +0100 Subject: [PATCH 17/88] Remove no longer required use of UnrefQueue as the new resource manager will naturally clear the cache from the worker thread --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/terrain/terraingrid.cpp | 8 +------- components/terrain/terraingrid.hpp | 9 +-------- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 090eddd0d..534030b43 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -218,7 +218,7 @@ namespace MWRender Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - mTerrainStorage, Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get())); + mTerrainStorage, Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager())); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index fa7b93684..966f161f2 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -5,18 +5,15 @@ #include #include -#include - #include "texturemanager.hpp" #include "chunkmanager.hpp" namespace Terrain { -TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager, SceneUtil::UnrefQueue* unrefQueue) +TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) , mNumSplits(4) - , mUnrefQueue(unrefQueue) , mShaderManager(shaderManager) { osg::ref_ptr material (new osg::Material); @@ -92,9 +89,6 @@ void TerrainGrid::unloadCell(int x, int y) osg::ref_ptr terrainNode = it->second; mTerrainRoot->removeChild(terrainNode); - if (mUnrefQueue.get()) - mUnrefQueue->push(terrainNode); - mGrid.erase(it); } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 59d93dc6f..0a9647bd5 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -7,11 +7,6 @@ #include "world.hpp" -namespace SceneUtil -{ - class UnrefQueue; -} - namespace Shader { class ShaderManager; @@ -29,7 +24,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager = NULL, SceneUtil::UnrefQueue* unrefQueue = NULL); + TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager = NULL); ~TerrainGrid(); /// Load a terrain cell and store it in cache for later use. @@ -56,8 +51,6 @@ namespace Terrain typedef std::map, osg::ref_ptr > Grid; Grid mGrid; - osg::ref_ptr mUnrefQueue; - Shader::ShaderManager* mShaderManager; }; From 16b5cadd9e0560676ee7b8a346c24480f865db7b Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Mar 2017 20:45:46 +0100 Subject: [PATCH 18/88] Fix order of operations w.r.t clearing cache --- components/terrain/world.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 007303243..9c3d08687 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -27,16 +27,16 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt mParent->addChild(mTerrainRoot); mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); - mResourceSystem->addResourceManager(mTextureManager.get()); - mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get())); + mResourceSystem->addResourceManager(mChunkManager.get()); + mResourceSystem->addResourceManager(mTextureManager.get()); } World::~World() { - mResourceSystem->removeResourceManager(mTextureManager.get()); mResourceSystem->removeResourceManager(mChunkManager.get()); + mResourceSystem->removeResourceManager(mTextureManager.get()); mParent->removeChild(mTerrainRoot); From 20d30bb8d736b9fdb116b733741a2036dc02eb8f Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 00:11:01 +0100 Subject: [PATCH 19/88] Move mDataLoaded into LandData --- components/esm/loadland.cpp | 29 ++++++++++++++--------------- components/esm/loadland.hpp | 9 +++++++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 5e33e6082..858b45704 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -18,7 +18,6 @@ namespace ESM , mY(0) , mPlugin(0) , mDataTypes(0) - , mDataLoaded(false) , mLandData(NULL) { } @@ -76,7 +75,6 @@ namespace ESM mContext = esm.getContext(); - mDataLoaded = 0; mLandData = NULL; // Skip the land data here. Load it when the cell is loaded. @@ -186,7 +184,7 @@ namespace ESM // Try to load only available data flags = flags & mDataTypes; // Return if all required data is loaded - if ((mDataLoaded & flags) == flags) { + if (mLandData && (mLandData->mDataLoaded & flags) == flags) { return; } // Create storage if nothing is loaded @@ -236,19 +234,18 @@ namespace ESM void Land::unloadData() const { - if (mDataLoaded) + if (mLandData) { delete mLandData; mLandData = NULL; - mDataLoaded = 0; } } bool Land::condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const { - if ((mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { + if ((mLandData->mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { reader.getHExact(ptr, size); - mDataLoaded |= dataFlag; + mLandData->mDataLoaded |= dataFlag; return true; } reader.skipHSubSize(size); @@ -258,13 +255,12 @@ namespace ESM bool Land::isDataLoaded(int flags) const { OpenThreads::ScopedLock lock(mMutex); - return (mDataLoaded & flags) == (flags & mDataTypes); + return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); } Land::Land (const Land& land) : mFlags (land.mFlags), mX (land.mX), mY (land.mY), mPlugin (land.mPlugin), mContext (land.mContext), mDataTypes (land.mDataTypes), - mDataLoaded (land.mDataLoaded), mLandData (land.mLandData ? new LandData (*land.mLandData) : 0) {} @@ -282,7 +278,6 @@ namespace ESM std::swap (mPlugin, land.mPlugin); std::swap (mContext, land.mContext); std::swap (mDataTypes, land.mDataTypes); - std::swap (mDataLoaded, land.mDataLoaded); std::swap (mLandData, land.mLandData); } @@ -311,18 +306,22 @@ namespace ESM mLandData = new LandData; mDataTypes |= flags; - mDataLoaded |= flags; + mLandData->mDataLoaded |= flags; } void Land::remove (int flags) { mDataTypes &= ~flags; - mDataLoaded &= ~flags; - if (!mDataLoaded) + if (mLandData) { - delete mLandData; - mLandData = 0; + mLandData->mDataLoaded &= ~flags; + + if (!mLandData->mDataLoaded) + { + delete mLandData; + mLandData = 0; + } } } } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index bdaf6cce8..eaffb2e46 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -80,6 +80,11 @@ struct Land struct LandData { + LandData() + : mDataLoaded(0) + { + } + // Initial reference height for the first vertex, only needed for filling mHeights float mHeightOffset; // Height in world space for each vertex @@ -99,6 +104,8 @@ struct Land // ??? short mUnk1; uint8_t mUnk2; + + int mDataLoaded; }; // low-LOD heightmap (used for rendering the global map) @@ -157,8 +164,6 @@ struct Land mutable OpenThreads::Mutex mMutex; - mutable int mDataLoaded; - mutable LandData *mLandData; }; From 80a0398f9d987aa37c21c1f1b0c19ec6a4817ae8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 00:25:04 +0100 Subject: [PATCH 20/88] Load LandData into the LandObject to avoid threading conflicts when the same data is being loaded by two threads --- apps/opencs/view/render/terrainstorage.cpp | 4 +- components/esm/loadland.cpp | 44 +++++++++++----------- components/esm/loadland.hpp | 13 +++---- components/esmterrain/storage.cpp | 8 ++-- components/esmterrain/storage.hpp | 21 ++++++----- 5 files changed, 43 insertions(+), 47 deletions(-) diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index 9894ce17c..c63d41be3 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -21,9 +21,7 @@ namespace CSVRender return NULL; const ESM::Land& land = mData.getLand().getRecord(index).get(); - int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX; - land.loadData (mask); - return new ESMTerrain::LandObject(&land, 0); + return new ESMTerrain::LandObject(&land, ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); } const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 858b45704..8d57c72eb 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,8 +2,6 @@ #include -#include - #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" @@ -177,45 +175,48 @@ namespace ESM } - void Land::loadData(int flags) const + void Land::loadData(int flags, LandData* target) const { - OpenThreads::ScopedLock lock(mMutex); + // Create storage if nothing is loaded + if (!target && !mLandData) + { + mLandData = new LandData; + } + + if (!target) + target = mLandData; // Try to load only available data flags = flags & mDataTypes; // Return if all required data is loaded - if (mLandData && (mLandData->mDataLoaded & flags) == flags) { + if ((target->mDataLoaded & flags) == flags) { return; } - // Create storage if nothing is loaded - if (mLandData == NULL) { - mLandData = new LandData; - } ESM::ESMReader reader; reader.restoreContext(mContext); if (reader.isNextSub("VNML")) { - condLoad(reader, flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals)); + condLoad(reader, flags, target->mDataLoaded, DATA_VNML, target->mNormals, sizeof(target->mNormals)); } if (reader.isNextSub("VHGT")) { VHGT vhgt; - if (condLoad(reader, flags, DATA_VHGT, &vhgt, sizeof(vhgt))) { + if (condLoad(reader, flags, target->mDataLoaded, DATA_VHGT, &vhgt, sizeof(vhgt))) { float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; - mLandData->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; + target->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; float colOffset = rowOffset; for (int x = 1; x < LAND_SIZE; x++) { colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; - mLandData->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; + target->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; } } - mLandData->mUnk1 = vhgt.mUnk1; - mLandData->mUnk2 = vhgt.mUnk2; + target->mUnk1 = vhgt.mUnk1; + target->mUnk2 = vhgt.mUnk2; } } @@ -223,11 +224,11 @@ namespace ESM reader.skipHSub(); if (reader.isNextSub("VCLR")) - condLoad(reader, flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS); + condLoad(reader, flags, target->mDataLoaded, DATA_VCLR, target->mColours, 3 * LAND_NUM_VERTS); if (reader.isNextSub("VTEX")) { uint16_t vtex[LAND_NUM_TEXTURES]; - if (condLoad(reader, flags, DATA_VTEX, vtex, sizeof(vtex))) { - transposeTextureData(vtex, mLandData->mTextures); + if (condLoad(reader, flags, target->mDataLoaded, DATA_VTEX, vtex, sizeof(vtex))) { + transposeTextureData(vtex, target->mTextures); } } } @@ -241,11 +242,11 @@ namespace ESM } } - bool Land::condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const + bool Land::condLoad(ESM::ESMReader& reader, int flags, int& targetFlags, int dataFlag, void *ptr, unsigned int size) const { - if ((mLandData->mDataLoaded & dataFlag) == 0 && (flags & dataFlag) != 0) { + if ((targetFlags & dataFlag) == 0 && (flags & dataFlag) != 0) { reader.getHExact(ptr, size); - mLandData->mDataLoaded |= dataFlag; + targetFlags |= dataFlag; return true; } reader.skipHSubSize(size); @@ -254,7 +255,6 @@ namespace ESM bool Land::isDataLoaded(int flags) const { - OpenThreads::ScopedLock lock(mMutex); return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); } diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index eaffb2e46..261708893 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -3,8 +3,6 @@ #include -#include - #include "esmcommon.hpp" namespace ESM @@ -117,12 +115,13 @@ struct Land void blank() {} /** - * Actually loads data + * Actually loads data into target + * If target is NULL, assumed target is mLandData */ - void loadData(int flags) const; + void loadData(int flags, LandData* target = NULL) const; /** - * Frees memory allocated for land data + * Frees memory allocated for mLandData */ void unloadData() const; @@ -160,9 +159,7 @@ struct Land /// Loads data and marks it as loaded /// \return true if data is actually loaded from file, false otherwise /// including the case when data is already loaded - bool condLoad(ESM::ESMReader& reader, int flags, int dataFlag, void *ptr, unsigned int size) const; - - mutable OpenThreads::Mutex mMutex; + bool condLoad(ESM::ESMReader& reader, int flags, int& targetFlags, int dataFlag, void *ptr, unsigned int size) const; mutable LandData *mLandData; }; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 5d1591e79..3ce8f4882 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -25,7 +25,7 @@ namespace ESMTerrain : mLand(land) , mLoadFlags(loadFlags) { - mLand->loadData(mLoadFlags); + mLand->loadData(mLoadFlags, &mData); } LandObject::LandObject(const LandObject ©, const osg::CopyOp ©op) @@ -34,13 +34,13 @@ namespace ESMTerrain LandObject::~LandObject() { - if (mLand && mLoadFlags) // only unload if we were responsible for loading to begin with. - mLand->unloadData(); } const ESM::Land::LandData *LandObject::getData(int flags) const { - return mLand->getLandData(flags); + if ((mData.mDataLoaded & flags) != flags) + return NULL; + return &mData; } int LandObject::getPlugin() const diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 7502f44cd..e159313c5 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -16,24 +16,25 @@ namespace VFS namespace ESMTerrain { - /// @brief Wrapper around ESM::Land with reference counting. The wrapper needs to be held as long as the data is still in use - /// Data will be unloaded when wrapper object is deleted + /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use class LandObject : public osg::Object { - private: - const ESM::Land* mLand; - int mLoadFlags; - public: + LandObject(); + LandObject(const ESM::Land* land, int loadFlags); + LandObject(const LandObject& copy, const osg::CopyOp& copyop); + virtual ~LandObject(); + META_Object(ESMTerrain, LandObject) const ESM::Land::LandData* getData(int flags) const; int getPlugin() const; - LandObject(); - LandObject(const ESM::Land* land, int loadFlags); - LandObject(const LandObject& copy, const osg::CopyOp& copyop); - virtual ~LandObject(); + private: + const ESM::Land* mLand; + int mLoadFlags; + + ESM::Land::LandData mData; }; /// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture) From 5fb854036de07ad1e0e27c682b214827f583f932 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 00:34:25 +0100 Subject: [PATCH 21/88] Use a forward declaration to avoid having to workaround Qt MOC Don't think that guard was still needed, but a forward declaration is better anyways. --- apps/opencs/view/render/cell.cpp | 5 ++++ apps/opencs/view/render/cell.hpp | 23 ++++++++++--------- .../view/render/pagedworldspacewidget.cpp | 1 + .../view/render/unpagedworldspacewidget.cpp | 1 + 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index adc038836..9ba5eeb01 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "../../model/world/idtable.hpp" #include "../../model/world/columns.hpp" @@ -17,9 +18,13 @@ #include "../../model/world/cellcoordinates.hpp" #include "cellwater.hpp" +#include "cellborder.hpp" +#include "cellarrow.hpp" +#include "cellmarker.hpp" #include "mask.hpp" #include "pathgrid.hpp" #include "terrainstorage.hpp" +#include "object.hpp" bool CSVRender::Cell::removeObject (const std::string& id) { diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index c89c1517f..e8e0d2c63 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -6,18 +6,9 @@ #include #include -#include - #include -#ifndef Q_MOC_RUN -#include -#endif - -#include "object.hpp" -#include "cellarrow.hpp" -#include "cellmarker.hpp" -#include "cellborder.hpp" +#include "../../model/world/cellcoordinates.hpp" class QModelIndex; @@ -31,7 +22,11 @@ namespace osg namespace CSMWorld { class Data; - class CellCoordinates; +} + +namespace Terrain +{ + class TerrainGrid; } namespace CSVRender @@ -39,6 +34,12 @@ namespace CSVRender class CellWater; class Pathgrid; class TagBase; + class Object; + + class CellArrow; + class CellBorder; + class CellMarker; + class CellWater; class Cell { diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index 901dadc85..e855f7e41 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -20,6 +20,7 @@ #include "editmode.hpp" #include "mask.hpp" #include "cameracontroller.hpp" +#include "cellarrow.hpp" bool CSVRender::PagedWorldspaceWidget::adjustCells() { diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 03a97bf1a..de999db0f 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -16,6 +16,7 @@ #include "../widget/scenetooltoggle2.hpp" #include "mask.hpp" +#include "tagbase.hpp" void CSVRender::UnpagedWorldspaceWidget::update() { From 051c17a1847f524f1982c4c1ab2dad5ce75e5509 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 04:02:06 +0100 Subject: [PATCH 22/88] Make reportStats const --- apps/openmw/mwrender/landmanager.cpp | 2 +- apps/openmw/mwrender/landmanager.hpp | 2 +- apps/openmw/mwrender/renderingmanager.cpp | 2 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- components/resource/bulletshapemanager.cpp | 2 +- components/resource/bulletshapemanager.hpp | 2 +- components/resource/imagemanager.cpp | 2 +- components/resource/imagemanager.hpp | 2 +- components/resource/keyframemanager.cpp | 2 +- components/resource/keyframemanager.hpp | 2 +- components/resource/niffilemanager.cpp | 2 +- components/resource/niffilemanager.hpp | 2 +- components/resource/resourcemanager.hpp | 2 +- components/resource/resourcesystem.cpp | 4 ++-- components/resource/resourcesystem.hpp | 2 +- components/resource/scenemanager.cpp | 2 +- components/resource/scenemanager.hpp | 4 ++-- components/terrain/chunkmanager.cpp | 2 +- components/terrain/chunkmanager.hpp | 2 +- components/terrain/texturemanager.cpp | 2 +- components/terrain/texturemanager.hpp | 2 +- 21 files changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwrender/landmanager.cpp b/apps/openmw/mwrender/landmanager.cpp index 082a136d5..5edce2c3f 100644 --- a/apps/openmw/mwrender/landmanager.cpp +++ b/apps/openmw/mwrender/landmanager.cpp @@ -39,7 +39,7 @@ osg::ref_ptr LandManager::getLand(int x, int y) } } -void LandManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +void LandManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Land", mCache->getCacheSize()); } diff --git a/apps/openmw/mwrender/landmanager.hpp b/apps/openmw/mwrender/landmanager.hpp index 4fd05f064..392a8b406 100644 --- a/apps/openmw/mwrender/landmanager.hpp +++ b/apps/openmw/mwrender/landmanager.hpp @@ -22,7 +22,7 @@ namespace MWRender /// @note Will return NULL if not found. osg::ref_ptr getLand(int x, int y); - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: int mLoadFlags; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 534030b43..a4236df3b 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -907,7 +907,7 @@ namespace MWRender mStateUpdater->setFogColor(color); } - void RenderingManager::reportStats() + void RenderingManager::reportStats() const { osg::Stats* stats = mViewer->getViewerStats(); unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index a1c962ff0..230fe3f7d 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -200,7 +200,7 @@ namespace MWRender void updateAmbient(); void setFogColor(const osg::Vec4f& color); - void reportStats(); + void reportStats() const; osg::ref_ptr mViewer; osg::ref_ptr mRootNode; diff --git a/components/resource/bulletshapemanager.cpp b/components/resource/bulletshapemanager.cpp index 18c37d97b..ef35b31ef 100644 --- a/components/resource/bulletshapemanager.cpp +++ b/components/resource/bulletshapemanager.cpp @@ -185,7 +185,7 @@ void BulletShapeManager::updateCache(double referenceTime) mInstanceCache->removeUnreferencedObjectsInCache(); } -void BulletShapeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +void BulletShapeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Shape", mCache->getCacheSize()); stats->setAttribute(frameNumber, "Shape Instance", mInstanceCache->getCacheSize()); diff --git a/components/resource/bulletshapemanager.hpp b/components/resource/bulletshapemanager.hpp index e6dba001c..fec7251ac 100644 --- a/components/resource/bulletshapemanager.hpp +++ b/components/resource/bulletshapemanager.hpp @@ -42,7 +42,7 @@ namespace Resource /// @see ResourceManager::updateCache virtual void updateCache(double referenceTime); - void reportStats(unsigned int frameNumber, osg::Stats *stats); + void reportStats(unsigned int frameNumber, osg::Stats *stats) const; private: osg::ref_ptr createInstance(const std::string& name); diff --git a/components/resource/imagemanager.cpp b/components/resource/imagemanager.cpp index 2e1422cf6..db47324fb 100644 --- a/components/resource/imagemanager.cpp +++ b/components/resource/imagemanager.cpp @@ -141,7 +141,7 @@ namespace Resource return mWarningImage; } - void ImageManager::reportStats(unsigned int frameNumber, osg::Stats *stats) + void ImageManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Image", mCache->getCacheSize()); } diff --git a/components/resource/imagemanager.hpp b/components/resource/imagemanager.hpp index 2c7a61828..8eea4a70b 100644 --- a/components/resource/imagemanager.hpp +++ b/components/resource/imagemanager.hpp @@ -32,7 +32,7 @@ namespace Resource osg::Image* getWarningImage(); - void reportStats(unsigned int frameNumber, osg::Stats* stats); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: osg::ref_ptr mWarningImage; diff --git a/components/resource/keyframemanager.cpp b/components/resource/keyframemanager.cpp index 699fdfdd6..8c5c50adc 100644 --- a/components/resource/keyframemanager.cpp +++ b/components/resource/keyframemanager.cpp @@ -34,7 +34,7 @@ namespace Resource } } - void KeyframeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) + void KeyframeManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Keyframe", mCache->getCacheSize()); } diff --git a/components/resource/keyframemanager.hpp b/components/resource/keyframemanager.hpp index a5c1a7818..3496342fa 100644 --- a/components/resource/keyframemanager.hpp +++ b/components/resource/keyframemanager.hpp @@ -23,7 +23,7 @@ namespace Resource /// @note Throws an exception if the resource is not found. osg::ref_ptr get(const std::string& name); - void reportStats(unsigned int frameNumber, osg::Stats* stats); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const; }; } diff --git a/components/resource/niffilemanager.cpp b/components/resource/niffilemanager.cpp index dcc222ed1..8d932ca64 100644 --- a/components/resource/niffilemanager.cpp +++ b/components/resource/niffilemanager.cpp @@ -56,7 +56,7 @@ namespace Resource } } - void NifFileManager::reportStats(unsigned int frameNumber, osg::Stats *stats) + void NifFileManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Nif", mCache->getCacheSize()); } diff --git a/components/resource/niffilemanager.hpp b/components/resource/niffilemanager.hpp index 378daabf7..c783bb7e0 100644 --- a/components/resource/niffilemanager.hpp +++ b/components/resource/niffilemanager.hpp @@ -23,7 +23,7 @@ namespace Resource /// to be done in advance by other managers accessing the NifFileManager. Nif::NIFFilePtr get(const std::string& name); - void reportStats(unsigned int frameNumber, osg::Stats *stats); + void reportStats(unsigned int frameNumber, osg::Stats *stats) const; }; } diff --git a/components/resource/resourcemanager.hpp b/components/resource/resourcemanager.hpp index f07ac6f77..61599cd5e 100644 --- a/components/resource/resourcemanager.hpp +++ b/components/resource/resourcemanager.hpp @@ -33,7 +33,7 @@ namespace Resource const VFS::Manager* getVFS() const; - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const {} protected: const VFS::Manager* mVFS; diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 091614a10..19b280ff1 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -85,9 +85,9 @@ namespace Resource return mVFS; } - void ResourceSystem::reportStats(unsigned int frameNumber, osg::Stats *stats) + void ResourceSystem::reportStats(unsigned int frameNumber, osg::Stats *stats) const { - for (std::vector::iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) + for (std::vector::const_iterator it = mResourceManagers.begin(); it != mResourceManagers.end(); ++it) (*it)->reportStats(frameNumber, stats); } diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index 71541ade5..6e308fd9b 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -54,7 +54,7 @@ namespace Resource /// @note May be called from any thread. const VFS::Manager* getVFS() const; - void reportStats(unsigned int frameNumber, osg::Stats* stats); + void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: std::auto_ptr mSceneManager; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index da8ba180e..167ab6221 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -703,7 +703,7 @@ namespace Resource mSharedStateMutex.unlock(); } - void SceneManager::reportStats(unsigned int frameNumber, osg::Stats *stats) + void SceneManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { { OpenThreads::ScopedLock lock(*mIncrementalCompileOperation->getToCompiledMutex()); diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 3ed7d1bd3..2da28eaf4 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -143,7 +143,7 @@ namespace Resource /// @see ResourceManager::updateCache virtual void updateCache(double referenceTime); - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: @@ -160,7 +160,7 @@ namespace Resource osg::ref_ptr mInstanceCache; osg::ref_ptr mSharedStateManager; - OpenThreads::Mutex mSharedStateMutex; + mutable OpenThreads::Mutex mSharedStateMutex; Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 96bea517b..ccd4d6a3a 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -71,7 +71,7 @@ void ChunkManager::setShaderManager(Shader::ShaderManager *shaderManager) mShaderManager = shaderManager; } -void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); } diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 4bb4c5432..d7f9901c8 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -42,7 +42,7 @@ namespace Terrain // Optional void setShaderManager(Shader::ShaderManager* shaderManager); - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center); diff --git a/components/terrain/texturemanager.cpp b/components/terrain/texturemanager.cpp index dd1e87bf6..b901fa159 100644 --- a/components/terrain/texturemanager.cpp +++ b/components/terrain/texturemanager.cpp @@ -54,7 +54,7 @@ osg::ref_ptr TextureManager::getTexture(const std::string &name) } } -void TextureManager::reportStats(unsigned int frameNumber, osg::Stats *stats) +void TextureManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Terrain Texture", mCache->getCacheSize()); } diff --git a/components/terrain/texturemanager.hpp b/components/terrain/texturemanager.hpp index 9aba2f092..e1205606e 100644 --- a/components/terrain/texturemanager.hpp +++ b/components/terrain/texturemanager.hpp @@ -27,7 +27,7 @@ namespace Terrain osg::ref_ptr getTexture(const std::string& name); - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats); + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: Resource::SceneManager* mSceneManager; From 0fc465da59f51df85b51f19cc4ef45281fe103d1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 15:00:16 +0100 Subject: [PATCH 23/88] Store the min/max height in LandData --- apps/openmw/mwphysics/physicssystem.cpp | 20 +++++--------------- apps/openmw/mwphysics/physicssystem.hpp | 2 +- apps/openmw/mwworld/scene.cpp | 4 ++-- components/esm/loadland.cpp | 11 +++++++++++ components/esm/loadland.hpp | 2 ++ components/esmterrain/storage.cpp | 2 -- 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 3193e71a4..5e79e2a09 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -516,21 +516,11 @@ namespace MWPhysics class HeightField { public: - HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) + HeightField(const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) { - // find the minimum and maximum heights (needed for bullet) - float minh = heights[0]; - float maxh = heights[0]; - for(int i = 1;i < sqrtVerts*sqrtVerts;++i) - { - float h = heights[i]; - if(h > maxh) maxh = h; - if(h < minh) minh = h; - } - mShape = new btHeightfieldTerrainShape( sqrtVerts, sqrtVerts, heights, 1, - minh, maxh, 2, + minH, maxH, 2, PHY_FLOAT, false ); mShape->setUseDiamondSubdivision(true); @@ -539,7 +529,7 @@ namespace MWPhysics btTransform transform(btQuaternion::getIdentity(), btVector3((x+0.5f) * triSize * (sqrtVerts-1), (y+0.5f) * triSize * (sqrtVerts-1), - (maxh+minh)*0.5f)); + (maxH+minH)*0.5f)); mCollisionObject = new btCollisionObject; mCollisionObject->setCollisionShape(mShape); @@ -1143,9 +1133,9 @@ namespace MWPhysics return MovementSolver::traceDown(ptr, position, found->second, mCollisionWorld, maxHeight); } - void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject) + void PhysicsSystem::addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject) { - HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts, holdObject); + HeightField *heightfield = new HeightField(heights, x, y, triSize, sqrtVerts, minH, maxH, holdObject); mHeightFields[std::make_pair(x,y)] = heightfield; mCollisionWorld->addCollisionObject(heightfield->getCollisionObject(), CollisionType_HeightMap, diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index ae585281b..3312f10a0 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -81,7 +81,7 @@ namespace MWPhysics void updatePosition (const MWWorld::Ptr& ptr); - void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, const osg::Object* holdObject); + void addHeightField (const float* heights, int x, int y, float triSize, float sqrtVerts, float minH, float maxH, const osg::Object* holdObject); void removeHeightField (int x, int y); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 13546eb3f..3e1227b37 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -278,13 +278,13 @@ namespace MWWorld const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : 0; if (data) { - mPhysics->addHeightField (data->mHeights, cellX, cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); + mPhysics->addHeightField (data->mHeights, cellX, cell->getCell()->getGridY(), worldsize / (verts-1), verts, data->mMinHeight, data->mMaxHeight, land.get()); } else { static std::vector defaultHeight; defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT); - mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, land.get()); + mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(), worldsize / (verts-1), verts, ESM::Land::DEFAULT_HEIGHT, ESM::Land::DEFAULT_HEIGHT, land.get()); } } diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 8d57c72eb..72c3eb98d 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -203,16 +203,27 @@ namespace ESM if (reader.isNextSub("VHGT")) { VHGT vhgt; if (condLoad(reader, flags, target->mDataLoaded, DATA_VHGT, &vhgt, sizeof(vhgt))) { + target->mMinHeight = FLT_MAX; + target->mMaxHeight = -FLT_MAX; float rowOffset = vhgt.mHeightOffset; for (int y = 0; y < LAND_SIZE; y++) { rowOffset += vhgt.mHeightData[y * LAND_SIZE]; target->mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE; + if (rowOffset * HEIGHT_SCALE > target->mMaxHeight) + target->mMaxHeight = rowOffset * HEIGHT_SCALE; + if (rowOffset * HEIGHT_SCALE < target->mMinHeight) + target->mMinHeight = rowOffset * HEIGHT_SCALE; float colOffset = rowOffset; for (int x = 1; x < LAND_SIZE; x++) { colOffset += vhgt.mHeightData[y * LAND_SIZE + x]; target->mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE; + + if (colOffset * HEIGHT_SCALE > target->mMaxHeight) + target->mMaxHeight = colOffset * HEIGHT_SCALE; + if (colOffset * HEIGHT_SCALE < target->mMinHeight) + target->mMinHeight = colOffset * HEIGHT_SCALE; } } target->mUnk1 = vhgt.mUnk1; diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 261708893..9f33c37da 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -87,6 +87,8 @@ struct Land float mHeightOffset; // Height in world space for each vertex float mHeights[LAND_NUM_VERTS]; + float mMinHeight; + float mMaxHeight; // 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage. VNML mNormals[LAND_NUM_VERTS * 3]; diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 3ce8f4882..4b489f9ea 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -65,8 +65,6 @@ namespace ESMTerrain { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); - /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead - osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); int cellX = static_cast(std::floor(origin.x())); From e323b2fa7ba5a40b4fa4c778e87ad095b574fddf Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 15:04:41 +0100 Subject: [PATCH 24/88] Use the SceneManager's ShaderManager --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/terrain/chunkmanager.cpp | 12 ++++-------- components/terrain/chunkmanager.hpp | 9 --------- components/terrain/terraingrid.cpp | 5 +---- components/terrain/terraingrid.hpp | 9 +-------- 5 files changed, 7 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a4236df3b..469f5d63d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -218,7 +218,7 @@ namespace MWRender Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - mTerrainStorage, Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager())); + mTerrainStorage, Mask_Terrain)); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index ccd4d6a3a..5ce51cf30 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -44,7 +44,6 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) - , mShaderManager(NULL) { } @@ -66,11 +65,6 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen } } -void ChunkManager::setShaderManager(Shader::ShaderManager *shaderManager) -{ - mShaderManager = shaderManager; -} - void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const { stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); @@ -165,8 +159,10 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve float blendmapScale = mStorage->getBlendmapScale(chunkSize); - geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mSceneManager->getForcePerPixelLighting(), - mSceneManager->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); + Shader::ShaderManager* shaderManager = &mSceneManager->getShaderManager(); + + geometry->setPasses(createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); transform->addChild(geometry); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index d7f9901c8..182a4ee70 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -20,11 +20,6 @@ namespace Resource class SceneManager; } -namespace Shader -{ - class ShaderManager; -} - namespace Terrain { @@ -39,9 +34,6 @@ namespace Terrain osg::ref_ptr getChunk(float size, const osg::Vec2f& center); - // Optional - void setShaderManager(Shader::ShaderManager* shaderManager); - virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: @@ -50,7 +42,6 @@ namespace Terrain Terrain::Storage* mStorage; Resource::SceneManager* mSceneManager; TextureManager* mTextureManager; - Shader::ShaderManager* mShaderManager; BufferCache mBufferCache; }; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 966f161f2..c4e491d7a 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -11,16 +11,13 @@ namespace Terrain { -TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager) +TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask) : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) , mNumSplits(4) - , mShaderManager(shaderManager) { osg::ref_ptr material (new osg::Material); material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); - - mChunkManager->setShaderManager(mShaderManager); } TerrainGrid::~TerrainGrid() diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 0a9647bd5..1b4d2de57 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -7,11 +7,6 @@ #include "world.hpp" -namespace Shader -{ - class ShaderManager; -} - namespace osg { class Texture2D; @@ -24,7 +19,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, Shader::ShaderManager* shaderManager = NULL); + TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask); ~TerrainGrid(); /// Load a terrain cell and store it in cache for later use. @@ -50,8 +45,6 @@ namespace Terrain typedef std::map, osg::ref_ptr > Grid; Grid mGrid; - - Shader::ShaderManager* mShaderManager; }; } From b1d4bb570854cf688e2908c04e9512fe24ae3973 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 16:33:31 +0100 Subject: [PATCH 25/88] Add CompositeMapRenderer Temporarily render all terrain using composite maps for testing purposes --- components/CMakeLists.txt | 2 +- components/esmterrain/storage.cpp | 8 --- components/terrain/chunkmanager.cpp | 64 ++++++++++++++++- components/terrain/chunkmanager.hpp | 11 ++- components/terrain/compositemaprenderer.cpp | 78 +++++++++++++++++++++ components/terrain/compositemaprenderer.hpp | 48 +++++++++++++ components/terrain/material.cpp | 5 +- components/terrain/material.hpp | 3 +- components/terrain/world.cpp | 6 +- 9 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 components/terrain/compositemaprenderer.cpp create mode 100644 components/terrain/compositemaprenderer.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 37c19f527..3327caa8b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager + storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer ) add_component_dir (loadinglistener diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 4b489f9ea..2242ed554 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -368,10 +368,6 @@ namespace ESMTerrain void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, bool pack, ImageVector &blendmaps, std::vector &layerList) { - // TODO - blending isn't completely right yet; the blending radius appears to be - // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap - // and interpolate the rest of the cell by hand? :/ - osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f); int cellX = static_cast(std::floor(origin.x())); int cellY = static_cast(std::floor(origin.y())); @@ -383,10 +379,6 @@ namespace ESMTerrain int rowEnd = rowStart + chunkSize * (realTextureSize-1) + 1; int colEnd = colStart + chunkSize * (realTextureSize-1) + 1; - assert (rowStart >= 0 && colStart >= 0); - assert (rowEnd <= realTextureSize); - assert (colEnd <= realTextureSize); - // Save the used texture indices so we know the total number of textures // and number of required blend maps std::set textureIndices; diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5ce51cf30..64708cdf3 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -15,6 +15,7 @@ #include "material.hpp" #include "storage.hpp" #include "texturemanager.hpp" +#include "compositemaprenderer.hpp" namespace { @@ -39,11 +40,13 @@ namespace namespace Terrain { -ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager) +ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer) : ResourceManager(NULL) , mStorage(storage) , mSceneManager(sceneMgr) , mTextureManager(textureManager) + , mCompositeMapRenderer(renderer) + , mCompositeMapSize(512) { } @@ -70,6 +73,33 @@ void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) cons stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); } +osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptr& texture) +{ + texture = new osg::Texture2D; + texture->setTextureWidth(mCompositeMapSize); + texture->setTextureHeight(mCompositeMapSize); + texture->setInternalFormat(GL_RGB); + texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + osg::ref_ptr camera (new osg::Camera); + camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); + camera->attach(osg::Camera::COLOR_BUFFER, texture); + camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + camera->setViewMatrix(osg::Matrix::identity()); + camera->setProjectionMatrix(osg::Matrix::identity()); + camera->setProjectionResizePolicy(osg::Camera::FIXED); + camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); + camera->setClearMask(GL_COLOR_BUFFER_BIT); + camera->setViewport(0, 0, mCompositeMapSize, mCompositeMapSize); + camera->setRenderOrder(osg::Camera::PRE_RENDER); + camera->setImplicitBufferAttachmentMask(osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT); // no need for a depth buffer + + return camera; +} + osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter) { float minH, maxH; @@ -161,8 +191,36 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve Shader::ShaderManager* shaderManager = &mSceneManager->getShaderManager(); - geometry->setPasses(createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), - mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); + if (1) // useCompositeMap + { + osg::ref_ptr compositeMap; + osg::ref_ptr compositeMapNode = createCompositeMapRTT(compositeMap); + osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0)); + geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); + std::vector > passes = createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale, true); + for (std::vector >::iterator it = passes.begin(); it != passes.end(); ++it) + { + osg::ref_ptr group = new osg::Group; + group->setStateSet(*it); + group->addChild(geom); + compositeMapNode->addChild(group); + } + + compositeMapNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + mCompositeMapRenderer->addCompositeMap(compositeMapNode, true); + + std::vector > passes2; + passes2.push_back(new osg::StateSet); + passes2[0]->setTextureAttributeAndModes(0, compositeMap, osg::StateAttribute::ON); + geometry->setPasses(passes2); + } + else + { + geometry->setPasses(createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); + } transform->addChild(geometry); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 182a4ee70..3941fc843 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -7,7 +7,8 @@ namespace osg { - class Geometry; + class Group; + class Texture2D; } namespace SceneUtil @@ -24,13 +25,14 @@ namespace Terrain { class TextureManager; + class CompositeMapRenderer; class Storage; /// @brief Handles loading and caching of terrain chunks class ChunkManager : public Resource::ResourceManager { public: - ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager); + ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); osg::ref_ptr getChunk(float size, const osg::Vec2f& center); @@ -39,10 +41,15 @@ namespace Terrain private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center); + osg::ref_ptr createCompositeMapRTT(osg::ref_ptr& texture); + Terrain::Storage* mStorage; Resource::SceneManager* mSceneManager; TextureManager* mTextureManager; + CompositeMapRenderer* mCompositeMapRenderer; BufferCache mBufferCache; + + unsigned int mCompositeMapSize; }; } diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp new file mode 100644 index 000000000..496162f7b --- /dev/null +++ b/components/terrain/compositemaprenderer.cpp @@ -0,0 +1,78 @@ +#include "compositemaprenderer.hpp" + +#include + +#include + +namespace Terrain +{ + +CompositeMapRenderer::CompositeMapRenderer() + : mNumCompilePerFrame(1) + , mLastFrame(0) +{ + setCullingActive(false); +} + +void CompositeMapRenderer::traverse(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) + return; + + if (mLastFrame == nv.getTraversalNumber()) + return; + mLastFrame = nv.getTraversalNumber(); + + mCompiled.clear(); + + unsigned int numCompiled = 0; + + OpenThreads::ScopedLock lock(mMutex); + + while (!mImmediateCompileSet.empty()) + { + osg::Node* node = *mImmediateCompileSet.begin(); + mCompiled.insert(node); + + node->accept(nv); + mImmediateCompileSet.erase(mImmediateCompileSet.begin()); + + ++numCompiled; + } + + while (!mCompileSet.empty() && numCompiled <= mNumCompilePerFrame) + { + osg::Node* node = *mCompileSet.begin(); + mCompiled.insert(node); + + node->accept(nv); + mCompileSet.erase(mCompileSet.begin()); + + ++numCompiled; + } +} + +void CompositeMapRenderer::setNumCompilePerFrame(int num) +{ + mNumCompilePerFrame = num; +} + +void CompositeMapRenderer::addCompositeMap(osg::Node *node, bool immediate) +{ + OpenThreads::ScopedLock lock(mMutex); + if (immediate) + mImmediateCompileSet.insert(node); + else + mCompileSet.insert(node); +} + +void CompositeMapRenderer::setImmediate(osg::Node *node) +{ + OpenThreads::ScopedLock lock(mMutex); + mImmediateCompileSet.insert(node); + mCompileSet.erase(node); +} + + + +} diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp new file mode 100644 index 000000000..53b38362f --- /dev/null +++ b/components/terrain/compositemaprenderer.hpp @@ -0,0 +1,48 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H +#define OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H + +#include + +#include + +#include + +namespace Terrain +{ + + /** + * @brief The CompositeMapRenderer is responsible for updating composite map textures in a blocking or non-blocking way. + */ + class CompositeMapRenderer : public osg::Node + { + public: + CompositeMapRenderer(); + + virtual void traverse(osg::NodeVisitor& nv); + + /// Set the maximum number of (non-immediate) composite maps to compile per frame + void setNumCompilePerFrame(int num); + + /// Add a composite map to be rendered + void addCompositeMap(osg::Node* node, bool immediate=false); + + /// Mark this composite map to be required for the current frame + void setImmediate(osg::Node* node); + + private: + unsigned int mNumCompilePerFrame; + unsigned int mLastFrame; + + typedef std::set > CompileSet; + + CompileSet mCompileSet; + CompileSet mImmediateCompileSet; + + CompileSet mCompiled; + + OpenThreads::Mutex mMutex; + }; + +} + +#endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 496a2cc75..5b84da3f7 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -58,7 +58,8 @@ namespace Terrain return depth; } - std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, const std::vector > &blendmaps, int blendmapScale, float layerTileSize) + std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, + const std::vector > &blendmaps, int blendmapScale, float layerTileSize, bool renderCompositeMap) { std::vector > passes; @@ -103,6 +104,8 @@ namespace Terrain stateset->addUniform(new osg::Uniform("normalMap", texunit)); } + // TODO: fix shader for renderCompositeMap=True + Shader::ShaderManager::DefineMap defineMap; defineMap["forcePPL"] = forcePerPixelLighting ? "1" : "0"; defineMap["clamp"] = clampLighting ? "1" : "0"; diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 0ffd4db54..4e8a222d1 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -28,7 +28,8 @@ namespace Terrain std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize); + const std::vector >& blendmaps, int blendmapScale, float layerTileSize, bool renderCompositeMap=false); + } #endif diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 9c3d08687..29d3281e5 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -8,6 +8,7 @@ #include "storage.hpp" #include "texturemanager.hpp" #include "chunkmanager.hpp" +#include "compositemaprenderer.hpp" namespace Terrain { @@ -24,10 +25,13 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); mTerrainRoot->setName("Terrain Root"); + osg::ref_ptr renderer (new CompositeMapRenderer); + mTerrainRoot->addChild(renderer); + mParent->addChild(mTerrainRoot); mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); - mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get())); + mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), renderer)); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); From 7e4450da554f286e0b499029de27765224d58bc2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 16:55:05 +0100 Subject: [PATCH 26/88] Change the renderOrder of composite maps to ensure they are updated before water reflections or other cameras that may be using it. --- components/terrain/chunkmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 64708cdf3..ab660dfb9 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -94,7 +94,7 @@ osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptrsetClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); camera->setClearMask(GL_COLOR_BUFFER_BIT); camera->setViewport(0, 0, mCompositeMapSize, mCompositeMapSize); - camera->setRenderOrder(osg::Camera::PRE_RENDER); + camera->setRenderOrder(osg::Camera::PRE_RENDER, -1); camera->setImplicitBufferAttachmentMask(osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT); // no need for a depth buffer return camera; From 5eff286c713695bd8042b7ad96e151b061771738 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 17:10:46 +0100 Subject: [PATCH 27/88] Use separate node mask and parent for CompositeMapRenderer to allow the loading screen to pre compile composite maps. --- apps/opencs/view/render/cell.cpp | 2 +- apps/openmw/mwgui/loadingscreen.cpp | 4 ++-- apps/openmw/mwrender/renderingmanager.cpp | 4 ++-- apps/openmw/mwrender/vismask.hpp | 4 +++- components/terrain/terraingrid.cpp | 4 ++-- components/terrain/terraingrid.hpp | 2 +- components/terrain/world.cpp | 9 ++++++--- components/terrain/world.hpp | 5 +++-- 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 9ba5eeb01..5474d300b 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -107,7 +107,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st if (esmLand.getLandData (ESM::Land::DATA_VHGT)) { - mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Mask_Terrain)); + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Mask_Terrain)); mTerrain->loadCell(esmLand.mX, esmLand.mY); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 35a825bcd..ca6a0b0a4 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -297,8 +297,8 @@ namespace MWGui // Turn off rendering except the GUI int oldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); int oldCullMask = mViewer->getCamera()->getCullMask(); - mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI); - mViewer->getCamera()->setCullMask(MWRender::Mask_GUI); + mViewer->getUpdateVisitor()->setTraversalMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); + mViewer->getCamera()->setCullMask(MWRender::Mask_GUI|MWRender::Mask_PreCompile); MWBase::Environment::get().getInputManager()->update(0, true, true); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 469f5d63d..db5b77266 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -217,8 +217,8 @@ namespace MWRender mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), - mTerrainStorage, Mask_Terrain)); + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mViewer->getIncrementalCompileOperation(), + mTerrainStorage, Mask_Terrain, Mask_PreCompile)); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/apps/openmw/mwrender/vismask.hpp b/apps/openmw/mwrender/vismask.hpp index c70bcc71b..d52c7c232 100644 --- a/apps/openmw/mwrender/vismask.hpp +++ b/apps/openmw/mwrender/vismask.hpp @@ -48,8 +48,10 @@ namespace MWRender // Set on cameras within the main scene graph Mask_RenderToTexture = (1<<15), + Mask_PreCompile = (1<<16), + // Set on a camera's cull mask to enable the LightManager - Mask_Lighting = (1<<16) + Mask_Lighting = (1<<17) }; } diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index c4e491d7a..ed18af74b 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -11,8 +11,8 @@ namespace Terrain { -TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask) - : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) +TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, int preCompileMask) + : Terrain::World(parent, compileRoot, resourceSystem, ico, storage, nodeMask, preCompileMask) , mNumSplits(4) { osg::ref_ptr material (new osg::Material); diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 1b4d2de57..0bfc43075 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -19,7 +19,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask); + TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, int preCompileMask=~0); ~TerrainGrid(); /// Load a terrain cell and store it in cache for later use. diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 29d3281e5..015270a43 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -13,8 +13,8 @@ namespace Terrain { -World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask) +World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask, int preCompileMask) : mStorage(storage) , mParent(parent) , mResourceSystem(resourceSystem) @@ -26,7 +26,9 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt mTerrainRoot->setName("Terrain Root"); osg::ref_ptr renderer (new CompositeMapRenderer); - mTerrainRoot->addChild(renderer); + renderer->setNodeMask(preCompileMask); + compileRoot->addChild(renderer); + mCompositeMapRenderer = renderer; mParent->addChild(mTerrainRoot); @@ -43,6 +45,7 @@ World::~World() mResourceSystem->removeResourceManager(mTextureManager.get()); mParent->removeChild(mTerrainRoot); + mCompositeMapRenderer->getParent(0)->removeChild(mCompositeMapRenderer); delete mStorage; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index a8cc0f0db..f5cb10c37 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -42,8 +42,8 @@ namespace Terrain /// @note takes ownership of \a storage /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root - World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask); + World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask, int preCompileMask); virtual ~World(); virtual void updateTextureFiltering() {} @@ -63,6 +63,7 @@ namespace Terrain osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; + osg::ref_ptr mCompositeMapRenderer; Resource::ResourceSystem* mResourceSystem; From c487df0abbc6b12ec99036e84e9fb1073b69eb7c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 17:22:47 +0100 Subject: [PATCH 28/88] Move updateTextureFiltering and material into the base class --- components/terrain/terraingrid.cpp | 10 ---------- components/terrain/terraingrid.hpp | 4 ---- components/terrain/world.cpp | 11 +++++++++++ components/terrain/world.hpp | 4 +++- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index ed18af74b..c4e69bab1 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -2,10 +2,8 @@ #include -#include #include -#include "texturemanager.hpp" #include "chunkmanager.hpp" namespace Terrain @@ -15,9 +13,6 @@ TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource:: : Terrain::World(parent, compileRoot, resourceSystem, ico, storage, nodeMask, preCompileMask) , mNumSplits(4) { - osg::ref_ptr material (new osg::Material); - material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); - mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); } TerrainGrid::~TerrainGrid() @@ -89,9 +84,4 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } -void TerrainGrid::updateTextureFiltering() -{ - mTextureManager->updateTextureFiltering(); -} - } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 0bfc43075..f481c62e4 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -33,10 +33,6 @@ namespace Terrain /// @note Not thread safe. virtual void unloadCell(int x, int y); - /// Apply the scene manager's texture filtering settings to all cached textures. - /// @note Thread safe. - void updateTextureFiltering(); - private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 015270a43..ac6b5a6aa 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -1,6 +1,8 @@ #include "world.hpp" #include +#include + #include #include @@ -23,6 +25,10 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); + mTerrainRoot->setName("Terrain Root"); osg::ref_ptr renderer (new CompositeMapRenderer); @@ -55,4 +61,9 @@ float World::getHeightAt(const osg::Vec3f &worldPos) return mStorage->getHeightAt(worldPos); } +void World::updateTextureFiltering() +{ + mTextureManager->updateTextureFiltering(); +} + } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index f5cb10c37..132d5720b 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -46,7 +46,9 @@ namespace Terrain Storage* storage, int nodeMask, int preCompileMask); virtual ~World(); - virtual void updateTextureFiltering() {} + /// Apply the scene manager's texture filtering settings to all cached textures. + /// @note Thread safe. + void updateTextureFiltering(); float getHeightAt (const osg::Vec3f& worldPos); From 14225a42c642a947153a111ab81fa3817cc54534 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 17:25:23 +0100 Subject: [PATCH 29/88] Remove unused pointer to IncrementalCompileOperation --- apps/opencs/view/render/cell.cpp | 2 +- apps/openmw/mwrender/objects.hpp | 5 ----- apps/openmw/mwrender/renderingmanager.cpp | 7 +++---- components/terrain/terraingrid.cpp | 4 ++-- components/terrain/terraingrid.hpp | 2 +- components/terrain/world.cpp | 6 +----- components/terrain/world.hpp | 11 ++--------- 7 files changed, 10 insertions(+), 27 deletions(-) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 5474d300b..7cf542835 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -107,7 +107,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st if (esmLand.getLandData (ESM::Land::DATA_VHGT)) { - mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), NULL, new TerrainStorage(mData), Mask_Terrain)); + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, mCellNode, data.getResourceSystem().get(), new TerrainStorage(mData), Mask_Terrain)); mTerrain->loadCell(esmLand.mX, esmLand.mY); diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index cc66ae20d..8864e4483 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -15,11 +15,6 @@ namespace osg class Group; } -namespace osgUtil -{ - class IncrementalCompileOperation; -} - namespace Resource { class ResourceSystem; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index db5b77266..ff7f6ae9a 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -215,10 +215,9 @@ namespace MWRender mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), - Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), - Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mViewer->getIncrementalCompileOperation(), - mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), + Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index c4e69bab1..f7489fcde 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -9,8 +9,8 @@ namespace Terrain { -TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, int preCompileMask) - : Terrain::World(parent, compileRoot, resourceSystem, ico, storage, nodeMask, preCompileMask) +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) , mNumSplits(4) { } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index f481c62e4..2cd5b5d7a 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -19,7 +19,7 @@ namespace Terrain class TerrainGrid : public Terrain::World { public: - TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, Storage* storage, int nodeMask, int preCompileMask=~0); + TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0); ~TerrainGrid(); /// Load a terrain cell and store it in cache for later use. diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index ac6b5a6aa..3b0a05491 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -3,8 +3,6 @@ #include #include -#include - #include #include "storage.hpp" @@ -15,12 +13,10 @@ namespace Terrain { -World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask, int preCompileMask) +World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask) : mStorage(storage) , mParent(parent) , mResourceSystem(resourceSystem) - , mIncrementalCompileOperation(ico) { mTerrainRoot = new osg::Group; mTerrainRoot->setNodeMask(nodeMask); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 132d5720b..84941d087 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -15,11 +15,6 @@ namespace osg class Node; } -namespace osgUtil -{ - class IncrementalCompileOperation; -} - namespace Resource { class ResourceSystem; @@ -42,8 +37,8 @@ namespace Terrain /// @note takes ownership of \a storage /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) /// @param nodeMask mask for the terrain root - World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, - Storage* storage, int nodeMask, int preCompileMask); + /// @param preCompileMask mask for pre compiling textures + World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask); virtual ~World(); /// Apply the scene manager's texture filtering settings to all cached textures. @@ -69,8 +64,6 @@ namespace Terrain Resource::ResourceSystem* mResourceSystem; - osg::ref_ptr mIncrementalCompileOperation; - std::auto_ptr mTextureManager; std::auto_ptr mChunkManager; }; From 2d549d088e77ee5fdbda7085f72208f4dfa46e34 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Mar 2017 18:31:53 +0100 Subject: [PATCH 30/88] Get the world size from the ESM::Land store --- apps/openmw/mwrender/terrainstorage.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 9d229c96c..ff2a8bfc7 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -31,17 +31,17 @@ namespace MWRender const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); - MWWorld::Store::iterator it = esmStore.get().extBegin(); - for (; it != esmStore.get().extEnd(); ++it) + MWWorld::Store::iterator it = esmStore.get().begin(); + for (; it != esmStore.get().end(); ++it) { - if (it->getGridX() < minX) - minX = static_cast(it->getGridX()); - if (it->getGridX() > maxX) - maxX = static_cast(it->getGridX()); - if (it->getGridY() < minY) - minY = static_cast(it->getGridY()); - if (it->getGridY() > maxY) - maxY = static_cast(it->getGridY()); + if (it->mX < minX) + minX = static_cast(it->mX); + if (it->mX > maxX) + maxX = static_cast(it->mX); + if (it->mY < minY) + minY = static_cast(it->mY); + if (it->mY > maxY) + maxY = static_cast(it->mY); } // since grid coords are at cell origin, we need to add 1 cell From ce8c4ad4f55eff2ffb5b5364b885c5adad928769 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 01:53:13 +0100 Subject: [PATCH 31/88] Add quad tree implementation (no rendering yet) --- components/CMakeLists.txt | 2 +- components/terrain/quadtreenode.cpp | 169 ++++++++++++++++++++ components/terrain/quadtreenode.hpp | 66 ++++++++ components/terrain/quadtreeworld.cpp | 227 +++++++++++++++++++++++++++ components/terrain/quadtreeworld.hpp | 47 ++++++ components/terrain/storage.hpp | 1 + components/terrain/terraingrid.hpp | 7 +- components/terrain/world.cpp | 5 + components/terrain/world.hpp | 14 +- 9 files changed, 529 insertions(+), 9 deletions(-) create mode 100644 components/terrain/quadtreenode.cpp create mode 100644 components/terrain/quadtreenode.hpp create mode 100644 components/terrain/quadtreeworld.cpp create mode 100644 components/terrain/quadtreeworld.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3327caa8b..8b019e470 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer + storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode ) add_component_dir (loadinglistener diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp new file mode 100644 index 000000000..518274df5 --- /dev/null +++ b/components/terrain/quadtreenode.cpp @@ -0,0 +1,169 @@ +#include "quadtreenode.hpp" + +#include + +#include + +#include "defs.hpp" + +namespace +{ + + float distance(const osg::BoundingBox& box, const osg::Vec3f& v) + { + if (box.contains(v)) + return 0; + else + { + osg::Vec3f maxDist(0,0,0); + + if (v.x() < box.xMin()) + maxDist.x() = box.xMin() - v.x(); + else if (v.x() > box.xMax()) + maxDist.x() = v.x() - box.xMax(); + + if (v.y() < box.yMin()) + maxDist.y() = box.yMin() - v.y(); + else if (v.y() > box.yMax()) + maxDist.y() = v.y() - box.yMax(); + + if (v.z() < box.zMin()) + maxDist.z() = box.zMin() - v.z(); + else if (v.z() > box.zMax()) + maxDist.z() = v.z() - box.zMax(); + + return maxDist.length(); + } + } + + int Log2( int n ) + { + assert(n > 0); + int targetlevel = 0; + while (n >>= 1) ++targetlevel; + return targetlevel; + } + +} + +namespace Terrain +{ + +ChildDirection reflect(ChildDirection dir, Direction dir2) +{ + assert(dir != Root); + const int lookupTable[4][4] = + { + // NW NE SW SE + { SW, SE, NW, NE }, // N + { NE, NW, SE, SW }, // E + { SW, SE, NW, NE }, // S + { NE, NW, SE, SW } // W + }; + return (ChildDirection)lookupTable[dir2][dir]; +} + +bool adjacent(ChildDirection dir, Direction dir2) +{ + assert(dir != Root); + const bool lookupTable[4][4] = + { + // NW NE SW SE + { true, true, false, false }, // N + { false, true, false, true }, // E + { false, false, true, true }, // S + { true, false, true, false } // W + }; + return lookupTable[dir2][dir]; +} + +QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) +{ + if (currentNode->getDirection() == Root) + return NULL; // Arrived at root node, the root node does not have neighbours + + QuadTreeNode* nextNode; + if (adjacent(currentNode->getDirection(), dir)) + nextNode = searchNeighbour(currentNode->getParent(), dir); + else + nextNode = currentNode->getParent(); + + if (nextNode && nextNode->getNumChildren()) + return nextNode->getChild(reflect(currentNode->getDirection(), dir)); + else + return NULL; +} + +QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center) + : mParent(parent) + , mDirection(direction) + , mSize(size) + , mCenter(center) +{ + for (unsigned int i=0; i<4; ++i) + mNeighbours[i] = 0; +} + +QuadTreeNode* QuadTreeNode::getParent() +{ + return mParent; +} + +QuadTreeNode *QuadTreeNode::getChild(unsigned int i) +{ + return static_cast(Group::getChild(i)); +} + +void QuadTreeNode::initNeighbours() +{ + for (int i=0; i<4; ++i) + mNeighbours[i] = searchNeighbour(this, (Direction)i); + + for (unsigned int i=0; iinitNeighbours(); +} + +void QuadTreeNode::traverse(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) + return; + + osgUtil::CullVisitor* cv = static_cast(&nv); + + // do another culling test against bounding box as its much more accurate than the bounding sphere. + if (cv->isCulled(mBoundingBox)) + return; + + //float dist = distance(getBoundingBox(), nv.getEyePoint()); + + osg::Group::traverse(nv); +} + +void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) +{ + mBoundingBox = boundingBox; + dirtyBound(); + getBound(); +} + +const osg::BoundingBox &QuadTreeNode::getBoundingBox() const +{ + return mBoundingBox; +} + +osg::BoundingSphere QuadTreeNode::computeBound() const +{ + return osg::BoundingSphere(mBoundingBox); +} + +float QuadTreeNode::getSize() const +{ + return mSize; +} + +const osg::Vec2f &QuadTreeNode::getCenter() const +{ + return mCenter; +} + +} diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp new file mode 100644 index 000000000..aab54c69c --- /dev/null +++ b/components/terrain/quadtreenode.hpp @@ -0,0 +1,66 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H +#define OPENMW_COMPONENTS_TERRAIN_QUADTREENODE_H + +#include + +#include "defs.hpp" + +namespace Terrain +{ + + enum ChildDirection + { + NW = 0, + NE = 1, + SW = 2, + SE = 3, + Root + }; + + class QuadTreeNode : public osg::Group + { + public: + QuadTreeNode(QuadTreeNode* parent, ChildDirection dir, float size, const osg::Vec2f& center); + + QuadTreeNode* getParent(); + + QuadTreeNode* getChild(unsigned int i); + using osg::Group::getNumChildren; + + /// Returns our direction relative to the parent node, or Root if we are the root node. + ChildDirection getDirection() { return mDirection; } + + /// Get neighbour node in this direction + QuadTreeNode* getNeighbour (Direction dir); + + /// Initialize neighbours - do this after the quadtree is built + void initNeighbours(); + + void setBoundingBox(const osg::BoundingBox& boundingBox); + const osg::BoundingBox& getBoundingBox() const; + + virtual osg::BoundingSphere computeBound() const; + + /// size in cell coordinates + float getSize() const; + + /// center in cell coordinates + const osg::Vec2f& getCenter() const; + + virtual void traverse(osg::NodeVisitor& nv); + + private: + QuadTreeNode* mParent; + + QuadTreeNode* mNeighbours[4]; + + ChildDirection mDirection; + + osg::BoundingBox mBoundingBox; + float mSize; + osg::Vec2f mCenter; + }; + +} + +#endif diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp new file mode 100644 index 000000000..25aecd2a7 --- /dev/null +++ b/components/terrain/quadtreeworld.cpp @@ -0,0 +1,227 @@ +#include "quadtreeworld.hpp" + +#include + +#include "quadtreenode.hpp" +#include "storage.hpp" + + +#include +#include + +namespace +{ + + bool isPowerOfTwo(int x) + { + return ( (x > 0) && ((x & (x - 1)) == 0) ); + } + + int nextPowerOfTwo (int v) + { + if (isPowerOfTwo(v)) return v; + int depth=0; + while(v) + { + v >>= 1; + depth++; + } + return 1 << depth; + } + +} + +namespace Terrain +{ + + +class RootNode : public QuadTreeNode +{ +public: + RootNode(float size, const osg::Vec2f& center) + : QuadTreeNode(NULL, Root, size, center) + , mWorld(NULL) + { + } + + void setWorld(QuadTreeWorld* world) + { + mWorld = world; + } + + virtual void accept(osg::NodeVisitor &nv) + { + nv.pushOntoNodePath(this); + mWorld->accept(nv); + nv.popFromNodePath(); + } + +private: + QuadTreeWorld* mWorld; +}; + +class BuildQuadTreeItem : public SceneUtil::WorkItem +{ +public: + BuildQuadTreeItem(Terrain::Storage* storage, float minSize) + : mStorage(storage) + , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f) + , mMinSize(minSize) + { + } + + virtual void doWork() + { + mStorage->getBounds(mMinX, mMaxX, mMinY, mMaxY); + + int origSizeX = static_cast(mMaxX - mMinX); + int origSizeY = static_cast(mMaxY - mMinY); + + // Dividing a quad tree only works well for powers of two, so round up to the nearest one + int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); + + float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; + float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; + + mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); + addChildren(mRootNode); + + mRootNode->initNeighbours(); + } + + void addChildren(QuadTreeNode* parent) + { + float halfSize = parent->getSize()/2.f; + osg::BoundingBox boundingBox; + for (unsigned int i=0; i<4; ++i) + { + QuadTreeNode* child = addChild(parent, static_cast(i), halfSize); + if (child) + boundingBox.expandBy(child->getBoundingBox()); + } + + parent->setBoundingBox(boundingBox); + } + + QuadTreeNode* addChild(QuadTreeNode* parent, ChildDirection direction, float size) + { + osg::Vec2f center; + switch (direction) + { + case SW: + center = parent->getCenter() + osg::Vec2f(-size/2.f,-size/2.f); + break; + case SE: + center = parent->getCenter() + osg::Vec2f(size/2.f, -size/2.f); + break; + case NW: + center = parent->getCenter() + osg::Vec2f(-size/2.f, size/2.f); + break; + case NE: + center = parent->getCenter() + osg::Vec2f(size/2.f, size/2.f); + break; + default: + break; + } + + osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); + parent->addChild(node); + + if (center.x() - size > mMaxX + || center.x() + size < mMinX + || center.y() - size > mMaxY + || center.y() + size < mMinY ) + // Out of bounds of the actual terrain - this will happen because + // we rounded the size up to the next power of two + { + // Still create and return an empty node so as to not break the assumption that each QuadTreeNode has either 4 or 0 children. + return node; + } + + if (node->getSize() <= mMinSize) + { + // We arrived at a leaf + float minZ,maxZ; + if (mStorage->getMinMaxHeights(size, center, minZ, maxZ)) + { + float cellWorldSize = mStorage->getCellWorldSize(); + osg::BoundingBox boundingBox(osg::Vec3f((center.x()-size)*cellWorldSize, (center.y()-size)*cellWorldSize, minZ), + osg::Vec3f((center.x()+size)*cellWorldSize, (center.y()+size)*cellWorldSize, maxZ)); + node->setBoundingBox(boundingBox); + } + return node; + } + else + { + addChildren(node); + return node; + } + } + + osg::ref_ptr getRootNode() + { + return mRootNode; + } + +private: + Terrain::Storage* mStorage; + + float mMinX, mMaxX, mMinY, mMaxY; + float mMinSize; + + osg::ref_ptr mRootNode; +}; + +QuadTreeWorld::QuadTreeWorld(SceneUtil::WorkQueue* workQueue, osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask) + : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask) + , mWorkQueue(workQueue) + , mQuadTreeBuilt(false) +{ +} + +QuadTreeWorld::~QuadTreeWorld() +{ + if (mWorkItem) + { + mWorkItem->abort(); + mWorkItem->waitTillDone(); + } +} + +void QuadTreeWorld::accept(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + return; + + mRootNode->traverse(nv); +} + +void QuadTreeWorld::loadCell(int x, int y) +{ + if (mQuadTreeBuilt && !mRootNode) + { + mRootNode = mWorkItem->getRootNode(); + mRootNode->setWorld(this); + mTerrainRoot->addChild(mRootNode); + mWorkItem = NULL; + } +} + +osg::ref_ptr QuadTreeWorld::cacheCell(int x, int y) +{ + if (!mQuadTreeBuilt) + { + const float minSize = 1/4.f; + mWorkItem = new BuildQuadTreeItem(mStorage, minSize); + mWorkQueue->addWorkItem(mWorkItem); + + mWorkItem->waitTillDone(); + + mQuadTreeBuilt = true; + } + return NULL; +} + + + +} diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp new file mode 100644 index 000000000..320f53120 --- /dev/null +++ b/components/terrain/quadtreeworld.hpp @@ -0,0 +1,47 @@ +#ifndef COMPONENTS_TERRAIN_QUADTREEWORLD_H +#define COMPONENTS_TERRAIN_QUADTREEWORLD_H + +#include + +#include "world.hpp" + +namespace SceneUtil +{ + class WorkQueue; +} + +namespace osg +{ + class NodeVisitor; +} + +namespace Terrain +{ + class RootNode; + class BuildQuadTreeItem; + + /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. The entire world is displayed at all times. + class QuadTreeWorld : public Terrain::World + { + public: + QuadTreeWorld(SceneUtil::WorkQueue* workQueue, osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0); + ~QuadTreeWorld(); + + void accept(osg::NodeVisitor& nv); + + virtual void loadCell(int x, int y); + virtual osg::ref_ptr cacheCell(int x, int y); + + private: + osg::ref_ptr mRootNode; + + osg::ref_ptr mWorkQueue; + + osg::ref_ptr mWorkItem; + + bool mQuadTreeBuilt; + }; + +} + +#endif diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index efe8fa037..a4a8bc9fd 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -18,6 +18,7 @@ namespace osg namespace Terrain { /// We keep storage of terrain data abstract here since we need different implementations for game and editor + /// @note The implementation must be thread safe. class Storage { public: diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 2cd5b5d7a..dc4523eef 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -7,15 +7,10 @@ #include "world.hpp" -namespace osg -{ - class Texture2D; -} - namespace Terrain { - /// @brief Simple terrain implementation that loads cells in a grid, with no LOD + /// @brief Simple terrain implementation that loads cells in a grid, with no LOD. Only requested cells are loaded. class TerrainGrid : public Terrain::World { public: diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 3b0a05491..b662cd314 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -57,6 +57,11 @@ float World::getHeightAt(const osg::Vec3f &worldPos) return mStorage->getHeightAt(worldPos); } +osg::ref_ptr World::cacheCell(int x, int y) +{ + return NULL; +} + void World::updateTextureFiltering() { mTextureManager->updateTextureFiltering(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 84941d087..d4867d2ba 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -47,10 +47,20 @@ namespace Terrain float getHeightAt (const osg::Vec3f& worldPos); - virtual osg::ref_ptr cacheCell(int x, int y) {return NULL;} + /// Load a terrain cell and store it in cache for later use. + /// @note The returned ref_ptr should be kept by the caller to ensure that the terrain stays in cache for as long as needed. + /// @note Thread safe. + /// @note May be ignored by derived implementations that don't organize the terrain into cells. + virtual osg::ref_ptr cacheCell(int x, int y); - // This is only a hint and may be ignored by the implementation. + /// 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) {} + + /// 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) {} Storage* getStorage() { return mStorage; } From 819860081f6e5e194b730abbad601615d0248cce Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 04:20:59 +0100 Subject: [PATCH 32/88] Remove custom bounding box --- components/esmterrain/storage.cpp | 2 +- components/terrain/chunkmanager.cpp | 35 +---------------------------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 2242ed554..d1138a147 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -98,7 +98,7 @@ namespace ESMTerrain min = defaultHeight; max = defaultHeight; - return true; + return false; } void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index ab660dfb9..94c521d5b 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -17,26 +17,6 @@ #include "texturemanager.hpp" #include "compositemaprenderer.hpp" -namespace -{ - class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback - { - public: - StaticBoundingBoxCallback(const osg::BoundingBox& bounds) - : mBoundingBox(bounds) - { - } - - virtual osg::BoundingBox computeBound(const osg::Drawable&) const - { - return mBoundingBox; - } - - private: - osg::BoundingBox mBoundingBox; - }; -} - namespace Terrain { @@ -102,10 +82,6 @@ osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter) { - float minH, maxH; - if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH)) - return NULL; // no terrain defined - osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); @@ -134,16 +110,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0)); - - // we already know the bounding box, so no need to let OSG compute it. - osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize, - -0.5f*mStorage->getCellWorldSize()*chunkSize, - minH); - osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize, - 0.5f*mStorage->getCellWorldSize()*chunkSize, - maxH); - osg::BoundingBox bounds(min, max); - geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); + geometry->getBound(); std::vector layerList; std::vector > blendmaps; From b384087e289dfbbb9af2c69e8d04b77bb055dcea Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 17:44:54 +0100 Subject: [PATCH 33/88] Fix handling in fillVertexBuffers when the chunk is >1 ESM::Cell --- components/esm/loadland.cpp | 3 +++ components/esmterrain/storage.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 72c3eb98d..0869eb7be 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -335,4 +335,7 @@ namespace ESM } } } + + const int Land::LAND_SIZE; + } diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index d1138a147..8009f48ad 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -232,8 +232,8 @@ namespace ESMTerrain // Only relevant for chunks smaller than (contained in) one cell rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE; colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE; - int rowEnd = rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; - int colEnd = colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1; + int rowEnd = std::min(static_cast(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), ESM::Land::LAND_SIZE); + int colEnd = std::min(static_cast(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE-1) + 1), ESM::Land::LAND_SIZE); vertY = vertY_; for (int col=colStart; col Date: Wed, 8 Mar 2017 18:11:09 +0100 Subject: [PATCH 34/88] Fix handling in getBlendmaps when the chunk is >1 ESM::Cell --- components/esmterrain/storage.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 8009f48ad..1f4d1b7cf 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -324,7 +324,12 @@ namespace ESMTerrain --cellX; x += ESM::Land::LAND_TEXTURE_SIZE; } - if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? + while (x >= ESM::Land::LAND_TEXTURE_SIZE) + { + ++cellX; + x -= ESM::Land::LAND_TEXTURE_SIZE; + } + while (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { ++cellY; y -= ESM::Land::LAND_TEXTURE_SIZE; From 0756fc4ae6609be5aeade2a6b6ad7f0a4bac16a7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 18:46:30 +0100 Subject: [PATCH 35/88] Optimize getBlendmaps for the general case that most points are within the given cell --- components/esmterrain/storage.cpp | 15 +++++++++++---- components/esmterrain/storage.hpp | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 1f4d1b7cf..1463d5e6a 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -314,7 +314,7 @@ namespace ESMTerrain } Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, - int x, int y) + int x, int y, osg::ref_ptr land) { // For the first/last row/column, we need to get the texture from the neighbour cell // to get consistent blending at the borders @@ -323,22 +323,27 @@ namespace ESMTerrain { --cellX; x += ESM::Land::LAND_TEXTURE_SIZE; + land = NULL; } while (x >= ESM::Land::LAND_TEXTURE_SIZE) { ++cellX; x -= ESM::Land::LAND_TEXTURE_SIZE; + land = NULL; } while (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { ++cellY; y -= ESM::Land::LAND_TEXTURE_SIZE; + land = NULL; } assert(x land = getLand(cellX, cellY); + if (!land) + land = getLand(cellX, cellY); + const ESM::Land::LandData *data = land ? land->getData(ESM::Land::DATA_VTEX) : 0; if (data) { @@ -393,10 +398,12 @@ namespace ESMTerrain // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. textureIndices.insert(std::make_pair(0,0)); + osg::ref_ptr land = getLand(cellX, cellY); + for (int y=colStart; ysecond; int blendIndex = (pack ? static_cast(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index e159313c5..8c4ec230f 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -117,7 +117,7 @@ namespace ESMTerrain typedef std::pair UniqueTextureId; UniqueTextureId getVtexIndexAt(int cellX, int cellY, - int x, int y); + int x, int y, osg::ref_ptr land); std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; From c684860e3b64fbcd4e306ac396fff87134158aaf Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 21:13:17 +0100 Subject: [PATCH 36/88] Change ordering of LocalMap nodes to make sure they are traversed before the CompositeMapRenderer. --- apps/openmw/mwrender/localmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0f77efafe..61b93b9a3 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -222,7 +222,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int camera->attach(osg::Camera::COLOR_BUFFER, texture); camera->addChild(mSceneRoot); - mRoot->addChild(camera); + mRoot->insertChild(0, camera); mActiveCameras.push_back(camera); MapSegment& segment = mSegments[std::make_pair(x, y)]; From 0efc54c7491520730f7af3889cf99fd794ee8e1a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 22:13:05 +0100 Subject: [PATCH 37/88] Subdivide the composite maps --- components/terrain/chunkmanager.cpp | 126 +++++++++++++++++----------- components/terrain/chunkmanager.hpp | 8 +- components/terrain/terraingrid.cpp | 2 +- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 94c521d5b..5f6a83376 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -31,7 +31,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, int lod) { std::ostringstream stream; stream << size << " " << center.x() << " " << center.y(); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center); + osg::ref_ptr node = createChunk(size, center, lod); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -80,44 +80,44 @@ osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter) +void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, osg::Group* compositeMapNode) { - osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); - osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); - transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); - - osg::ref_ptr positions (new osg::Vec3Array); - osg::ref_ptr normals (new osg::Vec3Array); - osg::ref_ptr colors (new osg::Vec4Array); - - osg::ref_ptr vbo (new osg::VertexBufferObject); - positions->setVertexBufferObject(vbo); - normals->setVertexBufferObject(vbo); - colors->setVertexBufferObject(vbo); - - unsigned int lod = 0; - - mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors); - - osg::ref_ptr geometry (new TerrainDrawable); - geometry->setVertexArray(positions); - geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); - geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); - geometry->setUseDisplayList(false); - geometry->setUseVertexBufferObjects(true); - - unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; + if (chunkSize > 1.f) + { + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y()+ texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); + } + else + { + float left = texCoords.x()*2.f-1; + float top = texCoords.y()*2.f-1; + float width = texCoords.z()*2.f; + float height = texCoords.w()*2.f; - geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0)); + osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3(left,top,0), osg::Vec3(width,0,0), osg::Vec3(0,height,0)); + geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); - geometry->getBound(); + std::vector > passes = createPasses(chunkSize, chunkCenter, true); + for (std::vector >::iterator it = passes.begin(); it != passes.end(); ++it) + { + osg::ref_ptr group = new osg::Group; + group->setStateSet(*it); + group->addChild(geom); + compositeMapNode->addChild(group); + } + } +} +std::vector > ChunkManager::createPasses(float chunkSize, const osg::Vec2f &chunkCenter, bool forCompositeMap) +{ std::vector layerList; std::vector > blendmaps; mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); bool useShaders = mSceneManager->getForceShaders(); - if (!mSceneManager->getClampLighting()) + if (!mSceneManager->getClampLighting() && !forCompositeMap) useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps std::vector layers; { @@ -129,7 +129,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap); - if (!it->mNormalMap.empty()) + if (!forCompositeMap && !it->mNormalMap.empty()) textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap); if (it->requiresShaders()) @@ -150,29 +150,54 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve blendmapTextures.push_back(texture); } - // use texture coordinates for both texture units, the layer texture and blend texture - for (unsigned int i=0; i<2; ++i) - geometry->setTexCoordArray(i, mBufferCache.getUVBuffer(numVerts)); - float blendmapScale = mStorage->getBlendmapScale(chunkSize); - Shader::ShaderManager* shaderManager = &mSceneManager->getShaderManager(); + return ::Terrain::createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); +} + +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, int lod) +{ + osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); + osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); + transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); + + osg::ref_ptr positions (new osg::Vec3Array); + osg::ref_ptr normals (new osg::Vec3Array); + osg::ref_ptr colors (new osg::Vec4Array); + + osg::ref_ptr vbo (new osg::VertexBufferObject); + positions->setVertexBufferObject(vbo); + normals->setVertexBufferObject(vbo); + colors->setVertexBufferObject(vbo); + + mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors); + + osg::ref_ptr geometry (new TerrainDrawable); + geometry->setVertexArray(positions); + geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX); + geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX); + geometry->setUseDisplayList(false); + geometry->setUseVertexBufferObjects(true); - if (1) // useCompositeMap + unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; + + geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0)); + + geometry->getBound(); + + bool useCompositeMap = chunkSize >= 1.f; + unsigned int numUvSets = useCompositeMap ? 1 : 2; + + for (unsigned int i=0; isetTexCoordArray(i, mBufferCache.getUVBuffer(numVerts)); + + if (useCompositeMap) { osg::ref_ptr compositeMap; osg::ref_ptr compositeMapNode = createCompositeMapRTT(compositeMap); - osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3(-1,-1,0), osg::Vec3(2,0,0), osg::Vec3(0,2,0)); - geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); - std::vector > passes = createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), - mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale, true); - for (std::vector >::iterator it = passes.begin(); it != passes.end(); ++it) - { - osg::ref_ptr group = new osg::Group; - group->setStateSet(*it); - group->addChild(geom); - compositeMapNode->addChild(group); - } + + createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0,0,1,1), compositeMapNode); compositeMapNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); @@ -185,8 +210,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve } else { - geometry->setPasses(createPasses(useShaders, mSceneManager->getForcePerPixelLighting(), - mSceneManager->getClampLighting(), shaderManager, layers, blendmapTextures, blendmapScale, blendmapScale)); + geometry->setPasses(createPasses(chunkSize, chunkCenter, false)); } transform->addChild(geometry); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 3941fc843..382e4a6b0 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -34,15 +34,19 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, int lod); virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod); osg::ref_ptr createCompositeMapRTT(osg::ref_ptr& texture); + void createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, osg::Group* compositeMapNode); + + std::vector > createPasses(float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap); + Terrain::Storage* mStorage; Resource::SceneManager* mSceneManager; TextureManager* mTextureManager; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index f7489fcde..bb6020dd8 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -47,7 +47,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0); if (!node) return NULL; if (parent) From e36bdb490e7d17129d3af5549ca8ec0273ef9b30 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 22:16:03 +0100 Subject: [PATCH 38/88] Add view data structure for efficient collection of LOD nodes to use for a given camera/intersection --- components/CMakeLists.txt | 2 +- components/terrain/viewdata.cpp | 132 ++++++++++++++++++++++++++++++++ components/terrain/viewdata.hpp | 78 +++++++++++++++++++ 3 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 components/terrain/viewdata.cpp create mode 100644 components/terrain/viewdata.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8b019e470..6430dbfce 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -114,7 +114,7 @@ add_component_dir (translation ) add_component_dir (terrain - storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode + storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager compositemaprenderer quadtreeworld quadtreenode viewdata ) add_component_dir (loadinglistener diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp new file mode 100644 index 000000000..aff20cd54 --- /dev/null +++ b/components/terrain/viewdata.cpp @@ -0,0 +1,132 @@ +#include "viewdata.hpp" + +namespace Terrain +{ + +ViewData::ViewData() + : mNumEntries(0) + , mFrameLastUsed(0) + , mChanged(false) + , mPersistent(false) +{ + +} + +void ViewData::add(QuadTreeNode *node) +{ + int index = mNumEntries++; + + mEntries.resize(index+1); + + Entry& entry = mEntries[index]; + if (entry.set(node)) + mChanged = true; +} + +unsigned int ViewData::getNumEntries() const +{ + return mNumEntries; +} + +ViewData::Entry &ViewData::getEntry(unsigned int i) +{ + return mEntries[i]; +} + +void ViewData::reset(unsigned int frame) +{ + // clear any unused entries + for (unsigned int i=mNumEntries; isetViewer(viewer); + mViews[viewer] = vd; + return vd; + } + else + return found->second; +} + +ViewData *ViewDataMap::createOrReuseView() +{ + if (mUnusedViews.size()) + { + ViewData* vd = mUnusedViews.front(); + mUnusedViews.pop_front(); + return vd; + } + else + { + mViewVector.push_back(ViewData()); + return &mViewVector.back(); + } +} + +void ViewDataMap::clearUnusedViews(unsigned int frame) +{ + for (Map::iterator it = mViews.begin(); it != mViews.end(); ) + { + ViewData* vd = it->second; + if (!vd->getPersistent() && + (!vd->getViewer() // if no ref was held, always need to clear to avoid holding a dangling ref. + || vd->getFrameLastUsed() + 2 < frame)) + { + vd->setViewer(NULL); + vd->clear(); + mUnusedViews.push_back(vd); + mViews.erase(it++); + } + else + ++it; + } +} + +void ViewDataMap::clear() +{ + mViews.clear(); + mUnusedViews.clear(); + mViewVector.clear(); +} + + +} diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp new file mode 100644 index 000000000..0ceb8313f --- /dev/null +++ b/components/terrain/viewdata.hpp @@ -0,0 +1,78 @@ +#ifndef OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H +#define OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H + +#include +#include + +#include + +namespace Terrain +{ + + class QuadTreeNode; + + class ViewData + { + public: + ViewData(); + + void add(QuadTreeNode* node); + + void reset(unsigned int frame); + + void clear(); + + struct Entry + { + Entry(); + + bool set(QuadTreeNode* node); + + QuadTreeNode* mNode; + + osg::ref_ptr mRenderingNode; + }; + + unsigned int getNumEntries() const; + + Entry& getEntry(unsigned int i); + + bool getPersistent() const { return mPersistent; } + + osg::Object* getViewer() const { return mViewer.get(); } + void setViewer(osg::Object* viewer) { mViewer = viewer; } + + unsigned int getFrameLastUsed() const { return mFrameLastUsed; } + + private: + std::vector mEntries; + unsigned int mNumEntries; + unsigned int mFrameLastUsed; + bool mChanged; + bool mPersistent; + osg::ref_ptr mViewer; + }; + + class ViewDataMap : public osg::Referenced + { + public: + ViewData* getViewData(osg::Object* viewer, bool ref); + + ViewData* createOrReuseView(); + + void clearUnusedViews(unsigned int frame); + + void clear(); + + private: + std::list mViewVector; + + typedef std::map Map; + Map mViews; + + std::deque mUnusedViews; + }; + +} + +#endif From 7d004bf757e24dd15436cfdb2390d99a848dbf60 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 22:19:48 +0100 Subject: [PATCH 39/88] Preliminary rendering of QuadTreeWorld --- components/terrain/quadtreenode.cpp | 90 +++++++++++++--------------- components/terrain/quadtreenode.hpp | 28 +++++++++ components/terrain/quadtreeworld.cpp | 75 +++++++++++++++++++++-- 3 files changed, 140 insertions(+), 53 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 518274df5..99f60827d 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -5,46 +5,7 @@ #include #include "defs.hpp" - -namespace -{ - - float distance(const osg::BoundingBox& box, const osg::Vec3f& v) - { - if (box.contains(v)) - return 0; - else - { - osg::Vec3f maxDist(0,0,0); - - if (v.x() < box.xMin()) - maxDist.x() = box.xMin() - v.x(); - else if (v.x() > box.xMax()) - maxDist.x() = v.x() - box.xMax(); - - if (v.y() < box.yMin()) - maxDist.y() = box.yMin() - v.y(); - else if (v.y() > box.yMax()) - maxDist.y() = v.y() - box.yMax(); - - if (v.z() < box.zMin()) - maxDist.z() = box.zMin() - v.z(); - else if (v.z() > box.zMax()) - maxDist.z() = v.z() - box.zMax(); - - return maxDist.length(); - } - } - - int Log2( int n ) - { - assert(n > 0); - int targetlevel = 0; - while (n >>= 1) ++targetlevel; - return targetlevel; - } - -} +#include "viewdata.hpp" namespace Terrain { @@ -104,6 +65,10 @@ QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float mNeighbours[i] = 0; } +QuadTreeNode::~QuadTreeNode() +{ +} + QuadTreeNode* QuadTreeNode::getParent() { return mParent; @@ -125,18 +90,47 @@ void QuadTreeNode::initNeighbours() void QuadTreeNode::traverse(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) - return; + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = static_cast(&nv); + + // do another culling test against bounding box as its much more accurate than the bounding sphere. + if (cv->isCulled(mBoundingBox)) + return; + } - osgUtil::CullVisitor* cv = static_cast(&nv); + if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv)) || !getNumChildren()) + getView(nv)->add(this); + else + osg::Group::traverse(nv); +} - // do another culling test against bounding box as its much more accurate than the bounding sphere. - if (cv->isCulled(mBoundingBox)) - return; +void QuadTreeNode::setLodCallback(LodCallback *lodCallback) +{ + mLodCallback = lodCallback; +} - //float dist = distance(getBoundingBox(), nv.getEyePoint()); +void QuadTreeNode::setViewDataMap(ViewDataMap *map) +{ + mViewDataMap = map; +} - osg::Group::traverse(nv); +ViewDataMap *QuadTreeNode::getViewDataMap() +{ + return mViewDataMap; +} + +ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) +{ + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = static_cast(&nv); + return mViewDataMap->getViewData(cv->getCurrentCamera(), true); + } + else // INTERSECTION_VISITOR + { + return mViewDataMap->getViewData(&nv, false); + } } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index aab54c69c..0f7eb05ef 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -17,10 +17,23 @@ namespace Terrain Root }; + class QuadTreeNode; + class LodCallback : public osg::Referenced + { + public: + virtual ~LodCallback() {} + + virtual bool isSufficientDetail(QuadTreeNode *node, osg::NodeVisitor &nv) = 0; + }; + + class ViewDataMap; + class ViewData; + class QuadTreeNode : public osg::Group { public: QuadTreeNode(QuadTreeNode* parent, ChildDirection dir, float size, const osg::Vec2f& center); + virtual ~QuadTreeNode(); QuadTreeNode* getParent(); @@ -49,6 +62,17 @@ namespace Terrain virtual void traverse(osg::NodeVisitor& nv); + /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. + void setLodCallback(LodCallback* lodCallback); + + /// Set the view data map that the finally used nodes for a given camera/intersection are pushed onto. + void setViewDataMap(ViewDataMap* map); + + ViewDataMap* getViewDataMap(); + + /// Create or retrieve a view for the given traversal. + ViewData* getView(osg::NodeVisitor& nv); + private: QuadTreeNode* mParent; @@ -59,6 +83,10 @@ namespace Terrain osg::BoundingBox mBoundingBox; float mSize; osg::Vec2f mCenter; + + osg::ref_ptr mLodCallback; + + osg::ref_ptr mViewDataMap; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 25aecd2a7..24f24ff37 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -4,10 +4,8 @@ #include "quadtreenode.hpp" #include "storage.hpp" - - -#include -#include +#include "viewdata.hpp" +#include "chunkmanager.hpp" namespace { @@ -29,11 +27,57 @@ namespace return 1 << depth; } + int Log2( unsigned int n ) + { + int targetlevel = 0; + while (n >>= 1) ++targetlevel; + return targetlevel; + } + + float distance(const osg::BoundingBox& box, const osg::Vec3f& v) + { + if (box.contains(v)) + return 0; + else + { + osg::Vec3f maxDist(0,0,0); + + if (v.x() < box.xMin()) + maxDist.x() = box.xMin() - v.x(); + else if (v.x() > box.xMax()) + maxDist.x() = v.x() - box.xMax(); + + if (v.y() < box.yMin()) + maxDist.y() = box.yMin() - v.y(); + else if (v.y() > box.yMax()) + maxDist.y() = v.y() - box.yMax(); + + if (v.z() < box.zMin()) + maxDist.z() = box.zMin() - v.z(); + else if (v.z() > box.zMax()) + maxDist.z() = v.z() - box.zMax(); + + return maxDist.length(); + } + } + } namespace Terrain { +class DefaultLodCallback : public LodCallback +{ +public: + virtual bool isSufficientDetail(QuadTreeNode* node, osg::NodeVisitor& nv) + { + float dist = distance(node->getBoundingBox(), nv.getEyePoint()); + int nativeLodLevel = Log2(static_cast(node->getSize()*4)); + int lodLevel = Log2(static_cast(dist/2048.0)); + + return nativeLodLevel <= lodLevel; + } +}; class RootNode : public QuadTreeNode { @@ -84,6 +128,7 @@ public: float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); + mRootNode->setViewDataMap(new ViewDataMap); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -125,6 +170,8 @@ public: } osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); + node->setLodCallback(new DefaultLodCallback); + node->setViewDataMap(parent->getViewDataMap()); parent->addChild(node); if (center.x() - size > mMaxX @@ -190,10 +237,28 @@ QuadTreeWorld::~QuadTreeWorld() void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) return; mRootNode->traverse(nv); + + ViewData* vd = mRootNode->getView(nv); + + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + if (!entry.mRenderingNode) + { + int lod = Log2(int(entry.mNode->getSize())); + entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), lod); + } + + entry.mRenderingNode->accept(nv); + } + + vd->reset(nv.getTraversalNumber()); + + mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber()); } void QuadTreeWorld::loadCell(int x, int y) From f19a88be9dd10eddd4edb24a5370f811bc9c0e52 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 8 Mar 2017 23:49:58 +0100 Subject: [PATCH 40/88] Reject QuadTreeNodes with invalid bounds --- components/terrain/quadtreenode.cpp | 5 +++++ components/terrain/quadtreenode.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 99f60827d..5eb59a6a2 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -58,6 +58,7 @@ QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir) QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center) : mParent(parent) , mDirection(direction) + , mValidBounds(false) , mSize(size) , mCenter(center) { @@ -90,6 +91,9 @@ void QuadTreeNode::initNeighbours() void QuadTreeNode::traverse(osg::NodeVisitor &nv) { + if (!hasValidBounds()) + return; + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); @@ -136,6 +140,7 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) { mBoundingBox = boundingBox; + mValidBounds = boundingBox.valid(); dirtyBound(); getBound(); } diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0f7eb05ef..1d996487e 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -51,6 +51,7 @@ namespace Terrain void setBoundingBox(const osg::BoundingBox& boundingBox); const osg::BoundingBox& getBoundingBox() const; + bool hasValidBounds() const { return mValidBounds; } virtual osg::BoundingSphere computeBound() const; @@ -81,6 +82,7 @@ namespace Terrain ChildDirection mDirection; osg::BoundingBox mBoundingBox; + bool mValidBounds; float mSize; osg::Vec2f mCenter; From 8c151364df0b7a50b6ba1cd6a7409966a05d5c64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 00:01:13 +0100 Subject: [PATCH 41/88] Add special handling for CullVisitor to QuadTreeWorld. - Cull only against bounding box, not bounding sphere, as this appears to perform better. - Also traverse into non visible nodes to compute their LOD, this is to avoid nodes having to be loaded in when the player turns the view around, and will avoid unnecessary refs/unrefs of rendering data in the View. This should probably be turned off at some point for static cameras, such as the local maps. --- components/terrain/quadtreenode.cpp | 7 +++++- components/terrain/quadtreenode.hpp | 2 ++ components/terrain/quadtreeworld.cpp | 33 +++++++++++++++++++++++++--- components/terrain/viewdata.cpp | 12 +++++----- components/terrain/viewdata.hpp | 5 +++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 5eb59a6a2..d556bb4fe 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -104,7 +104,7 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) } if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv)) || !getNumChildren()) - getView(nv)->add(this); + getView(nv)->add(this, true); else osg::Group::traverse(nv); } @@ -114,6 +114,11 @@ void QuadTreeNode::setLodCallback(LodCallback *lodCallback) mLodCallback = lodCallback; } +LodCallback *QuadTreeNode::getLodCallback() +{ + return mLodCallback; +} + void QuadTreeNode::setViewDataMap(ViewDataMap *map) { mViewDataMap = map; diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 1d996487e..c7a6a09d9 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -66,6 +66,8 @@ namespace Terrain /// Set the Lod callback to use for determining when to stop traversing further down the quad tree. void setLodCallback(LodCallback* lodCallback); + LodCallback* getLodCallback(); + /// Set the view data map that the finally used nodes for a given camera/intersection are pushed onto. void setViewDataMap(ViewDataMap* map); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 24f24ff37..9c27fa1ad 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -1,5 +1,7 @@ #include "quadtreeworld.hpp" +#include + #include #include "quadtreenode.hpp" @@ -235,15 +237,39 @@ QuadTreeWorld::~QuadTreeWorld() } } + +void traverse(QuadTreeNode* node, ViewData* vd, osgUtil::CullVisitor* cv, bool visible) +{ + if (!node->hasValidBounds()) + return; + + visible = visible && !cv->isCulled(node->getBoundingBox()); + bool stopTraversal = (node->getLodCallback() && node->getLodCallback()->isSufficientDetail(node, *cv)) || !node->getNumChildren(); + + if (stopTraversal) + vd->add(node, visible); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + traverse(node->getChild(i), vd, cv, visible); + } +} + void QuadTreeWorld::accept(osg::NodeVisitor &nv) { if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) return; - mRootNode->traverse(nv); - ViewData* vd = mRootNode->getView(nv); + if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + { + osgUtil::CullVisitor* cv = static_cast(&nv); + traverse(mRootNode.get(), vd, cv, true); + } + else + mRootNode->traverse(nv); + for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); @@ -253,7 +279,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), lod); } - entry.mRenderingNode->accept(nv); + if (entry.mVisible) + entry.mRenderingNode->accept(nv); } vd->reset(nv.getTraversalNumber()); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index aff20cd54..ce89150c1 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -12,14 +12,14 @@ ViewData::ViewData() } -void ViewData::add(QuadTreeNode *node) +void ViewData::add(QuadTreeNode *node, bool visible) { int index = mNumEntries++; mEntries.resize(index+1); Entry& entry = mEntries[index]; - if (entry.set(node)) + if (entry.set(node, visible)) mChanged = true; } @@ -37,7 +37,7 @@ void ViewData::reset(unsigned int frame) { // clear any unused entries for (unsigned int i=mNumEntries; i mRenderingNode; }; From 6bd286d924835b7c27844031a85bc315bc6ef2f3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 00:44:21 +0100 Subject: [PATCH 42/88] Fix unnecessary resizing in ViewData --- components/terrain/viewdata.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index ce89150c1..71768bd7c 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -14,9 +14,10 @@ ViewData::ViewData() void ViewData::add(QuadTreeNode *node, bool visible) { - int index = mNumEntries++; + unsigned int index = mNumEntries++; - mEntries.resize(index+1); + if (index+1 > mEntries.size()) + mEntries.resize(index+1); Entry& entry = mEntries[index]; if (entry.set(node, visible)) From bb991850da5d6d91ea317600f899452159a43edc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 00:45:31 +0100 Subject: [PATCH 43/88] Add LOD stitches --- components/terrain/chunkmanager.cpp | 10 ++++----- components/terrain/chunkmanager.hpp | 4 ++-- components/terrain/quadtreenode.cpp | 5 +++++ components/terrain/quadtreeworld.cpp | 33 ++++++++++++++++++++++++++-- components/terrain/terraingrid.cpp | 2 +- components/terrain/viewdata.cpp | 8 +++++++ components/terrain/viewdata.hpp | 2 ++ 7 files changed, 54 insertions(+), 10 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 5f6a83376..08b7c1300 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -31,10 +31,10 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T } -osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, int lod) +osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f ¢er, int lod, unsigned int lodFlags) { std::ostringstream stream; - stream << size << " " << center.x() << " " << center.y(); + stream << size << " " << center.x() << " " << center.y() << " " << lod << " " << lodFlags; std::string id = stream.str(); osg::ref_ptr obj = mCache->getRefFromObjectCache(id); @@ -42,7 +42,7 @@ osg::ref_ptr ChunkManager::getChunk(float size, const osg::Vec2f &cen return obj->asNode(); else { - osg::ref_ptr node = createChunk(size, center, lod); + osg::ref_ptr node = createChunk(size, center, lod, lodFlags); mCache->addEntryToObjectCache(id, node.get()); return node; } @@ -156,7 +156,7 @@ std::vector > ChunkManager::createPasses(float chunk mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), layers, blendmapTextures, blendmapScale, blendmapScale); } -osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, int lod) +osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, int lod, unsigned int lodFlags) { osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize(); osg::ref_ptr transform (new SceneUtil::PositionAttitudeTransform); @@ -182,7 +182,7 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; - geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0)); + geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags)); geometry->getBound(); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 382e4a6b0..c7c1566a8 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -34,12 +34,12 @@ namespace Terrain public: ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer); - osg::ref_ptr getChunk(float size, const osg::Vec2f& center, int lod); + osg::ref_ptr getChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; private: - osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod); + osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); osg::ref_ptr createCompositeMapRTT(osg::ref_ptr& texture); diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index d556bb4fe..caf967e22 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -80,6 +80,11 @@ QuadTreeNode *QuadTreeNode::getChild(unsigned int i) return static_cast(Group::getChild(i)); } +QuadTreeNode *QuadTreeNode::getNeighbour(Direction dir) +{ + return mNeighbours[dir]; +} + void QuadTreeNode::initNeighbours() { for (int i=0; i<4; ++i) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 9c27fa1ad..74e9fd512 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -255,6 +255,34 @@ void traverse(QuadTreeNode* node, ViewData* vd, osgUtil::CullVisitor* cv, bool v } } +unsigned int getLodFlags(QuadTreeNode* node, int ourLod, ViewData* vd) +{ + unsigned int lodFlags = 0; + for (unsigned int i=0; i<4; ++i) + { + QuadTreeNode* neighbour = node->getNeighbour(static_cast(i)); + + // If the neighbour isn't currently rendering itself, + // go up until we find one. NOTE: We don't need to go down, + // because in that case neighbour's detail would be higher than + // our detail and the neighbour would handle stitching by itself. + while (neighbour && !vd->contains(neighbour)) + neighbour = neighbour->getParent(); + int lod = 0; + if (neighbour) + lod = Log2(int(neighbour->getSize())); + + if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - + lod = 0; // neighbours with more detail will do the stitching themselves + // Use 4 bits for each LOD delta + if (lod > 0) + { + lodFlags |= static_cast(lod - ourLod) << (4*i); + } + } + return lodFlags; +} + void QuadTreeWorld::accept(osg::NodeVisitor &nv) { if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) @@ -275,8 +303,9 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) ViewData::Entry& entry = vd->getEntry(i); if (!entry.mRenderingNode) { - int lod = Log2(int(entry.mNode->getSize())); - entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), lod); + int ourLod = Log2(int(entry.mNode->getSize())); + unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); + entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, lodFlags); } if (entry.mVisible) diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index bb6020dd8..04b29e169 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -47,7 +47,7 @@ osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chu } else { - osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0); + osg::ref_ptr node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0); if (!node) return NULL; if (parent) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 71768bd7c..bc8448b0e 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -54,6 +54,14 @@ void ViewData::clear() mFrameLastUsed = 0; } +bool ViewData::contains(QuadTreeNode *node) +{ + for (unsigned int i=0; i Date: Thu, 9 Mar 2017 01:31:01 +0100 Subject: [PATCH 44/88] Clear the ViewData on exit --- components/terrain/quadtreeworld.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 74e9fd512..54c6c1470 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -235,6 +235,11 @@ QuadTreeWorld::~QuadTreeWorld() mWorkItem->abort(); mWorkItem->waitTillDone(); } + + if (mRootNode && mRootNode->getViewDataMap()) + { + mRootNode->getViewDataMap()->clear(); + } } From 5a3c645c891399b6ea2089d20eaa431eec208d01 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 01:56:43 +0100 Subject: [PATCH 45/88] Enable lazy compiling of composite maps --- components/terrain/chunkmanager.cpp | 4 +++- components/terrain/quadtreeworld.cpp | 13 +++++++++++++ components/terrain/world.cpp | 9 ++++----- components/terrain/world.hpp | 3 ++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 08b7c1300..42a44ceff 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -201,12 +201,14 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve compositeMapNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - mCompositeMapRenderer->addCompositeMap(compositeMapNode, true); + mCompositeMapRenderer->addCompositeMap(compositeMapNode, false); std::vector > passes2; passes2.push_back(new osg::StateSet); passes2[0]->setTextureAttributeAndModes(0, compositeMap, osg::StateAttribute::ON); geometry->setPasses(passes2); + + transform->getOrCreateUserDataContainer()->addUserObject(compositeMapNode); } else { diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 54c6c1470..15835b004 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -8,6 +8,7 @@ #include "storage.hpp" #include "viewdata.hpp" #include "chunkmanager.hpp" +#include "compositemaprenderer.hpp" namespace { @@ -314,7 +315,19 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) } if (entry.mVisible) + { + osg::UserDataContainer* udc = entry.mRenderingNode->getUserDataContainer(); + if (udc && udc->getNumUserObjects() > 0) + { + osg::Node* compositeMapNode = udc->getUserObject(0)->asNode(); + if (compositeMapNode) + { + mCompositeMapRenderer->setImmediate(compositeMapNode); + udc->removeUserObject(0); + } + } entry.mRenderingNode->accept(nv); + } } vd->reset(nv.getTraversalNumber()); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index b662cd314..401252334 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -27,15 +27,14 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTerrainRoot->setName("Terrain Root"); - osg::ref_ptr renderer (new CompositeMapRenderer); - renderer->setNodeMask(preCompileMask); - compileRoot->addChild(renderer); - mCompositeMapRenderer = renderer; + mCompositeMapRenderer = new CompositeMapRenderer; + mCompositeMapRenderer->setNodeMask(preCompileMask); + compileRoot->addChild(mCompositeMapRenderer); mParent->addChild(mTerrainRoot); mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager())); - mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), renderer)); + mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer)); mResourceSystem->addResourceManager(mChunkManager.get()); mResourceSystem->addResourceManager(mTextureManager.get()); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d4867d2ba..091ddadd2 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -26,6 +26,7 @@ namespace Terrain class TextureManager; class ChunkManager; + class CompositeMapRenderer; /** * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed @@ -70,7 +71,7 @@ namespace Terrain osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; - osg::ref_ptr mCompositeMapRenderer; + osg::ref_ptr mCompositeMapRenderer; Resource::ResourceSystem* mResourceSystem; From 2580de11a4324cc48c53758ec686add4a9bd3d44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 02:03:51 +0100 Subject: [PATCH 46/88] Refactor ownership of ViewDataMap --- components/terrain/quadtreenode.hpp | 2 +- components/terrain/quadtreeworld.cpp | 16 ++++++++-------- components/terrain/quadtreeworld.hpp | 3 +++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index c7a6a09d9..ca0ee97c4 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -90,7 +90,7 @@ namespace Terrain osg::ref_ptr mLodCallback; - osg::ref_ptr mViewDataMap; + ViewDataMap* mViewDataMap; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 15835b004..535b3a583 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -110,10 +110,11 @@ private: class BuildQuadTreeItem : public SceneUtil::WorkItem { public: - BuildQuadTreeItem(Terrain::Storage* storage, float minSize) + BuildQuadTreeItem(Terrain::Storage* storage, ViewDataMap* viewDataMap, float minSize) : mStorage(storage) , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f) , mMinSize(minSize) + , mViewDataMap(viewDataMap) { } @@ -131,7 +132,7 @@ public: float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); - mRootNode->setViewDataMap(new ViewDataMap); + mRootNode->setViewDataMap(mViewDataMap); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -174,7 +175,7 @@ public: osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); node->setLodCallback(new DefaultLodCallback); - node->setViewDataMap(parent->getViewDataMap()); + node->setViewDataMap(mViewDataMap); parent->addChild(node); if (center.x() - size > mMaxX @@ -218,6 +219,7 @@ private: float mMinX, mMaxX, mMinY, mMaxY; float mMinSize; + ViewDataMap* mViewDataMap; osg::ref_ptr mRootNode; }; @@ -225,6 +227,7 @@ private: QuadTreeWorld::QuadTreeWorld(SceneUtil::WorkQueue* workQueue, osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask) : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask) , mWorkQueue(workQueue) + , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) { } @@ -237,10 +240,7 @@ QuadTreeWorld::~QuadTreeWorld() mWorkItem->waitTillDone(); } - if (mRootNode && mRootNode->getViewDataMap()) - { - mRootNode->getViewDataMap()->clear(); - } + mViewDataMap->clear(); } @@ -351,7 +351,7 @@ osg::ref_ptr QuadTreeWorld::cacheCell(int x, int y) if (!mQuadTreeBuilt) { const float minSize = 1/4.f; - mWorkItem = new BuildQuadTreeItem(mStorage, minSize); + mWorkItem = new BuildQuadTreeItem(mStorage, mViewDataMap.get(), minSize); mWorkQueue->addWorkItem(mWorkItem); mWorkItem->waitTillDone(); diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 320f53120..ebea60fc5 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -19,6 +19,7 @@ namespace Terrain { class RootNode; class BuildQuadTreeItem; + class ViewDataMap; /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. The entire world is displayed at all times. class QuadTreeWorld : public Terrain::World @@ -39,6 +40,8 @@ namespace Terrain osg::ref_ptr mWorkItem; + osg::ref_ptr mViewDataMap; + bool mQuadTreeBuilt; }; From 4dbd224249b24d14b0cc210d816204e50a52b29e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 02:17:25 +0100 Subject: [PATCH 47/88] Hide the terrain in non-exterior cells --- apps/openmw/mwrender/renderingmanager.cpp | 5 +++++ apps/openmw/mwrender/renderingmanager.hpp | 2 ++ apps/openmw/mwworld/scene.cpp | 2 ++ components/terrain/quadtreeworld.cpp | 8 ++++++++ components/terrain/quadtreeworld.hpp | 2 ++ components/terrain/world.hpp | 2 ++ 6 files changed, 21 insertions(+) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index ff7f6ae9a..a8baf9530 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -421,6 +421,11 @@ namespace MWRender mWater->removeCell(store); } + void RenderingManager::enableTerrain(bool enable) + { + mTerrain->enable(enable); + } + void RenderingManager::setSkyEnabled(bool enabled) { mSky->setEnabled(enabled); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 230fe3f7d..d81dbf180 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -102,6 +102,8 @@ namespace MWRender void addCell(const MWWorld::CellStore* store); void removeCell(const MWWorld::CellStore* store); + void enableTerrain(bool enable); + void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated); void rotateObject(const MWWorld::Ptr& ptr, const osg::Quat& rot); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3e1227b37..41dcce1d9 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -440,6 +440,8 @@ namespace MWWorld { mCurrentCell = cell; + mRendering.enableTerrain(cell->isExterior()); + MWBase::World *world = MWBase::Environment::get().getWorld(); MWWorld::Ptr old = world->getPlayerPtr(); world->getPlayer().setCell(cell); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 535b3a583..bc4a4abc5 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -98,6 +98,8 @@ public: virtual void accept(osg::NodeVisitor &nv) { + if (!nv.validNodeMask(*this)) + return; nv.pushOntoNodePath(this); mWorld->accept(nv); nv.popFromNodePath(); @@ -361,6 +363,12 @@ osg::ref_ptr QuadTreeWorld::cacheCell(int x, int y) return NULL; } +void QuadTreeWorld::enable(bool enabled) +{ + if (mRootNode) + mRootNode->setNodeMask(enabled ? ~0 : 0); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index ebea60fc5..bec901b09 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -33,6 +33,8 @@ namespace Terrain virtual void loadCell(int x, int y); virtual osg::ref_ptr cacheCell(int x, int y); + virtual void enable(bool enabled); + private: osg::ref_ptr mRootNode; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 091ddadd2..7abc9a4ca 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -64,6 +64,8 @@ namespace Terrain /// @note May be ignored by derived implementations that don't organize the terrain into cells. virtual void unloadCell(int x, int y) {} + virtual void enable(bool enabled) {} + Storage* getStorage() { return mStorage; } protected: From 433900fca5c3757d316a16c5dc5749b26a236df8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 03:31:30 +0100 Subject: [PATCH 48/88] Fix waiting on initial quad tree build --- components/terrain/quadtreeworld.cpp | 37 ++++++++++++++++------------ components/terrain/quadtreeworld.hpp | 8 +++--- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index bc4a4abc5..66748a4aa 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -337,34 +337,39 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber()); } -void QuadTreeWorld::loadCell(int x, int y) +void QuadTreeWorld::ensureQuadTreeBuilt() { - if (mQuadTreeBuilt && !mRootNode) { + OpenThreads::ScopedLock lock(mQuadTreeMutex); + if (mQuadTreeBuilt) + return; + if (!mQuadTreeBuilt && !mWorkItem) + { + const float minSize = 1/4.f; + mWorkItem = new BuildQuadTreeItem(mStorage, mViewDataMap.get(), minSize); + mWorkQueue->addWorkItem(mWorkItem); + } + } + + mWorkItem->waitTillDone(); + { + OpenThreads::ScopedLock lock(mQuadTreeMutex); mRootNode = mWorkItem->getRootNode(); mRootNode->setWorld(this); - mTerrainRoot->addChild(mRootNode); - mWorkItem = NULL; + mQuadTreeBuilt = true; } } -osg::ref_ptr QuadTreeWorld::cacheCell(int x, int y) +void QuadTreeWorld::enable(bool enabled) { - if (!mQuadTreeBuilt) + if (enabled) { - const float minSize = 1/4.f; - mWorkItem = new BuildQuadTreeItem(mStorage, mViewDataMap.get(), minSize); - mWorkQueue->addWorkItem(mWorkItem); - - mWorkItem->waitTillDone(); + ensureQuadTreeBuilt(); - mQuadTreeBuilt = true; + if (!mRootNode->getNumParents()) + mTerrainRoot->addChild(mRootNode); } - return NULL; -} -void QuadTreeWorld::enable(bool enabled) -{ if (mRootNode) mRootNode->setNodeMask(enabled ? ~0 : 0); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index bec901b09..f66426ed2 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -5,6 +5,8 @@ #include "world.hpp" +#include + namespace SceneUtil { class WorkQueue; @@ -30,12 +32,11 @@ namespace Terrain void accept(osg::NodeVisitor& nv); - virtual void loadCell(int x, int y); - virtual osg::ref_ptr cacheCell(int x, int y); - virtual void enable(bool enabled); private: + void ensureQuadTreeBuilt(); + osg::ref_ptr mRootNode; osg::ref_ptr mWorkQueue; @@ -44,6 +45,7 @@ namespace Terrain osg::ref_ptr mViewDataMap; + OpenThreads::Mutex mQuadTreeMutex; bool mQuadTreeBuilt; }; From 19d516cbda3d87262470fce898a4f76b5717817f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 03:49:40 +0100 Subject: [PATCH 49/88] Use the QuadTreeWorld based on distant terrain setting now that it sort of works. --- apps/openmw/mwrender/renderingmanager.cpp | 10 +++++++--- files/settings-default.cfg | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a8baf9530..40403a4fb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -212,12 +213,15 @@ namespace MWRender mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath)); - - + const bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain"); mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); - mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + + if (distantTerrain) + mTerrain.reset(new Terrain::QuadTreeWorld(mWorkQueue.get(), sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + else + mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 91329b8ca..55f550d5a 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -76,6 +76,11 @@ preload cell expiry delay = 5 # How long to keep models/textures/collision shapes in cache after they're no longer referenced/required (in seconds) cache expiry delay = 5 +[Terrain] + +# If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells +distant terrain = false + [Map] # Size of each exterior cell in pixels in the world map. (e.g. 12 to 24). From 1c15686353060c89e3e5518b49fabd4d757fb3ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 04:01:27 +0100 Subject: [PATCH 50/88] Remove non required use of WorkQueue --- apps/openmw/mwrender/renderingmanager.cpp | 2 +- components/terrain/quadtreeworld.cpp | 46 ++++++++--------------- components/terrain/quadtreeworld.hpp | 12 +----- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 40403a4fb..a41adc2fc 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -219,7 +219,7 @@ namespace MWRender Settings::Manager::getBool("auto use terrain specular maps", "Shaders")); if (distantTerrain) - mTerrain.reset(new Terrain::QuadTreeWorld(mWorkQueue.get(), sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 66748a4aa..e91a513cb 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -2,8 +2,6 @@ #include -#include - #include "quadtreenode.hpp" #include "storage.hpp" #include "viewdata.hpp" @@ -109,10 +107,10 @@ private: QuadTreeWorld* mWorld; }; -class BuildQuadTreeItem : public SceneUtil::WorkItem +class QuadTreeBuilder { public: - BuildQuadTreeItem(Terrain::Storage* storage, ViewDataMap* viewDataMap, float minSize) + QuadTreeBuilder(Terrain::Storage* storage, ViewDataMap* viewDataMap, float minSize) : mStorage(storage) , mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f) , mMinSize(minSize) @@ -120,7 +118,7 @@ public: { } - virtual void doWork() + void build() { mStorage->getBounds(mMinX, mMaxX, mMinY, mMaxY); @@ -226,9 +224,8 @@ private: osg::ref_ptr mRootNode; }; -QuadTreeWorld::QuadTreeWorld(SceneUtil::WorkQueue* workQueue, osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask) +QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask) : World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask) - , mWorkQueue(workQueue) , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) { @@ -236,12 +233,7 @@ QuadTreeWorld::QuadTreeWorld(SceneUtil::WorkQueue* workQueue, osg::Group *parent QuadTreeWorld::~QuadTreeWorld() { - if (mWorkItem) - { - mWorkItem->abort(); - mWorkItem->waitTillDone(); - } - + ensureQuadTreeBuilt(); mViewDataMap->clear(); } @@ -339,25 +331,17 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) void QuadTreeWorld::ensureQuadTreeBuilt() { - { - OpenThreads::ScopedLock lock(mQuadTreeMutex); - if (mQuadTreeBuilt) - return; - if (!mQuadTreeBuilt && !mWorkItem) - { - const float minSize = 1/4.f; - mWorkItem = new BuildQuadTreeItem(mStorage, mViewDataMap.get(), minSize); - mWorkQueue->addWorkItem(mWorkItem); - } - } + OpenThreads::ScopedLock lock(mQuadTreeMutex); + if (mQuadTreeBuilt) + return; - mWorkItem->waitTillDone(); - { - OpenThreads::ScopedLock lock(mQuadTreeMutex); - mRootNode = mWorkItem->getRootNode(); - mRootNode->setWorld(this); - mQuadTreeBuilt = true; - } + const float minSize = 1/4.f; + QuadTreeBuilder builder(mStorage, mViewDataMap.get(), minSize); + builder.build(); + + mRootNode = builder.getRootNode(); + mRootNode->setWorld(this); + mQuadTreeBuilt = true; } void QuadTreeWorld::enable(bool enabled) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index f66426ed2..5838bcaa9 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -7,11 +7,6 @@ #include -namespace SceneUtil -{ - class WorkQueue; -} - namespace osg { class NodeVisitor; @@ -20,14 +15,13 @@ namespace osg namespace Terrain { class RootNode; - class BuildQuadTreeItem; class ViewDataMap; /// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD. The entire world is displayed at all times. class QuadTreeWorld : public Terrain::World { public: - QuadTreeWorld(SceneUtil::WorkQueue* workQueue, 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); ~QuadTreeWorld(); void accept(osg::NodeVisitor& nv); @@ -39,10 +33,6 @@ namespace Terrain osg::ref_ptr mRootNode; - osg::ref_ptr mWorkQueue; - - osg::ref_ptr mWorkItem; - osg::ref_ptr mViewDataMap; OpenThreads::Mutex mQuadTreeMutex; From ef704db877309e9c36a2cb74e37132181bbc2342 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 04:02:58 +0100 Subject: [PATCH 51/88] Fix reset of changed flag --- components/terrain/viewdata.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index bc8448b0e..e07e3c784 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -42,6 +42,7 @@ void ViewData::reset(unsigned int frame) // reset index for next frame mNumEntries = 0; + mChanged = false; mFrameLastUsed = frame; } @@ -52,6 +53,7 @@ void ViewData::clear() mEntries[i].set(NULL, false); mNumEntries = 0; mFrameLastUsed = 0; + mChanged = false; } bool ViewData::contains(QuadTreeNode *node) From 9db71e3f621ce6cf09b1c38f25ea69f2dc73f9eb Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 04:14:07 +0100 Subject: [PATCH 52/88] Recompute the LOD stitching when the view changes --- components/terrain/quadtreeworld.cpp | 28 ++++++++++++++++++++++------ components/terrain/viewdata.cpp | 6 ++++++ components/terrain/viewdata.hpp | 4 ++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index e91a513cb..6d8e38400 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -283,6 +283,26 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, ViewData* vd) return lodFlags; } +void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunkManager) +{ + if (vd->hasChanged()) + { + // have to recompute the lodFlags in case a neighbour has changed LOD. + int ourLod = Log2(int(entry.mNode->getSize())); + unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); + if (lodFlags != entry.mLodFlags) + entry.mRenderingNode = NULL; + } + + if (!entry.mRenderingNode) + { + int ourLod = Log2(int(entry.mNode->getSize())); + unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); + entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, lodFlags); + entry.mLodFlags = lodFlags; + } +} + void QuadTreeWorld::accept(osg::NodeVisitor &nv) { if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) @@ -301,12 +321,8 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) for (unsigned int i=0; igetNumEntries(); ++i) { ViewData::Entry& entry = vd->getEntry(i); - if (!entry.mRenderingNode) - { - int ourLod = Log2(int(entry.mNode->getSize())); - unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); - entry.mRenderingNode = mChunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, lodFlags); - } + + loadRenderingNode(entry, vd, mChunkManager.get()); if (entry.mVisible) { diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e07e3c784..bd7e0b819 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -34,6 +34,11 @@ ViewData::Entry &ViewData::getEntry(unsigned int i) return mEntries[i]; } +bool ViewData::hasChanged() const +{ + return mChanged; +} + void ViewData::reset(unsigned int frame) { // clear any unused entries @@ -67,6 +72,7 @@ bool ViewData::contains(QuadTreeNode *node) ViewData::Entry::Entry() : mNode(NULL) , mVisible(true) + , mLodFlags(0) { } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 71433f61a..49b3722c8 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -33,6 +33,7 @@ namespace Terrain QuadTreeNode* mNode; bool mVisible; + unsigned int mLodFlags; osg::ref_ptr mRenderingNode; }; @@ -47,6 +48,9 @@ namespace Terrain unsigned int getFrameLastUsed() const { return mFrameLastUsed; } + /// @return Have any nodes changed since the last frame + bool hasChanged() const; + private: std::vector mEntries; unsigned int mNumEntries; From 4baa795152688dfc9a4b8bb9e26f7c7bb4dc28ec Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 04:17:25 +0100 Subject: [PATCH 53/88] Add preloading implementation to QuadTreeWorld --- components/terrain/quadtreenode.cpp | 2 +- components/terrain/quadtreenode.hpp | 2 +- components/terrain/quadtreeworld.cpp | 41 +++++++++++++++++++++++----- components/terrain/quadtreeworld.hpp | 4 +++ components/terrain/viewdata.cpp | 12 ++++++++ components/terrain/viewdata.hpp | 7 ++++- components/terrain/world.hpp | 24 ++++++++++++++++ 7 files changed, 82 insertions(+), 10 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index caf967e22..3f61e8b76 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -108,7 +108,7 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) return; } - if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv)) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv.getEyePoint())) || !getNumChildren()) getView(nv)->add(this, true); else osg::Group::traverse(nv); diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index ca0ee97c4..30e998119 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -23,7 +23,7 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, osg::NodeVisitor &nv) = 0; + virtual bool isSufficientDetail(QuadTreeNode *node, const osg::Vec3f& eyePoint) = 0; }; class ViewDataMap; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 6d8e38400..cb49f3392 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -70,9 +70,9 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: - virtual bool isSufficientDetail(QuadTreeNode* node, osg::NodeVisitor& nv) + virtual bool isSufficientDetail(QuadTreeNode* node, const osg::Vec3f& eyePoint) { - float dist = distance(node->getBoundingBox(), nv.getEyePoint()); + float dist = distance(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()*4)); int lodLevel = Log2(static_cast(dist/2048.0)); @@ -238,20 +238,22 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osgUtil::CullVisitor* cv, bool visible) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& eyePoint, bool visible) { if (!node->hasValidBounds()) return; - visible = visible && !cv->isCulled(node->getBoundingBox()); - bool stopTraversal = (node->getLodCallback() && node->getLodCallback()->isSufficientDetail(node, *cv)) || !node->getNumChildren(); + if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); + + bool stopTraversal = (node->getLodCallback() && node->getLodCallback()->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); if (stopTraversal) vd->add(node, visible); else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, cv, visible); + traverse(node->getChild(i), vd, nv, eyePoint, visible); } } @@ -313,7 +315,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - traverse(mRootNode.get(), vd, cv, true); + traverse(mRootNode.get(), vd, cv, cv->getEyePoint(), true); } else mRootNode->traverse(nv); @@ -374,6 +376,31 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } +View* QuadTreeWorld::createView() +{ + ViewData* vd = mViewDataMap->createOrReuseView(); + vd->setPersistent(true); + return vd; +} + +void QuadTreeWorld::removeView(View *view) +{ + mViewDataMap->removeView(static_cast(view)); +} + +void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) +{ + ensureQuadTreeBuilt(); + + ViewData* vd = static_cast(view); + traverse(mRootNode.get(), vd, NULL, eyePoint, false); + + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + loadRenderingNode(entry, vd, mChunkManager.get()); + } +} } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 5838bcaa9..edd9779d6 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -28,6 +28,10 @@ namespace Terrain virtual void enable(bool enabled); + View* createView(); + void removeView(View* view); + void preload(View* view, const osg::Vec3f& eyePoint); + private: void ensureQuadTreeBuilt(); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index bd7e0b819..1e7245a15 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -12,6 +12,11 @@ ViewData::ViewData() } +ViewData::~ViewData() +{ + +} + void ViewData::add(QuadTreeNode *node, bool visible) { unsigned int index = mNumEntries++; @@ -121,6 +126,13 @@ ViewData *ViewDataMap::createOrReuseView() } } +void ViewDataMap::removeView(ViewData* vd) +{ + vd->setPersistent(false); + vd->clear(); + mUnusedViews.push_back(vd); +} + void ViewDataMap::clearUnusedViews(unsigned int frame) { for (Map::iterator it = mViews.begin(); it != mViews.end(); ) diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 49b3722c8..c06e1dfff 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -6,15 +6,18 @@ #include +#include "world.hpp" + namespace Terrain { class QuadTreeNode; - class ViewData + class ViewData : public View { public: ViewData(); + ~ViewData(); void add(QuadTreeNode* node, bool visible); @@ -42,6 +45,7 @@ namespace Terrain Entry& getEntry(unsigned int i); bool getPersistent() const { return mPersistent; } + void setPersistent(bool persistent) { mPersistent = persistent; } osg::Object* getViewer() const { return mViewer.get(); } void setViewer(osg::Object* viewer) { mViewer = viewer; } @@ -66,6 +70,7 @@ namespace Terrain ViewData* getViewData(osg::Object* viewer, bool ref); ViewData* createOrReuseView(); + void removeView(ViewData* view); void clearUnusedViews(unsigned int frame); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 7abc9a4ca..6cba6c5b6 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -28,6 +28,19 @@ namespace Terrain class ChunkManager; class CompositeMapRenderer; + /** + * @brief A View is a collection of rendering objects that are visible from a given camera/intersection. + * The base View class is part of the interface for usage in conjunction with preload feature. + */ + class View + { + public: + virtual ~View() {} + + /// Reset internal structure so that the next addition to the view will override the previous frame's contents. + virtual void reset(unsigned int frame) = 0; + }; + /** * @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed * is up to the implementation. @@ -66,6 +79,17 @@ namespace Terrain virtual void enable(bool enabled) {} + /// Create a View to use with preload feature. If a View is returned, it will remain valid until the user calls 'removeView' or the World is destroyed. + /// @note Not thread safe. + virtual View* createView() { return NULL; } + + /// Remove a View that was previously created with 'createView'. + /// @note Not thread safe. + virtual void removeView(View* view) {} + + /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. + virtual void preload(View* view, const osg::Vec3f& eyePoint) {} + Storage* getStorage() { return mStorage; } protected: From 4549196b310d6bdc7f2797e19a5f69f34301d929 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 04:18:35 +0100 Subject: [PATCH 54/88] Use the new way of terrain preloading in CellPreloader/Scene --- apps/openmw/mwworld/cellpreloader.cpp | 51 +++++++++++++++++++++++++++ apps/openmw/mwworld/cellpreloader.hpp | 8 +++++ apps/openmw/mwworld/scene.cpp | 15 ++++++-- apps/openmw/mwworld/scene.hpp | 2 +- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index e45cf55bc..e633f5825 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -194,11 +194,22 @@ namespace MWWorld , mMaxCacheSize(0) , mPreloadInstances(true) , mLastResourceCacheUpdate(0.0) + , mTerrainView(NULL) { + mTerrainView = mTerrain->createView(); } CellPreloader::~CellPreloader() { + if (mTerrainPreloadItem) + { + mTerrainPreloadItem->waitTillDone(); + mTerrainPreloadItem = NULL; + } + + mTerrain->removeView(mTerrainView); + mTerrainView = NULL; + for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) it->second.mWorkItem->abort(); @@ -349,4 +360,44 @@ namespace MWWorld mUnrefQueue = unrefQueue; } + class TerrainPreloadItem : public SceneUtil::WorkItem + { + public: + TerrainPreloadItem(Terrain::View* view, Terrain::World* world, const std::vector& preloadPositions) + : mView(view) + , mWorld(world) + , mPreloadPositions(preloadPositions) + { + } + + virtual void doWork() + { + for (std::vector::const_iterator it = mPreloadPositions.begin(); it != mPreloadPositions.end(); ++it) + mWorld->preload(mView, *it); + + mView->reset(0); + } + + private: + Terrain::View* mView; + Terrain::World* mWorld; + std::vector mPreloadPositions; + }; + + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) + { + if (!mTerrainView) + return; + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + return; + else if (positions == mTerrainPreloadPositions) + return; + else + { + mTerrainPreloadPositions = positions; + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainView, mTerrain, positions); + mWorkQueue->addWorkItem(mTerrainPreloadItem); + } + } + } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index ee73a4ea1..9bd062547 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace Resource @@ -14,6 +15,7 @@ namespace Resource namespace Terrain { class World; + class View; } namespace SceneUtil @@ -65,6 +67,8 @@ namespace MWWorld void setUnrefQueue(SceneUtil::UnrefQueue* unrefQueue); + void setTerrainPreloadPositions(const std::vector& positions); + private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; @@ -98,6 +102,10 @@ namespace MWWorld // Cells that are currently being preloaded, or have already finished preloading PreloadMap mPreloadCells; + + Terrain::View* mTerrainView; + std::vector mTerrainPreloadPositions; + osg::ref_ptr mTerrainPreloadItem; }; } diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 41dcce1d9..a772abdc2 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -692,22 +692,29 @@ namespace MWWorld void Scene::preloadCells(float dt) { + std::vector exteriorPositions; + const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); osg::Vec3f moved = playerPos - mLastPlayerPos; osg::Vec3f predictedPos = playerPos + moved / dt; + if (mCurrentCell->isExterior()) + exteriorPositions.push_back(predictedPos); + mLastPlayerPos = playerPos; if (mPreloadDoors) - preloadTeleportDoorDestinations(playerPos, predictedPos); + preloadTeleportDoorDestinations(playerPos, predictedPos, exteriorPositions); if (mPreloadExteriorGrid) preloadExteriorGrid(playerPos, predictedPos); if (mPreloadFastTravel) preloadFastTravelDestinations(playerPos, predictedPos); + + mPreloader->setTerrainPreloadPositions(exteriorPositions); } - void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos) + void Scene::preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions) { std::vector teleportDoors; for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); @@ -739,9 +746,11 @@ namespace MWWorld preloadCell(MWBase::Environment::get().getWorld()->getInterior(door.getCellRef().getDestCell())); else { + osg::Vec3f pos = door.getCellRef().getDoorDest().asVec3(); int x,y; - MWBase::Environment::get().getWorld()->positionToIndex (door.getCellRef().getDoorDest().pos[0], door.getCellRef().getDoorDest().pos[1], x, y); + MWBase::Environment::get().getWorld()->positionToIndex (pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); + exteriorPositions.push_back(pos); } } catch (std::exception& e) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 5f17f8d59..437279ff7 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -78,7 +78,7 @@ namespace MWWorld void getGridCenter(int& cellX, int& cellY); void preloadCells(float dt); - void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); + void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); From 683e625c6cdbd2454012a2484c3a21204f5efe11 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:17:58 +0100 Subject: [PATCH 55/88] Rewrite CompositeMapRenderer to be based on Drawable and share the FBO --- components/terrain/chunkmanager.cpp | 58 +++++------ components/terrain/chunkmanager.hpp | 5 +- components/terrain/compositemaprenderer.cpp | 101 +++++++++++++++----- components/terrain/compositemaprenderer.hpp | 40 +++++--- components/terrain/quadtreeworld.cpp | 10 +- components/terrain/world.cpp | 20 +++- components/terrain/world.hpp | 2 + 7 files changed, 155 insertions(+), 81 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 42a44ceff..ddba8699f 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -53,9 +53,9 @@ void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) cons stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); } -osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptr& texture) +osg::ref_ptr ChunkManager::createCompositeMapRTT() { - texture = new osg::Texture2D; + osg::ref_ptr texture = new osg::Texture2D; texture->setTextureWidth(mCompositeMapSize); texture->setTextureHeight(mCompositeMapSize); texture->setInternalFormat(GL_RGB); @@ -64,30 +64,17 @@ osg::ref_ptr ChunkManager::createCompositeMapRTT(osg::ref_ptrsetWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); - osg::ref_ptr camera (new osg::Camera); - camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); - camera->attach(osg::Camera::COLOR_BUFFER, texture); - camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); - camera->setViewMatrix(osg::Matrix::identity()); - camera->setProjectionMatrix(osg::Matrix::identity()); - camera->setProjectionResizePolicy(osg::Camera::FIXED); - camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f)); - camera->setClearMask(GL_COLOR_BUFFER_BIT); - camera->setViewport(0, 0, mCompositeMapSize, mCompositeMapSize); - camera->setRenderOrder(osg::Camera::PRE_RENDER, -1); - camera->setImplicitBufferAttachmentMask(osg::DisplaySettings::IMPLICIT_COLOR_BUFFER_ATTACHMENT); // no need for a depth buffer - - return camera; + return texture; } -void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, osg::Group* compositeMapNode) +void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap) { if (chunkSize > 1.f) { - createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); - createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); - createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); - createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y()+ texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMapNode); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMap); + createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y()+texCoords.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMap); } else { @@ -96,16 +83,17 @@ void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& float width = texCoords.z()*2.f; float height = texCoords.w()*2.f; - osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3(left,top,0), osg::Vec3(width,0,0), osg::Vec3(0,height,0)); - geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); - std::vector > passes = createPasses(chunkSize, chunkCenter, true); for (std::vector >::iterator it = passes.begin(); it != passes.end(); ++it) { - osg::ref_ptr group = new osg::Group; - group->setStateSet(*it); - group->addChild(geom); - compositeMapNode->addChild(group); + osg::ref_ptr geom = osg::createTexturedQuadGeometry(osg::Vec3(left,top,0), osg::Vec3(width,0,0), osg::Vec3(0,height,0)); + geom->setUseDisplayList(false); // don't bother making a display list for an object that is just rendered once. + geom->setUseVertexBufferObjects(false); + geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX); + + geom->setStateSet(*it); + + compositeMap.mDrawables.push_back(geom); } } } @@ -194,21 +182,19 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve if (useCompositeMap) { - osg::ref_ptr compositeMap; - osg::ref_ptr compositeMapNode = createCompositeMapRTT(compositeMap); - - createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0,0,1,1), compositeMapNode); + osg::ref_ptr compositeMap = new CompositeMap; + compositeMap->mTexture = createCompositeMapRTT(); - compositeMapNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0,0,1,1), *compositeMap); - mCompositeMapRenderer->addCompositeMap(compositeMapNode, false); + mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false); std::vector > passes2; passes2.push_back(new osg::StateSet); - passes2[0]->setTextureAttributeAndModes(0, compositeMap, osg::StateAttribute::ON); + passes2[0]->setTextureAttributeAndModes(0, compositeMap->mTexture, osg::StateAttribute::ON); geometry->setPasses(passes2); - transform->getOrCreateUserDataContainer()->addUserObject(compositeMapNode); + transform->getOrCreateUserDataContainer()->setUserData(compositeMap); } else { diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index c7c1566a8..08cf2d70c 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -27,6 +27,7 @@ namespace Terrain class TextureManager; class CompositeMapRenderer; class Storage; + class CompositeMap; /// @brief Handles loading and caching of terrain chunks class ChunkManager : public Resource::ResourceManager @@ -41,9 +42,9 @@ namespace Terrain private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); - osg::ref_ptr createCompositeMapRTT(osg::ref_ptr& texture); + osg::ref_ptr createCompositeMapRTT(); - void createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, osg::Group* compositeMapNode); + void createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& map); std::vector > createPasses(float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap); diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 496162f7b..21d017623 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -2,39 +2,42 @@ #include -#include +#include +#include +#include namespace Terrain { CompositeMapRenderer::CompositeMapRenderer() : mNumCompilePerFrame(1) - , mLastFrame(0) { + setSupportsDisplayList(false); setCullingActive(false); -} -void CompositeMapRenderer::traverse(osg::NodeVisitor &nv) -{ - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) - return; + mFBO = new osg::FrameBufferObject; - if (mLastFrame == nv.getTraversalNumber()) - return; - mLastFrame = nv.getTraversalNumber(); + getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); +} +void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const +{ mCompiled.clear(); unsigned int numCompiled = 0; OpenThreads::ScopedLock lock(mMutex); + if (mImmediateCompileSet.empty() && mCompileSet.empty()) + return; + while (!mImmediateCompileSet.empty()) { - osg::Node* node = *mImmediateCompileSet.begin(); + CompositeMap* node = *mImmediateCompileSet.begin(); mCompiled.insert(node); - node->accept(nv); + compile(*node, renderInfo); + mImmediateCompileSet.erase(mImmediateCompileSet.begin()); ++numCompiled; @@ -42,14 +45,63 @@ void CompositeMapRenderer::traverse(osg::NodeVisitor &nv) while (!mCompileSet.empty() && numCompiled <= mNumCompilePerFrame) { - osg::Node* node = *mCompileSet.begin(); - mCompiled.insert(node); + CompositeMap* node = *mCompileSet.begin(); - node->accept(nv); - mCompileSet.erase(mCompileSet.begin()); + compile(*node, renderInfo); ++numCompiled; + + mCompiled.insert(node); + mCompileSet.erase(mCompileSet.begin()); + } +} + +void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo) const +{ + osg::State& state = *renderInfo.getState(); + osg::GLExtensions* ext = state.get(); + + if (!mFBO) + return; + + if (!ext->isFrameBufferObjectSupported) + return; + + osg::FrameBufferAttachment attach (compositeMap.mTexture); + mFBO->setAttachment(osg::Camera::COLOR_BUFFER, attach); + mFBO->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER); + + GLenum status = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0; + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); + OSG_ALWAYS << "Error attaching FBO" << std::endl; + return; + } + + for (unsigned int i=0; igetStateSet(); + + if (stateset) + renderInfo.getState()->pushStateSet(stateset); + + renderInfo.getState()->apply(); + + glViewport(0,0,compositeMap.mTexture->getTextureWidth(), compositeMap.mTexture->getTextureHeight()); + drw->drawImplementation(renderInfo); + + if (stateset) + renderInfo.getState()->popStateSet(); } + + state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT); + + GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0; + ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); } void CompositeMapRenderer::setNumCompilePerFrame(int num) @@ -57,20 +109,25 @@ void CompositeMapRenderer::setNumCompilePerFrame(int num) mNumCompilePerFrame = num; } -void CompositeMapRenderer::addCompositeMap(osg::Node *node, bool immediate) +void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate) { OpenThreads::ScopedLock lock(mMutex); if (immediate) - mImmediateCompileSet.insert(node); + mImmediateCompileSet.insert(compositeMap); else - mCompileSet.insert(node); + mCompileSet.insert(compositeMap); } -void CompositeMapRenderer::setImmediate(osg::Node *node) +void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) { OpenThreads::ScopedLock lock(mMutex); - mImmediateCompileSet.insert(node); - mCompileSet.erase(node); + mImmediateCompileSet.insert(compositeMap); + mCompileSet.erase(compositeMap); +} + +CompositeMap::~CompositeMap() +{ + } diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 53b38362f..c3579d8bb 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -1,46 +1,64 @@ #ifndef OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H #define OPENMW_COMPONENTS_TERRAIN_COMPOSITEMAPRENDERER_H -#include +#include #include #include +namespace osg +{ + class FrameBufferObject; + class RenderInfo; + class Texture2D; +} + namespace Terrain { + class CompositeMap : public osg::Referenced + { + public: + ~CompositeMap(); + std::vector > mDrawables; + osg::ref_ptr mTexture; + }; + /** * @brief The CompositeMapRenderer is responsible for updating composite map textures in a blocking or non-blocking way. */ - class CompositeMapRenderer : public osg::Node + class CompositeMapRenderer : public osg::Drawable { public: CompositeMapRenderer(); - virtual void traverse(osg::NodeVisitor& nv); + virtual void drawImplementation(osg::RenderInfo& renderInfo) const; + + void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo) const; /// Set the maximum number of (non-immediate) composite maps to compile per frame void setNumCompilePerFrame(int num); /// Add a composite map to be rendered - void addCompositeMap(osg::Node* node, bool immediate=false); + void addCompositeMap(CompositeMap* map, bool immediate=false); /// Mark this composite map to be required for the current frame - void setImmediate(osg::Node* node); + void setImmediate(CompositeMap* map); private: unsigned int mNumCompilePerFrame; - unsigned int mLastFrame; - typedef std::set > CompileSet; + typedef std::set > CompileSet; + + mutable CompileSet mCompileSet; + mutable CompileSet mImmediateCompileSet; - CompileSet mCompileSet; - CompileSet mImmediateCompileSet; + mutable CompileSet mCompiled; - CompileSet mCompiled; + mutable OpenThreads::Mutex mMutex; - OpenThreads::Mutex mMutex; + osg::ref_ptr mFBO; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index cb49f3392..95252f513 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -329,14 +329,10 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (entry.mVisible) { osg::UserDataContainer* udc = entry.mRenderingNode->getUserDataContainer(); - if (udc && udc->getNumUserObjects() > 0) + if (udc && udc->getUserData()) { - osg::Node* compositeMapNode = udc->getUserObject(0)->asNode(); - if (compositeMapNode) - { - mCompositeMapRenderer->setImmediate(compositeMapNode); - udc->removeUserObject(0); - } + mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); + udc->setUserData(NULL); } entry.mRenderingNode->accept(nv); } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 401252334..cbd4327c3 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -27,9 +28,20 @@ World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSyst mTerrainRoot->setName("Terrain Root"); + osg::ref_ptr compositeCam = new osg::Camera; + compositeCam->setRenderOrder(osg::Camera::PRE_RENDER, -1); + compositeCam->setProjectionMatrix(osg::Matrix::identity()); + compositeCam->setViewMatrix(osg::Matrix::identity()); + compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + compositeCam->setClearMask(0); + compositeCam->setNodeMask(preCompileMask); + mCompositeMapCamera = compositeCam; + + compileRoot->addChild(compositeCam); + + mCompositeMapRenderer = new CompositeMapRenderer; - mCompositeMapRenderer->setNodeMask(preCompileMask); - compileRoot->addChild(mCompositeMapRenderer); + compositeCam->addChild(mCompositeMapRenderer); mParent->addChild(mTerrainRoot); @@ -46,7 +58,9 @@ World::~World() mResourceSystem->removeResourceManager(mTextureManager.get()); mParent->removeChild(mTerrainRoot); - mCompositeMapRenderer->getParent(0)->removeChild(mCompositeMapRenderer); + + mCompositeMapCamera->removeChild(mCompositeMapRenderer); + mCompositeMapCamera->getParent(0)->removeChild(mCompositeMapCamera); delete mStorage; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 6cba6c5b6..59c390319 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -97,6 +97,8 @@ namespace Terrain osg::ref_ptr mParent; osg::ref_ptr mTerrainRoot; + + osg::ref_ptr mCompositeMapCamera; osg::ref_ptr mCompositeMapRenderer; Resource::ResourceSystem* mResourceSystem; From 8a6d909b22622d89c8a86fd23d329ba136bb76d7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:28:02 +0100 Subject: [PATCH 56/88] Fix composite map being compiled twice --- components/terrain/compositemaprenderer.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 21d017623..2b7a33686 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -121,8 +121,14 @@ void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool imme void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) { OpenThreads::ScopedLock lock(mMutex); - mImmediateCompileSet.insert(compositeMap); - mCompileSet.erase(compositeMap); + CompileSet::iterator found = mCompileSet.find(compositeMap); + if (found == mCompileSet.end()) + return; + else + { + mImmediateCompileSet.insert(compositeMap); + mCompileSet.erase(found); + } } CompositeMap::~CompositeMap() From 47ca8aeee54f9649fcaf68aa87e0759b4c95d31c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:29:45 +0100 Subject: [PATCH 57/88] Use the time elapsed instead of # compiled as limit --- components/terrain/compositemaprenderer.cpp | 26 ++++++++++----------- components/terrain/compositemaprenderer.hpp | 8 +++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 2b7a33686..8e63575f1 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -10,7 +10,7 @@ namespace Terrain { CompositeMapRenderer::CompositeMapRenderer() - : mNumCompilePerFrame(1) + : mTimeAvailable(0.0005) { setSupportsDisplayList(false); setCullingActive(false); @@ -24,8 +24,6 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const { mCompiled.clear(); - unsigned int numCompiled = 0; - OpenThreads::ScopedLock lock(mMutex); if (mImmediateCompileSet.empty() && mCompileSet.empty()) @@ -36,28 +34,27 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const CompositeMap* node = *mImmediateCompileSet.begin(); mCompiled.insert(node); - compile(*node, renderInfo); + compile(*node, renderInfo, NULL); mImmediateCompileSet.erase(mImmediateCompileSet.begin()); - - ++numCompiled; } - while (!mCompileSet.empty() && numCompiled <= mNumCompilePerFrame) + double timeLeft = mTimeAvailable; + + while (!mCompileSet.empty() && timeLeft > 0) { CompositeMap* node = *mCompileSet.begin(); - compile(*node, renderInfo); - - ++numCompiled; + compile(*node, renderInfo, &timeLeft); mCompiled.insert(node); mCompileSet.erase(mCompileSet.begin()); } } -void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo) const +void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo, double* timeLeft) const { + osg::Timer timer; osg::State& state = *renderInfo.getState(); osg::GLExtensions* ext = state.get(); @@ -102,11 +99,14 @@ void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo & GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0; ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); + + if (timeLeft) + *timeLeft -= timer.time_s(); } -void CompositeMapRenderer::setNumCompilePerFrame(int num) +void CompositeMapRenderer::setTimeAvailableForCompile(double time) { - mNumCompilePerFrame = num; + mTimeAvailable = time; } void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate) diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index c3579d8bb..5bc45da45 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -35,10 +35,10 @@ namespace Terrain virtual void drawImplementation(osg::RenderInfo& renderInfo) const; - void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo) const; + void compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo, double* timeLeft) const; - /// Set the maximum number of (non-immediate) composite maps to compile per frame - void setNumCompilePerFrame(int num); + /// Set the available time in seconds for compiling (non-immediate) composite maps each frame + void setTimeAvailableForCompile(double time); /// Add a composite map to be rendered void addCompositeMap(CompositeMap* map, bool immediate=false); @@ -47,7 +47,7 @@ namespace Terrain void setImmediate(CompositeMap* map); private: - unsigned int mNumCompilePerFrame; + double mTimeAvailable; typedef std::set > CompileSet; From c921620ef34692469c42728399613633235cc7b2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:43:27 +0100 Subject: [PATCH 58/88] Compile the drawables of a composite map one by one to avoid frame drops for larger maps --- components/terrain/compositemaprenderer.cpp | 28 ++++++++++++++++----- components/terrain/compositemaprenderer.hpp | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 8e63575f1..b00dfd2d9 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -47,8 +47,11 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const compile(*node, renderInfo, &timeLeft); - mCompiled.insert(node); - mCompileSet.erase(mCompileSet.begin()); + if (node->mCompiled >= node->mDrawables.size()) + { + mCompiled.insert(node); + mCompileSet.erase(mCompileSet.begin()); + } } } @@ -78,7 +81,7 @@ void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo & return; } - for (unsigned int i=0; igetStateSet(); @@ -93,15 +96,23 @@ void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo & if (stateset) renderInfo.getState()->popStateSet(); + + ++compositeMap.mCompiled; + + if (timeLeft) + { + *timeLeft -= timer.time_s(); + timer.setStartTick(); + + if (*timeLeft <= 0) + break; + } } state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT); GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0; ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); - - if (timeLeft) - *timeLeft -= timer.time_s(); } void CompositeMapRenderer::setTimeAvailableForCompile(double time) @@ -131,6 +142,11 @@ void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) } } +CompositeMap::CompositeMap() + : mCompiled(0) +{ +} + CompositeMap::~CompositeMap() { diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 5bc45da45..f84d2121f 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -20,9 +20,11 @@ namespace Terrain class CompositeMap : public osg::Referenced { public: + CompositeMap(); ~CompositeMap(); std::vector > mDrawables; osg::ref_ptr mTexture; + unsigned int mCompiled; }; /** From ec0b74312362e2937f6b3f3f52407f3e0d4f2dd0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:44:39 +0100 Subject: [PATCH 59/88] Revert "Change ordering of LocalMap nodes to make sure they are traversed before the CompositeMapRenderer." This reverts commit 7d72c70c93ee3c0cc3d00d37b22c339d0103cd19. --- apps/openmw/mwrender/localmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 61b93b9a3..0f77efafe 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -222,7 +222,7 @@ void LocalMap::setupRenderToTexture(osg::ref_ptr camera, int x, int camera->attach(osg::Camera::COLOR_BUFFER, texture); camera->addChild(mSceneRoot); - mRoot->insertChild(0, camera); + mRoot->addChild(camera); mActiveCameras.push_back(camera); MapSegment& segment = mSegments[std::make_pair(x, y)]; From e7a0878c107b86b2cc7797dd64ec2fad61641919 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 19:52:30 +0100 Subject: [PATCH 60/88] Add CompositeMapRenderer info to the stats panel --- apps/openmw/mwrender/renderingmanager.cpp | 2 ++ components/resource/stats.cpp | 2 +- components/terrain/compositemaprenderer.cpp | 6 ++++++ components/terrain/compositemaprenderer.hpp | 2 ++ components/terrain/quadtreeworld.cpp | 5 +++++ components/terrain/quadtreeworld.hpp | 2 ++ components/terrain/world.hpp | 2 ++ 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a41adc2fc..932f3d8b7 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -922,6 +922,8 @@ namespace MWRender if (stats->collectStats("resource")) { stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems()); + + mTerrain->reportStats(frameNumber, stats); } } diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index e2a91749e..bf34d5534 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer) _resourceStatsChildNum = _switch->getNumChildren(); _switch->addChild(group, false); - const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Chunk", "Terrain Texture", "Land", "", "UnrefQueue"}; + const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "", "Terrain Chunk", "Terrain Texture", "Land", "Composite", "", "UnrefQueue"}; int numLines = sizeof(statNames) / sizeof(statNames[0]); diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index b00dfd2d9..49d72979f 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -142,6 +142,12 @@ void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap) } } +unsigned int CompositeMapRenderer::getCompileSetSize() const +{ + OpenThreads::ScopedLock lock(mMutex); + return mCompileSet.size(); +} + CompositeMap::CompositeMap() : mCompiled(0) { diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index f84d2121f..8e53cbdaa 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -48,6 +48,8 @@ namespace Terrain /// Mark this composite map to be required for the current frame void setImmediate(CompositeMap* map); + unsigned int getCompileSetSize() const; + private: double mTimeAvailable; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 95252f513..3df49c41c 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -398,5 +398,10 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) } } +void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) +{ + stats->setAttribute(frameNumber, "Composite", mCompositeMapRenderer->getCompileSetSize()); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index edd9779d6..e5ab097b3 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -32,6 +32,8 @@ namespace Terrain void removeView(View* view); void preload(View* view, const osg::Vec3f& eyePoint); + void reportStats(unsigned int frameNumber, osg::Stats* stats); + private: void ensureQuadTreeBuilt(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 59c390319..f8613b443 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -90,6 +90,8 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. virtual void preload(View* view, const osg::Vec3f& eyePoint) {} + virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} + Storage* getStorage() { return mStorage; } protected: From 28fd49271169e768fc78aa989f9bf4b83d838130 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 20:30:38 +0100 Subject: [PATCH 61/88] Don't use terrain LOD for the map camera --- apps/openmw/mwrender/localmap.cpp | 1 + components/terrain/quadtreeworld.cpp | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 0f77efafe..b53e16c4e 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -160,6 +160,7 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); + camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 3df49c41c..00311711a 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -133,6 +133,7 @@ public: mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); mRootNode->setViewDataMap(mViewDataMap); + mRootNode->setLodCallback(new DefaultLodCallback); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -238,7 +239,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, const osg::Vec3f& eyePoint, bool visible) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, bool traverseNonVisible) { if (!node->hasValidBounds()) return; @@ -246,14 +247,17 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, const osg: if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - bool stopTraversal = (node->getLodCallback() && node->getLodCallback()->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); + if (!visible && !traverseNonVisible) + return; + + bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); if (stopTraversal) vd->add(node, visible); else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, eyePoint, visible); + traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, traverseNonVisible); } } @@ -315,7 +319,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - traverse(mRootNode.get(), vd, cv, cv->getEyePoint(), true); + + LodCallback* lodCallback = mRootNode->getLodCallback(); + if (osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer()) + { + if (udc->getNumDescriptions() && udc->getDescriptions()[0] == "NoTerrainLod") + lodCallback = NULL; + } + + traverse(mRootNode.get(), vd, cv, lodCallback, cv->getEyePoint(), true, lodCallback != NULL); } else mRootNode->traverse(nv); @@ -389,7 +401,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, NULL, eyePoint, false); + traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false, true); for (unsigned int i=0; igetNumEntries(); ++i) { From 3c29e2dbeb140d8aeb8f467452069ce15c3715ba Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 20:42:19 +0100 Subject: [PATCH 62/88] Refactor ownership of terrain views --- apps/openmw/mwworld/cellpreloader.cpp | 1 - apps/openmw/mwworld/cellpreloader.hpp | 2 +- components/terrain/quadtreeworld.cpp | 9 +-------- components/terrain/quadtreeworld.hpp | 1 - components/terrain/viewdata.cpp | 11 +---------- components/terrain/viewdata.hpp | 5 ----- components/terrain/world.hpp | 11 ++++------- 7 files changed, 7 insertions(+), 33 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index e633f5825..5d4a2717f 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -207,7 +207,6 @@ namespace MWWorld mTerrainPreloadItem = NULL; } - mTerrain->removeView(mTerrainView); mTerrainView = NULL; for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index 9bd062547..bc1227b2d 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -103,7 +103,7 @@ namespace MWWorld // Cells that are currently being preloaded, or have already finished preloading PreloadMap mPreloadCells; - Terrain::View* mTerrainView; + osg::ref_ptr mTerrainView; std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; }; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 00311711a..f17fa8bbe 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -386,14 +386,7 @@ void QuadTreeWorld::enable(bool enabled) View* QuadTreeWorld::createView() { - ViewData* vd = mViewDataMap->createOrReuseView(); - vd->setPersistent(true); - return vd; -} - -void QuadTreeWorld::removeView(View *view) -{ - mViewDataMap->removeView(static_cast(view)); + return new ViewData; } void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index e5ab097b3..d24b9b511 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -29,7 +29,6 @@ namespace Terrain virtual void enable(bool enabled); View* createView(); - void removeView(View* view); void preload(View* view, const osg::Vec3f& eyePoint); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 1e7245a15..7b3df56b9 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -7,7 +7,6 @@ ViewData::ViewData() : mNumEntries(0) , mFrameLastUsed(0) , mChanged(false) - , mPersistent(false) { } @@ -126,20 +125,12 @@ ViewData *ViewDataMap::createOrReuseView() } } -void ViewDataMap::removeView(ViewData* vd) -{ - vd->setPersistent(false); - vd->clear(); - mUnusedViews.push_back(vd); -} - void ViewDataMap::clearUnusedViews(unsigned int frame) { for (Map::iterator it = mViews.begin(); it != mViews.end(); ) { ViewData* vd = it->second; - if (!vd->getPersistent() && - (!vd->getViewer() // if no ref was held, always need to clear to avoid holding a dangling ref. + if ((!vd->getViewer() // if no ref was held, always need to clear to avoid holding a dangling ref. || vd->getFrameLastUsed() + 2 < frame)) { vd->setViewer(NULL); diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index c06e1dfff..30563d566 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -44,9 +44,6 @@ namespace Terrain Entry& getEntry(unsigned int i); - bool getPersistent() const { return mPersistent; } - void setPersistent(bool persistent) { mPersistent = persistent; } - osg::Object* getViewer() const { return mViewer.get(); } void setViewer(osg::Object* viewer) { mViewer = viewer; } @@ -60,7 +57,6 @@ namespace Terrain unsigned int mNumEntries; unsigned int mFrameLastUsed; bool mChanged; - bool mPersistent; osg::ref_ptr mViewer; }; @@ -70,7 +66,6 @@ namespace Terrain ViewData* getViewData(osg::Object* viewer, bool ref); ViewData* createOrReuseView(); - void removeView(ViewData* view); void clearUnusedViews(unsigned int frame); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index f8613b443..f7454c60a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -2,6 +2,7 @@ #define COMPONENTS_TERRAIN_WORLD_H #include +#include #include #include @@ -32,7 +33,7 @@ namespace Terrain * @brief A View is a collection of rendering objects that are visible from a given camera/intersection. * The base View class is part of the interface for usage in conjunction with preload feature. */ - class View + class View : public osg::Referenced { public: virtual ~View() {} @@ -79,14 +80,10 @@ namespace Terrain virtual void enable(bool enabled) {} - /// Create a View to use with preload feature. If a View is returned, it will remain valid until the user calls 'removeView' or the World is destroyed. - /// @note Not thread safe. + /// Create a View to use with preload feature. The caller is responsible for deleting the view. + /// @note Thread safe. virtual View* createView() { return NULL; } - /// Remove a View that was previously created with 'createView'. - /// @note Not thread safe. - virtual void removeView(View* view) {} - /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. virtual void preload(View* view, const osg::Vec3f& eyePoint) {} From 6ccb6009eee61b92dfe1072d6efffef6b1ede145 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 20:52:50 +0100 Subject: [PATCH 63/88] Use the View-based preloading for TerrainGrid as well --- apps/openmw/mwworld/cellpreloader.cpp | 6 +++++- components/terrain/terraingrid.cpp | 17 +++++++++++++++-- components/terrain/terraingrid.hpp | 7 +++---- components/terrain/world.cpp | 5 ----- components/terrain/world.hpp | 6 ++---- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 5d4a2717f..d3affecc4 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -60,6 +60,8 @@ namespace MWWorld , mPreloadInstances(preloadInstances) , mAbort(false) { + mTerrainView = mTerrain->createView(); + ListModelsVisitor visitor (mMeshes); if (cell->getState() == MWWorld::CellStore::State_Loaded) { @@ -92,7 +94,7 @@ namespace MWWorld { try { - mPreloadedObjects.push_back(mTerrain->cacheCell(mX, mY)); + mTerrain->cacheCell(mTerrainView.get(), mX, mY); mPreloadedObjects.push_back(mLandManager->getLand(mX, mY)); } catch(std::exception& e) @@ -160,6 +162,8 @@ namespace MWWorld volatile bool mAbort; + osg::ref_ptr mTerrainView; + // keep a ref to the loaded objects to make sure it stays loaded as long as this cell is in the preloaded state std::vector > mPreloadedObjects; }; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index 04b29e169..466cddddc 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -9,6 +9,14 @@ namespace Terrain { +class MyView : public View +{ +public: + osg::ref_ptr mLoaded; + + 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) , mNumSplits(4) @@ -23,10 +31,10 @@ TerrainGrid::~TerrainGrid() } } -osg::ref_ptr TerrainGrid::cacheCell(int x, int y) +void TerrainGrid::cacheCell(View* view, int x, int y) { osg::Vec2f center(x+0.5f, y+0.5f); - return buildTerrain(NULL, 1.f, center); + static_cast(view)->mLoaded = buildTerrain(NULL, 1.f, center); } osg::ref_ptr TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter) @@ -84,4 +92,9 @@ void TerrainGrid::unloadCell(int x, int y) mGrid.erase(it); } +View *TerrainGrid::createView() +{ + return new MyView; +} + } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index dc4523eef..189fe7f63 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -17,10 +17,7 @@ namespace Terrain TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0); ~TerrainGrid(); - /// Load a terrain cell and store it in cache for later use. - /// @note The returned ref_ptr should be kept by the caller to ensure that the terrain stays in cache for as long as needed. - /// @note Thread safe. - virtual osg::ref_ptr cacheCell(int x, int y); + virtual void cacheCell(View* view, int x, int y); /// @note Not thread safe. virtual void loadCell(int x, int y); @@ -28,6 +25,8 @@ namespace Terrain /// @note Not thread safe. virtual void unloadCell(int x, int y); + View* createView(); + private: osg::ref_ptr buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter); diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index cbd4327c3..b7cc0ae01 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -70,11 +70,6 @@ float World::getHeightAt(const osg::Vec3f &worldPos) return mStorage->getHeightAt(worldPos); } -osg::ref_ptr World::cacheCell(int x, int y) -{ - return NULL; -} - void World::updateTextureFiltering() { mTextureManager->updateTextureFiltering(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index f7454c60a..6aac963ce 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -62,11 +62,9 @@ namespace Terrain float getHeightAt (const osg::Vec3f& worldPos); - /// Load a terrain cell and store it in cache for later use. - /// @note The returned ref_ptr should be kept by the caller to ensure that the terrain stays in cache for as long as needed. + /// Load a terrain cell at maximum LOD and store it in the View for later use. /// @note Thread safe. - /// @note May be ignored by derived implementations that don't organize the terrain into cells. - virtual osg::ref_ptr cacheCell(int x, int y); + virtual void cacheCell(View* view, int x, int y) {} /// Load the cell into the scene graph. /// @note Not thread safe. From 7d50b6c2e21739bd432c7bdf6c711c7491977d4c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 21:05:25 +0100 Subject: [PATCH 64/88] Add QuadTreeWorld::cacheCell to preload cells at max LOD for local maps --- components/terrain/quadtreeworld.cpp | 35 ++++++++++++++++++++++++++++ components/terrain/quadtreeworld.hpp | 2 ++ 2 files changed, 37 insertions(+) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index f17fa8bbe..506c9dc4d 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -384,6 +384,41 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } +void traverseToCell(QuadTreeNode* node, ViewData* vd, int cellX, int cellY) +{ + if (!node->hasValidBounds()) + return; + + if (node->getCenter().x() + node->getSize()/2.f <= cellX + || node->getCenter().x() - node->getSize()/2.f >= cellX+1 + || node->getCenter().y() + node->getSize()/2.f <= cellY + || node->getCenter().y() - node->getSize()/2.f >= cellY+1) + return; + + bool stopTraversal = !node->getNumChildren(); + + if (stopTraversal) + vd->add(node, true); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + traverseToCell(node->getChild(i), vd, cellX, cellY); + } +} + +void QuadTreeWorld::cacheCell(View *view, int x, int y) +{ + ensureQuadTreeBuilt(); + ViewData* vd = static_cast(view); + traverseToCell(mRootNode.get(), vd, x, y); + + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + loadRenderingNode(entry, vd, mChunkManager.get()); + } +} + View* QuadTreeWorld::createView() { return new ViewData; diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index d24b9b511..6176d768f 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -28,6 +28,8 @@ namespace Terrain virtual void enable(bool enabled); + void cacheCell(View *view, int x, int y); + View* createView(); void preload(View* view, const osg::Vec3f& eyePoint); From d055dc25bf061cfc57bb70403bbe6d471dceda8f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 21:15:12 +0100 Subject: [PATCH 65/88] Add custom traversal for local map camera to avoid loading terrain nodes that are exactly outside the border to another cell --- apps/openmw/mwrender/localmap.cpp | 6 ++- components/terrain/quadtreeworld.cpp | 70 ++++++++++++++-------------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index b53e16c4e..02bc5d01f 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -160,7 +160,6 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell) osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax) { osg::ref_ptr camera (new osg::Camera); - camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10); camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); @@ -350,6 +349,11 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell) osg::ref_ptr camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize, osg::Vec3d(0,1,0), zmin, zmax); + camera->getOrCreateUserDataContainer()->addDescription("NoTerrainLod"); + std::ostringstream stream; + stream << x << " " << y; + camera->getOrCreateUserDataContainer()->addDescription(stream.str()); + setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY()); MapSegment& segment = mSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())]; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 506c9dc4d..0893b8f18 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -2,6 +2,8 @@ #include +#include + #include "quadtreenode.hpp" #include "storage.hpp" #include "viewdata.hpp" @@ -239,7 +241,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, bool traverseNonVisible) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible) { if (!node->hasValidBounds()) return; @@ -247,9 +249,6 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - if (!visible && !traverseNonVisible) - return; - bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); if (stopTraversal) @@ -257,7 +256,29 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, traverseNonVisible); + traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible); + } +} + +void traverseToCell(QuadTreeNode* node, ViewData* vd, int cellX, int cellY) +{ + if (!node->hasValidBounds()) + return; + + if (node->getCenter().x() + node->getSize()/2.f <= cellX + || node->getCenter().x() - node->getSize()/2.f >= cellX+1 + || node->getCenter().y() + node->getSize()/2.f <= cellY + || node->getCenter().y() - node->getSize()/2.f >= cellY+1) + return; + + bool stopTraversal = !node->getNumChildren(); + + if (stopTraversal) + vd->add(node, true); + else + { + for (unsigned int i=0; igetNumChildren(); ++i) + traverseToCell(node->getChild(i), vd, cellX, cellY); } } @@ -320,14 +341,17 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { osgUtil::CullVisitor* cv = static_cast(&nv); - LodCallback* lodCallback = mRootNode->getLodCallback(); - if (osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer()) + osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); + if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") { - if (udc->getNumDescriptions() && udc->getDescriptions()[0] == "NoTerrainLod") - lodCallback = NULL; + std::istringstream stream(udc->getDescriptions()[1]); + int x,y; + stream >> x; + stream >> y; + traverseToCell(mRootNode.get(), vd, x,y); } - - traverse(mRootNode.get(), vd, cv, lodCallback, cv->getEyePoint(), true, lodCallback != NULL); + else + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getEyePoint(), true); } else mRootNode->traverse(nv); @@ -384,28 +408,6 @@ void QuadTreeWorld::enable(bool enabled) mRootNode->setNodeMask(enabled ? ~0 : 0); } -void traverseToCell(QuadTreeNode* node, ViewData* vd, int cellX, int cellY) -{ - if (!node->hasValidBounds()) - return; - - if (node->getCenter().x() + node->getSize()/2.f <= cellX - || node->getCenter().x() - node->getSize()/2.f >= cellX+1 - || node->getCenter().y() + node->getSize()/2.f <= cellY - || node->getCenter().y() - node->getSize()/2.f >= cellY+1) - return; - - bool stopTraversal = !node->getNumChildren(); - - if (stopTraversal) - vd->add(node, true); - else - { - for (unsigned int i=0; igetNumChildren(); ++i) - traverseToCell(node->getChild(i), vd, cellX, cellY); - } -} - void QuadTreeWorld::cacheCell(View *view, int x, int y) { ensureQuadTreeBuilt(); @@ -429,7 +431,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false, true); + traverse(mRootNode.get(), vd, NULL, mRootNode->getLodCallback(), eyePoint, false); for (unsigned int i=0; igetNumEntries(); ++i) { From 9eed7fa6f5a7129c7c5e06fd29efd0f7e675ff2f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 21:23:06 +0100 Subject: [PATCH 66/88] Fix composite map when force shaders is enabled --- components/terrain/chunkmanager.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index ddba8699f..3eb48d88f 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -105,8 +105,9 @@ std::vector > ChunkManager::createPasses(float chunk mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList); bool useShaders = mSceneManager->getForceShaders(); - if (!mSceneManager->getClampLighting() && !forCompositeMap) + if (!mSceneManager->getClampLighting()) useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps + std::vector layers; { for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) @@ -127,6 +128,9 @@ std::vector > ChunkManager::createPasses(float chunk } } + if (forCompositeMap) + useShaders = false; + std::vector > blendmapTextures; for (std::vector >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) { From 86e75f09875906ee7b0d4d4e300d6bf79827758f Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 9 Mar 2017 21:45:38 +0100 Subject: [PATCH 67/88] Preload terrain even when cell preloading is disabled --- apps/openmw/mwworld/scene.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index a772abdc2..437753f95 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -209,14 +209,11 @@ namespace MWWorld void Scene::update (float duration, bool paused) { - if (mPreloadEnabled) + mPreloadTimer += duration; + if (mPreloadTimer > 0.1f) { - mPreloadTimer += duration; - if (mPreloadTimer > 0.1f) - { - preloadCells(0.1f); - mPreloadTimer = 0.f; - } + preloadCells(0.1f); + mPreloadTimer = 0.f; } mRendering.update (duration, paused); @@ -704,12 +701,15 @@ namespace MWWorld mLastPlayerPos = playerPos; - if (mPreloadDoors) - preloadTeleportDoorDestinations(playerPos, predictedPos, exteriorPositions); - if (mPreloadExteriorGrid) - preloadExteriorGrid(playerPos, predictedPos); - if (mPreloadFastTravel) - preloadFastTravelDestinations(playerPos, predictedPos); + if (mPreloadEnabled) + { + if (mPreloadDoors) + preloadTeleportDoorDestinations(playerPos, predictedPos, exteriorPositions); + if (mPreloadExteriorGrid) + preloadExteriorGrid(playerPos, predictedPos); + if (mPreloadFastTravel) + preloadFastTravelDestinations(playerPos, predictedPos); + } mPreloader->setTerrainPreloadPositions(exteriorPositions); } From 03c07d3bd58ded12cbcf7fd1eb8e9f5921f27e29 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Mar 2017 20:34:31 +0100 Subject: [PATCH 68/88] Remove old code --- apps/openmw/mwworld/scene.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 437753f95..01c323136 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -343,7 +343,6 @@ namespace MWWorld int newX, newY; MWBase::Environment::get().getWorld()->positionToIndex(pos.x(), pos.y(), newX, newY); changeCellGrid(newX, newY); - //mRendering.updateTerrain(); } } @@ -352,8 +351,6 @@ namespace MWWorld Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); Loading::ScopedLoad load(loadingListener); - //mRendering.enableTerrain(true); - std::string loadingExteriorText = "#{sLoadingMessage3}"; loadingListener->setLabel(loadingExteriorText); @@ -522,8 +519,6 @@ namespace MWWorld loadingListener->setLabel(loadingInteriorText); Loading::ScopedLoad load(loadingListener); - //mRendering.enableTerrain(false); - if(!loadcell) { MWBase::World *world = MWBase::Environment::get().getWorld(); @@ -584,8 +579,6 @@ namespace MWWorld CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); - - //mRendering.updateTerrain(); } CellStore* Scene::getCurrentCell () From 11bee6ee356e7e2f9bb8746b29af7977921ae345 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 10 Mar 2017 21:46:51 +0100 Subject: [PATCH 69/88] Avoid compiling composite maps that are no longer referenced --- components/terrain/compositemaprenderer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/terrain/compositemaprenderer.cpp b/components/terrain/compositemaprenderer.cpp index 49d72979f..5300aac85 100644 --- a/components/terrain/compositemaprenderer.cpp +++ b/components/terrain/compositemaprenderer.cpp @@ -57,6 +57,13 @@ void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo, double* timeLeft) const { + // if there are no more external references we can assume the texture is no longer required + if (compositeMap.mTexture->referenceCount() <= 1) + { + compositeMap.mCompiled = compositeMap.mDrawables.size(); + return; + } + osg::Timer timer; osg::State& state = *renderInfo.getState(); osg::GLExtensions* ext = state.get(); From 59bf100907bd3d1940eb703e6e457e4fc95c2cb8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 00:47:06 +0100 Subject: [PATCH 70/88] Fill exteriorPositions in preloadFastTravel --- apps/openmw/mwworld/scene.cpp | 8 +++++--- apps/openmw/mwworld/scene.hpp | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 01c323136..6be1dbc97 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -701,7 +701,7 @@ namespace MWWorld if (mPreloadExteriorGrid) preloadExteriorGrid(playerPos, predictedPos); if (mPreloadFastTravel) - preloadFastTravelDestinations(playerPos, predictedPos); + preloadFastTravelDestinations(playerPos, predictedPos, exteriorPositions); } mPreloader->setTerrainPreloadPositions(exteriorPositions); @@ -839,7 +839,7 @@ namespace MWWorld std::vector mList; }; - void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/) // ignore predictedPos here since opening dialogue with travel service takes extra time + void Scene::preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& /*predictedPos*/, std::vector& exteriorPositions) // ignore predictedPos here since opening dialogue with travel service takes extra time { const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); ListFastTravelDestinationsVisitor listVisitor(mPreloadDistance, player.getRefData().getPosition().asVec3()); @@ -857,9 +857,11 @@ namespace MWWorld preloadCell(MWBase::Environment::get().getWorld()->getInterior(it->mCellName)); else { + osg::Vec3f pos = it->mPos.asVec3(); int x,y; - MWBase::Environment::get().getWorld()->positionToIndex( it->mPos.pos[0], it->mPos.pos[1], x, y); + MWBase::Environment::get().getWorld()->positionToIndex( pos.x(), pos.y(), x, y); preloadCell(MWBase::Environment::get().getWorld()->getExterior(x,y), true); + exteriorPositions.push_back(pos); } } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 437279ff7..82b8dcaba 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -80,7 +80,7 @@ namespace MWWorld void preloadCells(float dt); void preloadTeleportDoorDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); - void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); + void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); public: From db00d47ca2668fd77c45abb1dba2454df0f7c9e4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 19:47:59 +0100 Subject: [PATCH 71/88] Hold a ref to the intersection visitor's view if possible --- components/terrain/quadtreenode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 3f61e8b76..2577ab169 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -143,7 +143,7 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) } else // INTERSECTION_VISITOR { - return mViewDataMap->getViewData(&nv, false); + return mViewDataMap->getViewData(&nv, (nv.referenceCount() > 0)); // if no referenceCount, the visitor was allocated on the stack } } From 9371100fde620bd8c6779b5d07e5190edc9ae89e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 19:48:41 +0100 Subject: [PATCH 72/88] Reuse the intersection visitor and set a traversal number to allow the terrain component to manage its view more efficiently --- apps/openmw/mwrender/renderingmanager.cpp | 19 ++++++++++++------- apps/openmw/mwrender/renderingmanager.hpp | 10 ++++++++++ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 932f3d8b7..6ee9b8ae8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -730,18 +730,23 @@ namespace MWRender } - osg::ref_ptr createIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors) + osg::ref_ptr RenderingManager::getIntersectionVisitor(osgUtil::Intersector *intersector, bool ignorePlayer, bool ignoreActors) { - osg::ref_ptr intersectionVisitor( new osgUtil::IntersectionVisitor(intersector)); - int mask = intersectionVisitor->getTraversalMask(); + if (!mIntersectionVisitor) + mIntersectionVisitor = new osgUtil::IntersectionVisitor; + + mIntersectionVisitor->setTraversalNumber(mViewer->getFrameStamp()->getFrameNumber()); + mIntersectionVisitor->setIntersector(intersector); + + int mask = ~0; mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater); if (ignorePlayer) mask &= ~(Mask_Player); if (ignoreActors) mask &= ~(Mask_Actor|Mask_Player); - intersectionVisitor->setTraversalMask(mask); - return intersectionVisitor; + mIntersectionVisitor->setTraversalMask(mask); + return mIntersectionVisitor; } RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors) @@ -750,7 +755,7 @@ namespace MWRender origin, dest)); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); - mRootNode->accept(*createIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); + mRootNode->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); return getIntersectionResult(intersector); } @@ -769,7 +774,7 @@ namespace MWRender intersector->setEnd(end); intersector->setIntersectionLimit(osgUtil::LineSegmentIntersector::LIMIT_NEAREST); - mViewer->getCamera()->accept(*createIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); + mViewer->getCamera()->accept(*getIntersectionVisitor(intersector, ignorePlayer, ignoreActors)); return getIntersectionResult(intersector); } diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d81dbf180..075357335 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -17,6 +17,12 @@ namespace osg class PositionAttitudeTransform; } +namespace osgUtil +{ + class IntersectionVisitor; + class Intersector; +} + namespace Resource { class ResourceSystem; @@ -204,6 +210,10 @@ namespace MWRender void reportStats() const; + osg::ref_ptr getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors); + + osg::ref_ptr mIntersectionVisitor; + osg::ref_ptr mViewer; osg::ref_ptr mRootNode; osg::ref_ptr mSceneRoot; From 2aa09639a93866c996e501fe26a5beb466dce5c8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 19:49:15 +0100 Subject: [PATCH 73/88] Re-enable terrain intersections --- components/terrain/quadtreeworld.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 0893b8f18..236c5310f 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -332,7 +332,7 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)// && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) return; ViewData* vd = mRootNode->getView(nv); From e4e8821902dde4ef085a7ca66fe3c934f1ef53bd Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 20:17:16 +0100 Subject: [PATCH 74/88] Refactor update of lodFlags --- components/terrain/quadtreeworld.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 236c5310f..dfcf20509 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -318,15 +318,16 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk int ourLod = Log2(int(entry.mNode->getSize())); unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); if (lodFlags != entry.mLodFlags) + { entry.mRenderingNode = NULL; + entry.mLodFlags = lodFlags; + } } if (!entry.mRenderingNode) { int ourLod = Log2(int(entry.mNode->getSize())); - unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd); - entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, lodFlags); - entry.mLodFlags = lodFlags; + entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags); } } From c22fde2bcdab0f136e76c703eb409b0d895e513c Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 22:38:50 +0100 Subject: [PATCH 75/88] Preload terrain while reading savegame --- apps/openmw/mwworld/scene.cpp | 7 +++++++ apps/openmw/mwworld/scene.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 1 + 3 files changed, 9 insertions(+) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 6be1dbc97..e27c0b585 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -809,6 +809,13 @@ namespace MWWorld mPreloader->preload(cell, mRendering.getReferenceTime()); } + void Scene::preloadTerrain(const osg::Vec3f &pos) + { + std::vector vec; + vec.push_back(pos); + mPreloader->setTerrainPreloadPositions(vec); + } + struct ListFastTravelDestinationsVisitor { ListFastTravelDestinationsVisitor(float preloadDist, const osg::Vec3f& playerPos) diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 82b8dcaba..f3c2d31ca 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -89,6 +89,7 @@ namespace MWWorld ~Scene(); void preloadCell(MWWorld::CellStore* cell, bool preloadSurrounding=false); + void preloadTerrain(const osg::Vec3f& pos); void unloadCell (CellStoreCollection::iterator iter); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9d3110a67..851b02ba8 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -379,6 +379,7 @@ namespace MWWorld case ESM::REC_PLAY: mPlayer->readRecord(reader, type); mWorldScene->preloadCell(getPlayerPtr().getCell(), true); + mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3()); break; default: if (!mStore.readRecord (reader, type) && From a041546b54f95b64ba9bee7baebdea2314095171 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 22:39:53 +0100 Subject: [PATCH 76/88] Use the quad tree's minSize in the LodCallback --- components/terrain/quadtreeworld.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index dfcf20509..7afdccca6 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -72,14 +72,22 @@ namespace Terrain class DefaultLodCallback : public LodCallback { public: + DefaultLodCallback(float minSize) + : mMinSize(minSize) + { + } + virtual bool isSufficientDetail(QuadTreeNode* node, const osg::Vec3f& eyePoint) { float dist = distance(node->getBoundingBox(), eyePoint); - int nativeLodLevel = Log2(static_cast(node->getSize()*4)); - int lodLevel = Log2(static_cast(dist/2048.0)); + int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); + int lodLevel = Log2(static_cast(dist/(8192*mMinSize))); return nativeLodLevel <= lodLevel; } + +private: + float mMinSize; }; class RootNode : public QuadTreeNode @@ -135,7 +143,7 @@ public: mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY)); mRootNode->setViewDataMap(mViewDataMap); - mRootNode->setLodCallback(new DefaultLodCallback); + mRootNode->setLodCallback(new DefaultLodCallback(mMinSize)); addChildren(mRootNode); mRootNode->initNeighbours(); @@ -177,7 +185,7 @@ public: } osg::ref_ptr node = new QuadTreeNode(parent, direction, size, center); - node->setLodCallback(new DefaultLodCallback); + node->setLodCallback(parent->getLodCallback()); node->setViewDataMap(mViewDataMap); parent->addChild(node); From fb8ac06524ac583ee6cd1ffd3aa2f2474746e704 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 22:42:04 +0100 Subject: [PATCH 77/88] Reduce the minSize of quad tree nodes for better performance --- components/terrain/quadtreeworld.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 7afdccca6..649496e8d 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -394,7 +394,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt() if (mQuadTreeBuilt) return; - const float minSize = 1/4.f; + const float minSize = 1/8.f; QuadTreeBuilder builder(mStorage, mViewDataMap.get(), minSize); builder.build(); From 0782839a4286723ba08181632e89342fd9e33a10 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 23:04:08 +0100 Subject: [PATCH 78/88] Avoid redundant culling tests on the transform/drawable --- components/terrain/chunkmanager.cpp | 12 ++++++++++++ components/terrain/chunkmanager.hpp | 4 ++++ components/terrain/quadtreeworld.cpp | 2 ++ components/terrain/terraindrawable.cpp | 2 +- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 3eb48d88f..b0860ecf2 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -27,6 +27,7 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T , mTextureManager(textureManager) , mCompositeMapRenderer(renderer) , mCompositeMapSize(512) + , mCullingActive(true) { } @@ -53,6 +54,11 @@ void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) cons stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize()); } +void ChunkManager::setCullingActive(bool active) +{ + mCullingActive = active; +} + osg::ref_ptr ChunkManager::createCompositeMapRTT() { osg::ref_ptr texture = new osg::Texture2D; @@ -207,6 +213,12 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve transform->addChild(geometry); + if (!mCullingActive) + { + transform->setCullingActive(false); + geometry->setCullingActive(false); + } + if (mSceneManager->getIncrementalCompileOperation()) { mSceneManager->getIncrementalCompileOperation()->add(geometry); diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 08cf2d70c..58db98dc9 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -39,6 +39,8 @@ namespace Terrain virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; + void setCullingActive(bool active); + private: osg::ref_ptr createChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); @@ -55,6 +57,8 @@ namespace Terrain BufferCache mBufferCache; unsigned int mCompositeMapSize; + + bool mCullingActive; }; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 649496e8d..d65b1d5ed 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -240,6 +240,8 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour , mViewDataMap(new ViewDataMap) , mQuadTreeBuilt(false) { + // No need for culling on the Drawable / Transform level as the quad tree performs the culling already. + mChunkManager->setCullingActive(false); } QuadTreeWorld::~QuadTreeWorld() diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index b5934eb12..8557233ed 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -43,7 +43,7 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) { const osg::BoundingBox& bb = getBoundingBox(); - if (cv->isCulled(getBoundingBox())) + if (_cullingActive && cv->isCulled(getBoundingBox())) return; osg::RefMatrix& matrix = *cv->getModelViewMatrix(); From 9e9c028f1de92e68bdb17d7e062cbe3481ade060 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Mar 2017 23:17:50 +0100 Subject: [PATCH 79/88] Skip light collection for far away terrain --- components/terrain/chunkmanager.cpp | 4 ++++ components/terrain/terraindrawable.cpp | 8 ++++++-- components/terrain/terraindrawable.hpp | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index b0860ecf2..1d6fb9e61 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "terraindrawable.hpp" #include "material.hpp" @@ -178,6 +179,9 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->setUseDisplayList(false); geometry->setUseVertexBufferObjects(true); + if (chunkSize <= 2.f) + geometry->setLightListCallback(new SceneUtil::LightListCallback); + unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1; geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags)); diff --git a/components/terrain/terraindrawable.cpp b/components/terrain/terraindrawable.cpp index 8557233ed..f3080e31c 100644 --- a/components/terrain/terraindrawable.cpp +++ b/components/terrain/terraindrawable.cpp @@ -9,7 +9,6 @@ namespace Terrain TerrainDrawable::TerrainDrawable() { - mLightListCallback = new SceneUtil::LightListCallback; } TerrainDrawable::TerrainDrawable(const TerrainDrawable ©, const osg::CopyOp ©op) @@ -52,7 +51,7 @@ void TerrainDrawable::cull(osgUtil::CullVisitor *cv) if (osg::isNaN(depth)) return; - bool pushedLight = mLightListCallback->pushLightState(this, cv); + bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv); for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) { @@ -70,6 +69,11 @@ void TerrainDrawable::setPasses(const TerrainDrawable::PassVector &passes) mPasses = passes; } +void TerrainDrawable::setLightListCallback(SceneUtil::LightListCallback *lightListCallback) +{ + mLightListCallback = lightListCallback; +} + void TerrainDrawable::compileGLObjects(osg::RenderInfo &renderInfo) const { for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it) diff --git a/components/terrain/terraindrawable.hpp b/components/terrain/terraindrawable.hpp index d7c2bfa72..79a28deeb 100644 --- a/components/terrain/terraindrawable.hpp +++ b/components/terrain/terraindrawable.hpp @@ -37,6 +37,8 @@ namespace Terrain typedef std::vector > PassVector; void setPasses (const PassVector& passes); + void setLightListCallback(SceneUtil::LightListCallback* lightListCallback); + virtual void compileGLObjects(osg::RenderInfo& renderInfo) const; private: From b66c2abfe3b714c88d7eedc9e0a4dafea4eea9bb Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Mar 2017 00:13:42 +0100 Subject: [PATCH 80/88] Build the bounding sphere in the loading thread (only relevant for TerrainGrid) --- components/terrain/chunkmanager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 1d6fb9e61..bdb3b6a5f 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -222,6 +222,8 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve transform->setCullingActive(false); geometry->setCullingActive(false); } + else + transform->getBound(); if (mSceneManager->getIncrementalCompileOperation()) { From 5044816770cd4762d56cc0c9ad73b1b25f578df7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 13 Mar 2017 00:15:36 +0100 Subject: [PATCH 81/88] Remove unused code --- components/terrain/chunkmanager.cpp | 2 -- components/terrain/material.cpp | 4 +--- components/terrain/material.hpp | 2 +- components/terrain/quadtreenode.cpp | 9 --------- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index bdb3b6a5f..85f7e26c9 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -186,8 +186,6 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags)); - geometry->getBound(); - bool useCompositeMap = chunkSize >= 1.f; unsigned int numUvSets = useCompositeMap ? 1 : 2; diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 5b84da3f7..d35fd39ac 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -59,7 +59,7 @@ namespace Terrain } std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector &layers, - const std::vector > &blendmaps, int blendmapScale, float layerTileSize, bool renderCompositeMap) + const std::vector > &blendmaps, int blendmapScale, float layerTileSize) { std::vector > passes; @@ -104,8 +104,6 @@ namespace Terrain stateset->addUniform(new osg::Uniform("normalMap", texunit)); } - // TODO: fix shader for renderCompositeMap=True - Shader::ShaderManager::DefineMap defineMap; defineMap["forcePPL"] = forcePerPixelLighting ? "1" : "0"; defineMap["clamp"] = clampLighting ? "1" : "0"; diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index 4e8a222d1..25aa69540 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -28,7 +28,7 @@ namespace Terrain std::vector > createPasses(bool useShaders, bool forcePerPixelLighting, bool clampLighting, Shader::ShaderManager* shaderManager, const std::vector& layers, - const std::vector >& blendmaps, int blendmapScale, float layerTileSize, bool renderCompositeMap=false); + const std::vector >& blendmaps, int blendmapScale, float layerTileSize); } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 2577ab169..438303c27 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -99,15 +99,6 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) if (!hasValidBounds()) return; - if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) - { - osgUtil::CullVisitor* cv = static_cast(&nv); - - // do another culling test against bounding box as its much more accurate than the bounding sphere. - if (cv->isCulled(mBoundingBox)) - return; - } - if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv.getEyePoint())) || !getNumChildren()) getView(nv)->add(this, true); else From 7f5beb3172e1f60f80a834ea58faea486121ab6a Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 03:45:37 +0100 Subject: [PATCH 82/88] Remove unused includes --- components/terrain/chunkmanager.hpp | 5 ----- components/terrain/compositemaprenderer.hpp | 2 +- components/terrain/material.cpp | 1 - components/terrain/quadtreeworld.hpp | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/components/terrain/chunkmanager.hpp b/components/terrain/chunkmanager.hpp index 58db98dc9..553e06d97 100644 --- a/components/terrain/chunkmanager.hpp +++ b/components/terrain/chunkmanager.hpp @@ -11,11 +11,6 @@ namespace osg class Texture2D; } -namespace SceneUtil -{ - class PositionAttitudeTransform; -} - namespace Resource { class SceneManager; diff --git a/components/terrain/compositemaprenderer.hpp b/components/terrain/compositemaprenderer.hpp index 8e53cbdaa..15e939389 100644 --- a/components/terrain/compositemaprenderer.hpp +++ b/components/terrain/compositemaprenderer.hpp @@ -5,7 +5,7 @@ #include -#include +#include namespace osg { diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index d35fd39ac..e3e6f6de3 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -1,6 +1,5 @@ #include "material.hpp" -#include #include #include diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 6176d768f..8ec75917b 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -1,8 +1,6 @@ #ifndef COMPONENTS_TERRAIN_QUADTREEWORLD_H #define COMPONENTS_TERRAIN_QUADTREEWORLD_H -#include - #include "world.hpp" #include From 088d5604bf6fb401338994c43516cc98b3b9ff17 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 04:07:23 +0100 Subject: [PATCH 83/88] Use a shader if required to display the composite map Fixes composited terrain not respecting the 'clamp lighting' setting. --- components/terrain/chunkmanager.cpp | 12 +++++++----- components/terrain/material.cpp | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/components/terrain/chunkmanager.cpp b/components/terrain/chunkmanager.cpp index 85f7e26c9..e4a7c2d68 100644 --- a/components/terrain/chunkmanager.cpp +++ b/components/terrain/chunkmanager.cpp @@ -201,12 +201,14 @@ osg::ref_ptr ChunkManager::createChunk(float chunkSize, const osg::Ve mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false); - std::vector > passes2; - passes2.push_back(new osg::StateSet); - passes2[0]->setTextureAttributeAndModes(0, compositeMap->mTexture, osg::StateAttribute::ON); - geometry->setPasses(passes2); - transform->getOrCreateUserDataContainer()->setUserData(compositeMap); + + TextureLayer layer; + layer.mDiffuseMap = compositeMap->mTexture; + layer.mParallax = false; + layer.mSpecular = false; + geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), mSceneManager->getForcePerPixelLighting(), + mSceneManager->getClampLighting(), &mSceneManager->getShaderManager(), std::vector(1, layer), std::vector >(), 1.f, 1.f)); } else { diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index e3e6f6de3..8aec54835 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -81,7 +81,8 @@ namespace Terrain { stateset->setTextureAttributeAndModes(texunit, it->mDiffuseMap); - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + if (layerTileSize != 1.f) + stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("diffuseMap", texunit)); @@ -147,7 +148,8 @@ namespace Terrain osg::ref_ptr tex = it->mDiffuseMap; stateset->setTextureAttributeAndModes(texunit, tex.get()); - stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); + if (layerTileSize != 1.f) + stateset->setTextureAttributeAndModes(texunit, getLayerTexMat(layerTileSize), osg::StateAttribute::ON); } firstLayer = false; From fd215caa028318fa2f5409734eb22a56571666c4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 19:08:42 +0100 Subject: [PATCH 84/88] Add local LandCache to cut down on store searches --- components/esmterrain/storage.cpp | 60 +++++++++++++++++++------------ components/esmterrain/storage.hpp | 12 ++++--- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 1463d5e6a..4af1c4c0d 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -16,6 +16,12 @@ namespace ESMTerrain { + class LandCache + { + public: + typedef std::map, osg::ref_ptr > Map; + Map mMap; + }; LandObject::LandObject() { @@ -101,7 +107,7 @@ namespace ESMTerrain return false; } - void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) + void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache) { while (col >= ESM::Land::LAND_SIZE-1) { @@ -124,7 +130,7 @@ namespace ESMTerrain row += ESM::Land::LAND_SIZE-1; } - osg::ref_ptr land = getLand(cellX, cellY); + const LandObject* land = getLand(cellX, cellY, cache); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : 0; if (data) { @@ -137,18 +143,18 @@ namespace ESMTerrain normal = osg::Vec3f(0,0,1); } - void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row) + void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row, LandCache& cache) { osg::Vec3f n1,n2,n3,n4; - fixNormal(n1, cellX, cellY, col+1, row); - fixNormal(n2, cellX, cellY, col-1, row); - fixNormal(n3, cellX, cellY, col, row+1); - fixNormal(n4, cellX, cellY, col, row-1); + fixNormal(n1, cellX, cellY, col+1, row, cache); + fixNormal(n2, cellX, cellY, col-1, row, cache); + fixNormal(n3, cellX, cellY, col, row+1, cache); + fixNormal(n4, cellX, cellY, col, row-1, cache); normal = (n1+n2+n3+n4); normal.normalize(); } - void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row) + void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row, LandCache& cache) { if (col == ESM::Land::LAND_SIZE-1) { @@ -161,7 +167,7 @@ namespace ESMTerrain row = 0; } - osg::ref_ptr land = getLand(cellX, cellY); + const LandObject* land = getLand(cellX, cellY, cache); const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : 0; if (data) { @@ -202,13 +208,15 @@ namespace ESMTerrain float vertY = 0; float vertX = 0; + LandCache cache; + float vertY_ = 0; // of current cell corner for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY) { float vertX_ = 0; // of current cell corner for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX) { - osg::ref_ptr land = getLand(cellX, cellY); + const LandObject* land = getLand(cellX, cellY, cache); const ESM::Land::LandData *heightData = 0; const ESM::Land::LandData *normalData = 0; const ESM::Land::LandData *colourData = 0; @@ -270,11 +278,11 @@ namespace ESMTerrain // Normals apparently don't connect seamlessly between cells if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixNormal(normal, cellX, cellY, col, row); + fixNormal(normal, cellX, cellY, col, row, cache); // some corner normals appear to be complete garbage (z < 0) if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) - averageNormal(normal, cellX, cellY, col, row); + averageNormal(normal, cellX, cellY, col, row, cache); assert(normal.z() > 0); @@ -294,7 +302,7 @@ namespace ESMTerrain // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) - fixColour(color, cellX, cellY, col, row); + fixColour(color, cellX, cellY, col, row, cache); color.a() = 1; @@ -314,7 +322,7 @@ namespace ESMTerrain } Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, - int x, int y, osg::ref_ptr land) + int x, int y, LandCache& cache) { // For the first/last row/column, we need to get the texture from the neighbour cell // to get consistent blending at the borders @@ -323,26 +331,22 @@ namespace ESMTerrain { --cellX; x += ESM::Land::LAND_TEXTURE_SIZE; - land = NULL; } while (x >= ESM::Land::LAND_TEXTURE_SIZE) { ++cellX; x -= ESM::Land::LAND_TEXTURE_SIZE; - land = NULL; } while (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not? { ++cellY; y -= ESM::Land::LAND_TEXTURE_SIZE; - land = NULL; } assert(xgetData(ESM::Land::DATA_VTEX) : 0; if (data) @@ -398,12 +402,12 @@ namespace ESMTerrain // So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell. textureIndices.insert(std::make_pair(0,0)); - osg::ref_ptr land = getLand(cellX, cellY); + LandCache cache; for (int y=colStart; ysecond; int blendIndex = (pack ? static_cast(std::floor((layerIndex - 1) / 4.f)) : layerIndex - 1); @@ -547,6 +551,18 @@ namespace ESMTerrain return data->mHeights[y * ESM::Land::LAND_SIZE + x]; } + const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache) + { + LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY)); + if (found != cache.mMap.end()) + return found->second; + else + { + found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first; + return found->second; + } + } + Terrain::LayerInfo Storage::getLayerInfo(const std::string& texture) { OpenThreads::ScopedLock lock(mLayerInfoMutex); diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index 8c4ec230f..0bb24a4ab 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -16,6 +16,8 @@ namespace VFS namespace ESMTerrain { + class LandCache; + /// @brief Wrapper around Land Data with reference counting. The wrapper needs to be held as long as the data is still in use class LandObject : public osg::Object { @@ -105,19 +107,21 @@ namespace ESMTerrain private: const VFS::Manager* mVFS; - void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); - void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row); - void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); + void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); + void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row, LandCache& cache); + void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache); float getVertexHeight (const ESM::Land::LandData* data, int x, int y); + const LandObject* getLand(int cellX, int cellY, LandCache& cache); + // Since plugins can define new texture palettes, we need to know the plugin index too // in order to retrieve the correct texture name. // pair typedef std::pair UniqueTextureId; UniqueTextureId getVtexIndexAt(int cellX, int cellY, - int x, int y, osg::ref_ptr land); + int x, int y, LandCache&); std::string getTextureName (UniqueTextureId id); std::map mLayerInfoMap; From 97ed999097a5fbec29cc9a5d55a91eda4352d16d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 20:02:22 +0100 Subject: [PATCH 85/88] Fix cleanup issue --- apps/openmw/mwworld/cellpreloader.cpp | 14 +++++++++++--- apps/openmw/mwworld/cellpreloader.hpp | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index d3affecc4..649d17334 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -210,9 +210,14 @@ namespace MWWorld mTerrainPreloadItem->waitTillDone(); mTerrainPreloadItem = NULL; } - mTerrainView = NULL; + if (mUpdateCacheItem) + { + mUpdateCacheItem->waitTillDone(); + mUpdateCacheItem = NULL; + } + for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();++it) it->second.mWorkItem->abort(); @@ -320,10 +325,11 @@ namespace MWWorld ++it; } - if (timestamp - mLastResourceCacheUpdate > 1.0) + if (timestamp - mLastResourceCacheUpdate > 1.0 && (!mUpdateCacheItem || mUpdateCacheItem->isDone())) { // the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations - mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp), true); + mUpdateCacheItem = new UpdateCacheItem(mResourceSystem, timestamp); + mWorkQueue->addWorkItem(mUpdateCacheItem, true); mLastResourceCacheUpdate = timestamp; } } @@ -397,6 +403,8 @@ namespace MWWorld return; else { + // TODO: provide some way of giving the preloaded view to the main thread when we enter the cell + // right now, we just use it to make sure the resources are preloaded mTerrainPreloadPositions = positions; mTerrainPreloadItem = new TerrainPreloadItem(mTerrainView, mTerrain, positions); mWorkQueue->addWorkItem(mTerrainPreloadItem); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index bc1227b2d..e188f21ee 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -106,6 +106,7 @@ namespace MWWorld osg::ref_ptr mTerrainView; std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; + osg::ref_ptr mUpdateCacheItem; }; } From 42e989150434ab6c6ad870fca40e55b96a6c9fe4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 20:27:55 +0100 Subject: [PATCH 86/88] Fix issues caused by loading multiple views into the same terrain View --- apps/openmw/mwworld/cellpreloader.cpp | 34 +++++++++++++++++---------- apps/openmw/mwworld/cellpreloader.hpp | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 649d17334..9e9c85320 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -198,9 +198,7 @@ namespace MWWorld , mMaxCacheSize(0) , mPreloadInstances(true) , mLastResourceCacheUpdate(0.0) - , mTerrainView(NULL) { - mTerrainView = mTerrain->createView(); } CellPreloader::~CellPreloader() @@ -210,7 +208,6 @@ namespace MWWorld mTerrainPreloadItem->waitTillDone(); mTerrainPreloadItem = NULL; } - mTerrainView = NULL; if (mUpdateCacheItem) { @@ -372,8 +369,8 @@ namespace MWWorld class TerrainPreloadItem : public SceneUtil::WorkItem { public: - TerrainPreloadItem(Terrain::View* view, Terrain::World* world, const std::vector& preloadPositions) - : mView(view) + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + : mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) { @@ -381,32 +378,43 @@ namespace MWWorld virtual void doWork() { - for (std::vector::const_iterator it = mPreloadPositions.begin(); it != mPreloadPositions.end(); ++it) - mWorld->preload(mView, *it); - - mView->reset(0); + for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i]); + mTerrainViews[i]->reset(0); + } } private: - Terrain::View* mView; + std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; }; void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { - if (!mTerrainView) - return; if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) return; else if (positions == mTerrainPreloadPositions) return; else { + if (mTerrainViews.size() > positions.size()) + { + for (unsigned int i=positions.size(); ipush(mTerrainViews[i]); + mTerrainViews.resize(positions.size()); + } + else if (mTerrainViews.size() < positions.size()) + { + for (unsigned int i=mTerrainViews.size(); icreateView()); + } + // TODO: provide some way of giving the preloaded view to the main thread when we enter the cell // right now, we just use it to make sure the resources are preloaded mTerrainPreloadPositions = positions; - mTerrainPreloadItem = new TerrainPreloadItem(mTerrainView, mTerrain, positions); + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); mWorkQueue->addWorkItem(mTerrainPreloadItem); } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index e188f21ee..df878a55d 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -103,7 +103,7 @@ namespace MWWorld // Cells that are currently being preloaded, or have already finished preloading PreloadMap mPreloadCells; - osg::ref_ptr mTerrainView; + std::vector > mTerrainViews; std::vector mTerrainPreloadPositions; osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; From aed4cbaf29c594db8387671587ef32c57448b561 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 21:03:11 +0100 Subject: [PATCH 87/88] Update settings documentation --- docs/source/reference/modding/settings/camera.rst | 2 +- docs/source/reference/modding/settings/index.rst | 1 + .../source/reference/modding/settings/terrain.rst | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 docs/source/reference/modding/settings/terrain.rst diff --git a/docs/source/reference/modding/settings/camera.rst b/docs/source/reference/modding/settings/camera.rst index c9b69add6..2edc98d02 100644 --- a/docs/source/reference/modding/settings/camera.rst +++ b/docs/source/reference/modding/settings/camera.rst @@ -59,7 +59,7 @@ The constant 8192 is the size of a cell, and 1024 is the threshold distance for Reductions of up to 25% or more can be required to completely eliminate pop-in for wide fields of view and long viewing distances near the edges of the screen, but such situations are unusual and probably not worth the performance penalty introduced by loading geometry obscured by fog in the center of the screen. See RenderingManager::configureFog for the relevant source code. -Enabling the distant land setting is an alternative to increasing exterior cell load distance. Note that the distant land setting does not include rendering of distant static objects, so the resulting visual effect is not the same. +Enabling the distant terrain setting is an alternative to increasing exterior cell load distance. Note that the distant land setting does not include rendering of distant static objects, so the resulting visual effect is not the same. The default value is 6666.0. This setting can be adjusted in game from the ridiculously low value of 2000.0 to a maximum of 6666.0, using the View Distance slider in the Detail tab of the Video panel of the Options menu. diff --git a/docs/source/reference/modding/settings/index.rst b/docs/source/reference/modding/settings/index.rst index 345c29dda..0d0c9323d 100644 --- a/docs/source/reference/modding/settings/index.rst +++ b/docs/source/reference/modding/settings/index.rst @@ -23,6 +23,7 @@ Although this guide attempts to be comprehensive and up to date, you will always input saves sound + terrain video water windows diff --git a/docs/source/reference/modding/settings/terrain.rst b/docs/source/reference/modding/settings/terrain.rst new file mode 100644 index 000000000..8c550b269 --- /dev/null +++ b/docs/source/reference/modding/settings/terrain.rst @@ -0,0 +1,15 @@ +Terrain Settings +############### + +distant terrain +--------------- + +:Type: boolean +:Range: True/False +:Default: False + +Controls whether the engine will use paging and LOD algorithms to render the terrain of the entire world at all times. Otherwise, only the terrain of the loaded cells is displayed. This setting is best used together with the 'viewing distance' setting in the camera section. + +To avoid frame drops as the player moves around, nearby terrain pages are always preloaded in the background, regardless of the preloading settings in the 'Cells' section, but the preloading of terrain behind a door or a travel destination, for example, will still be controlled by cell preloading settings. + +The distant terrain engine is currently considered experimental and may receive updates and/or further configuration options in the future. The glaring omission of non-terrain objects in the distance somewhat limits this setting's usefulness. From e987fe85d03011a0fb68b811fe211a39c9eb2078 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 14 Mar 2017 21:25:53 +0100 Subject: [PATCH 88/88] Add abort flag to TerrainPreloadItem --- apps/openmw/mwworld/cellpreloader.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 9e9c85320..700b91b37 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -7,16 +7,16 @@ #include #include #include +#include #include #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" -#include "../mwworld/inventorystore.hpp" -#include "../mwworld/esmstore.hpp" #include "../mwrender/landmanager.hpp" #include "cellstore.hpp" @@ -205,6 +205,7 @@ namespace MWWorld { if (mTerrainPreloadItem) { + mTerrainPreloadItem->abort(); mTerrainPreloadItem->waitTillDone(); mTerrainPreloadItem = NULL; } @@ -370,7 +371,8 @@ namespace MWWorld { public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) - : mTerrainViews(views) + : mAbort(false) + , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) { @@ -378,14 +380,20 @@ namespace MWWorld virtual void doWork() { - for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i]); mTerrainViews[i]->reset(0); } } + virtual void abort() + { + mAbort = true; + } + private: + volatile bool mAbort; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions;