Multiple Light Terrain
This is a Terrain Material Generator that supports multiple point lights.
[+] .h File
#ifndef __Ogre_TerrainMaterialGeneratorB_H__ #define __Ogre_TerrainMaterialGeneratorB_H__ #include <Terrain\OgreTerrainMaterialGenerator.h> #include <Terrain\OgreTerrainPrerequisites.h> #include <OgreGpuProgramParams.h> namespace Ogre { class PSSMShadowCameraSetup; /** \addtogroup Optional Components * @{ */ /** \addtogroup Terrain * Some details on the terrain component * @{ */ /** A TerrainMaterialGenerator which can cope with normal mapped, specular mapped terrain. Supports 8 point lights and 1 directional light. @note Requires the Cg plugin to render correctly @note shader model 2 */ class TerrainMaterialGeneratorB : public TerrainMaterialGenerator { public: TerrainMaterialGeneratorB(); ~TerrainMaterialGeneratorB(); /** Shader model 2 profile target. */ class SM2Profile : public TerrainMaterialGenerator::Profile { public: SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc); ~SM2Profile(); bool isVertexCompressionSupported() const {return false;} MaterialPtr generate(const Terrain* terrain); MaterialPtr generateForCompositeMap(const Terrain* terrain); uint8 getMaxLayers(const Terrain* terrain) const; void updateParams(const MaterialPtr& mat, const Terrain* terrain); void updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain); void requestOptions(Terrain* terrain); void setShadowFar(float far); void setShadowFadeStart(float fadestart); /** Whether to support normal mapping per layer in the shader (default true). */ bool isLayerNormalMappingEnabled() const { return mLayerNormalMappingEnabled; } /** Whether to support normal mapping per layer in the shader (default true). */ void setLayerNormalMappingEnabled(bool enabled); /** Whether to support parallax mapping per layer in the shader (default true). */ bool isLayerParallaxMappingEnabled() const { return mLayerParallaxMappingEnabled; } /** Whether to support parallax mapping per layer in the shader (default true). */ void setLayerParallaxMappingEnabled(bool enabled); /** Whether to support specular mapping per layer in the shader (default true). */ bool isLayerSpecularMappingEnabled() const { return mLayerSpecularMappingEnabled; } /** Whether to support specular mapping per layer in the shader (default true). */ void setLayerSpecularMappingEnabled(bool enabled); /** Whether to support a global colour map over the terrain in the shader, if it's present (default true). */ bool isGlobalColourMapEnabled() const { return mGlobalColourMapEnabled; } /** Whether to support a global colour map over the terrain in the shader, if it's present (default true). */ void setGlobalColourMapEnabled(bool enabled); /** Whether to support a light map over the terrain in the shader, if it's present (default true). */ bool isLightmapEnabled() const { return mLightmapEnabled; } /** Whether to support a light map over the terrain in the shader, if it's present (default true). */ void setLightmapEnabled(bool enabled); /** Whether to use the composite map to provide a lower LOD technique in the distance (default true). */ bool isCompositeMapEnabled() const { return mCompositeMapEnabled; } /** Whether to use the composite map to provide a lower LOD technique in the distance (default true). */ void setCompositeMapEnabled(bool enabled); /** Whether to support dynamic texture shadows received from other objects, on the terrain (default true). */ bool getReceiveDynamicShadowsEnabled() const { return mReceiveDynamicShadows; } /** Whether to support dynamic texture shadows received from other objects, on the terrain (default true). */ void setReceiveDynamicShadowsEnabled(bool enabled); /** Whether to use PSSM support dynamic texture shadows, and if so the settings to use (default 0). */ void setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings); /** Whether to use PSSM support dynamic texture shadows, and if so the settings to use (default 0). */ PSSMShadowCameraSetup* getReceiveDynamicShadowsPSSM() const { return mPSSM; } /** Whether to use depth shadows (default false). */ void setReceiveDynamicShadowsDepth(bool enabled); /** Whether to use depth shadows (default false). */ bool getReceiveDynamicShadowsDepth() const { return mDepthShadows; } /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). */ void setReceiveDynamicShadowsLowLod(bool enabled); /** Whether to use shadows on low LOD material rendering (when using composite map) (default false). */ bool getReceiveDynamicShadowsLowLod() const { return mLowLodShadows; } int getNumberOfLightsSupported() const; /// Internal bool _isSM3Available() const { return mSM3Available; } protected: enum TechniqueType { HIGH_LOD, LOW_LOD, RENDER_COMPOSITE_MAP }; void addTechnique(const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt); /// Interface definition for helper class to generate shaders class ShaderHelper : public TerrainAlloc { public: ShaderHelper() : mShadowSamplerStartHi(0), mShadowSamplerStartLo(0) {} virtual ~ShaderHelper() {} virtual HighLevelGpuProgramPtr generateVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); virtual HighLevelGpuProgramPtr generateFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); virtual void updateParams(const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap); protected: virtual String getVertexProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); virtual String getFragmentProgramName(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); virtual HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; virtual HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) = 0; virtual void generateVertexProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); virtual void generateFragmentProgramSource(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); virtual void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; virtual void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; virtual void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; virtual void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) = 0; virtual void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; virtual void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) = 0; virtual void defaultVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); virtual void defaultFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog); virtual void updateVpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); virtual void updateFpParams(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params); static String getChannel(uint idx); size_t mShadowSamplerStartHi; size_t mShadowSamplerStartLo; }; /// Utility class to help with generating shaders for Cg / HLSL. class ShaderHelperCg : public ShaderHelper { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream); void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); uint generateVpDynamicShadowsParams(uint texCoordStart, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateVpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateFpDynamicShadowsHelpers(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateFpDynamicShadowsParams(uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); void generateFpDynamicShadows(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream); }; class ShaderHelperHLSL : public ShaderHelperCg { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); }; /// Utility class to help with generating shaders for GLSL. class ShaderHelperGLSL : public ShaderHelper { protected: HighLevelGpuProgramPtr createVertexProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); HighLevelGpuProgramPtr createFragmentProgram(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt); void generateVpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} void generateFpHeader(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} void generateVpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} void generateFpLayer(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) {} void generateVpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} void generateFpFooter(const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) {} }; ShaderHelper* mShaderGen; bool mLayerNormalMappingEnabled; bool mLayerParallaxMappingEnabled; bool mLayerSpecularMappingEnabled; bool mGlobalColourMapEnabled; bool mLightmapEnabled; bool mCompositeMapEnabled; bool mReceiveDynamicShadows; PSSMShadowCameraSetup* mPSSM; bool mDepthShadows; bool mLowLodShadows; bool mSM3Available; float mShadowFar; float mShadowFadeStart; bool isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const; }; }; /** @} */ /** @} */ } #endif
[+] .cpp File
#include "TerrainMaterialGeneratorB.h" #include <Terrain\OgreTerrain.h> #include <OgreMaterialManager.h> #include <OgreTechnique.h> #include <OgrePass.h> #include <OgreTextureUnitState.h> #include <OgreGpuProgramManager.h> #include <OgreHighLevelGpuProgramManager.h> #include <OgreHardwarePixelBuffer.h> #include <OgreShadowCameraSetupPSSM.h> namespace Ogre { //--------------------------------------------------------------------- TerrainMaterialGeneratorB::TerrainMaterialGeneratorB() { // define the layers // We expect terrain textures to have no alpha, so we use the alpha channel // in the albedo texture to store specular reflection // similarly we double-up the normal and height (for parallax) mLayerDecl.samplers.push_back(TerrainLayerSampler("albedo_specular", PF_BYTE_RGBA)); mLayerDecl.samplers.push_back(TerrainLayerSampler("normal_height", PF_BYTE_RGBA)); mLayerDecl.elements.push_back( TerrainLayerSamplerElement(0, TLSS_ALBEDO, 0, 3)); mLayerDecl.elements.push_back( TerrainLayerSamplerElement(0, TLSS_SPECULAR, 3, 1)); mLayerDecl.elements.push_back( TerrainLayerSamplerElement(1, TLSS_NORMAL, 0, 3)); mLayerDecl.elements.push_back( TerrainLayerSamplerElement(1, TLSS_HEIGHT, 3, 1)); mProfiles.push_back(OGRE_NEW SM2Profile(this, "SM2", "Profile for rendering on Shader Model 2 capable cards")); //! @todo - check hardware capabilities & use fallbacks if required (more profiles needed) setActiveProfile("SM2"); } //--------------------------------------------------------------------- TerrainMaterialGeneratorB::~TerrainMaterialGeneratorB() { } //--------------------------------------------------------------------- //--------------------------------------------------------------------- TerrainMaterialGeneratorB::SM2Profile::SM2Profile(TerrainMaterialGenerator* parent, const String& name, const String& desc) : Profile(parent, name, desc) , mShaderGen(0) , mLayerNormalMappingEnabled(true) , mLayerParallaxMappingEnabled(true) , mLayerSpecularMappingEnabled(true) , mGlobalColourMapEnabled(true) , mLightmapEnabled(true) , mCompositeMapEnabled(true) , mReceiveDynamicShadows(true) , mPSSM(0) , mDepthShadows(false) , mLowLodShadows(false) { } //--------------------------------------------------------------------- TerrainMaterialGeneratorB::SM2Profile::~SM2Profile() { OGRE_DELETE mShaderGen; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::requestOptions(Terrain* terrain) { terrain->_setMorphRequired(true); terrain->_setNormalMapRequired(true); terrain->_setLightMapRequired(mLightmapEnabled, true); terrain->_setCompositeMapRequired(mCompositeMapEnabled); } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setLayerNormalMappingEnabled(bool enabled) { if (enabled != mLayerNormalMappingEnabled) { mLayerNormalMappingEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setLayerParallaxMappingEnabled(bool enabled) { if (enabled != mLayerParallaxMappingEnabled) { mLayerParallaxMappingEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setLayerSpecularMappingEnabled(bool enabled) { if (enabled != mLayerSpecularMappingEnabled) { mLayerSpecularMappingEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setGlobalColourMapEnabled(bool enabled) { if (enabled != mGlobalColourMapEnabled) { mGlobalColourMapEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setLightmapEnabled(bool enabled) { if (enabled != mLightmapEnabled) { mLightmapEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setCompositeMapEnabled(bool enabled) { if (enabled != mCompositeMapEnabled) { mCompositeMapEnabled = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsEnabled(bool enabled) { if (enabled != mReceiveDynamicShadows) { mReceiveDynamicShadows = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsPSSM(PSSMShadowCameraSetup* pssmSettings) { if (pssmSettings != mPSSM) { mPSSM = pssmSettings; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsDepth(bool enabled) { if (enabled != mDepthShadows) { mDepthShadows = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::setReceiveDynamicShadowsLowLod(bool enabled) { if (enabled != mLowLodShadows) { mLowLodShadows = enabled; mParent->_markChanged(); } } //--------------------------------------------------------------------- uint8 TerrainMaterialGeneratorB::SM2Profile::getMaxLayers(const Terrain* terrain) const { // count the texture units free uint8 freeTextureUnits = 16; // lightmap --freeTextureUnits; // normalmap --freeTextureUnits; // colourmap if (terrain->getGlobalColourMapEnabled()) --freeTextureUnits; if (isShadowingEnabled(HIGH_LOD, terrain)) { uint numShadowTextures = 1; if (getReceiveDynamicShadowsPSSM()) { numShadowTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); } freeTextureUnits -= numShadowTextures; } // each layer needs 2.25 units (1xdiffusespec, 1xnormalheight, 0.25xblend) return static_cast<uint8>(freeTextureUnits / 2.25f); } //--------------------------------------------------------------------- //added to return 9 light total; 1 directional, 8 point int TerrainMaterialGeneratorB::SM2Profile::getNumberOfLightsSupported() const { return 9; } //--------------------------------------------------------------------- MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generate(const Terrain* terrain) { // re-use old material if exists MaterialPtr mat = terrain->_getMaterial(); if (mat.isNull()) { MaterialManager& matMgr = MaterialManager::getSingleton(); // it's important that the names are deterministic for a given terrain, so // use the terrain pointer as an ID const String& matName = terrain->getMaterialName(); mat = matMgr.getByName(matName); if (mat.isNull()) { mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); } } // clear everything mat->removeAllTechniques(); // Automatically disable normal & parallax mapping if card cannot handle it // We do this rather than having a specific technique for it since it's simpler GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); if (!gmgr.isSyntaxSupported("ps_3_0") && !gmgr.isSyntaxSupported("ps_2_x") && !gmgr.isSyntaxSupported("fp40") && !gmgr.isSyntaxSupported("arbfp1")) { setLayerNormalMappingEnabled(false); setLayerParallaxMappingEnabled(false); } addTechnique(mat, terrain, HIGH_LOD); // LOD if(mCompositeMapEnabled) { addTechnique(mat, terrain, LOW_LOD); Material::LodValueList lodValues; lodValues.push_back(TerrainGlobalOptions::getSingleton().getCompositeMapDistance()); mat->setLodLevels(lodValues); Technique* lowLodTechnique = mat->getTechnique(1); lowLodTechnique->setLodIndex(1); } updateParams(mat, terrain); return mat; } //--------------------------------------------------------------------- MaterialPtr TerrainMaterialGeneratorB::SM2Profile::generateForCompositeMap(const Terrain* terrain) { // re-use old material if exists MaterialPtr mat = terrain->_getCompositeMapMaterial(); if (mat.isNull()) { MaterialManager& matMgr = MaterialManager::getSingleton(); // it's important that the names are deterministic for a given terrain, so // use the terrain pointer as an ID const String& matName = terrain->getMaterialName() + "/comp"; mat = matMgr.getByName(matName); if (mat.isNull()) { mat = matMgr.create(matName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); } } // clear everything mat->removeAllTechniques(); addTechnique(mat, terrain, RENDER_COMPOSITE_MAP); updateParamsForCompositeMap(mat, terrain); return mat; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::addTechnique( const MaterialPtr& mat, const Terrain* terrain, TechniqueType tt) { Technique* tech = mat->createTechnique(); // Only supporting one pass Pass* pass = tech->createPass(); GpuProgramManager& gmgr = GpuProgramManager::getSingleton(); HighLevelGpuProgramManager& hmgr = HighLevelGpuProgramManager::getSingleton(); if (!mShaderGen) { bool check2x = mLayerNormalMappingEnabled || mLayerParallaxMappingEnabled; if (hmgr.isLanguageSupported("cg")) mShaderGen = OGRE_NEW ShaderHelperCg(); else if (hmgr.isLanguageSupported("hlsl") && ((check2x && gmgr.isSyntaxSupported("ps_2_x")) || (!check2x && gmgr.isSyntaxSupported("ps_2_0")))) mShaderGen = OGRE_NEW ShaderHelperHLSL(); else if (hmgr.isLanguageSupported("glsl")) mShaderGen = OGRE_NEW ShaderHelperGLSL(); else { // todo } // check SM3 features mSM3Available = GpuProgramManager::getSingleton().isSyntaxSupported("ps_3_0"); } HighLevelGpuProgramPtr vprog = mShaderGen->generateVertexProgram(this, terrain, tt); HighLevelGpuProgramPtr fprog = mShaderGen->generateFragmentProgram(this, terrain, tt); pass->setVertexProgram(vprog->getName()); pass->setFragmentProgram(fprog->getName()); if (tt == HIGH_LOD || tt == RENDER_COMPOSITE_MAP) { // global normal map TextureUnitState* tu = pass->createTextureUnitState(); tu->setTextureName(terrain->getTerrainNormalMap()->getName()); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); // global colour map if (terrain->getGlobalColourMapEnabled() && isGlobalColourMapEnabled()) { tu = pass->createTextureUnitState(terrain->getGlobalColourMap()->getName()); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); } // light map if (isLightmapEnabled()) { tu = pass->createTextureUnitState(terrain->getLightmap()->getName()); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); } // blend maps uint maxLayers = getMaxLayers(terrain); uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); for (uint i = 0; i < numBlendTextures; ++i) { tu = pass->createTextureUnitState(terrain->getBlendTextureName(i)); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); } // layer textures for (uint i = 0; i < numLayers; ++i) { // diffuse / specular tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 0)); // normal / height tu = pass->createTextureUnitState(terrain->getLayerTextureName(i, 1)); } } else { // LOW_LOD textures // composite map TextureUnitState* tu = pass->createTextureUnitState(); tu->setTextureName(terrain->getCompositeMap()->getName()); tu->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); // That's it! } // Add shadow textures (always at the end) if (isShadowingEnabled(tt, terrain)) { uint numTextures = 1; if (getReceiveDynamicShadowsPSSM()) { numTextures = getReceiveDynamicShadowsPSSM()->getSplitCount(); } for (uint i = 0; i < numTextures; ++i) { TextureUnitState* tu = pass->createTextureUnitState(); tu->setContentType(TextureUnitState::CONTENT_SHADOW); tu->setTextureAddressingMode(TextureUnitState::TAM_BORDER); tu->setTextureBorderColour(ColourValue::White); } } //! @todo caustics still don't work //! @todo maybe add optin to set here if(true) { TextureUnitState* tu = pass->createTextureUnitState(); tu->setAnimatedTextureName("Caustic.bmp",16,1.5); tu->setColourOperationEx(LayerBlendOperationEx::LBX_MODULATE,LayerBlendSource::LBS_CURRENT,LayerBlendSource::LBS_TEXTURE); tu->setTextureScale(0.1,0.1); } } //--------------------------------------------------------------------- bool TerrainMaterialGeneratorB::SM2Profile::isShadowingEnabled(TechniqueType tt, const Terrain* terrain) const { return getReceiveDynamicShadowsEnabled() && tt != RENDER_COMPOSITE_MAP && (tt != LOW_LOD || mLowLodShadows) && terrain->getSceneManager()->isShadowTechniqueTextureBased(); } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::updateParams(const MaterialPtr& mat, const Terrain* terrain) { mShaderGen->updateParams(this, mat, terrain, false); } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::updateParamsForCompositeMap(const MaterialPtr& mat, const Terrain* terrain) { mShaderGen->updateParams(this, mat, terrain, true); } //--------------------------------------------------------------------- //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramPtr ret = createVertexProgram(prof, terrain, tt); StringUtil::StrStreamType sourceStr; generateVertexProgramSource(prof, terrain, tt, sourceStr); ret->setSource(sourceStr.str()); ret->load(); defaultVpParams(prof, terrain, tt, ret); #if OGRE_DEBUG_MODE LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Vertex Program: " << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; #endif return ret; } //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramPtr ret = createFragmentProgram(prof, terrain, tt); StringUtil::StrStreamType sourceStr; generateFragmentProgramSource(prof, terrain, tt, sourceStr); ret->setSource(sourceStr.str()); ret->load(); defaultFpParams(prof, terrain, tt, ret); #if OGRE_DEBUG_MODE LogManager::getSingleton().stream(LML_TRIVIAL) << "*** Terrain Fragment Program: " << ret->getName() << " ***\n" << ret->getSource() << "\n*** ***"; #endif return ret; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateVertexProgramSource( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { generateVpHeader(prof, terrain, tt, outStream); if (tt != LOW_LOD) { uint maxLayers = prof->getMaxLayers(terrain); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); for (uint i = 0; i < numLayers; ++i) generateVpLayer(prof, terrain, tt, i, outStream); } generateVpFooter(prof, terrain, tt, outStream); } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::generateFragmentProgramSource( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { generateFpHeader(prof, terrain, tt, outStream); if (tt != LOW_LOD) { uint maxLayers = prof->getMaxLayers(terrain); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); for (uint i = 0; i < numLayers; ++i) generateFpLayer(prof, terrain, tt, i, outStream); } generateFpFooter(prof, terrain, tt, outStream); } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultVpParams( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) { GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); params->setIgnoreMissingParams(true); params->setNamedAutoConstant("worldMatrix", GpuProgramParameters::ACT_WORLD_MATRIX); params->setNamedAutoConstant("viewProjMatrix", GpuProgramParameters::ACT_VIEWPROJ_MATRIX); params->setNamedAutoConstant("lodMorph", GpuProgramParameters::ACT_CUSTOM, Terrain::LOD_MORPH_CUSTOM_PARAM); params->setNamedAutoConstant("fogParams", GpuProgramParameters::ACT_FOG_PARAMS); if (prof->isShadowingEnabled(tt, terrain)) { uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); } for (uint i = 0; i < numTextures; ++i) { params->setNamedAutoConstant("texViewProjMatrix" + StringConverter::toString(i), GpuProgramParameters::ACT_TEXTURE_VIEWPROJ_MATRIX, i); if (prof->getReceiveDynamicShadowsDepth()) { params->setNamedAutoConstant("depthRange" + StringConverter::toString(i), GpuProgramParameters::ACT_SHADOW_SCENE_DEPTH_RANGE, i); } } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::defaultFpParams( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const HighLevelGpuProgramPtr& prog) { GpuProgramParametersSharedPtr params = prog->getDefaultParameters(); params->setIgnoreMissingParams(true); params->setNamedAutoConstant("ambient", GpuProgramParameters::ACT_AMBIENT_LIGHT_COLOUR); // openMW MULTILIGHT for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) { params->setNamedAutoConstant("lightPosObjSpace"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, i); params->setNamedAutoConstant("lightDiffuseColour"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, i); if (i > 0) params->setNamedAutoConstant("lightAttenuation"+StringConverter::toString(i), GpuProgramParameters::ACT_LIGHT_ATTENUATION, i); if(i == 0) params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, i); } //params->setNamedAutoConstant("lightPosObjSpace", GpuProgramParameters::ACT_LIGHT_POSITION_OBJECT_SPACE, 0); //params->setNamedAutoConstant("lightDiffuseColour", GpuProgramParameters::ACT_LIGHT_DIFFUSE_COLOUR, 0); //params->setNamedAutoConstant("lightSpecularColour", GpuProgramParameters::ACT_LIGHT_SPECULAR_COLOUR, 0); params->setNamedAutoConstant("eyePosObjSpace", GpuProgramParameters::ACT_CAMERA_POSITION_OBJECT_SPACE); params->setNamedAutoConstant("fogColour", GpuProgramParameters::ACT_FOG_COLOUR); if (prof->isShadowingEnabled(tt, terrain)) { uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { PSSMShadowCameraSetup* pssm = prof->getReceiveDynamicShadowsPSSM(); numTextures = pssm->getSplitCount(); Vector4 splitPoints; const PSSMShadowCameraSetup::SplitPointList& splitPointList = pssm->getSplitPoints(); // Populate from split point 1, not 0, since split 0 isn't useful (usually 0) for (uint i = 1; i < numTextures; ++i) { splitPoints[i-1] = splitPointList[i]; } params->setNamedConstant("pssmSplitPoints", splitPoints); } if (prof->getReceiveDynamicShadowsDepth()) { size_t samplerOffset = (tt == HIGH_LOD) ? mShadowSamplerStartHi : mShadowSamplerStartLo; for (uint i = 0; i < numTextures; ++i) { params->setNamedAutoConstant("inverseShadowmapSize" + StringConverter::toString(i), GpuProgramParameters::ACT_INVERSE_TEXTURE_SIZE, i + samplerOffset); } } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateParams( const SM2Profile* prof, const MaterialPtr& mat, const Terrain* terrain, bool compositeMap) { Pass* p = mat->getTechnique(0)->getPass(0); if (compositeMap) { updateVpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getVertexProgramParameters()); updateFpParams(prof, terrain, RENDER_COMPOSITE_MAP, p->getFragmentProgramParameters()); } else { // high lod updateVpParams(prof, terrain, HIGH_LOD, p->getVertexProgramParameters()); updateFpParams(prof, terrain, HIGH_LOD, p->getFragmentProgramParameters()); if(prof->isCompositeMapEnabled()) { // low lod p = mat->getTechnique(1)->getPass(0); updateVpParams(prof, terrain, LOW_LOD, p->getVertexProgramParameters()); updateFpParams(prof, terrain, LOW_LOD, p->getFragmentProgramParameters()); } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateVpParams( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) { params->setIgnoreMissingParams(true); uint maxLayers = prof->getMaxLayers(terrain); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); uint numUVMul = numLayers / 4; if (numLayers % 4) ++numUVMul; for (uint i = 0; i < numUVMul; ++i) { Vector4 uvMul( terrain->getLayerUVMultiplier(i * 4), terrain->getLayerUVMultiplier(i * 4 + 1), terrain->getLayerUVMultiplier(i * 4 + 2), terrain->getLayerUVMultiplier(i * 4 + 3) ); params->setNamedConstant("uvMul" + StringConverter::toString(i), uvMul); } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::updateFpParams( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, const GpuProgramParametersSharedPtr& params) { params->setIgnoreMissingParams(true); // TODO - parameterise this? Vector4 scaleBiasSpecular(0.03, -0.04, 32, 0.5); params->setNamedConstant("scaleBiasSpecular", scaleBiasSpecular); } //--------------------------------------------------------------------- String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getChannel(uint idx) { uint rem = idx % 4; switch(rem) { case 0: default: return "r"; case 1: return "g"; case 2: return "b"; case 3: return "a"; }; } //--------------------------------------------------------------------- String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getVertexProgramName( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { String progName = terrain->getMaterialName() + "/sm2/vp"; switch(tt) { case HIGH_LOD: progName += "/hlod"; break; case LOW_LOD: progName += "/llod"; break; case RENDER_COMPOSITE_MAP: progName += "/comp"; break; } return progName; } //--------------------------------------------------------------------- String TerrainMaterialGeneratorB::SM2Profile::ShaderHelper::getFragmentProgramName( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { String progName = terrain->getMaterialName() + "/sm2/fp"; switch(tt) { case HIGH_LOD: progName += "/hlod"; break; case LOW_LOD: progName += "/llod"; break; case RENDER_COMPOSITE_MAP: progName += "/comp"; break; } return progName; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createVertexProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getVertexProgramName(prof, terrain, tt); HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_VERTEX_PROGRAM); } else { ret->unload(); } ret->setParameter("profiles", "vs_3_0 vs_2_0 arbvp1"); ret->setParameter("entry_point", "main_vp"); return ret; } //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::createFragmentProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getFragmentProgramName(prof, terrain, tt); HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "cg", GPT_FRAGMENT_PROGRAM); } else { ret->unload(); } if(prof->isLayerNormalMappingEnabled() || prof->isLayerParallaxMappingEnabled()) ret->setParameter("profiles", "ps_3_0 ps_2_x fp40 arbfp1"); else ret->setParameter("profiles", "ps_3_0 ps_2_0 fp30 arbfp1"); ret->setParameter("entry_point", "main_fp"); return ret; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpHeader( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { outStream << "void main_vp(\n" "float4 pos : POSITION,\n" "float2 uv : TEXCOORD0,\n"; if (tt != RENDER_COMPOSITE_MAP) outStream << "float2 delta : TEXCOORD1,\n"; // lodDelta, lodThreshold outStream << "uniform float4x4 worldMatrix,\n" "uniform float4x4 viewProjMatrix,\n" "uniform float2 lodMorph,\n"; // morph amount, morph LOD target // uv multipliers uint maxLayers = prof->getMaxLayers(terrain); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); uint numUVMultipliers = (numLayers / 4); if (numLayers % 4) ++numUVMultipliers; for (uint i = 0; i < numUVMultipliers; ++i) outStream << "uniform float4 uvMul" << i << ", \n"; outStream << "out float4 oPos : POSITION,\n" "out float4 oPosObj : TEXCOORD0 \n"; uint texCoordSet = 1; outStream << ", out float4 oUVMisc : TEXCOORD" << texCoordSet++ <<" // xy = uv, z = camDepth\n"; // layer UV's premultiplied, packed as xy/zw uint numUVSets = numLayers / 2; if (numLayers % 2) ++numUVSets; if (tt != LOW_LOD) { for (uint i = 0; i < numUVSets; ++i) { outStream << ", out float4 oUV" << i << " : TEXCOORD" << texCoordSet++ << "\n"; } } if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) { outStream << ", out float2 lodInfo : TEXCOORD" << texCoordSet++ << "\n"; } bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; if (fog) { outStream << ", uniform float4 fogParams\n" ", out float fogVal : COLOR\n"; } if (prof->isShadowingEnabled(tt, terrain)) { texCoordSet = generateVpDynamicShadowsParams(texCoordSet, prof, terrain, tt, outStream); } // check we haven't exceeded texture coordinates if (texCoordSet > 8) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Requested options require too many texture coordinate sets! Try reducing the number of layers.", __FUNCTION__); } outStream << ")\n" "{\n" " float4 worldPos = mul(worldMatrix, pos);\n" " oPosObj = pos;\n"; if (tt != RENDER_COMPOSITE_MAP) { // determine whether to apply the LOD morph to this vertex // we store the deltas against all vertices so we only want to apply // the morph to the ones which would disappear. The target LOD which is // being morphed to is stored in lodMorph.y, and the LOD at which // the vertex should be morphed is stored in uv.w. If we subtract // the former from the latter, and arrange to only morph if the // result is negative (it will only be -1 in fact, since after that // the vertex will never be indexed), we will achieve our aim. // sign(vertexLOD - targetLOD) == -1 is to morph outStream << " float toMorph = -min(0, sign(delta.y - lodMorph.y));\n"; // this will either be 1 (morph) or 0 (don't morph) if (prof->getParent()->getDebugLevel()) { // x == LOD level (-1 since value is target level, we want to display actual) outStream << "lodInfo.x = (lodMorph.y - 1) / " << terrain->getNumLodLevels() << ";\n"; // y == LOD morph outStream << "lodInfo.y = toMorph * lodMorph.x;\n"; } // morph switch (terrain->getAlignment()) { case Terrain::ALIGN_X_Y: outStream << " worldPos.z += delta.x * toMorph * lodMorph.x;\n"; break; case Terrain::ALIGN_X_Z: outStream << " worldPos.y += delta.x * toMorph * lodMorph.x;\n"; break; case Terrain::ALIGN_Y_Z: outStream << " worldPos.x += delta.x * toMorph * lodMorph.x;\n"; break; }; } // generate UVs if (tt != LOW_LOD) { for (uint i = 0; i < numUVSets; ++i) { uint layer = i * 2; uint uvMulIdx = layer / 4; outStream << " oUV" << i << ".xy = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer) << ";\n"; outStream << " oUV" << i << ".zw = " << " uv.xy * uvMul" << uvMulIdx << "." << getChannel(layer+1) << ";\n"; } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpHeader( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { // Main header outStream << // helpers "float4 expand(float4 v)\n" "{ \n" " return v * 2 - 1;\n" "}\n\n\n"; if (prof->isShadowingEnabled(tt, terrain)) generateFpDynamicShadowsHelpers(prof, terrain, tt, outStream); outStream << "float4 main_fp(\n" "float4 position : TEXCOORD0,\n"; uint texCoordSet = 1; outStream << "float4 uvMisc : TEXCOORD" << texCoordSet++ << ",\n"; // UV's premultiplied, packed as xy/zw uint maxLayers = prof->getMaxLayers(terrain); uint numBlendTextures = std::min(terrain->getBlendTextureCount(maxLayers), terrain->getBlendTextureCount()); uint numLayers = std::min(maxLayers, static_cast<uint>(terrain->getLayerCount())); uint numUVSets = numLayers / 2; if (numLayers % 2) ++numUVSets; if (tt != LOW_LOD) { for (uint i = 0; i < numUVSets; ++i) { outStream << "float4 layerUV" << i << " : TEXCOORD" << texCoordSet++ << ", \n"; } } if (prof->getParent()->getDebugLevel() && tt != RENDER_COMPOSITE_MAP) { outStream << "float2 lodInfo : TEXCOORD" << texCoordSet++ << ", \n"; } bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; if (fog) { outStream << "uniform float3 fogColour, \n" "float fogVal : COLOR,\n"; } uint currentSamplerIdx = 0; outStream << // Only 1 light supported in this version // deferred shading profile / generator later, ok? :) "uniform float3 ambient,\n"; //MULTILIGHT for loop num lights here //"uniform float4 lightPosObjSpace,\n" //"uniform float3 lightDiffuseColour,\n" //"uniform float3 lightSpecularColour,\n" //openmw adds attenuation for num light - 9 lights here; probly one directional light all more pont lights //en here //openmw multi for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) { outStream << "uniform float4 lightPosObjSpace"<<i<<",\n" "uniform float3 lightDiffuseColour"<<i<<",\n" //"uniform float3 lightSpecularColour"<<i<<",\n" ; if(i == 0) outStream << "uniform float3 lightSpecularColour,\n"; if (i > 0) outStream << "uniform float4 lightAttenuation"<<i<<",\n"; } outStream << "uniform float3 eyePosObjSpace,\n" // pack scale, bias and specular "uniform float4 scaleBiasSpecular,\n"; if (tt == LOW_LOD) { // single composite map covers all the others below outStream << "uniform sampler2D compositeMap : register(s" << currentSamplerIdx++ << ")\n"; } else { outStream << "uniform sampler2D globalNormal : register(s" << currentSamplerIdx++ << ")\n"; if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) { outStream << ", uniform sampler2D globalColourMap : register(s" << currentSamplerIdx++ << ")\n"; } if (prof->isLightmapEnabled()) { outStream << ", uniform sampler2D lightMap : register(s" << currentSamplerIdx++ << ")\n"; } // Blend textures - sampler definitions for (uint i = 0; i < numBlendTextures; ++i) { outStream << ", uniform sampler2D blendTex" << i << " : register(s" << currentSamplerIdx++ << ")\n"; } // Layer textures - sampler definitions & UV multipliers for (uint i = 0; i < numLayers; ++i) { outStream << ", uniform sampler2D difftex" << i << " : register(s" << currentSamplerIdx++ << ")\n"; outStream << ", uniform sampler2D normtex" << i << " : register(s" << currentSamplerIdx++ << ")\n"; } } if (prof->isShadowingEnabled(tt, terrain)) { generateFpDynamicShadowsParams(&texCoordSet, ¤tSamplerIdx, prof, terrain, tt, outStream); } // check we haven't exceeded samplers if (currentSamplerIdx > 16) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Requested options require too many texture samplers! Try reducing the number of layers.", __FUNCTION__); } outStream << ") : COLOR\n" "{\n" " float4 outputCol;\n" " float shadow = 1.0;\n" " float2 uv = uvMisc.xy;\n" // base colour " outputCol = float4(0,0,0,1);\n"; if (tt != LOW_LOD) { outStream << // global normal " float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n"; } // openMW MULTILIAGHT for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " float3 lightDir"<<i<<" = \n" " lightPosObjSpace"<<i<<".xyz - (position.xyz * lightPosObjSpace"<<i<<".w);\n"; //outStream << // " float3 lightDir = \n" // " lightPosObjSpace.xyz - (position.xyz * lightPosObjSpace.w);\n"; outStream << " float3 eyeDir = eyePosObjSpace - position.xyz;\n" // set up accumulation areas " float3 diffuse = float3(0,0,0);\n" " float specular = 0;\n"; if (tt == LOW_LOD) { // we just do a single calculation from composite map outStream << " float4 composite = tex2D(compositeMap, uv);\n" " diffuse = composite.rgb;\n"; // TODO - specular; we'll need normals for this! } else { // set up the blend values for (uint i = 0; i < numBlendTextures; ++i) { outStream << " float4 blendTexVal" << i << " = tex2D(blendTex" << i << ", uv);\n"; } if (prof->isLayerNormalMappingEnabled()) { // derive the tangent space basis // we do this in the pixel shader because we don't have per-vertex normals // because of the LOD, we use a normal map // tangent is always +x or -z in object space depending on alignment switch(terrain->getAlignment()) { case Terrain::ALIGN_X_Y: case Terrain::ALIGN_X_Z: outStream << " float3 tangent = float3(1, 0, 0);\n"; break; case Terrain::ALIGN_Y_Z: outStream << " float3 tangent = float3(0, 0, -1);\n"; break; }; outStream << " float3 binormal = normalize(cross(tangent, normal));\n"; // note, now we need to re-cross to derive tangent again because it wasn't orthonormal outStream << " tangent = normalize(cross(normal, binormal));\n"; // derive final matrix outStream << " float3x3 TBN = float3x3(tangent, binormal, normal);\n"; // set up lighting result placeholders for interpolation outStream << " float4 litRes, litResLayer;\n"; for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " float4 litResLayer"<<i<<";\n"; //outStream << " float3 TSlightDir;\n"; //outStream << " float3 TShalfAngle;\n"; for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " float3 TShalfAngle"<<i<<";\n"; outStream << " float3 TSeyeDir, TSnormal;\n"; if (prof->isLayerParallaxMappingEnabled()) outStream << " float displacement;\n"; // move //outStream << " TSlightDir = normalize(mul(TBN, lightDir));\n"; outStream << " TSeyeDir = normalize(mul(TBN, eyeDir));\n"; //init litRes<i> vars if (prof->getNumberOfLightsSupported() > 1) outStream << "float d; \n" "float attn; \n"; outStream << " eyeDir = normalize(eyeDir); \n"; // simple per-pixel lighting with no normal mapping for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) { outStream << " float4 litRes"<<i<<" = lit(dot(normalize(lightDir"<<i<<"), normalize(normal)), dot(normalize(lightDir"<<i<<"+eyeDir),normalize(normal)), scaleBiasSpecular.z);\n"; outStream << " float3 TSlightDir"<<i<<" = normalize(mul(TBN, lightDir"<<i<<"));\n"; if (i > 0) outStream << // pre-multiply light color with attenuation factor "d = length( lightDir"<<i<<" ); \n" "attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n" "lightDiffuseColour"<<i<<" *= attn; \n"; } //init litRes<i> vars END } else { // openMW MULTILIGHT if (prof->getNumberOfLightsSupported() > 1) outStream << "float d; \n" "float attn; \n"; outStream << " eyeDir = normalize(eyeDir); \n"; // simple per-pixel lighting with no normal mapping for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) { outStream << //" float4 litRes"<<i<<" = lit(dot(normalize(lightDir"<<i<<"), normalize(normal)), 0, scaleBiasSpecular.z);\n"; " float4 litRes"<<i<<" = lit(dot(normalize(lightDir"<<i<<"), normalize(normal)), dot(normalize(lightDir"<<i<<"+eyeDir),normalize(normal)), scaleBiasSpecular.z);\n"; if (i > 0) outStream << // pre-multiply light color with attenuation factor "d = length( lightDir"<<i<<" ); \n" "attn = ( 1.0 / (( lightAttenuation"<<i<<".y ) + ( lightAttenuation"<<i<<".z * d ) + ( lightAttenuation"<<i<<".w * d * d ))); \n" "lightDiffuseColour"<<i<<" *= attn; \n"; } // simple per-pixel lighting with no normal mapping //outStream << " lightDir = normalize(lightDir);\n"; //outStream << " eyeDir = normalize(eyeDir);\n"; //outStream << " float3 halfAngle = normalize(lightDir + eyeDir);\n"; //outStream << " float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n"; } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpLayer( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) { // nothing to do } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpLayer( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, uint layer, StringUtil::StrStreamType& outStream) { uint uvIdx = layer / 2; String uvChannels = layer % 2 ? ".zw" : ".xy"; uint blendIdx = (layer-1) / 4; String blendChannel = getChannel(layer-1); String blendWeightStr = String("blendTexVal") + StringConverter::toString(blendIdx) + "." + blendChannel; // generate early-out conditional /* Disable - causing some issues even when trying to force the use of texldd if (layer && prof->_isSM3Available()) outStream << " if (" << blendWeightStr << " > 0.0003)\n { \n"; */ // generate UV outStream << " float2 uv" << layer << " = layerUV" << uvIdx << uvChannels << ";\n"; // calculate lighting here if normal mapping if (prof->isLayerNormalMappingEnabled()) { if (prof->isLayerParallaxMappingEnabled() && tt != RENDER_COMPOSITE_MAP) { // modify UV - note we have to sample an extra time outStream << " displacement = tex2D(normtex" << layer << ", uv" << layer << ").a\n" " * scaleBiasSpecular.x + scaleBiasSpecular.y;\n"; outStream << " uv" << layer << " += TSeyeDir.xy * displacement;\n"; } // access TS normal map outStream << " TSnormal = expand(tex2D(normtex" << layer << ", uv" << layer << ")).rgb;\n"; //outStream << " TShalfAngle = normalize(TSlightDir + TSeyeDir);\n"; for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " TShalfAngle"<<i<<"= normalize(TSlightDir"<<i<<" + TSeyeDir);\n"; for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " litResLayer"<<i<<" = lit(dot(TSlightDir"<<i<<", TSnormal), dot(TShalfAngle"<<i<<", TSnormal), scaleBiasSpecular.z);\n"; if (!layer) { for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " litRes"<<i<<" = litResLayer"<<i<<";\n"; } else { for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) outStream << " litRes"<<i<<" = lerp(litRes"<<i<<", litResLayer"<<i<<", " << blendWeightStr << ");\n"; } } // sample diffuse texture outStream << " float4 diffuseSpecTex" << layer << " = tex2D(difftex" << layer << ", uv" << layer << ");\n"; // apply to common if (!layer) { outStream << " diffuse = diffuseSpecTex0.rgb;\n"; if (prof->isLayerSpecularMappingEnabled()) outStream << " specular = diffuseSpecTex0.a;\n"; } else { outStream << " diffuse = lerp(diffuse, diffuseSpecTex" << layer << ".rgb, " << blendWeightStr << ");\n"; if (prof->isLayerSpecularMappingEnabled()) outStream << " specular = lerp(specular, diffuseSpecTex" << layer << ".a, " << blendWeightStr << ");\n"; } // End early-out /* Disable - causing some issues even when trying to force the use of texldd if (layer && prof->_isSM3Available()) outStream << " } // early-out blend value\n"; */ } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpFooter( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { outStream << " oPos = mul(viewProjMatrix, worldPos);\n" " oUVMisc.xy = uv.xy;\n"; bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; if (fog) { if (terrain->getSceneManager()->getFogMode() == FOG_LINEAR) { outStream << " fogVal = saturate((oPos.z - fogParams.y) * fogParams.w);\n"; } else { outStream << " fogVal = saturate(1 / (exp(oPos.z * fogParams.x)));\n"; } } if (prof->isShadowingEnabled(tt, terrain)) generateVpDynamicShadows(prof, terrain, tt, outStream); outStream << "}\n"; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpFooter( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { if (tt == LOW_LOD) { if (prof->isShadowingEnabled(tt, terrain)) { generateFpDynamicShadows(prof, terrain, tt, outStream); outStream << " outputCol.rgb = diffuse * rtshadow;\n"; } else { outStream << " outputCol.rgb = diffuse;\n"; } } else { if (terrain->getGlobalColourMapEnabled() && prof->isGlobalColourMapEnabled()) { // sample colour map and apply to diffuse outStream << " diffuse *= tex2D(globalColourMap, uv).rgb;\n"; } if (prof->isLightmapEnabled()) { // sample lightmap outStream << " shadow = tex2D(lightMap, uv).r;\n"; } if (prof->isShadowingEnabled(tt, terrain)) { generateFpDynamicShadows(prof, terrain, tt, outStream); } // diffuse lighting //outStream << " outputCol.rgb += ambient * diffuse + litRes.y * lightDiffuseColour * diffuse * shadow;\n"; //openMW MULTILIGHT outStream << " outputCol.rgb += ambient * diffuse; \n"; // diffuse lighting for (int i=0; i<prof->getNumberOfLightsSupported(); ++i) { // shadows only for first light (directional) if (i==0) outStream << " outputCol.rgb += litRes"<<i<<".y * lightDiffuseColour"<<i<<" * diffuse * shadow;\n"; else outStream << " outputCol.rgb += litRes"<<i<<".y * lightDiffuseColour"<<i<<" * diffuse;\n"; } // specular default if (!prof->isLayerSpecularMappingEnabled()) outStream << " specular = 1.0;\n"; if (tt == RENDER_COMPOSITE_MAP) { // Lighting embedded in alpha outStream << " outputCol.a = shadow;\n"; } else { // Apply specular outStream << " outputCol.rgb += litRes0.z * lightSpecularColour * specular * shadow;\n"; if (prof->getParent()->getDebugLevel()) { outStream << " outputCol.rg += lodInfo.xy;\n"; } } } bool fog = terrain->getSceneManager()->getFogMode() != FOG_NONE && tt != RENDER_COMPOSITE_MAP; if (fog) { outStream << " outputCol.rgb = lerp(outputCol.rgb, fogColour, fogVal);\n"; } // Final return outStream << " return outputCol;\n" << "}\n"; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsHelpers( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { // TODO make filtering configurable outStream << "// Simple PCF \n" "// Number of samples in one dimension (square for total samples) \n" "#define NUM_SHADOW_SAMPLES_1D 2.0 \n" "#define SHADOW_FILTER_SCALE 1 \n" "#define SHADOW_SAMPLES NUM_SHADOW_SAMPLES_1D*NUM_SHADOW_SAMPLES_1D \n" "float4 offsetSample(float4 uv, float2 offset, float invMapSize) \n" "{ \n" " return float4(uv.xy + offset * invMapSize * uv.w, uv.z, uv.w); \n" "} \n"; if (prof->getReceiveDynamicShadowsDepth()) { outStream << "float calcDepthShadow(sampler2D shadowMap, float4 uv, float invShadowMapSize) \n" "{ \n" " // 4-sample PCF \n" " float shadow = 0.0; \n" " float offset = (NUM_SHADOW_SAMPLES_1D/2 - 0.5) * SHADOW_FILTER_SCALE; \n" " for (float y = -offset; y <= offset; y += SHADOW_FILTER_SCALE) \n" " for (float x = -offset; x <= offset; x += SHADOW_FILTER_SCALE) \n" " { \n" " float4 newUV = offsetSample(uv, float2(x, y), invShadowMapSize);\n" " // manually project and assign derivatives \n" " // to avoid gradient issues inside loops \n" " newUV = newUV / newUV.w; \n" " float depth = tex2D(shadowMap, newUV.xy, 1, 1).x; \n" " if (depth >= 1 || depth >= uv.z)\n" " shadow += 1.0;\n" " } \n" " shadow /= SHADOW_SAMPLES; \n" " return shadow; \n" "} \n"; } else { outStream << "float calcSimpleShadow(sampler2D shadowMap, float4 shadowMapPos) \n" "{ \n" " return tex2Dproj(shadowMap, shadowMapPos).x; \n" "} \n"; } if (prof->getReceiveDynamicShadowsPSSM()) { uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); if (prof->getReceiveDynamicShadowsDepth()) { outStream << "float calcPSSMDepthShadow("; } else { outStream << "float calcPSSMSimpleShadow("; } outStream << "\n "; for (uint i = 0; i < numTextures; ++i) outStream << "sampler2D shadowMap" << i << ", "; outStream << "\n "; for (uint i = 0; i < numTextures; ++i) outStream << "float4 lsPos" << i << ", "; if (prof->getReceiveDynamicShadowsDepth()) { outStream << "\n "; for (uint i = 0; i < numTextures; ++i) outStream << "float invShadowmapSize" << i << ", "; } outStream << "\n" " float4 pssmSplitPoints, float camDepth) \n" "{ \n" " float shadow; \n" " // calculate shadow \n"; for (uint i = 0; i < numTextures; ++i) { if (!i) outStream << " if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; else if (i < numTextures - 1) outStream << " else if (camDepth <= pssmSplitPoints." << ShaderHelper::getChannel(i) << ") \n"; else outStream << " else \n"; outStream << " { \n"; if (prof->getReceiveDynamicShadowsDepth()) { outStream << " shadow = calcDepthShadow(shadowMap" << i << ", lsPos" << i << ", invShadowmapSize" << i << "); \n"; } else { outStream << " shadow = calcSimpleShadow(shadowMap" << i << ", lsPos" << i << "); \n"; } outStream << " } \n"; } outStream << " return shadow; \n" "} \n\n\n"; } } //--------------------------------------------------------------------- uint TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadowsParams( uint texCoord, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { // out semantics & params uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); } for (uint i = 0; i < numTextures; ++i) { outStream << ", out float4 oLightSpacePos" << i << " : TEXCOORD" << texCoord++ << " \n" << ", uniform float4x4 texViewProjMatrix" << i << " \n"; if (prof->getReceiveDynamicShadowsDepth()) { outStream << ", uniform float4 depthRange" << i << " // x = min, y = max, z = range, w = 1/range \n"; } } return texCoord; } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateVpDynamicShadows( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); } // Calculate the position of vertex in light space for (uint i = 0; i < numTextures; ++i) { outStream << " oLightSpacePos" << i << " = mul(texViewProjMatrix" << i << ", worldPos); \n"; if (prof->getReceiveDynamicShadowsDepth()) { // make linear outStream << "oLightSpacePos" << i << ".z = (oLightSpacePos" << i << ".z - depthRange" << i << ".x) * depthRange" << i << ".w;\n"; } } if (prof->getReceiveDynamicShadowsPSSM()) { outStream << " // pass cam depth\n" " oUVMisc.z = oPos.z;\n"; } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadowsParams( uint* texCoord, uint* sampler, const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { if (tt == HIGH_LOD) mShadowSamplerStartHi = *sampler; else if (tt == LOW_LOD) mShadowSamplerStartLo = *sampler; // in semantics & params uint numTextures = 1; if (prof->getReceiveDynamicShadowsPSSM()) { numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); outStream << ", uniform float4 pssmSplitPoints \n"; } for (uint i = 0; i < numTextures; ++i) { outStream << ", float4 lightSpacePos" << i << " : TEXCOORD" << *texCoord << " \n" << ", uniform sampler2D shadowMap" << i << " : register(s" << *sampler << ") \n"; *sampler = *sampler + 1; *texCoord = *texCoord + 1; if (prof->getReceiveDynamicShadowsDepth()) { outStream << ", uniform float inverseShadowmapSize" << i << " \n"; } } } //--------------------------------------------------------------------- void TerrainMaterialGeneratorB::SM2Profile::ShaderHelperCg::generateFpDynamicShadows( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt, StringUtil::StrStreamType& outStream) { if (prof->getReceiveDynamicShadowsPSSM()) { uint numTextures = prof->getReceiveDynamicShadowsPSSM()->getSplitCount(); outStream << " float camDepth = uvMisc.z;\n"; if (prof->getReceiveDynamicShadowsDepth()) { outStream << " float rtshadow = calcPSSMDepthShadow("; } else { outStream << " float rtshadow = calcPSSMSimpleShadow("; } for (uint i = 0; i < numTextures; ++i) outStream << "shadowMap" << i << ", "; outStream << "\n "; for (uint i = 0; i < numTextures; ++i) outStream << "lightSpacePos" << i << ", "; if (prof->getReceiveDynamicShadowsDepth()) { outStream << "\n "; for (uint i = 0; i < numTextures; ++i) outStream << "inverseShadowmapSize" << i << ", "; } outStream << "\n" << " pssmSplitPoints, camDepth);\n"; } else { if (prof->getReceiveDynamicShadowsDepth()) { outStream << " float rtshadow = calcDepthShadow(shadowMap0, lightSpacePos0, inverseShadowmapSize0);"; } else { outStream << " float rtshadow = calcSimpleShadow(shadowMap0, lightSpacePos0);"; } } outStream << " shadow = min(shadow, rtshadow);\n"; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createVertexProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getVertexProgramName(prof, terrain, tt); HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "hlsl", GPT_VERTEX_PROGRAM); } else { ret->unload(); } if (prof->_isSM3Available()) ret->setParameter("target", "vs_3_0"); else ret->setParameter("target", "vs_2_0"); ret->setParameter("entry_point", "main_vp"); return ret; } //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperHLSL::createFragmentProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getFragmentProgramName(prof, terrain, tt); HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "hlsl", GPT_FRAGMENT_PROGRAM); } else { ret->unload(); } if (prof->_isSM3Available()) ret->setParameter("target", "ps_3_0"); else ret->setParameter("target", "ps_2_x"); ret->setParameter("entry_point", "main_fp"); return ret; } //--------------------------------------------------------------------- //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createVertexProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getVertexProgramName(prof, terrain, tt); switch(tt) { case HIGH_LOD: progName += "/hlod"; break; case LOW_LOD: progName += "/llod"; break; case RENDER_COMPOSITE_MAP: progName += "/comp"; break; } HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "glsl", GPT_VERTEX_PROGRAM); } else { ret->unload(); } return ret; } //--------------------------------------------------------------------- HighLevelGpuProgramPtr TerrainMaterialGeneratorB::SM2Profile::ShaderHelperGLSL::createFragmentProgram( const SM2Profile* prof, const Terrain* terrain, TechniqueType tt) { HighLevelGpuProgramManager& mgr = HighLevelGpuProgramManager::getSingleton(); String progName = getFragmentProgramName(prof, terrain, tt); HighLevelGpuProgramPtr ret = mgr.getByName(progName); if (ret.isNull()) { ret = mgr.createProgram(progName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, "glsl", GPT_FRAGMENT_PROGRAM); } else { ret->unload(); } return ret; } }
Use
To enable it do this when you make your terrain:
Ogre::TerrainMaterialGeneratorPtr matGen; Ogre::TerrainMaterialGeneratorB* matGenP = new Ogre::TerrainMaterialGeneratorB(); matGen.bind(matGenP); m_pTerrainGlobals->setDefaultMaterialGenerator(matGen);