#include "material.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { class BlendmapTexMat { public: static const osg::ref_ptr& value(const int blendmapScale) { static BlendmapTexMat instance; return instance.get(blendmapScale); } const osg::ref_ptr& get(const int blendmapScale) { const std::lock_guard lock(mMutex); auto texMat = mTexMatMap.find(blendmapScale); if (texMat == mTexMatMap.end()) { osg::Matrixf matrix; float scale = (blendmapScale / (static_cast(blendmapScale) + 1.f)); matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); matrix.preMultScale(osg::Vec3f(scale, scale, 1.f)); matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); // We need to nudge the blendmap to look like vanilla. // This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the // blendmap, apparently. matrix.preMultTranslate(osg::Vec3f(1.0f / blendmapScale / 4.0f, 1.0f / blendmapScale / 4.0f, 0.f)); texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first; } return texMat->second; } private: std::mutex mMutex; std::map> mTexMatMap; }; class LayerTexMat { public: static const osg::ref_ptr& value(const float layerTileSize) { static LayerTexMat instance; return instance.get(layerTileSize); } const osg::ref_ptr& get(const float layerTileSize) { const std::lock_guard lock(mMutex); auto texMat = mTexMatMap.find(layerTileSize); if (texMat == mTexMatMap.end()) { texMat = mTexMatMap .insert(std::make_pair(layerTileSize, new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))) .first; } return texMat->second; } private: std::mutex mMutex; std::map> mTexMatMap; }; class EqualDepth { public: static const osg::ref_ptr& value() { static EqualDepth instance; return instance.mValue; } private: osg::ref_ptr mValue; EqualDepth() : mValue(new SceneUtil::AutoDepth) { mValue->setFunction(osg::Depth::EQUAL); } }; class LequalDepth { public: static const osg::ref_ptr& value() { static LequalDepth instance; return instance.mValue; } private: osg::ref_ptr mValue; LequalDepth() : mValue(new SceneUtil::AutoDepth(osg::Depth::LEQUAL)) { } }; class BlendFuncFirst { public: static const osg::ref_ptr& value() { static BlendFuncFirst instance; return instance.mValue; } private: osg::ref_ptr mValue; BlendFuncFirst() : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ZERO)) { } }; class BlendFunc { public: static const osg::ref_ptr& value() { static BlendFunc instance; return instance.mValue; } private: osg::ref_ptr mValue; BlendFunc() : mValue(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE)) { } }; class TexEnvCombine { public: static const osg::ref_ptr& value() { static TexEnvCombine instance; return instance.mValue; } private: osg::ref_ptr mValue; TexEnvCombine() : mValue(new osg::TexEnvCombine) { mValue->setCombine_RGB(osg::TexEnvCombine::REPLACE); mValue->setSource0_RGB(osg::TexEnvCombine::PREVIOUS); } }; class DiscardAlphaCombine { public: static const osg::ref_ptr& value() { static DiscardAlphaCombine instance; return instance.mValue; } private: osg::ref_ptr mValue; DiscardAlphaCombine() : mValue(new osg::TexEnvCombine) { mValue->setCombine_Alpha(osg::TexEnvCombine::REPLACE); mValue->setSource0_Alpha(osg::TexEnvCombine::CONSTANT); mValue->setConstantColor(osg::Vec4(0.0, 0.0, 0.0, 1.0)); } }; class UniformCollection { public: static const UniformCollection& value() { static UniformCollection instance; return instance; } osg::ref_ptr mDiffuseMap; osg::ref_ptr mBlendMap; osg::ref_ptr mNormalMap; osg::ref_ptr mColorMode; UniformCollection() : mDiffuseMap(new osg::Uniform("diffuseMap", 0)) , mBlendMap(new osg::Uniform("blendMap", 1)) , mNormalMap(new osg::Uniform("normalMap", 2)) , mColorMode(new osg::Uniform("colorMode", 2)) { } }; } namespace Terrain { std::vector> createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector& layers, const std::vector>& blendmaps, int blendmapScale, float layerTileSize) { auto& shaderManager = sceneManager->getShaderManager(); std::vector> passes; unsigned int blendmapIndex = 0; for (std::vector::const_iterator it = layers.begin(); it != layers.end(); ++it) { bool firstLayer = (it == layers.begin()); osg::ref_ptr stateset(new osg::StateSet); if (!blendmaps.empty()) { stateset->setMode(GL_BLEND, osg::StateAttribute::ON); if (sceneManager->getSupportsNormalsRT()) stateset->setAttribute(new osg::Disablei(GL_BLEND, 1)); stateset->setRenderBinDetails(firstLayer ? 0 : 1, "RenderBin"); if (!firstLayer) { stateset->setAttributeAndModes(BlendFunc::value(), osg::StateAttribute::ON); stateset->setAttributeAndModes(EqualDepth::value(), osg::StateAttribute::ON); } else { stateset->setAttributeAndModes(BlendFuncFirst::value(), osg::StateAttribute::ON); stateset->setAttributeAndModes(LequalDepth::value(), osg::StateAttribute::ON); } } if (useShaders) { stateset->setTextureAttributeAndModes(0, it->mDiffuseMap); if (layerTileSize != 1.f) stateset->setTextureAttributeAndModes( 0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); stateset->addUniform(UniformCollection::value().mDiffuseMap); if (!blendmaps.empty()) { osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); stateset->setTextureAttributeAndModes(1, blendmap.get()); stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); stateset->addUniform(UniformCollection::value().mBlendMap); } bool parallax = it->mNormalMap && it->mParallax; bool reconstructNormalZ = false; if (it->mNormalMap) { stateset->setTextureAttributeAndModes(2, it->mNormalMap); stateset->addUniform(UniformCollection::value().mNormalMap); // Special handling for red-green normal maps (e.g. BC5 or R8G8). const osg::Image* image = it->mNormalMap->getImage(0); if (image) { switch (SceneUtil::computeUnsizedPixelFormat(image->getPixelFormat())) { case GL_RG: case GL_RG_INTEGER: { reconstructNormalZ = true; parallax = false; } } } } Shader::ShaderManager::DefineMap defineMap; defineMap["normalMap"] = (it->mNormalMap) ? "1" : "0"; defineMap["blendMap"] = (!blendmaps.empty()) ? "1" : "0"; defineMap["specularMap"] = it->mSpecular ? "1" : "0"; defineMap["parallax"] = parallax ? "1" : "0"; defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0"; defineMap["reconstructNormalZ"] = reconstructNormalZ ? "1" : "0"; Stereo::shaderStereoDefines(defineMap); stateset->setAttributeAndModes(shaderManager.getProgram("terrain", defineMap)); stateset->addUniform(UniformCollection::value().mColorMode); } else { // Add the actual layer texture osg::ref_ptr tex = it->mDiffuseMap; stateset->setTextureAttributeAndModes(0, tex.get()); if (layerTileSize != 1.f) stateset->setTextureAttributeAndModes( 0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(0, DiscardAlphaCombine::value(), osg::StateAttribute::ON); // Multiply by the alpha map if (!blendmaps.empty()) { osg::ref_ptr blendmap = blendmaps.at(blendmapIndex++); stateset->setTextureAttributeAndModes(1, blendmap.get()); // This is to map corner vertices directly to the center of a blendmap texel. stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale)); stateset->setTextureAttributeAndModes(1, TexEnvCombine::value(), osg::StateAttribute::ON); } } passes.push_back(stateset); } return passes; } }