1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-14 01:19:59 +00:00

Merge branch 'leaf_me_alone' into 'master'

tes5 - add leaf animations

See merge request OpenMW/openmw!4550
This commit is contained in:
Cody Glassman 2025-03-11 23:09:20 -07:00
commit bacd790008
29 changed files with 354 additions and 82 deletions

View File

@ -95,7 +95,7 @@ add_openmw_dir (mwphysics
add_openmw_dir (mwclass
classes activator creature npc weapon armor potion apparatus book clothing container door
ingredient creaturelevlist itemlevlist light lockpick misc probe repair static actor bodypart
esm4base esm4npc light4
esm4base esm4npc light4 tree4
)
add_openmw_dir (mwmechanics

View File

@ -48,6 +48,7 @@
#include "esm4base.hpp"
#include "esm4npc.hpp"
#include "light4.hpp"
#include "tree4.hpp"
namespace MWClass
{

View File

@ -120,15 +120,6 @@ namespace MWClass
}
};
class ESM4Tree final : public MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>
{
friend MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>;
ESM4Tree()
: MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>(ESM4::Tree::sRecordId)
{
}
};
// For records with `mFullName` that should be shown as a tooltip.
// All objects with a tooltip can be activated (activation can be handled in Lua).
template <typename Record>

View File

@ -0,0 +1,27 @@
#include "tree4.hpp"
#include <components/esm4/loadtree.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "../mwrender/objects.hpp"
#include "../mwrender/renderinginterface.hpp"
#include "../mwrender/vismask.hpp"
#include "../mwworld/ptr.hpp"
namespace MWClass
{
ESM4Tree::ESM4Tree()
: MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>(ESM4::Tree::sRecordId)
{
}
void ESM4Tree ::insertObjectRendering(
const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const
{
if (!model.empty())
{
renderingInterface.getObjects().insertModel(ptr, model);
ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static);
}
}
}

View File

@ -0,0 +1,22 @@
#ifndef OPENW_MWCLASS_TREE4
#define OPENW_MWCLASS_TREE4
#include "../mwworld/registeredclass.hpp"
#include "esm4base.hpp"
namespace MWClass
{
class ESM4Tree : public MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>
{
friend MWWorld::RegisteredClass<ESM4Tree, ESM4Base<ESM4::Tree>>;
ESM4Tree();
public:
void insertObjectRendering(const MWWorld::Ptr& ptr, const std::string& model,
MWRender::RenderingInterface& renderingInterface) const override;
///< Add reference into a cell for rendering
};
}
#endif

View File

@ -28,6 +28,7 @@
#include <components/esm3/loadnpc.hpp>
#include <components/esm3/loadrace.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadtree.hpp>
#include <components/misc/constants.hpp>
#include <components/misc/pathhelpers.hpp>
@ -67,6 +68,31 @@
namespace
{
class LeafParamsAssigner : public osg::NodeVisitor
{
public:
LeafParamsAssigner(const MWWorld::LiveCellRef<ESM4::Tree>* treeRef)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mTreeRef(treeRef)
, mVertexAttrib(new osg::Vec3Array(1))
, mTimeOffset(Misc::Rng::roll0to99())
{
mVertexAttrib->at(0) = osg::Vec3f(
mTreeRef->mBase->mParams.mLeafAmplitude, mTreeRef->mBase->mParams.mLeafFrequency, mTimeOffset);
}
void apply(osg::Geometry& geometry) override
{
geometry.setVertexAttribArray(7, mVertexAttrib, osg::Array::BIND_OVERALL);
traverse(geometry);
}
const MWWorld::LiveCellRef<ESM4::Tree>* mTreeRef = nullptr;
osg::ref_ptr<osg::Vec3Array> mVertexAttrib = nullptr;
float mTimeOffset = 0.f;
};
class MarkDrawablesVisitor : public osg::NodeVisitor
{
public:
@ -2079,6 +2105,11 @@ namespace MWRender
addExtraLight(getOrCreateObjectRoot(), SceneUtil::LightCommon(*ptr.get<ESM::Light>()->mBase));
if (ptr.getType() == ESM4::Light::sRecordId && allowLight)
addExtraLight(getOrCreateObjectRoot(), SceneUtil::LightCommon(*ptr.get<ESM4::Light>()->mBase));
if (ptr.getType() == ESM4::Tree::sRecordId)
{
LeafParamsAssigner nv{ ptr.get<ESM4::Tree>() };
ptr.getRefData().getBaseNode()->accept(nv);
}
if (!allowLight && mObjectRoot)
{

View File

@ -174,7 +174,6 @@ namespace MWRender
stateset->addUniform(new osg::Uniform("isReflection", false));
stateset->addUniform(new osg::Uniform("windSpeed", 0.0f));
stateset->addUniform(new osg::Uniform("playerPos", osg::Vec3f(0.f, 0.f, 0.f)));
stateset->addUniform(new osg::Uniform("useTreeAnim", false));
}
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
@ -447,6 +446,8 @@ namespace MWRender
globalDefines["useOVR_multiview"] = "0";
globalDefines["numViews"] = "1";
globalDefines["disableNormals"] = "1";
globalDefines["treeAnim"] = "0";
globalDefines["shadowCasting"] = "0";
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
globalDefines[itr->first] = itr->second;

View File

@ -53,11 +53,23 @@ void ESM4::Tree::load(ESM4::Reader& reader)
case ESM::fourCC("MODB"):
reader.get(mBoundRadius);
break;
case ESM::fourCC("CNAM"):
{
switch (subHdr.dataSize)
{
case 48: // TES5
reader.get(mParams);
break;
default:
reader.skipSubRecordData();
break;
}
break;
}
case ESM::fourCC("MODT"): // Model data
case ESM::fourCC("MODC"):
case ESM::fourCC("MODS"):
case ESM::fourCC("MODF"): // Model data end
case ESM::fourCC("CNAM"):
case ESM::fourCC("BNAM"):
case ESM::fourCC("SNAM"):
case ESM::fourCC("FULL"):

View File

@ -50,6 +50,24 @@ namespace ESM4
std::string mLeafTexture;
struct Params
{
float mTrunkFlexibility;
float mBranchFlexibility;
float mTrunkAmplitude;
float mFrontAmplitude;
float mBackAmplitude;
float mSideAmplitude;
float mFrontFrequency;
float mBackFrequency;
float mSideFrequency;
float mLeafFlexibility;
float mLeafAmplitude;
float mLeafFrequency;
};
Params mParams;
void load(ESM4::Reader& reader);
// void save(ESM4::Writer& writer) const;

View File

@ -5,4 +5,5 @@ namespace Misc
const std::string OsgUserValues::sFileHash = "fileHash";
const std::string OsgUserValues::sExtraData = "xData";
const std::string OsgUserValues::sXSoftEffect = "xSoftEffect";
const std::string OsgUserValues::sTreeAnim = "treeAnim";
}

View File

@ -10,6 +10,7 @@ namespace Misc
static const std::string sFileHash;
static const std::string sExtraData;
static const std::string sXSoftEffect;
static const std::string sTreeAnim;
};
}

