Merge branch 'tree_and_furniture' into 'master'

Load ESM4::Tree and ESM4::Furniture

See merge request OpenMW/openmw!3040
macos_ci
psi29a 2 years ago
commit 27a879de9a

@ -61,9 +61,11 @@ namespace MWClass
ESM4Named<ESM4::Clothing>::registerSelf();
ESM4Named<ESM4::Container>::registerSelf();
ESM4Named<ESM4::Door>::registerSelf();
ESM4Named<ESM4::Furniture>::registerSelf();
ESM4Named<ESM4::Ingredient>::registerSelf();
ESM4Named<ESM4::MiscItem>::registerSelf();
ESM4Static::registerSelf();
ESM4Tree::registerSelf();
ESM4Named<ESM4::Weapon>::registerSelf();
ESM4Light::registerSelf();
}

@ -2,6 +2,7 @@
#define GAME_MWCLASS_ESM4BASE_H
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadtree.hpp>
#include <components/misc/strings/algorithm.hpp>
#include "../mwgui/tooltips.hpp"
@ -88,6 +89,15 @@ 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>

@ -188,6 +188,9 @@ namespace MWLua
case ESM::REC_DOOR4:
cell.mStore->template forEachType<ESM4::Door>(visitor);
break;
case ESM::REC_FURN4:
cell.mStore->template forEachType<ESM4::Furniture>(visitor);
break;
case ESM::REC_INGR4:
cell.mStore->template forEachType<ESM4::Ingredient>(visitor);
break;
@ -203,6 +206,9 @@ namespace MWLua
case ESM::REC_STAT4:
cell.mStore->template forEachType<ESM4::Static>(visitor);
break;
case ESM::REC_TREE4:
cell.mStore->template forEachType<ESM4::Tree>(visitor);
break;
case ESM::REC_WEAP4:
cell.mStore->template forEachType<ESM4::Weapon>(visitor);
break;

@ -40,11 +40,13 @@ namespace MWLua
constexpr std::string_view ESM4Clothing = "ESM4Clothing";
constexpr std::string_view ESM4Container = "ESM4Container";
constexpr std::string_view ESM4Door = "ESM4Door";
constexpr std::string_view ESM4Furniture = "ESM4Furniture";
constexpr std::string_view ESM4Ingredient = "ESM4Ingredient";
constexpr std::string_view ESM4Light = "ESM4Light";
constexpr std::string_view ESM4MiscItem = "ESM4Miscellaneous";
constexpr std::string_view ESM4Potion = "ESM4Potion";
constexpr std::string_view ESM4Static = "ESM4Static";
constexpr std::string_view ESM4Tree = "ESM4Tree";
constexpr std::string_view ESM4Weapon = "ESM4Weapon";
}
@ -79,11 +81,13 @@ namespace MWLua
{ ESM::REC_CLOT4, ObjectTypeName::ESM4Clothing },
{ ESM::REC_CONT4, ObjectTypeName::ESM4Container },
{ ESM::REC_DOOR4, ObjectTypeName::ESM4Door },
{ ESM::REC_FURN4, ObjectTypeName::ESM4Furniture },
{ ESM::REC_INGR4, ObjectTypeName::ESM4Ingredient },
{ ESM::REC_LIGH4, ObjectTypeName::ESM4Light },
{ ESM::REC_MISC4, ObjectTypeName::ESM4MiscItem },
{ ESM::REC_ALCH4, ObjectTypeName::ESM4Potion },
{ ESM::REC_STAT4, ObjectTypeName::ESM4Static },
{ ESM::REC_TREE4, ObjectTypeName::ESM4Tree },
{ ESM::REC_WEAP4, ObjectTypeName::ESM4Weapon },
};
}
@ -210,11 +214,13 @@ namespace MWLua
addType(ObjectTypeName::ESM4Clothing, { ESM::REC_CLOT4 });
addType(ObjectTypeName::ESM4Container, { ESM::REC_CONT4 });
addESM4DoorBindings(addType(ObjectTypeName::ESM4Door, { ESM::REC_DOOR4 }), context);
addType(ObjectTypeName::ESM4Furniture, { ESM::REC_FURN4 });
addType(ObjectTypeName::ESM4Ingredient, { ESM::REC_INGR4 });
addType(ObjectTypeName::ESM4Light, { ESM::REC_LIGH4 });
addType(ObjectTypeName::ESM4MiscItem, { ESM::REC_MISC4 });
addType(ObjectTypeName::ESM4Potion, { ESM::REC_ALCH4 });
addType(ObjectTypeName::ESM4Static, { ESM::REC_STAT4 });
addType(ObjectTypeName::ESM4Tree, { ESM::REC_TREE4 });
addType(ObjectTypeName::ESM4Weapon, { ESM::REC_WEAP4 });
sol::table typeToPackage = getTypeToPackageTable(context.mLua->sol());

