From 1b91b9c3d9d7daad18ea57f1acec372e043001b9 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Sat, 22 Feb 2025 12:37:14 -0800 Subject: [PATCH 1/8] tes5 - add leaf animations --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwclass/classes.cpp | 1 + apps/openmw/mwclass/esm4base.hpp | 9 ----- apps/openmw/mwclass/tree4.cpp | 34 ++++++++++++++++++ apps/openmw/mwclass/tree4.hpp | 22 ++++++++++++ apps/openmw/mwrender/renderingmanager.cpp | 3 +- components/esm4/loadtree.cpp | 9 ++++- components/esm4/loadtree.hpp | 18 ++++++++++ components/misc/osguservalues.cpp | 1 + components/misc/osguservalues.hpp | 1 + components/nif/niffile.cpp | 4 +-- components/nif/record.hpp | 2 ++ components/nifosg/nifloader.cpp | 22 +++++++----- components/sceneutil/mwshadowtechnique.cpp | 10 +++--- components/sceneutil/shadowsbin.cpp | 36 ++++++++++++------- components/sceneutil/shadowsbin.hpp | 26 +++++--------- components/shader/shadermanager.cpp | 24 ++++++++----- components/shader/shadermanager.hpp | 14 ++++++++ components/shader/shadervisitor.cpp | 19 ++++++++++ components/shader/shadervisitor.hpp | 2 ++ files/shaders/CMakeLists.txt | 2 ++ files/shaders/compatibility/bs/default.frag | 16 +++++++-- files/shaders/compatibility/bs/default.vert | 30 ++++++++++++---- .../shaders/compatibility/bs/nolighting.frag | 12 +++++++ .../shaders/compatibility/bs/nolighting.vert | 25 ++++++++++--- .../shaders/compatibility/shadowcasting.vert | 3 +- files/shaders/lib/nature/leaves.glsl | 26 ++++++++++++++ files/shaders/lib/nature/leaves.h.glsl | 14 ++++++++ 28 files changed, 305 insertions(+), 82 deletions(-) create mode 100644 apps/openmw/mwclass/tree4.cpp create mode 100644 apps/openmw/mwclass/tree4.hpp create mode 100644 files/shaders/lib/nature/leaves.glsl create mode 100644 files/shaders/lib/nature/leaves.h.glsl diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 37de0abeab..c51805c32b 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -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 diff --git a/apps/openmw/mwclass/classes.cpp b/apps/openmw/mwclass/classes.cpp index e7d5cf394b..8513a028a9 100644 --- a/apps/openmw/mwclass/classes.cpp +++ b/apps/openmw/mwclass/classes.cpp @@ -48,6 +48,7 @@ #include "esm4base.hpp" #include "esm4npc.hpp" #include "light4.hpp" +#include "tree4.hpp" namespace MWClass { diff --git a/apps/openmw/mwclass/esm4base.hpp b/apps/openmw/mwclass/esm4base.hpp index f13d6007cd..e7d2dd7320 100644 --- a/apps/openmw/mwclass/esm4base.hpp +++ b/apps/openmw/mwclass/esm4base.hpp @@ -120,15 +120,6 @@ namespace MWClass } }; - class ESM4Tree final : public MWWorld::RegisteredClass> - { - friend MWWorld::RegisteredClass>; - ESM4Tree() - : MWWorld::RegisteredClass>(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 diff --git a/apps/openmw/mwclass/tree4.cpp b/apps/openmw/mwclass/tree4.cpp new file mode 100644 index 0000000000..3cfc4aa221 --- /dev/null +++ b/apps/openmw/mwclass/tree4.cpp @@ -0,0 +1,34 @@ +#include "tree4.hpp" + +#include "../mwrender/objects.hpp" +#include "../mwrender/renderinginterface.hpp" +#include "../mwrender/vismask.hpp" +#include "../mwworld/ptr.hpp" + +#include +#include + +namespace MWClass +{ + ESM4Tree::ESM4Tree() + : MWWorld::RegisteredClass>(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); + + MWWorld::LiveCellRef* ref = ptr.get(); + const auto& params = ref->mBase->mParams; + ptr.getRefData().getBaseNode()->getOrCreateStateSet()->addUniform( + new osg::Uniform("leafParams", osg::Vec2(params.mLeafAmplitude, params.mLeafFrequency))); + + ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); + } + } +} diff --git a/apps/openmw/mwclass/tree4.hpp b/apps/openmw/mwclass/tree4.hpp new file mode 100644 index 0000000000..8bef19a0da --- /dev/null +++ b/apps/openmw/mwclass/tree4.hpp @@ -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> + { + friend MWWorld::RegisteredClass>; + + 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 diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6afbfcfe7d..ed648698eb 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -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; diff --git a/components/esm4/loadtree.cpp b/components/esm4/loadtree.cpp index c433f11564..a41d74dfc4 100644 --- a/components/esm4/loadtree.cpp +++ b/components/esm4/loadtree.cpp @@ -53,11 +53,18 @@ void ESM4::Tree::load(ESM4::Reader& reader) case ESM::fourCC("MODB"): reader.get(mBoundRadius); break; + case ESM::fourCC("CNAM"): + { + if (subHdr.dataSize == 48) // tes5 + { + reader.get(mParams); + 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"): diff --git a/components/esm4/loadtree.hpp b/components/esm4/loadtree.hpp index 2442063086..e3a0e9aa33 100644 --- a/components/esm4/loadtree.hpp +++ b/components/esm4/loadtree.hpp @@ -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; diff --git a/components/misc/osguservalues.cpp b/components/misc/osguservalues.cpp index b70647a63e..4f45b98bf8 100644 --- a/components/misc/osguservalues.cpp +++ b/components/misc/osguservalues.cpp @@ -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"; } diff --git a/components/misc/osguservalues.hpp b/components/misc/osguservalues.hpp index 443e6132e1..2840ab419f 100644 --- a/components/misc/osguservalues.hpp +++ b/components/misc/osguservalues.hpp @@ -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; }; } diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 02b110a5a6..d13c6e166d 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -74,12 +74,12 @@ namespace Nif { "BSDistantObjectInstancedNode", &construct }, { "BSFadeNode", &construct }, - { "BSLeafAnimNode", &construct }, + { "BSLeafAnimNode", &construct }, { "BSMasterParticleSystem", &construct }, { "BSMultiBoundNode", &construct }, { "BSOrderedNode", &construct }, { "BSRangeNode", &construct }, - { "BSTreeNode", &construct }, + { "BSTreeNode", &construct }, { "BSValueNode", &construct }, // Switch nodes, 4.0.0.2 diff --git a/components/nif/record.hpp b/components/nif/record.hpp index fe38cc4cad..40ad62c913 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -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, diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index d817ed2c9f..b117678bf9 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -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,11 @@ namespace NifOsg node->setDataVariance(osg::Object::DYNAMIC); } + if (nifNode->recType == Nif::RC_BSLeafAnimNode || nifNode->recType == Nif::RC_BSTreeNode) + { + mHasTreeRoot = true; + } + osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; applyNodeProperties(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags); @@ -2182,8 +2188,8 @@ namespace NifOsg } } - void handleShaderMaterialNodeProperties( - const Bgsm::MaterialFile* material, osg::StateSet* stateset, std::vector& boundTextures) const + void handleShaderMaterialNodeProperties(osg::Node* node, const Bgsm::MaterialFile* material, + osg::StateSet* stateset, std::vector& boundTextures) const { const unsigned int uvSet = 0; const bool wrapS = material->wrapS(); @@ -2202,8 +2208,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 +2545,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 +2554,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 +2572,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()) diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 9fd435f40d..1e66dc5b94 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -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 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); } } diff --git a/components/sceneutil/shadowsbin.cpp b/components/sceneutil/shadowsbin.cpp index dd03879e8c..d7e9a6679d 100644 --- a/components/sceneutil/shadowsbin.cpp +++ b/components/sceneutil/shadowsbin.cpp @@ -1,10 +1,14 @@ #include "shadowsbin.hpp" + +#include + #include #include #include #include #include -#include + +#include using namespace osgUtil; @@ -115,6 +119,14 @@ namespace SceneUtil state.mImportantState = true; } + auto dedicatedCastingStateSet + = dynamic_cast(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; } diff --git a/components/sceneutil/shadowsbin.hpp b/components/sceneutil/shadowsbin.hpp index 4e5e19ee1f..f41cd6c894 100644 --- a/components/sceneutil/shadowsbin.hpp +++ b/components/sceneutil/shadowsbin.hpp @@ -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 diff --git a/components/shader/shadermanager.cpp b/components/shader/shadermanager.cpp index 96d6346149..e7bd2fec9c 100644 --- a/components/shader/shadermanager.cpp +++ b/components/shader/shadermanager.cpp @@ -609,18 +609,26 @@ namespace Shader { if (!programTemplate) programTemplate = mProgramTemplate; - osg::ref_ptr program - = programTemplate ? cloneProgram(programTemplate) : osg::ref_ptr(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 ShaderManager::createProgram(osg::ref_ptr vertexShader, + osg::ref_ptr fragmentShader, const osg::Program* programTemplate) + { + osg::ref_ptr program + = programTemplate ? cloneProgram(programTemplate) : osg::ref_ptr(new osg::Program); + program->addShader(vertexShader); + program->addShader(fragmentShader); + addLinkedShaders(vertexShader, program); + addLinkedShaders(fragmentShader, program); + return program; + } + osg::ref_ptr ShaderManager::cloneProgram(const osg::Program* src) { osg::ref_ptr program = static_cast(src->clone(osg::CopyOp::SHALLOW_COPY)); diff --git a/components/shader/shadermanager.hpp b/components/shader/shadermanager.hpp index 827fef7e88..5b7523c062 100644 --- a/components/shader/shadermanager.hpp +++ b/components/shader/shadermanager.hpp @@ -36,6 +36,17 @@ namespace Shader typedef std::map DefineMap; + class ShadowCastingStateSet : public osg::Referenced + { + public: + ShadowCastingStateSet(osg::ref_ptr&& stateSet) + : mStateSet(std::move(stateSet)) + { + } + + osg::ref_ptr 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 getProgram(osg::ref_ptr vertexShader, osg::ref_ptr fragmentShader, const osg::Program* programTemplate = nullptr); + osg::ref_ptr createProgram(osg::ref_ptr vertexShader, + osg::ref_ptr fragmentShader, const osg::Program* programTemplate = nullptr); + const osg::Program* getProgramTemplate() const { return mProgramTemplate; } void setProgramTemplate(const osg::Program* program) { mProgramTemplate = program; } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index d97276576f..fe35a39db7 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -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 = 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,15 @@ namespace Shader shaderPrefix = mDefaultShaderPrefix; auto program = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate); + if (reqs.mTreeAnim) + { + defineMap["shadowCasting"] = "1"; + osg::ref_ptr 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)); diff --git a/components/shader/shadervisitor.hpp b/components/shader/shadervisitor.hpp index 9ce0819bd3..823b8099e9 100644 --- a/components/shader/shadervisitor.hpp +++ b/components/shader/shadervisitor.hpp @@ -117,6 +117,8 @@ namespace Shader bool mSoftParticles; + bool mTreeAnim; + // the Node that requested these requirements osg::Node* mNode; }; diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt index bcb1a22fb2..c5720004a2 100644 --- a/files/shaders/CMakeLists.txt +++ b/files/shaders/CMakeLists.txt @@ -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 diff --git a/files/shaders/compatibility/bs/default.frag b/files/shaders/compatibility/bs/default.frag index 5068c01d80..bf3542b272 100644 --- a/files/shaders/compatibility/bs/default.frag +++ b/files/shaders/compatibility/bs/default.frag @@ -39,7 +39,6 @@ uniform float far; uniform float alphaRef; uniform float emissiveMult; uniform float specStrength; -uniform bool useTreeAnim; uniform float distortionStrength; #include "lib/light/lighting.glsl" @@ -53,6 +52,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); @@ -70,8 +81,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; diff --git a/files/shaders/compatibility/bs/default.vert b/files/shaders/compatibility/bs/default.vert index 21942ec91e..9a3b4b0849 100644 --- a/files/shaders/compatibility/bs/default.vert +++ b/files/shaders/compatibility/bs/default.vert @@ -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,8 @@ varying float linearDepth; varying vec3 passViewPos; varying vec3 passNormal; +uniform vec2 leafParams; + #include "lib/light/lighting.glsl" #include "lib/view/depth.glsl" @@ -40,9 +43,28 @@ 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( + leafParams.x, + leafParams.y + ), 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 +77,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 diff --git a/files/shaders/compatibility/bs/nolighting.frag b/files/shaders/compatibility/bs/nolighting.frag index c9e3ca4e13..3a559497b8 100644 --- a/files/shaders/compatibility/bs/nolighting.frag +++ b/files/shaders/compatibility/bs/nolighting.frag @@ -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); diff --git a/files/shaders/compatibility/bs/nolighting.vert b/files/shaders/compatibility/bs/nolighting.vert index 57cedc6e94..05d90acf1d 100644 --- a/files/shaders/compatibility/bs/nolighting.vert +++ b/files/shaders/compatibility/bs/nolighting.vert @@ -9,6 +9,7 @@ #endif #include "lib/core/vertex.h.glsl" +#include "lib/nature/leaves.h.glsl" #if @diffuseMap varying vec2 diffuseMapUV; @@ -22,6 +23,7 @@ varying float passFalloff; uniform bool useFalloff; uniform vec4 falloffParams; +uniform vec2 leafParams; #include "lib/view/depth.glsl" @@ -30,17 +32,30 @@ 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( + leafParams.x, + leafParams.y + ), 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; diff --git a/files/shaders/compatibility/shadowcasting.vert b/files/shaders/compatibility/shadowcasting.vert index c68bdd5c17..e36f21a4de 100644 --- a/files/shaders/compatibility/shadowcasting.vert +++ b/files/shaders/compatibility/shadowcasting.vert @@ -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; diff --git a/files/shaders/lib/nature/leaves.glsl b/files/shaders/lib/nature/leaves.glsl new file mode 100644 index 0000000000..dad9655137 --- /dev/null +++ b/files/shaders/lib/nature/leaves.glsl @@ -0,0 +1,26 @@ +#version 120 + +#include "lib/nature/leaves.h.glsl" + +uniform float osg_SimulationTime; +uniform float windSpeed; + +vec2 smoothstep_noclamp(vec2 x) +{ + return x * x * (3.0 - 2.0 * x); +} + +vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal) +{ + const vec2 axisFrequencyFactor = vec2(0.1, 0.25); + + float wind = windSpeed * osg_SimulationTime * 10.0; + float spatialOffset = dot(position.xyz, vec3(1.0)); + + vec2 phase = fract(axisFrequencyFactor * (wind * params.mLeafFrequency) + spatialOffset + 0.5); + vec2 leafMotion = smoothstep_noclamp(abs(2.0 * phase - 1.0)); + float normalMultiplier = (leafMotion.x + 0.1 * leafMotion.y) * (color.a * params.mLeafAmplitude); + position.xyz += normal.xyz * normalMultiplier; + + return position; +} diff --git a/files/shaders/lib/nature/leaves.h.glsl b/files/shaders/lib/nature/leaves.h.glsl new file mode 100644 index 0000000000..54be65aba7 --- /dev/null +++ b/files/shaders/lib/nature/leaves.h.glsl @@ -0,0 +1,14 @@ +#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; +}; + +vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal); + +#endif // OPENMW_LIB_NATURE_LEAVES_H_GLSL From 0ccd81d9fc65cb35b683c15b058a41bf997e9446 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Tue, 25 Feb 2025 08:00:01 -0800 Subject: [PATCH 2/8] tweak wind speed --- files/shaders/lib/nature/leaves.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/shaders/lib/nature/leaves.glsl b/files/shaders/lib/nature/leaves.glsl index dad9655137..890a0a483b 100644 --- a/files/shaders/lib/nature/leaves.glsl +++ b/files/shaders/lib/nature/leaves.glsl @@ -14,7 +14,7 @@ vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 norm { const vec2 axisFrequencyFactor = vec2(0.1, 0.25); - float wind = windSpeed * osg_SimulationTime * 10.0; + float wind = windSpeed * osg_SimulationTime * 100.0; float spatialOffset = dot(position.xyz, vec3(1.0)); vec2 phase = fract(axisFrequencyFactor * (wind * params.mLeafFrequency) + spatialOffset + 0.5); From d85890546b9f53e2c6d3fec043aff934b4aaddae Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Tue, 25 Feb 2025 08:28:32 -0800 Subject: [PATCH 3/8] add fallthrough to satisfy case --- components/esm4/loadtree.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/esm4/loadtree.cpp b/components/esm4/loadtree.cpp index a41d74dfc4..d5d222d958 100644 --- a/components/esm4/loadtree.cpp +++ b/components/esm4/loadtree.cpp @@ -60,6 +60,11 @@ void ESM4::Tree::load(ESM4::Reader& reader) reader.get(mParams); break; } + else + { + + [[fallthrough]]; + } } case ESM::fourCC("MODT"): // Model data case ESM::fourCC("MODC"): From 121f872a3637ba4be9c203cd8da588bf7e0e8801 Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Wed, 5 Mar 2025 07:04:14 -0800 Subject: [PATCH 4/8] fix formatting and explicit switch for CNAM handling --- apps/openmw/mwclass/tree4.cpp | 7 +++---- components/esm4/loadtree.cpp | 16 ++++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/openmw/mwclass/tree4.cpp b/apps/openmw/mwclass/tree4.cpp index 3cfc4aa221..e053b74184 100644 --- a/apps/openmw/mwclass/tree4.cpp +++ b/apps/openmw/mwclass/tree4.cpp @@ -1,13 +1,13 @@ #include "tree4.hpp" +#include +#include + #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwrender/vismask.hpp" #include "../mwworld/ptr.hpp" -#include -#include - namespace MWClass { ESM4Tree::ESM4Tree() @@ -18,7 +18,6 @@ namespace MWClass void ESM4Tree ::insertObjectRendering( const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const { - if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); diff --git a/components/esm4/loadtree.cpp b/components/esm4/loadtree.cpp index d5d222d958..f51dd5afad 100644 --- a/components/esm4/loadtree.cpp +++ b/components/esm4/loadtree.cpp @@ -55,16 +55,16 @@ void ESM4::Tree::load(ESM4::Reader& reader) break; case ESM::fourCC("CNAM"): { - if (subHdr.dataSize == 48) // tes5 + switch (subHdr.dataSize) { - reader.get(mParams); - break; - } - else - { - - [[fallthrough]]; + case 48: // TES5 + reader.get(mParams); + break; + default: + reader.skipSubRecordData(); + break; } + break; } case ESM::fourCC("MODT"): // Model data case ESM::fourCC("MODC"): From b08e5283765a0e4720805135a00311761a6b7aba Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Wed, 5 Mar 2025 08:54:05 -0800 Subject: [PATCH 5/8] only apply tree shader effects when the root node is a leaf/tree node --- components/nifosg/nifloader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index b117678bf9..561282ac63 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -747,7 +747,8 @@ namespace NifOsg node->setDataVariance(osg::Object::DYNAMIC); } - if (nifNode->recType == Nif::RC_BSLeafAnimNode || nifNode->recType == Nif::RC_BSTreeNode) + // 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; } From ea1b89741a47b719a73b8bab45f889b8a9c30f1e Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 7 Mar 2025 12:54:07 -0800 Subject: [PATCH 6/8] add a basic periodic wind/gust and use vertex attrib --- apps/openmw/mwclass/tree4.cpp | 34 ++++++++++++++++--- components/shader/shadervisitor.cpp | 1 + files/shaders/compatibility/bs/default.vert | 9 +++-- .../shaders/compatibility/bs/nolighting.vert | 10 ++++-- files/shaders/lib/nature/leaves.glsl | 27 ++++++++++----- files/shaders/lib/nature/leaves.h.glsl | 1 + 6 files changed, 63 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/tree4.cpp b/apps/openmw/mwclass/tree4.cpp index e053b74184..d38f570e0c 100644 --- a/apps/openmw/mwclass/tree4.cpp +++ b/apps/openmw/mwclass/tree4.cpp @@ -8,6 +8,34 @@ #include "../mwrender/vismask.hpp" #include "../mwworld/ptr.hpp" +namespace +{ + class LeafParamsAssigner : public osg::NodeVisitor + { + public: + LeafParamsAssigner(const MWWorld::LiveCellRef* 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* mTreeRef = nullptr; + osg::ref_ptr mVertexAttrib = nullptr; + float mTimeOffset = 0.f; + }; +} + namespace MWClass { ESM4Tree::ESM4Tree() @@ -22,10 +50,8 @@ namespace MWClass { renderingInterface.getObjects().insertModel(ptr, model); - MWWorld::LiveCellRef* ref = ptr.get(); - const auto& params = ref->mBase->mParams; - ptr.getRefData().getBaseNode()->getOrCreateStateSet()->addUniform( - new osg::Uniform("leafParams", osg::Vec2(params.mLeafAmplitude, params.mLeafFrequency))); + LeafParamsAssigner nv{ ptr.get() }; + ptr.getRefData().getBaseNode()->accept(nv); ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); } diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index fe35a39db7..d00891ccf3 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -776,6 +776,7 @@ namespace Shader auto program = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate); if (reqs.mTreeAnim) { + program->addBindAttribLocation("aLeafParams", 7); defineMap["shadowCasting"] = "1"; osg::ref_ptr shadowCastingStateSet = new osg::StateSet; auto shadowCastingProgram = mShaderManager.getProgram(shaderPrefix, defineMap, mProgramTemplate); diff --git a/files/shaders/compatibility/bs/default.vert b/files/shaders/compatibility/bs/default.vert index 9a3b4b0849..7c7bb0ef79 100644 --- a/files/shaders/compatibility/bs/default.vert +++ b/files/shaders/compatibility/bs/default.vert @@ -32,7 +32,9 @@ varying float linearDepth; varying vec3 passViewPos; varying vec3 passNormal; -uniform vec2 leafParams; +#if @treeAnim +attribute vec3 aLeafParams; +#endif #include "lib/light/lighting.glsl" #include "lib/view/depth.glsl" @@ -47,8 +49,9 @@ void main(void) #if @treeAnim vertex = transformLeafVertex(LeafParams( - leafParams.x, - leafParams.y + aLeafParams.x, + aLeafParams.y, + aLeafParams.z ), gl_Vertex, gl_Color, gl_Normal.xyz); #endif diff --git a/files/shaders/compatibility/bs/nolighting.vert b/files/shaders/compatibility/bs/nolighting.vert index 05d90acf1d..c0367df743 100644 --- a/files/shaders/compatibility/bs/nolighting.vert +++ b/files/shaders/compatibility/bs/nolighting.vert @@ -23,7 +23,10 @@ varying float passFalloff; uniform bool useFalloff; uniform vec4 falloffParams; -uniform vec2 leafParams; + +#if @treeAnim +attribute vec3 aLeafParams; +#endif #include "lib/view/depth.glsl" @@ -36,8 +39,9 @@ void main(void) #if @treeAnim vertex = transformLeafVertex(LeafParams( - leafParams.x, - leafParams.y + aLeafParams.x, + aLeafParams.y, + aLeafParams.z ), gl_Vertex, gl_Color, gl_Normal.xyz); #endif diff --git a/files/shaders/lib/nature/leaves.glsl b/files/shaders/lib/nature/leaves.glsl index 890a0a483b..c8d75e9300 100644 --- a/files/shaders/lib/nature/leaves.glsl +++ b/files/shaders/lib/nature/leaves.glsl @@ -5,21 +5,30 @@ uniform float osg_SimulationTime; uniform float windSpeed; -vec2 smoothstep_noclamp(vec2 x) -{ - return x * x * (3.0 - 2.0 * x); -} - vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal) { - const vec2 axisFrequencyFactor = vec2(0.1, 0.25); + // Default constants for leaf/tree animation fading from the Skyrim.ini + const float fLeafAnimDampenDistEnd = 4600.0; + const float fLeafAnimDampenDistStart = 3600.0; - float wind = windSpeed * osg_SimulationTime * 100.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; + + const vec2 axisFrequencyFactor = vec2(0.1, 0.25); + float gust = 0.5 * sin(0.15 * osg_SimulationTime * 6.136 + params.mTimeOffset) + 0.5; + float wind = sin(osg_SimulationTime * 10.0 + params.mTimeOffset) * windSpeed * gust; float spatialOffset = dot(position.xyz, vec3(1.0)); vec2 phase = fract(axisFrequencyFactor * (wind * params.mLeafFrequency) + spatialOffset + 0.5); - vec2 leafMotion = smoothstep_noclamp(abs(2.0 * phase - 1.0)); - float normalMultiplier = (leafMotion.x + 0.1 * leafMotion.y) * (color.a * params.mLeafAmplitude); + vec2 leafMotion = smoothstep(0.0, 1.0, abs(2.0 * phase - 1.0)) * 10.0; + float normalMultiplier = (leafMotion.x + 0.1 * leafMotion.y) * amplitude; position.xyz += normal.xyz * normalMultiplier; return position; diff --git a/files/shaders/lib/nature/leaves.h.glsl b/files/shaders/lib/nature/leaves.h.glsl index 54be65aba7..53585253f3 100644 --- a/files/shaders/lib/nature/leaves.h.glsl +++ b/files/shaders/lib/nature/leaves.h.glsl @@ -7,6 +7,7 @@ struct LeafParams { float mLeafAmplitude; float mLeafFrequency; + float mTimeOffset; }; vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 normal); From 935df3a239bc39ca9ab2dfc80ad3737ad4be633c Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Fri, 7 Mar 2025 18:53:32 -0800 Subject: [PATCH 7/8] move rendering related code to mwrender --- apps/openmw/mwclass/tree4.cpp | 32 ------------------------------ apps/openmw/mwrender/animation.cpp | 31 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwclass/tree4.cpp b/apps/openmw/mwclass/tree4.cpp index d38f570e0c..e4713b0385 100644 --- a/apps/openmw/mwclass/tree4.cpp +++ b/apps/openmw/mwclass/tree4.cpp @@ -8,34 +8,6 @@ #include "../mwrender/vismask.hpp" #include "../mwworld/ptr.hpp" -namespace -{ - class LeafParamsAssigner : public osg::NodeVisitor - { - public: - LeafParamsAssigner(const MWWorld::LiveCellRef* 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* mTreeRef = nullptr; - osg::ref_ptr mVertexAttrib = nullptr; - float mTimeOffset = 0.f; - }; -} - namespace MWClass { ESM4Tree::ESM4Tree() @@ -49,10 +21,6 @@ namespace MWClass if (!model.empty()) { renderingInterface.getObjects().insertModel(ptr, model); - - LeafParamsAssigner nv{ ptr.get() }; - ptr.getRefData().getBaseNode()->accept(nv); - ptr.getRefData().getBaseNode()->setNodeMask(MWRender::Mask_Static); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0a2ef7bef8..cc0c7c2cac 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,31 @@ namespace { + class LeafParamsAssigner : public osg::NodeVisitor + { + public: + LeafParamsAssigner(const MWWorld::LiveCellRef* 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* mTreeRef = nullptr; + osg::ref_ptr mVertexAttrib = nullptr; + float mTimeOffset = 0.f; + }; + class MarkDrawablesVisitor : public osg::NodeVisitor { public: @@ -2079,6 +2105,11 @@ namespace MWRender addExtraLight(getOrCreateObjectRoot(), SceneUtil::LightCommon(*ptr.get()->mBase)); if (ptr.getType() == ESM4::Light::sRecordId && allowLight) addExtraLight(getOrCreateObjectRoot(), SceneUtil::LightCommon(*ptr.get()->mBase)); + if (ptr.getType() == ESM4::Tree::sRecordId) + { + LeafParamsAssigner nv{ ptr.get() }; + ptr.getRefData().getBaseNode()->accept(nv); + } if (!allowLight && mObjectRoot) { From 07d62eaa01545e35d0c928ddf1202d439d61891f Mon Sep 17 00:00:00 2001 From: Cody Glassman Date: Mon, 10 Mar 2025 10:13:35 -0700 Subject: [PATCH 8/8] tweak leaf motion --- files/shaders/lib/nature/leaves.glsl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/files/shaders/lib/nature/leaves.glsl b/files/shaders/lib/nature/leaves.glsl index c8d75e9300..37ab9176d4 100644 --- a/files/shaders/lib/nature/leaves.glsl +++ b/files/shaders/lib/nature/leaves.glsl @@ -21,13 +21,14 @@ vec4 transformLeafVertex(LeafParams params, vec4 position, vec4 color, vec3 norm if (amplitude <= 0.f) return position; - const vec2 axisFrequencyFactor = vec2(0.1, 0.25); - float gust = 0.5 * sin(0.15 * osg_SimulationTime * 6.136 + params.mTimeOffset) + 0.5; - float wind = sin(osg_SimulationTime * 10.0 + params.mTimeOffset) * windSpeed * gust; + 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)); - vec2 phase = fract(axisFrequencyFactor * (wind * params.mLeafFrequency) + spatialOffset + 0.5); - vec2 leafMotion = smoothstep(0.0, 1.0, abs(2.0 * phase - 1.0)) * 10.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;