1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-29 04:06:40 +00:00

Merge branch 'tree_and_furniture' into 'master'

Load ESM4::Tree and ESM4::Furniture

See merge request OpenMW/openmw!3040
This commit is contained in:
psi29a 2023-06-01 09:15:46 +00:00
commit 27a879de9a
20 changed files with 157 additions and 44 deletions

View file

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

View file

@ -2,6 +2,7 @@
#define GAME_MWCLASS_ESM4BASE_H #define GAME_MWCLASS_ESM4BASE_H
#include <components/esm4/loadstat.hpp> #include <components/esm4/loadstat.hpp>
#include <components/esm4/loadtree.hpp>
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
#include "../mwgui/tooltips.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. // 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). // All objects with a tooltip can be activated (activation can be handled in Lua).
template <typename Record> template <typename Record>

View file

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

View file

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

View file

@ -172,6 +172,7 @@ namespace MWRender
stateset->addUniform(new osg::Uniform("isReflection", false)); stateset->addUniform(new osg::Uniform("isReflection", false));
stateset->addUniform(new osg::Uniform("windSpeed", 0.0f)); 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("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 void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override

View file

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

View file

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

View file

@ -39,8 +39,10 @@ namespace ESM4
struct Clothing; struct Clothing;
struct Container; struct Container;
struct Door; struct Door;
struct Furniture;
struct Ingredient; struct Ingredient;
struct MiscItem; struct MiscItem;
struct Tree;
struct Weapon; struct Weapon;
struct World; struct World;
struct Land; 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::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::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::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: private:
template <typename T> template <typename T>

View file

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

View file

@ -299,6 +299,7 @@ namespace
Nif::NiSkinInstance mNiSkinInstance; Nif::NiSkinInstance mNiSkinInstance;
Nif::NiStringExtraData mNiStringExtraData; Nif::NiStringExtraData mNiStringExtraData;
Nif::NiStringExtraData mNiStringExtraData2; Nif::NiStringExtraData mNiStringExtraData2;
Nif::NiIntegerExtraData mNiIntegerExtraData;
Nif::Controller mController; Nif::Controller mController;
btTransform mTransform{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(1, 2, 3) }; 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) }; btTransform mTransformScale2{ btMatrix3x3(btQuaternion(btVector3(1, 0, 0), 0.5f)), btVector3(2, 4, 6) };
@ -1157,6 +1158,25 @@ namespace
EXPECT_EQ(*result, expected); 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, 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) for_tri_shape_child_node_with_extra_data_string_mrk_and_other_collision_node_should_return_shape_with_triangle_mesh_shape_with_all_meshes)
{ {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -301,7 +301,13 @@ namespace NifBullet
<< ". Treating it as a common NiTriShape."; << ". Treating it as a common NiTriShape.";
// Check for extra data // Check for extra data
std::vector<Nif::ExtraPtr> extraCollection;
for (Nif::ExtraPtr e = node.extra; !e.empty(); e = e->next) 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) if (e->recType == Nif::RC_NiStringExtraData)
{ {
@ -326,6 +332,12 @@ namespace NifBullet
return; 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) if (isCollisionNode)

View file

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

View file

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

View file

@ -5,6 +5,7 @@ varying vec2 diffuseMapUV;
varying float alphaPassthrough; varying float alphaPassthrough;
uniform int colorMode; uniform int colorMode;
uniform bool useTreeAnim;
uniform bool useDiffuseMapForShadowAlpha = true; uniform bool useDiffuseMapForShadowAlpha = true;
uniform bool alphaTestShadows = true; uniform bool alphaTestShadows = true;
@ -20,7 +21,7 @@ void main(void)
else else
diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions diffuseMapUV = vec2(0.0); // Avoid undefined behaviour if running on hardware predating the concept of dynamically uniform expressions
if (colorMode == 2) if (colorMode == 2)
alphaPassthrough = gl_Color.a; alphaPassthrough = useTreeAnim ? 1.0 : gl_Color.a;
else 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 // 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; alphaPassthrough = gl_FrontMaterial.diffuse.a;