@ -172,6 +172,7 @@ 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

@ -68,8 +68,10 @@ namespace ESM4
struct Clothing;
struct Container;
struct Door;
struct Furniture;
struct Ingredient;
struct MiscItem;
struct Tree;
struct Weapon;
}
@ -87,8 +89,8 @@ namespace MWWorld
CellRefList<ESM4::Static>, CellRefList<ESM4::Light>, CellRefList<ESM4::Activator>, CellRefList<ESM4::Potion>,
CellRefList<ESM4::Ammunition>, CellRefList<ESM4::Armor>, CellRefList<ESM4::Book>, CellRefList<ESM4::Clothing>,
CellRefList<ESM4::Container>, CellRefList<ESM4::Door>, CellRefList<ESM4::Ingredient>,
CellRefList<ESM4::MiscItem>, CellRefList<ESM4::Weapon>>;
CellRefList<ESM4::Container>, CellRefList<ESM4::Door>, CellRefList<ESM4::Ingredient>, CellRefList<ESM4::Tree>,
CellRefList<ESM4::MiscItem>, CellRefList<ESM4::Weapon>, CellRefList<ESM4::Furniture>>;
/// \brief Mutable state of a cell
class CellStore

@ -286,8 +286,10 @@ namespace MWWorld
case ESM::REC_BOOK4:
case ESM::REC_CONT4:
case ESM::REC_DOOR4:
case ESM::REC_FURN4:
case ESM::REC_INGR4:
case ESM::REC_MISC4:
case ESM::REC_TREE4:
case ESM::REC_WEAP4:
return true;
break;

@ -39,8 +39,10 @@ namespace ESM4
struct Clothing;
struct Container;
struct Door;
struct Furniture;
struct Ingredient;
struct MiscItem;
struct Tree;
struct Weapon;
struct World;
struct Land;
@ -122,7 +124,7 @@ namespace MWWorld
Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>,
Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>,
Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>,
Store<ESM4::Weapon>, Store<ESM4::World>, Store<ESM4::Land>>;
Store<ESM4::Tree>, Store<ESM4::Weapon>, Store<ESM4::World>, Store<ESM4::Furniture>, Store<ESM4::Land>>;
private:
template <typename T>

@ -1344,9 +1344,11 @@ template class MWWorld::TypedDynamicStore<ESM4::Book>;
template class MWWorld::TypedDynamicStore<ESM4::Clothing>;
template class MWWorld::TypedDynamicStore<ESM4::Container>;
template class MWWorld::TypedDynamicStore<ESM4::Door>;
template class MWWorld::TypedDynamicStore<ESM4::Furniture>;
template class MWWorld::TypedDynamicStore<ESM4::Ingredient>;
template class MWWorld::TypedDynamicStore<ESM4::MiscItem>;
template class MWWorld::TypedDynamicStore<ESM4::Static>;
template class MWWorld::TypedDynamicStore<ESM4::Tree>;
template class MWWorld::TypedDynamicStore<ESM4::Light>;
template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>;
template class MWWorld::TypedDynamicStore<ESM4::Cell>;