View File

@ -74,12 +74,12 @@ namespace Nif
{ "BSDistantObjectInstancedNode",
&construct<BSDistantObjectInstancedNode, RC_BSDistantObjectInstancedNode> },
{ "BSFadeNode", &construct<NiNode, RC_NiNode> },
{ "BSLeafAnimNode", &construct<NiNode, RC_NiNode> },
{ "BSLeafAnimNode", &construct<NiNode, RC_BSLeafAnimNode> },
{ "BSMasterParticleSystem", &construct<BSMasterParticleSystem, RC_NiNode> },
{ "BSMultiBoundNode", &construct<BSMultiBoundNode, RC_NiNode> },
{ "BSOrderedNode", &construct<BSOrderedNode, RC_NiNode> },
{ "BSRangeNode", &construct<BSRangeNode, RC_NiNode> },
{ "BSTreeNode", &construct<BSTreeNode, RC_NiNode> },
{ "BSTreeNode", &construct<BSTreeNode, RC_BSTreeNode> },
{ "BSValueNode", &construct<BSValueNode, RC_NiNode> },
// Switch nodes, 4.0.0.2

View File

@ -102,6 +102,7 @@ namespace Nif
RC_BSInvMarker,
RC_BSKeyframeController,
RC_BSLagBoneController,
RC_BSLeafAnimNode,
RC_BSLightingShaderProperty,
RC_BSLightingShaderPropertyColorController,
RC_BSLightingShaderPropertyFloatController,
@ -145,6 +146,7 @@ namespace Nif
RC_BSSubIndexTriShape,
RC_BSTreadTransfInterpolator,
RC_BSTriShape,
RC_BSTreeNode,
RC_BSWArray,
RC_BSWaterShaderProperty,
RC_BSWindModifier,

