diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 1035558afa..9220f91ad2 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 323f41798e..496a2cc75e 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 c13b21e8fc..0ffd4db54a 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 0000000000..b5934eb128 --- /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 0000000000..d7c2bfa723 --- /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 6113178d3f..a542c8ceec 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;