@ -299,6 +299,7 @@ namespace
Nif::NiSkinInstance mNiSkinInstance;
Nif::NiStringExtraData mNiStringExtraData;
Nif::NiStringExtraData mNiStringExtraData2;
Nif::NiIntegerExtraData mNiIntegerExtraData;
Nif::Controller mController;
btTransform mTransform{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3) };
btTransform mTransformScale2{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(2, 4, 6) };
@ -1157,6 +1158,25 @@ namespace
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader, bsx_editor_marker_flag_disables_collision)
{
mNiIntegerExtraData.data = 32; // BSX flag "editor marker"
mNiIntegerExtraData.recType = Nif::RC_BSXFlags;
mNiTriShape.extralist.push_back(Nif::ExtraPtr(&mNiIntegerExtraData));
mNiTriShape.parents.push_back(&mNiNode);
mNiNode.children = Nif::NodeList(std::vector<Nif::NodePtr>({ Nif::NodePtr(&mNiTriShape) }));
Nif::NIFFile file("test.nif");
file.mRoots.push_back(&mNiNode);
file.mHash = mHash;
const auto result = mLoader.load(file);
Resource::BulletShape expected;
EXPECT_EQ(*result, expected);
}
TEST_F(TestBulletNifLoader,
for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
{

@ -52,11 +52,13 @@
#include <components/esm4/loadclot.hpp>
#include <components/esm4/loadcont.hpp>
#include <components/esm4/loaddoor.hpp>
#include <components/esm4/loadfurn.hpp>
#include <components/esm4/loadingr.hpp>
#include <components/esm4/loadligh.hpp>
#include <components/esm4/loadmisc.hpp>
#include <components/esm4/loadrefr.hpp>
#include <components/esm4/loadstat.hpp>
#include <components/esm4/loadtree.hpp>
#include <components/esm4/loadweap.hpp>
#include "defs.hpp"

@ -33,8 +33,7 @@
void ESM4::Furniture::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.getFormId();
reader.adjustFormId(mFormId);
mId = reader.getRefIdFromHeader();
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())

@ -30,6 +30,9 @@
#include <cstdint>
#include <string>
#include <components/esm/defs.hpp>
#include <components/esm/refid.hpp>
#include "formid.hpp"
namespace ESM4
@ -39,7 +42,7 @@ namespace ESM4
struct Furniture
{
FormId mFormId; // from the header
ESM::RefId mId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
@ -55,6 +58,7 @@ namespace ESM4
// void save(ESM4::Writer& writer) const;
// void blank();
static constexpr ESM::RecNameInts sRecordId = ESM::RecNameInts::REC_FURN4;
};
}

@ -33,8 +33,7 @@
void ESM4::Tree::load(ESM4::Reader& reader)
{
mFormId = reader.hdr().record.getFormId();
reader.adjustFormId(mFormId);
mId = reader.getRefIdFromHeader();
mFlags = reader.hdr().record.flags;
while (reader.getSubRecordHeader())

@ -30,7 +30,8 @@
#include <cstdint>
#include <string>
#include "formid.hpp"
#include <components/esm/defs.hpp>
#include <components/esm/refid.hpp>
namespace ESM4
{
@ -39,7 +40,7 @@ namespace ESM4
struct Tree
{
FormId mFormId; // from the header
ESM::RefId mId; // from the header
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
std::string mEditorId;
@ -53,6 +54,7 @@ namespace ESM4
// void save(ESM4::Writer& writer) const;
// void blank();
static constexpr ESM::RecNameInts sRecordId = ESM::RecNameInts::REC_TREE4;
};
}

@ -138,6 +138,9 @@ namespace Nif
unsigned int type{ 0u }, flags1{ 0u }, flags2{ 0u };
float envMapIntensity{ 0.f };
void read(NIFStream* nif) override;
bool doubleSided() const { return (flags2 >> 4) & 1; }
bool treeAnim() const { return (flags2 >> 29) & 1; }
};
struct BSShaderLightingProperty : public BSShaderProperty