View File

@ -258,6 +258,7 @@ namespace NifOsg
bool mHasNightDayLabel = false;
bool mHasHerbalismLabel = false;
bool mHasStencilProperty = false;
bool mHasTreeRoot = false;
const Nif::NiSortAdjustNode* mPushedSorter = nullptr;
const Nif::NiSortAdjustNode* mLastAppliedNoInheritSorter = nullptr;
@ -746,6 +747,12 @@ namespace NifOsg
node->setDataVariance(osg::Object::DYNAMIC);
}
// Root node must be a BSTreeNode or BSLeafAnimNode for tree/leaf related shader flags to apply
if (!parent && (nifNode->recType == Nif::RC_BSLeafAnimNode || nifNode->recType == Nif::RC_BSTreeNode))
{
mHasTreeRoot = true;
}
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
applyNodeProperties(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
@ -2182,8 +2189,8 @@ namespace NifOsg
}
}
void handleShaderMaterialNodeProperties(
const Bgsm::MaterialFile* material, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const
void handleShaderMaterialNodeProperties(osg::Node* node, const Bgsm::MaterialFile* material,
osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const
{
const unsigned int uvSet = 0;
const bool wrapS = material->wrapS();
@ -2202,8 +2209,8 @@ namespace NifOsg
if (bgsm->mGlowMapEnabled && !bgsm->mGlowMap.empty())
attachExternalTexture("emissiveMap", bgsm->mGlowMap, wrapS, wrapT, uvSet, stateset, boundTextures);
if (bgsm->mTree)
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
if (bgsm->mTree && mHasTreeRoot)
node->setUserValue(Misc::OsgUserValues::sTreeAnim, true);
}
else if (material->mShaderType == Bgsm::ShaderType::Effect)
{
@ -2539,7 +2546,7 @@ namespace NifOsg
clearBoundTextures(stateset, boundTextures);
if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager))
{
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
handleShaderMaterialNodeProperties(node, material.get(), stateset, boundTextures);
break;
}
if (!texprop->mTextureSet.empty())
@ -2548,8 +2555,8 @@ namespace NifOsg
handleTextureControllers(texprop, composite, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
if (texprop->treeAnim())
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
if (texprop->treeAnim() && mHasTreeRoot)
node->setUserValue(Misc::OsgUserValues::sTreeAnim, true);
handleDepthFlags(stateset, texprop->depthTest(), texprop->depthWrite());
if (texprop->refraction())
SceneUtil::setupDistortion(*node, texprop->mRefractionStrength);
@ -2566,7 +2573,7 @@ namespace NifOsg
clearBoundTextures(stateset, boundTextures);
if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager))
{
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
handleShaderMaterialNodeProperties(node, material.get(), stateset, boundTextures);
break;
}
if (!texprop->mSourceTexture.empty())

View File

@ -922,14 +922,12 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh
std::string useGPUShader4 = SceneUtil::getGLExtensions().isGpuShader4Supported ? "1" : "0";
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
{
auto& program = _castingPrograms[alphaFunc - GL_NEVER];
program = new osg::Program();
program->addShader(castingVertexShader);
program->addShader(shaderManager.getShader("shadowcasting.frag", { {"alphaFunc", std::to_string(alphaFunc)},
osg::ref_ptr<osg::Shader> castingFragmentShader = shaderManager.getShader("shadowcasting.frag", { {"alphaFunc", std::to_string(alphaFunc)},
{"alphaToCoverage", "0"},
{"adjustCoverage", "1"},
{"useGPUShader4", useGPUShader4}
}));
{"useGPUShader4", useGPUShader4}});
auto& program = _castingPrograms[alphaFunc - GL_NEVER];
program = shaderManager.createProgram(castingVertexShader, castingFragmentShader);
}
}

