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());