@ -301,7 +301,13 @@ namespace NifBullet
<< ". Treating it as a common NiTriShape.";
// Check for extra data
std::vector<Nif::ExtraPtr> extraCollection;
for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->next)
extraCollection.emplace_back(e);
for (const auto& extraNode : node.extralist)
if (!extraNode.empty())
extraCollection.emplace_back(extraNode);
for (const auto& e : extraCollection)
{
if (e->recType == Nif::RC_NiStringExtraData)
{
@ -326,6 +332,12 @@ namespace NifBullet
return;
}
}
else if (e->recType == Nif::RC_BSXFlags)
{
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
if (bsxFlags->data & 32) // Editor marker flag
return;
}
}
if (isCollisionNode)

@ -45,6 +45,7 @@
#include <components/nif/effect.hpp>
#include <components/nif/exception.hpp>
#include <components/nif/extra.hpp>
#include <components/nif/niffile.hpp>
#include <components/nif/node.hpp>
#include <components/nif/property.hpp>
#include <components/sceneutil/morphgeometry.hpp>
@ -318,6 +319,19 @@ namespace NifOsg
}
}
struct HandleNodeArgs
{
unsigned int mNifVersion;
Resource::ImageManager* mImageManager;
SceneUtil::TextKeyMap* mTextKeys;
std::vector<unsigned int> mBoundTextures = {};
int mAnimFlags = 0;
bool mSkipMeshes = false;
bool mHasMarkers = false;
bool mHasAnimatedParents = false;
osg::Node* mRootNode = nullptr;
};
osg::ref_ptr<osg::Node> load(Nif::FileView nif, Resource::ImageManager* imageManager)
{
const size_t numRoots = nif.numRoots();
@ -340,8 +354,10 @@ namespace NifOsg
created->setDataVariance(osg::Object::STATIC);
for (const Nif::Node* root : roots)
{
auto node = handleNode(root, nullptr, nullptr, imageManager, std::vector<unsigned int>(), 0, false,
false, false, &textkeys->mTextKeys);
auto node = handleNode(root, nullptr, nullptr,
{ .mNifVersion = nif.getVersion(),
.mImageManager = imageManager,
.mTextKeys = &textkeys->mTextKeys });
created->addChild(node);
}
if (mHasNightDayLabel)
@ -597,12 +613,10 @@ namespace NifOsg
return node;
}
osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode,
Resource::ImageManager* imageManager, std::vector<unsigned int> boundTextures, int animflags,
bool skipMeshes, bool hasMarkers, bool hasAnimatedParents, SceneUtil::TextKeyMap* textKeys,
osg::Node* rootNode = nullptr)
osg::ref_ptr<osg::Node> handleNode(
const Nif::Node* nifNode, const Nif::Parent* parent, osg::Group* parentNode, HandleNodeArgs args)
{
if (rootNode != nullptr && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
if (args.mRootNode && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box"))
return nullptr;
osg::ref_ptr<osg::Group> node = createNode(nifNode);
@ -617,8 +631,8 @@ namespace NifOsg
if (parentNode)
parentNode->addChild(node);
if (!rootNode)
rootNode = node;
if (!args.mRootNode)
args.mRootNode = node;
// The original NIF record index is used for a variety of features:
// - finding the correct emitter node for a particle system
@ -637,10 +651,10 @@ namespace NifOsg
for (const auto& e : extraCollection)
{
if (e->recType == Nif::RC_NiTextKeyExtraData && textKeys)
if (e->recType == Nif::RC_NiTextKeyExtraData && args.mTextKeys)
{
const Nif::NiTextKeyExtraData* tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
extractTextKeys(tk, *textKeys);
extractTextKeys(tk, *args.mTextKeys);
}
else if (e->recType == Nif::RC_NiStringExtraData)
{
@ -653,7 +667,7 @@ namespace NifOsg
if (sd->string == "MRK" && !Loader::getShowMarkers())
{
// Marker objects. These meshes are only visible in the editor.
hasMarkers = true;
args.mHasMarkers = true;
}
else if (sd->string == "BONE")
{
@ -665,10 +679,16 @@ namespace NifOsg
Misc::OsgUserValues::sExtraData, sd->string.substr(extraDataIdentifer.length()));
}
}
else if (e->recType == Nif::RC_BSXFlags)
{
auto bsxFlags = static_cast<const Nif::NiIntegerExtraData*>(e.getPtr());
if (bsxFlags->data & 32) // Editor marker flag
args.mHasMarkers = true;
}
}
if (nifNode->recType == Nif::RC_NiBSAnimationNode || nifNode->recType == Nif::RC_NiBSParticleNode)
animflags = nifNode->flags;
args.mAnimFlags = nifNode->flags;
if (nifNode->recType == Nif::RC_NiSortAdjustNode)
{
@ -692,7 +712,7 @@ namespace NifOsg
// We still need to animate the hidden bones so the physics system can access them
if (nifNode->recType == Nif::RC_RootCollisionNode)
{
skipMeshes = true;
args.mSkipMeshes = true;
node->setNodeMask(Loader::getHiddenNodeMask());
}
@ -709,8 +729,8 @@ namespace NifOsg
}
if (!hasVisController)
skipMeshes = true; // skip child meshes, but still create the child node hierarchy for animating
// collision shapes
args.mSkipMeshes = true; // skip child meshes, but still create the child node hierarchy for
// animating collision shapes
node->setNodeMask(Loader::getHiddenNodeMask());
}
@ -720,37 +740,44 @@ namespace NifOsg
osg::ref_ptr<SceneUtil::CompositeStateSetUpdater> composite = new SceneUtil::CompositeStateSetUpdater;
applyNodeProperties(nifNode, node, composite, imageManager, boundTextures, animflags);
applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags);
const bool isGeometry = isTypeGeometry(nifNode->recType);
if (isGeometry && !skipMeshes)
if (isGeometry && !args.mSkipMeshes)
{
const bool isMarker = hasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker");
if (!isMarker && !Misc::StringUtils::ciStartsWith(nifNode->name, "shadow")
&& !Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow"))
bool skip;
if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW)
{
skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "tri editormarker"))
|| Misc::StringUtils::ciStartsWith(nifNode->name, "shadow")
|| Misc::StringUtils::ciStartsWith(nifNode->name, "tri shadow");
}
else
skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->name, "EditorMarker");
if (!skip)
{
Nif::NiSkinInstancePtr skin = static_cast<const Nif::NiGeometry*>(nifNode)->skin;
if (skin.empty())
handleGeometry(nifNode, parent, node, composite, boundTextures, animflags);
handleGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
else
handleSkinnedGeometry(nifNode, parent, node, composite, boundTextures, animflags);
handleSkinnedGeometry(nifNode, parent, node, composite, args.mBoundTextures, args.mAnimFlags);
if (!nifNode->controller.empty())
handleMeshControllers(nifNode, node, composite, boundTextures, animflags);
handleMeshControllers(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags);
}
}
if (nifNode->recType == Nif::RC_NiParticles)
handleParticleSystem(nifNode, parent, node, composite, animflags);
handleParticleSystem(nifNode, parent, node, composite, args.mAnimFlags);
if (composite->getNumControllers() > 0)
{
osg::Callback* cb = composite;
if (composite->getNumControllers() == 1)
cb = composite->getController(0);
if (animflags & Nif::NiNode::AnimFlag_AutoPlay)
if (args.mAnimFlags & Nif::NiNode::AnimFlag_AutoPlay)
node->addCullCallback(cb);
else
node->addUpdateCallback(
@ -758,10 +785,10 @@ namespace NifOsg
}
bool isAnimated = false;
handleNodeControllers(nifNode, node, animflags, isAnimated);
hasAnimatedParents |= isAnimated;
handleNodeControllers(nifNode, node, args.mAnimFlags, isAnimated);
args.mHasAnimatedParents |= isAnimated;
// Make sure empty nodes and animated shapes are not optimized away so the physics system can find them.
if (isAnimated || (hasAnimatedParents && ((skipMeshes || hasMarkers) || isGeometry)))
if (isAnimated || (args.mHasAnimatedParents && ((args.mSkipMeshes || args.mHasMarkers) || isGeometry)))
node->setDataVariance(osg::Object::DYNAMIC);
// LOD and Switch nodes must be wrapped by a transform (the current node) to support transformations
@ -802,8 +829,7 @@ namespace NifOsg
const Nif::Parent currentParent{ *ninode, parent };
for (const auto& child : children)
if (!child.empty())
handleNode(child.getPtr(), &currentParent, currentNode, imageManager, boundTextures, animflags,
skipMeshes, hasMarkers, hasAnimatedParents, textKeys, rootNode);
handleNode(child.getPtr(), &currentParent, currentNode, args);
// Propagate effects to the the direct subgraph instead of the node itself
// This simulates their "affected node list" which Morrowind appears to replace with the subgraph (?)
@ -812,7 +838,7 @@ namespace NifOsg
if (!effect.empty())
{
osg::ref_ptr<osg::StateSet> effectStateSet = new osg::StateSet;
if (handleEffect(effect.getPtr(), effectStateSet, imageManager))
if (handleEffect(effect.getPtr(), effectStateSet, args.mImageManager))
for (unsigned int i = 0; i < currentNode->getNumChildren(); ++i)
currentNode->getChild(i)->getOrCreateStateSet()->merge(*effectStateSet);
}
@ -2143,6 +2169,8 @@ namespace NifOsg
textureSet, texprop->clamp, node->getName(), stateset, imageManager, boundTextures);
}
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
break;
}
case Nif::RC_BSShaderNoLightingProperty:
@ -2183,6 +2211,8 @@ namespace NifOsg
stateset->addUniform(new osg::Uniform("useFalloff", false));
}
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
break;
}
case Nif::RC_BSLightingShaderProperty:
@ -2196,6 +2226,10 @@ namespace NifOsg
handleTextureSet(texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset,
imageManager, boundTextures);
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
if (texprop->treeAnim())
stateset->addUniform(new osg::Uniform("useTreeAnim", true));
break;
}
case Nif::RC_BSEffectShaderProperty:
@ -2246,6 +2280,8 @@ namespace NifOsg
stateset->addUniform(new osg::Uniform("useFalloff", false)); // Should use the shader flag
stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams));
handleTextureControllers(texprop, composite, imageManager, stateset, animflags);
if (texprop->doubleSided())
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
break;
}
// unused by mw

@ -38,6 +38,7 @@ uniform float far;
uniform float alphaRef;
uniform float emissiveMult;
uniform float specStrength;
uniform bool useTreeAnim;
#include "lib/light/lighting.glsl"
#include "lib/material/alpha.glsl"
@ -58,7 +59,8 @@ void main()
#endif
vec4 diffuseColor = getDiffuseColor();
gl_FragData[0].a *= diffuseColor.a;
if (!useTreeAnim)
gl_FragData[0].a *= diffuseColor.a;
gl_FragData[0].a = alphaTest(gl_FragData[0].a, alphaRef);
#if @normalMap

@ -5,6 +5,7 @@ varying vec2 diffuseMapUV;
varying float alphaPassthrough;
uniform int colorMode;
uniform bool useTreeAnim;
uniform bool useDiffuseMapForShadowAlpha = true;
uniform bool alphaTestShadows = true;
@ -20,7 +21,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 = gl_Color.a;
alphaPassthrough = useTreeAnim ? 1.0 : 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;

Loading…
Cancel
Save