View File

@ -1,10 +1,14 @@
#include "shadowsbin.hpp"
#include <unordered_set>
#include <osg/AlphaFunc>
#include <osg/Material>
#include <osg/Program>
#include <osg/StateSet>
#include <osgUtil/StateGraph>
#include <unordered_set>
#include <components/shader/shadermanager.hpp>
using namespace osgUtil;
@ -115,6 +119,14 @@ namespace SceneUtil
state.mImportantState = true;
}
auto dedicatedCastingStateSet
= dynamic_cast<const Shader::ShaderManager::ShadowCastingStateSet*>(ss->getUserData());
if (dedicatedCastingStateSet)
{
state.mShadowCastingStateSet = dedicatedCastingStateSet->mStateSet;
}
if ((*itr) != sg && !state.interesting())
uninterestingCache.insert(*itr);
}
@ -132,24 +144,22 @@ namespace SceneUtil
return nullptr;
}
if (state.mAlphaBlend)
{
sg_new = sg->find_or_insert(mShaderAlphaTestStateSet);
auto find_or_insert = [&sg, &sg_new](const osg::StateSet* ss) {
sg_new = sg->find_or_insert(ss);
sg_new->_leaves = std::move(sg->_leaves);
for (RenderLeaf* leaf : sg_new->_leaves)
leaf->_parent = sg_new;
sg = sg_new;
}
};
if (state.mAlphaBlend)
find_or_insert(mShaderAlphaTestStateSet);
if (state.mShadowCastingStateSet)
find_or_insert(state.mShadowCastingStateSet);
// GL_ALWAYS is set by default by mwshadowtechnique
if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS)
{
sg_new = sg->find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]);
sg_new->_leaves = std::move(sg->_leaves);
for (RenderLeaf* leaf : sg_new->_leaves)
leaf->_parent = sg_new;
sg = sg_new;
}
else if (state.mAlphaFunc && state.mAlphaFunc->getFunction() != GL_ALWAYS)
find_or_insert(mAlphaFuncShaders[state.mAlphaFunc->getFunction() - GL_NEVER]);
return sg;
}

View File

