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:
commit
bacd790008
@ -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
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "esm4base.hpp"
|
||||
#include "esm4npc.hpp"
|
||||
#include "light4.hpp"
|
||||
#include "tree4.hpp"
|
||||
|
||||
namespace MWClass
|
||||
{
|
||||
|
@ -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>
|
||||
|
27
apps/openmw/mwclass/tree4.cpp
Normal file
27
apps/openmw/mwclass/tree4.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
22
apps/openmw/mwclass/tree4.hpp
Normal file
22
apps/openmw/mwclass/tree4.hpp
Normal 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
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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"):
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
@ -117,6 +117,8 @@ namespace Shader
|
||||
|
||||
bool mSoftParticles;
|
||||
|
||||
bool mTreeAnim;
|
||||
|
||||
// the Node that requested these requirements
|
||||
osg::Node* mNode;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
36
files/shaders/lib/nature/leaves.glsl
Normal file
36
files/shaders/lib/nature/leaves.glsl
Normal 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;
|
||||
}
|
15
files/shaders/lib/nature/leaves.h.glsl
Normal file
15
files/shaders/lib/nature/leaves.h.glsl
Normal 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
|
Loading…
x
Reference in New Issue
Block a user