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);
<HR>
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.
1. Definitions
- "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the Work in its entirety in unmodified form, along with a number of other contributions, constituting separate and independent works in themselves, are assembled into a collective whole. A work that constitutes a Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this License.
- "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works, such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound recording, art reproduction, abridgment, condensation, or any other form in which the Work may be recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered a Derivative Work for the purpose of this License.
- "Licensor" means the individual or entity that offers the Work under the terms of this License.
- "Original Author" means the individual or entity who created the Work.
- "Work" means the copyrightable work of authorship offered under the terms of this License.
- "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation.
- "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike.
2. Fair Use Rights
Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.
3. License Grant
Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below:
- to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the Work as incorporated in the Collective Works;
- to create and reproduce Derivative Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission the Work including as incorporated in Collective Works;
- to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means of a digital audio transmission Derivative Works.
- For the avoidance of doubt, where the work is a musical composition:
- Performance Royalties Under Blanket Licenses. Licensor waives the exclusive right to collect, whether individually or via a performance rights society (e.g. ASCAP, BMI, SESAC), royalties for the public performance or public digital performance (e.g. webcast) of the Work.
- Mechanical Rights and Statutory Royalties. Licensor waives the exclusive right to collect, whether individually or via a music rights society or designated agent (e.g. Harry Fox Agency), royalties for any phonorecord You create from the Work ("cover version") and distribute, subject to the compulsory license created by 17 USC Section 115 of the US Copyright Act (or the equivalent in other jurisdictions).
- Webcasting Rights and Statutory Royalties. For the avoidance of doubt, where the Work is a sound recording, Licensor waives the exclusive right to collect, whether individually or via a performance-rights society (e.g. SoundExchange), royalties for the public digital performance (e.g. webcast) of the Work, subject to the compulsory license created by 17 USC Section 114 of the US Copyright Act (or the equivalent in other jurisdictions).
The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by Licensor are hereby reserved.
4. Restrictions
The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
- You may distribute, publicly display, publicly perform, or publicly digitally perform the Work only under the terms of this License, and You must include a copy of, or the Uniform Resource Identifier for, this License with every copy or phonorecord of the Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Work that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Work itself to be made subject to the terms of this License. If You create a Collective Work, upon notice from any Licensor You must, to the extent practicable, remove from the Collective Work any credit as required by clause 4(c), as requested. If You create a Derivative Work, upon notice from any Licensor You must, to the extent practicable, remove from the Derivative Work any credit as required by clause 4(c), as requested.
- You may distribute, publicly display, publicly perform, or publicly digitally perform a Derivative Work only under the terms of this License, a later version of this License with the same License Elements as this License, or a Creative Commons iCommons license that contains the same License Elements as this License (e.g. Attribution-ShareAlike 2.5 Japan). You must include a copy of, or the Uniform Resource Identifier for, this License or other license specified in the previous sentence with every copy or phonorecord of each Derivative Work You distribute, publicly display, publicly perform, or publicly digitally perform. You may not offer or impose any terms on the Derivative Works that alter or restrict the terms of this License or the recipients' exercise of the rights granted hereunder, and You must keep intact all notices that refer to this License and to the disclaimer of warranties. You may not distribute, publicly display, publicly perform, or publicly digitally perform the Derivative Work with any technological measures that control access or use of the Work in a manner inconsistent with the terms of this License Agreement. The above applies to the Derivative Work as incorporated in a Collective Work, but this does not require the Collective Work apart from the Derivative Work itself to be made subject to the terms of this License.
- If you distribute, publicly display, publicly perform, or publicly digitally perform the Work or any Derivative Works or Collective Works, You must keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or (ii) if the Original Author and/or Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; the title of the Work if supplied; to the extent reasonably practicable, the Uniform Resource Identifier, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and in the case of a Derivative Work, a credit identifying the use of the Work in the Derivative Work (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). Such credit may be implemented in any reasonable manner; provided, however, that in the case of a Derivative Work or Collective Work, at a minimum such credit will appear where any other comparable authorship credit appears and in a manner at least as prominent as such other comparable authorship credit.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE MATERIALS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability.
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
- This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Derivative Works or Collective Works from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License.
- Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above.
8. Miscellaneous
- Each time You distribute or publicly digitally perform the Work or a Collective Work, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
- Each time You distribute or publicly digitally perform a Derivative Work, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
- If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
- No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent.
- This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.