@ -35,24 +35,14 @@ namespace SceneUtil
struct State
{
State()
: mAlphaBlend(false)
, mAlphaBlendOverride(false)
, mAlphaFunc(nullptr)
, mAlphaFuncOverride(false)
, mMaterial(nullptr)
, mMaterialOverride(false)
, mImportantState(false)
{
}
bool mAlphaBlend;
bool mAlphaBlendOverride;
osg::AlphaFunc* mAlphaFunc;
bool mAlphaFuncOverride;
osg::Material* mMaterial;
bool mMaterialOverride;
bool mImportantState;
bool mAlphaBlend = false;
bool mAlphaBlendOverride = false;
osg::AlphaFunc* mAlphaFunc = nullptr;
bool mAlphaFuncOverride = false;
osg::Material* mMaterial = nullptr;
bool mMaterialOverride = false;
bool mImportantState = false;
osg::StateSet* mShadowCastingStateSet = nullptr;
bool needTexture() const;
bool needShadows() const;
// A state is interesting if there's anything about it that might affect whether we can optimise child state

View File

@ -609,18 +609,26 @@ namespace Shader
{
if (!programTemplate)
programTemplate = mProgramTemplate;
osg::ref_ptr<osg::Program> program
= programTemplate ? cloneProgram(programTemplate) : osg::ref_ptr<osg::Program>(new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
addLinkedShaders(vertexShader, program);
addLinkedShaders(fragmentShader, program);
found = mPrograms.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader), program)).first;
found = mPrograms
.insert(std::make_pair(std::make_pair(vertexShader, fragmentShader),
createProgram(vertexShader, fragmentShader, programTemplate)))
.first;
}
return found->second;
}
osg::ref_ptr<osg::Program> ShaderManager::createProgram(osg::ref_ptr<osg::Shader> vertexShader,
osg::ref_ptr<osg::Shader> fragmentShader, const osg::Program* programTemplate)
{
osg::ref_ptr<osg::Program> program
= programTemplate ? cloneProgram(programTemplate) : osg::ref_ptr<osg::Program>(new osg::Program);
program->addShader(vertexShader);
program->addShader(fragmentShader);
addLinkedShaders(vertexShader, program);
addLinkedShaders(fragmentShader, program);
return program;
}
osg::ref_ptr<osg::Program> ShaderManager::cloneProgram(const osg::Program* src)
{
osg::ref_ptr<osg::Program> program = static_cast<osg::Program*>(src->clone(osg::CopyOp::SHALLOW_COPY));

View File

@ -36,6 +36,17 @@ namespace Shader
typedef std::map<std::string, std::string> DefineMap;
class ShadowCastingStateSet : public osg::Referenced
{
public:
ShadowCastingStateSet(osg::ref_ptr<osg::StateSet>&& stateSet)
: mStateSet(std::move(stateSet))
{
}
osg::ref_ptr<osg::StateSet> mStateSet = nullptr;
};
/// Create or retrieve a shader instance.
/// @param templateName The path of the shader template.
/// @param defines Define values that can be retrieved by the shader template.
@ -51,6 +62,9 @@ namespace Shader
osg::ref_ptr<osg::Program> getProgram(osg::ref_ptr<osg::Shader> vertexShader,
osg::ref_ptr<osg::Shader> fragmentShader, const osg::Program* programTemplate = nullptr);
osg::ref_ptr<osg::Program> createProgram(osg::ref_ptr<osg::Shader> vertexShader,
osg::ref_ptr<osg::Shader> fragmentShader, const osg::Program* programTemplate = nullptr);
const osg::Program* getProgramTemplate() const { return mProgramTemplate; }
void setProgramTemplate(const osg::Program* program) { mProgramTemplate = program; }

View File

@ -189,6 +189,7 @@ namespace Shader
, mReconstructNormalZ(false)
, mTexStageRequiringTangents(-1)
, mSoftParticles(false)
, mTreeAnim(false)
, mNode(nullptr)
{
}
@ -308,6 +309,10 @@ namespace Shader
if (node.getUserValue(Misc::OsgUserValues::sXSoftEffect, softEffect) && softEffect)
mRequirements.back().mSoftParticles = true;
bool treeAnim = false;
if (node.getUserValue(Misc::OsgUserValues::sTreeAnim, treeAnim) && treeAnim)
mRequirements.back().mTreeAnim = true;
// Make sure to disregard any state that came from a previous call to createProgram
osg::ref_ptr<AddedState> addedState = getAddedState(*stateset);
@ -736,6 +741,11 @@ namespace Shader
addedState->addUniform("opaqueDepthTex");
}
if (reqs.mTreeAnim)
{
defineMap["treeAnim"] = "1";
}
if (writableStateSet->getMode(GL_ALPHA_TEST) != osg::StateAttribute::INHERIT
&& !previousAddedState->hasMode(GL_ALPHA_TEST))
removedState->setMode(GL_ALPHA_TEST, writableStateSet->getMode(GL_ALPHA_TEST));
@ -764,6 +774,16 @@ namespace Shader
shaderPrefix = mDefaultShaderPrefix;
auto program = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate);
if (reqs.mTreeAnim)
{
program->addBindAttribLocation("aLeafParams", 7);
defineMap["shadowCasting"] = "1";
osg::ref_ptr<osg::StateSet> shadowCastingStateSet = new osg::StateSet;
auto shadowCastingProgram = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate);
shadowCastingStateSet->setAttribute(shadowCastingProgram,
osg::StateAttribute::ON | osg::StateAttribute::PROTECTED | osg::StateAttribute::OVERRIDE);
writableStateSet->setUserData(new ShaderManager::ShadowCastingStateSet(std::move(shadowCastingStateSet)));
}
writableStateSet->setAttributeAndModes(program, osg::StateAttribute::ON);
addedState->setAttributeAndModes(std::move(program));

View File

@ -117,6 +117,8 @@ namespace Shader
bool mSoftParticles;
bool mTreeAnim;
// the Node that requested these requirements
osg::Node* mNode;
};

View File

@ -10,6 +10,8 @@ set(SHADER_FILES
lib/water/fresnel.glsl
lib/water/rain_ripples.glsl
lib/water/ripples.glsl
lib/nature/leaves.glsl
lib/nature/leaves.h.glsl
lib/view/depth.glsl
lib/luminance/constants.glsl
lib/particle/soft.glsl

View File

@ -37,7 +37,6 @@ uniform float far;
uniform float alphaRef;
uniform float emissiveMult;
uniform float specStrength;
uniform bool useTreeAnim;
uniform float distortionStrength;
#include "lib/core/fragment.h.glsl"
@ -52,6 +51,18 @@ uniform float distortionStrength;
void main()
{
#if @shadowCasting
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
#endif
gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef);
if (gl_FragData[0].a <= 0.5)
discard;
return;
#endif
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
@ -69,8 +80,9 @@ void main()
#endif
vec4 diffuseColor = getDiffuseColor();
if (!useTreeAnim)
#if !@treeAnim
gl_FragData[0].a *= diffuseColor.a;
#endif
gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef);
vec3 specularColor = getSpecularColor().xyz;

View File

@ -11,6 +11,7 @@
#define PER_PIXEL_LIGHTING 1
#include "lib/core/vertex.h.glsl"
#include "lib/nature/leaves.h.glsl"
#if @diffuseMap
varying vec2 diffuseMapUV;
@ -31,6 +32,10 @@ varying float linearDepth;
varying vec3 passViewPos;
varying vec3 passNormal;
#if @treeAnim
attribute vec3 aLeafParams;
#endif
#include "lib/light/lighting.glsl"
#include "lib/view/depth.glsl"
@ -40,9 +45,29 @@ varying vec3 passNormal;
void main(void)
{
gl_Position = modelToClip(gl_Vertex);
vec4 vertex = gl_Vertex;
vec4 viewPos = modelToView(gl_Vertex);
#if @treeAnim
vertex = transformLeafVertex(LeafParams(
aLeafParams.x,
aLeafParams.y,
aLeafParams.z
), gl_Vertex, gl_Color, gl_Normal.xyz);
#endif
#if @diffuseMap
diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;
#endif
#if @shadowCasting
gl_Position = gl_ModelViewProjectionMatrix * vertex;
gl_ClipVertex = (gl_ModelViewMatrix * vertex);
return;
#endif
gl_Position = modelToClip(vertex);
vec4 viewPos = modelToView(vertex);
gl_ClipVertex = viewPos;
euclideanDepth = length(viewPos.xyz);
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
@ -55,10 +80,6 @@ void main(void)
normalToViewMatrix *= generateTangentSpace(gl_MultiTexCoord7.xyzw, passNormal);
#endif
#if @diffuseMap
diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;
#endif
#if @emissiveMap
emissiveMapUV = (gl_TextureMatrix[@emissiveMapUV] * gl_MultiTexCoord@emissiveMapUV).xy;
#endif

View File

@ -43,6 +43,18 @@ uniform float softFalloffDepth;
void main()
{
#if @shadowCasting
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
#endif
gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef);
if (gl_FragData[0].a <= 0.5)
discard;
return;
#endif
#if @diffuseMap
gl_FragData[0] = texture2D(diffuseMap, diffuseMapUV);
gl_FragData[0].a *= coveragePreservingAlphaScale(diffuseMap, diffuseMapUV);

View File

@ -9,6 +9,7 @@
#endif
#include "lib/core/vertex.h.glsl"
#include "lib/nature/leaves.h.glsl"
#if @diffuseMap
varying vec2 diffuseMapUV;
@ -23,6 +24,10 @@ varying float passFalloff;
uniform bool useFalloff;
uniform vec4 falloffParams;
#if @treeAnim
attribute vec3 aLeafParams;
#endif
#include "lib/view/depth.glsl"
#include "compatibility/vertexcolors.glsl"
@ -30,17 +35,31 @@ uniform vec4 falloffParams;
void main(void)
{
gl_Position = modelToClip(gl_Vertex);
vec4 vertex = gl_Vertex;
vec4 viewPos = modelToView(gl_Vertex);
gl_ClipVertex = viewPos;
euclideanDepth = length(viewPos.xyz);
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
#if @treeAnim
vertex = transformLeafVertex(LeafParams(
aLeafParams.x,
aLeafParams.y,
aLeafParams.z
), gl_Vertex, gl_Color, gl_Normal.xyz);
#endif
#if @diffuseMap
diffuseMapUV = (gl_TextureMatrix[@diffuseMapUV] * gl_MultiTexCoord@diffuseMapUV).xy;
#endif
#if @shadowCasting
gl_Position = gl_ModelViewProjectionMatrix * vertex;
gl_ClipVertex = (gl_ModelViewMatrix * vertex);
return;
#endif
vec4 viewPos = modelToView(vertex);
gl_ClipVertex = viewPos;
euclideanDepth = length(viewPos.xyz);
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
passColor = gl_Color;
passViewPos = viewPos.xyz;
passNormal = gl_Normal.xyz;

View File

@ -5,7 +5,6 @@ varying vec2 diffuseMapUV;
varying float alphaPassthrough;
uniform int colorMode;
uniform bool useTreeAnim;
uniform bool useDiffuseMapForShadowAlpha = true;
uniform bool alphaTestShadows = true;
@ -21,7 +20,7 @@ void main(void)
else
diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions
if (colorMode == 2)
alphaPassthrough = useTreeAnim ? 1.0 : gl_Color.a;
alphaPassthrough = gl_Color.a;
else
// This is uniform, so if it's too low, we might be able to put the position/clip vertex outside the view frustum and skip the fragment shader and rasteriser
alphaPassthrough = gl_FrontMaterial.diffuse.a;

View File

@ -0,0 +1,36 @@
#version 120
#include "lib/nature/leaves.h.glsl"
uniform float osg_SimulationTime;
uniform float windSpeed;
vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal)
{
// Default constants for leaf/tree animation fading from the Skyrim.ini
const float fLeafAnimDampenDistEnd = 4600.0;
const float fLeafAnimDampenDistStart = 3600.0;
float distance = length(gl_ModelViewMatrix * position);
float fade = 1.0
- clamp((distance - fLeafAnimDampenDistStart) / (fLeafAnimDampenDistEnd - fLeafAnimDampenDistStart), 0.0, 1.0);
float amplitude = color.a * params.mLeafAmplitude * fade;
if (amplitude <= 0.f)
return position;
float offset = params.mTimeOffset + position.x * 0.1 + position.y * 0.1;
float period = 0.5 * sin(0.15 * osg_SimulationTime * 6.136 + params.mTimeOffset) + 0.5;
float wind = sin(osg_SimulationTime * params.mLeafFrequency + offset) * params.mLeafAmplitude * windSpeed * period;
float spatialOffset = dot(position.xyz, vec3(1.0));
const vec2 axisFrequencyFactor = vec2(0.1, 0.25);
vec2 phase = fract(axisFrequencyFactor * (wind * params.mLeafFrequency * 10) + spatialOffset + 0.5);
vec2 leafMotion = smoothstep(0.0, 1.0, abs(2.0 * phase - 1.0));
float normalMultiplier = (leafMotion.x + 0.1 * leafMotion.y) * amplitude;
position.xyz += normal.xyz * normalMultiplier;
return position;
}

View File

@ -0,0 +1,15 @@
#ifndef OPENMW_LIB_NATURE_LEAVES_H_GLSL
#define OPENMW_LIB_NATURE_LEAVES_H_GLSL
@link "lib/nature/leaves.glsl"
struct LeafParams
{
float mLeafAmplitude;
float mLeafFrequency;
float mTimeOffset;
};
vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal);
#endif // OPENMW_LIB_NATURE_LEAVES_H_GLSL