From 0748e2094d4af3afa12f2f8ec756449bc3e22228 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 22 Oct 2023 10:12:31 +0300 Subject: [PATCH 1/5] Read NiParticleBomb --- components/nif/niffile.cpp | 1 + components/nif/particle.cpp | 22 ++++++++++++++++++---- components/nif/particle.hpp | 36 ++++++++++++++++++++++++++++++++---- components/nif/record.hpp | 1 + 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 35a9eee787..81a223e095 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -308,6 +308,7 @@ namespace Nif // Modifiers, 4.0.0.2 { "NiGravity", &construct }, + { "NiParticleBomb", &construct }, { "NiParticleColorModifier", &construct }, { "NiParticleGrowFade", &construct }, { "NiParticleRotation", &construct }, diff --git a/components/nif/particle.cpp b/components/nif/particle.cpp index d74473f468..0581c5a1d1 100644 --- a/components/nif/particle.cpp +++ b/components/nif/particle.cpp @@ -51,6 +51,20 @@ namespace Nif nif->read(mDirection); } + void NiParticleBomb::read(NIFStream* nif) + { + NiParticleModifier::read(nif); + + nif->read(mRange); + nif->read(mDuration); + nif->read(mStrength); + nif->read(mStartTime); + mDecayType = static_cast(nif->get()); + mSymmetryType = static_cast(nif->get()); + nif->read(mPosition); + nif->read(mDirection); + } + void NiParticleCollider::read(NIFStream* nif) { NiParticleModifier::read(nif); @@ -294,10 +308,10 @@ namespace Nif mBombObject.read(nif); nif->read(mBombAxis); - nif->read(mDecay); - nif->read(mDeltaV); - nif->read(mDecayType); - nif->read(mSymmetryType); + nif->read(mRange); + nif->read(mStrength); + mDecayType = static_cast(nif->get()); + mSymmetryType = static_cast(nif->get()); } void NiPSysBombModifier::post(Reader& nif) diff --git a/components/nif/particle.hpp b/components/nif/particle.hpp index 45b6296891..1bdbd3a94a 100644 --- a/components/nif/particle.hpp +++ b/components/nif/particle.hpp @@ -40,6 +40,20 @@ namespace Nif Point = 1, // Fixed origin }; + enum class DecayType : uint32_t + { + None = 0, // f(Distance) = 1.0 + Linear = 1, // f(Distance) = (Range - Distance) / Range + Exponential = 2, // f(Distance) = exp(-Distance / Range) + }; + + enum class SymmetryType : uint32_t + { + Spherical = 0, + Cylindrical = 1, // Perpendicular to direction axis + Planar = 2, // Parallel to direction axis + }; + struct NiGravity : NiParticleModifier { float mDecay{ 0.f }; @@ -51,6 +65,20 @@ namespace Nif void read(NIFStream* nif) override; }; + struct NiParticleBomb : NiParticleModifier + { + float mRange; + float mDuration; + float mStrength; + float mStartTime; + DecayType mDecayType; + SymmetryType mSymmetryType; + osg::Vec3f mPosition; + osg::Vec3f mDirection; + + void read(NIFStream* nif); + }; + struct NiParticleCollider : NiParticleModifier { float mBounceFactor; @@ -210,10 +238,10 @@ namespace Nif { NiAVObjectPtr mBombObject; osg::Vec3f mBombAxis; - float mDecay; - float mDeltaV; - uint32_t mDecayType; - uint32_t mSymmetryType; + float mRange; + float mStrength; + DecayType mDecayType; + SymmetryType mSymmetryType; void read(NIFStream* nif) override; void post(Reader& nif) override; diff --git a/components/nif/record.hpp b/components/nif/record.hpp index 79958b361d..d2a30b1317 100644 --- a/components/nif/record.hpp +++ b/components/nif/record.hpp @@ -206,6 +206,7 @@ namespace Nif RC_NiMultiTargetTransformController, RC_NiNode, RC_NiPalette, + RC_NiParticleBomb, RC_NiParticleColorModifier, RC_NiParticleGrowFade, RC_NiParticleRotation, From 6081dcc43cd274f308658ff53a02cbd5bb212d23 Mon Sep 17 00:00:00 2001 From: uramer Date: Sat, 21 Oct 2023 21:40:02 +0200 Subject: [PATCH 2/5] Work around MyGUI bug in a less destructive way --- components/lua_ui/widget.cpp | 20 ++++++++++++-------- components/lua_ui/widget.hpp | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/components/lua_ui/widget.cpp b/components/lua_ui/widget.cpp index ff9f4d90a2..767d425453 100644 --- a/components/lua_ui/widget.cpp +++ b/components/lua_ui/widget.cpp @@ -92,15 +92,21 @@ namespace LuaUi w->widget()->detachFromWidget(); } + void WidgetExtension::updateVisible() + { + // workaround for MyGUI bug + // parent visibility doesn't affect added children + MyGUI::Widget* widget = this->widget(); + MyGUI::Widget* parent = widget->getParent(); + bool inheritedVisible = widget->getVisible() && (parent == nullptr || parent->getInheritedVisible()); + widget->setVisible(inheritedVisible); + } + void WidgetExtension::attach(WidgetExtension* ext) { ext->mParent = this; ext->mTemplateChild = false; ext->widget()->attachToWidget(mSlot->widget()); - // workaround for MyGUI bug - // parent visibility doesn't affect added children - ext->widget()->setVisible(!ext->widget()->getVisible()); - ext->widget()->setVisible(!ext->widget()->getVisible()); } void WidgetExtension::attachTemplate(WidgetExtension* ext) @@ -108,10 +114,6 @@ namespace LuaUi ext->mParent = this; ext->mTemplateChild = true; ext->widget()->attachToWidget(widget()); - // workaround for MyGUI bug - // parent visibility doesn't affect added children - ext->widget()->setVisible(!ext->widget()->getVisible()); - ext->widget()->setVisible(!ext->widget()->getVisible()); } WidgetExtension* WidgetExtension::findDeep(std::string_view flagName) @@ -256,6 +258,8 @@ namespace LuaUi void WidgetExtension::updateCoord() { + updateVisible(); + MyGUI::IntCoord oldCoord = mWidget->getCoord(); MyGUI::IntCoord newCoord = calculateCoord(); diff --git a/components/lua_ui/widget.hpp b/components/lua_ui/widget.hpp index 3dbc14b6c3..81698b0479 100644 --- a/components/lua_ui/widget.hpp +++ b/components/lua_ui/widget.hpp @@ -173,6 +173,8 @@ namespace LuaUi void focusLoss(MyGUI::Widget*, MyGUI::Widget*); std::optional> mOnCoordChange; + + void updateVisible(); }; class LuaWidget : public MyGUI::Widget, public WidgetExtension From 76939aae45a72aba52bbcb2237f2c3d186a527c1 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Tue, 3 Oct 2023 01:27:14 +0300 Subject: [PATCH 3/5] Refurbish RigGeometry Restructure/untangle influence data Don't store the input influence data Overall cleanup --- components/sceneutil/riggeometry.cpp | 141 ++++++++------------------- components/sceneutil/riggeometry.hpp | 35 +++---- 2 files changed, 54 insertions(+), 122 deletions(-) diff --git a/components/sceneutil/riggeometry.cpp b/components/sceneutil/riggeometry.cpp index b940fb60ec..1572fab338 100644 --- a/components/sceneutil/riggeometry.cpp +++ b/components/sceneutil/riggeometry.cpp @@ -1,5 +1,7 @@ #include "riggeometry.hpp" +#include + #include #include @@ -10,39 +12,10 @@ #include "skeleton.hpp" #include "util.hpp" -namespace -{ - inline void accumulateMatrix( - const osg::Matrixf& invBindMatrix, const osg::Matrixf& matrix, const float weight, osg::Matrixf& result) - { - osg::Matrixf m = invBindMatrix * matrix; - float* ptr = m.ptr(); - float* ptrresult = result.ptr(); - ptrresult[0] += ptr[0] * weight; - ptrresult[1] += ptr[1] * weight; - ptrresult[2] += ptr[2] * weight; - - ptrresult[4] += ptr[4] * weight; - ptrresult[5] += ptr[5] * weight; - ptrresult[6] += ptr[6] * weight; - - ptrresult[8] += ptr[8] * weight; - ptrresult[9] += ptr[9] * weight; - ptrresult[10] += ptr[10] * weight; - - ptrresult[12] += ptr[12] * weight; - ptrresult[13] += ptr[13] * weight; - ptrresult[14] += ptr[14] * weight; - } -} - namespace SceneUtil { RigGeometry::RigGeometry() - : mSkeleton(nullptr) - , mLastFrameNumber(0) - , mBoundsFirstFrame(true) { setNumChildrenRequiringUpdateTraversal(1); // update done in accept(NodeVisitor&) @@ -50,12 +23,7 @@ namespace SceneUtil RigGeometry::RigGeometry(const RigGeometry& copy, const osg::CopyOp& copyop) : Drawable(copy, copyop) - , mSkeleton(nullptr) - , mInfluenceMap(copy.mInfluenceMap) - , mBone2VertexVector(copy.mBone2VertexVector) - , mBoneSphereVector(copy.mBoneSphereVector) - , mLastFrameNumber(0) - , mBoundsFirstFrame(true) + , mData(copy.mData) { setSourceGeometry(copy.mSourceGeometry); setNumChildrenRequiringUpdateTraversal(1); @@ -151,42 +119,18 @@ namespace SceneUtil return false; } - if (!mInfluenceMap) + if (!mData) { - Log(Debug::Error) << "Error: No InfluenceMap set on RigGeometry"; + Log(Debug::Error) << "Error: No influence data set on RigGeometry"; return false; } - mBoneNodesVector.clear(); - for (auto& bonePair : mBoneSphereVector->mData) + mNodes.clear(); + for (const BoneInfo& info : mData->mBones) { - const std::string& boneName = bonePair.first; - Bone* bone = mSkeleton->getBone(boneName); - if (!bone) - { - mBoneNodesVector.push_back(nullptr); - Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName; - continue; - } - - mBoneNodesVector.push_back(bone); - } - - for (auto& pair : mBone2VertexVector->mData) - { - for (auto& weight : pair.first) - { - const std::string& boneName = weight.first.first; - Bone* bone = mSkeleton->getBone(boneName); - if (!bone) - { - mBoneNodesVector.push_back(nullptr); - Log(Debug::Error) << "Error: RigGeometry did not find bone " << boneName; - continue; - } - - mBoneNodesVector.push_back(bone); - } + mNodes.push_back(mSkeleton->getBone(info.mName)); + if (!mNodes.back()) + Log(Debug::Error) << "Error: RigGeometry did not find bone " << info.mName; } return true; @@ -226,25 +170,28 @@ namespace SceneUtil osg::Vec3Array* normalDst = static_cast(geom.getNormalArray()); osg::Vec4Array* tangentDst = static_cast(geom.getTexCoordArray(7)); - int index = mBoneSphereVector->mData.size(); - for (auto& pair : mBone2VertexVector->mData) + for (const auto& [influences, vertices] : mData->mInfluences) { osg::Matrixf resultMat(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - for (auto& weight : pair.first) + for (const auto& [index, weight] : influences) { - Bone* bone = mBoneNodesVector[index]; + const Bone* bone = mNodes[index]; if (bone == nullptr) continue; - accumulateMatrix(weight.first.second, bone->mMatrixInSkeletonSpace, weight.second, resultMat); - index++; + osg::Matrixf boneMat = mData->mBones[index].mInvBindMatrix * bone->mMatrixInSkeletonSpace; + float* boneMatPtr = boneMat.ptr(); + float* resultMatPtr = resultMat.ptr(); + for (int i = 0; i < 16; ++i, ++resultMatPtr, ++boneMatPtr) + if (i % 4 != 3) + *resultMatPtr += *boneMatPtr * weight; } if (mGeomToSkelMatrix) resultMat *= (*mGeomToSkelMatrix); - for (auto& vertex : pair.second) + for (unsigned short vertex : vertices) { (*positionDst)[vertex] = resultMat.preMult((*positionSrc)[vertex]); if (normalDst) @@ -291,15 +238,14 @@ namespace SceneUtil osg::BoundingBox box; - int index = 0; - for (auto& boundPair : mBoneSphereVector->mData) + size_t index = 0; + for (const BoneInfo& info : mData->mBones) { - Bone* bone = mBoneNodesVector[index]; + const Bone* bone = mNodes[index++]; if (bone == nullptr) continue; - index++; - osg::BoundingSpheref bs = boundPair.second; + osg::BoundingSpheref bs = info.mBoundSphere; if (mGeomToSkelMatrix) transformBoundingSphere(bone->mMatrixInSkeletonSpace * (*mGeomToSkelMatrix), bs); else @@ -357,35 +303,26 @@ namespace SceneUtil void RigGeometry::setInfluenceMap(osg::ref_ptr influenceMap) { - mInfluenceMap = influenceMap; - - typedef std::map> Vertex2BoneMap; - Vertex2BoneMap vertex2BoneMap; - mBoneSphereVector = new BoneSphereVector; - mBoneSphereVector->mData.reserve(mInfluenceMap->mData.size()); - mBone2VertexVector = new Bone2VertexVector; - for (auto& influencePair : mInfluenceMap->mData) - { - const std::string& boneName = influencePair.first; - const BoneInfluence& bi = influencePair.second; - mBoneSphereVector->mData.emplace_back(boneName, bi.mBoundSphere); + mData = new InfluenceData; + mData->mBones.reserve(influenceMap->mData.size()); - for (auto& weightPair : bi.mWeights) - { - std::vector& vec = vertex2BoneMap[weightPair.first]; + std::unordered_map> vertexToInfluences; + size_t index = 0; + for (const auto& [boneName, bi] : influenceMap->mData) + { + mData->mBones.push_back({ boneName, bi.mBoundSphere, bi.mInvBindMatrix }); - vec.emplace_back(std::make_pair(boneName, bi.mInvBindMatrix), weightPair.second); - } + for (const auto& [vertex, weight] : bi.mWeights) + vertexToInfluences[vertex].emplace_back(index, weight); + index++; } - Bone2VertexMap bone2VertexMap; - for (auto& vertexPair : vertex2BoneMap) - { - bone2VertexMap[vertexPair.second].emplace_back(vertexPair.first); - } + std::map, VertexList> influencesToVertices; + for (const auto& [vertex, weights] : vertexToInfluences) + influencesToVertices[weights].emplace_back(vertex); - mBone2VertexVector->mData.reserve(bone2VertexMap.size()); - mBone2VertexVector->mData.assign(bone2VertexMap.begin(), bone2VertexMap.end()); + mData->mInfluences.reserve(influencesToVertices.size()); + mData->mInfluences.assign(influencesToVertices.begin(), influencesToVertices.end()); } void RigGeometry::accept(osg::NodeVisitor& nv) diff --git a/components/sceneutil/riggeometry.hpp b/components/sceneutil/riggeometry.hpp index 72e8fcd032..d1c077288d 100644 --- a/components/sceneutil/riggeometry.hpp +++ b/components/sceneutil/riggeometry.hpp @@ -36,6 +36,7 @@ namespace SceneUtil // static parts of the model. void compileGLObjects(osg::RenderInfo& renderInfo) const override {} + // TODO: Make InfluenceMap more similar to InfluenceData struct BoneInfluence { osg::Matrixf mInvBindMatrix; @@ -84,35 +85,29 @@ namespace SceneUtil osg::ref_ptr mSourceGeometry; osg::ref_ptr mSourceTangents; - Skeleton* mSkeleton; + Skeleton* mSkeleton{ nullptr }; osg::ref_ptr mGeomToSkelMatrix; - osg::ref_ptr mInfluenceMap; - - typedef std::pair BoneBindMatrixPair; - - typedef std::pair BoneWeight; - - typedef std::vector VertexList; - - typedef std::map, VertexList> Bone2VertexMap; - - struct Bone2VertexVector : public osg::Referenced + struct BoneInfo { - std::vector, VertexList>> mData; + std::string mName; + osg::BoundingSpheref mBoundSphere; + osg::Matrixf mInvBindMatrix; }; - osg::ref_ptr mBone2VertexVector; - struct BoneSphereVector : public osg::Referenced + using BoneWeight = std::pair; + using VertexList = std::vector; + struct InfluenceData : public osg::Referenced { - std::vector> mData; + std::vector mBones; + std::vector, VertexList>> mInfluences; }; - osg::ref_ptr mBoneSphereVector; - std::vector mBoneNodesVector; + osg::ref_ptr mData; + std::vector mNodes; - unsigned int mLastFrameNumber; - bool mBoundsFirstFrame; + unsigned int mLastFrameNumber{ 0 }; + bool mBoundsFirstFrame{ true }; bool initFromParentSkeleton(osg::NodeVisitor* nv); From 715efe6cb19e24ebb3d33d8b235748cc66bd3f14 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sun, 22 Oct 2023 11:03:13 +0300 Subject: [PATCH 4/5] Handle NiParticleBomb (feature #7634) --- CHANGELOG.md | 1 + components/nifosg/nifloader.cpp | 12 +++++ components/nifosg/particle.cpp | 78 ++++++++++++++++++++++++++++++ components/nifosg/particle.hpp | 25 ++++++++++ components/sceneutil/serialize.cpp | 2 +- 5 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bbc5b5b1b..0f6ed2de6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,7 @@ Feature #7546: Start the game on Fredas Feature #7568: Uninterruptable scripted music Feature #7618: Show the player character's health in the save details + Feature #7634: Support NiParticleBomb Task #5896: Do not use deprecated MyGUI properties Task #7113: Move from std::atoi to std::from_char Task #7117: Replace boost::scoped_array with std::vector diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3b8c47e071..a5f1032c69 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -1092,6 +1092,18 @@ namespace NifOsg const Nif::NiGravity* gr = static_cast(modifier.getPtr()); program->addOperator(new GravityAffector(gr)); } + else if (modifier->recType == Nif::RC_NiParticleBomb) + { + auto bomb = static_cast(modifier.getPtr()); + osg::ref_ptr bombProgram(new osgParticle::ModularProgram); + attachTo->addChild(bombProgram); + bombProgram->setParticleSystem(partsys); + bombProgram->setReferenceFrame(rf); + bombProgram->setStartTime(bomb->mStartTime); + bombProgram->setLifeTime(bomb->mDuration); + bombProgram->setEndless(false); + bombProgram->addOperator(new ParticleBomb(bomb)); + } else if (modifier->recType == Nif::RC_NiParticleColorModifier) { const Nif::NiParticleColorModifier* cl diff --git a/components/nifosg/particle.cpp b/components/nifosg/particle.cpp index 551cbfeae8..f15678c879 100644 --- a/components/nifosg/particle.cpp +++ b/components/nifosg/particle.cpp @@ -346,6 +346,84 @@ namespace NifOsg } } + ParticleBomb::ParticleBomb(const Nif::NiParticleBomb* bomb) + : mRange(bomb->mRange) + , mStrength(bomb->mStrength) + , mDecayType(bomb->mDecayType) + , mSymmetryType(bomb->mSymmetryType) + , mPosition(bomb->mPosition) + , mDirection(bomb->mDirection) + { + } + + ParticleBomb::ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop) + : osgParticle::Operator(copy, copyop) + { + mRange = copy.mRange; + mStrength = copy.mStrength; + mDecayType = copy.mDecayType; + mSymmetryType = copy.mSymmetryType; + mCachedWorldPosition = copy.mCachedWorldPosition; + mCachedWorldDirection = copy.mCachedWorldDirection; + } + + void ParticleBomb::beginOperate(osgParticle::Program* program) + { + bool absolute = (program->getReferenceFrame() == osgParticle::ParticleProcessor::ABSOLUTE_RF); + + mCachedWorldPosition = absolute ? program->transformLocalToWorld(mPosition) : mPosition; + + // We don't need the direction for Spherical bomb + if (mSymmetryType != Nif::SymmetryType::Spherical) + { + mCachedWorldDirection = absolute ? program->rotateLocalToWorld(mDirection) : mDirection; + mCachedWorldDirection.normalize(); + } + } + + void ParticleBomb::operate(osgParticle::Particle* particle, double dt) + { + float decay = 1.f; + osg::Vec3f explosionDir; + + osg::Vec3f particleDir = particle->getPosition() - mCachedWorldPosition; + float distance = particleDir.length(); + particleDir.normalize(); + + switch (mDecayType) + { + case Nif::DecayType::None: + break; + case Nif::DecayType::Linear: + decay = 1.f - distance / mRange; + break; + case Nif::DecayType::Exponential: + decay = std::exp(-distance / mRange); + break; + } + + if (decay <= 0.f) + return; + + switch (mSymmetryType) + { + case Nif::SymmetryType::Spherical: + explosionDir = particleDir; + break; + case Nif::SymmetryType::Cylindrical: + explosionDir = particleDir - mCachedWorldDirection * (mCachedWorldDirection * particleDir); + explosionDir.normalize(); + break; + case Nif::SymmetryType::Planar: + explosionDir = mCachedWorldDirection; + if (explosionDir * particleDir < 0) + explosionDir = -explosionDir; + break; + } + + particle->addVelocity(explosionDir * mStrength * decay * dt); + } + Emitter::Emitter() : osgParticle::Emitter() , mFlags(0) diff --git a/components/nifosg/particle.hpp b/components/nifosg/particle.hpp index 967531013a..272bc5baed 100644 --- a/components/nifosg/particle.hpp +++ b/components/nifosg/particle.hpp @@ -199,6 +199,31 @@ namespace NifOsg osg::Vec3f mCachedWorldDirection; }; + class ParticleBomb : public osgParticle::Operator + { + public: + ParticleBomb(const Nif::NiParticleBomb* bomb); + ParticleBomb() = default; + ParticleBomb(const ParticleBomb& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + ParticleBomb& operator=(const ParticleBomb&) = delete; + + META_Object(NifOsg, ParticleBomb) + + void operate(osgParticle::Particle* particle, double dt) override; + void beginOperate(osgParticle::Program*) override; + + private: + float mRange{ 0.f }; + float mStrength{ 0.f }; + Nif::DecayType mDecayType{ Nif::DecayType::None }; + Nif::SymmetryType mSymmetryType{ Nif::SymmetryType::Spherical }; + osg::Vec3f mPosition; + osg::Vec3f mDirection; + osg::Vec3f mCachedWorldPosition; + osg::Vec3f mCachedWorldDirection; + }; + // NodeVisitor to find a Group node with the given record index, stored in the node's user data container. // Alternatively, returns the node's parent Group if that node is not a Group (i.e. a leaf node). class FindGroupByRecIndex : public osg::NodeVisitor diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 066f43d123..784dafafa5 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -167,7 +167,7 @@ namespace SceneUtil "SceneUtil::DisableLight", "SceneUtil::MWShadowTechnique", "SceneUtil::TextKeyMapHolder", "Shader::AddedState", "Shader::RemovedAlphaFunc", "NifOsg::FlipController", "NifOsg::KeyframeController", "NifOsg::Emitter", "NifOsg::ParticleColorAffector", - "NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::GrowFadeAffector", + "NifOsg::ParticleSystem", "NifOsg::GravityAffector", "NifOsg::ParticleBomb", "NifOsg::GrowFadeAffector", "NifOsg::InverseWorldMatrix", "NifOsg::StaticBoundingBoxCallback", "NifOsg::GeomMorpherController", "NifOsg::UpdateMorphGeometry", "NifOsg::UVController", "NifOsg::VisController", "osgMyGUI::Drawable", "osg::DrawCallback", "osg::UniformBufferObject", "osgOQ::ClearQueriesCallback", From db42a91867e61e64bc4f58c3597ead99e4f1db9b Mon Sep 17 00:00:00 2001 From: Zackhasacat Date: Tue, 24 Oct 2023 09:23:25 +0000 Subject: [PATCH 5/5] Add global variable access to world.mwscript (#7597) --- apps/openmw/mwlua/mwscriptbindings.cpp | 51 ++++++++++++++++++++++++++ files/lua_api/openmw/world.lua | 6 +++ 2 files changed, 57 insertions(+) diff --git a/apps/openmw/mwlua/mwscriptbindings.cpp b/apps/openmw/mwlua/mwscriptbindings.cpp index dbe02a9fed..92957efb71 100644 --- a/apps/openmw/mwlua/mwscriptbindings.cpp +++ b/apps/openmw/mwlua/mwscriptbindings.cpp @@ -7,6 +7,7 @@ #include "../mwbase/scriptmanager.hpp" #include "../mwbase/world.hpp" #include "../mwscript/globalscripts.hpp" +#include "../mwworld/esmstore.hpp" #include "object.hpp" @@ -43,6 +44,10 @@ namespace sol struct is_automagical : std::false_type { }; + template <> + struct is_automagical : std::false_type + { + }; } namespace MWLua @@ -76,6 +81,7 @@ namespace MWLua // api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts // api["getLocalScripts"] = [](const GObject& obj) -> list of scripts + sol::state_view& lua = context.mLua->sol(); sol::usertype mwscript = context.mLua->sol().new_usertype("MWScript"); sol::usertype mwscriptVars = context.mLua->sol().new_usertype("MWScriptVariables"); @@ -108,6 +114,51 @@ namespace MWLua "No variable \"" + std::string(var) + "\" in mwscript " + s.mRef.mId.toDebugString()); }; + using GlobalStore = MWWorld::Store; + sol::usertype globalStoreT = lua.new_usertype("ESM3_GlobalStore"); + const GlobalStore* globalStore = &MWBase::Environment::get().getWorld()->getStore().get(); + globalStoreT[sol::meta_function::to_string] = [](const GlobalStore& store) { + return "ESM3_GlobalStore{" + std::to_string(store.getSize()) + " globals}"; + }; + globalStoreT[sol::meta_function::length] = [](const GlobalStore& store) { return store.getSize(); }; + globalStoreT[sol::meta_function::index] + = sol::overload([](const GlobalStore& store, std::string_view globalId) -> sol::optional { + auto g = store.search(ESM::RefId::deserializeText(globalId)); + if (g == nullptr) + return sol::nullopt; + char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId); + if (varType == 's' || varType == 'l') + { + return static_cast(MWBase::Environment::get().getWorld()->getGlobalInt(globalId)); + } + else + { + return MWBase::Environment::get().getWorld()->getGlobalFloat(globalId); + } + }); + globalStoreT[sol::meta_function::new_index] + = sol::overload([](const GlobalStore& store, std::string_view globalId, float val) { + auto g = store.search(ESM::RefId::deserializeText(globalId)); + if (g == nullptr) + return; + char varType = MWBase::Environment::get().getWorld()->getGlobalVariableType(globalId); + if (varType == 's' || varType == 'l') + { + MWBase::Environment::get().getWorld()->setGlobalInt(globalId, static_cast(val)); + } + else + { + MWBase::Environment::get().getWorld()->setGlobalFloat(globalId, val); + } + }); + globalStoreT[sol::meta_function::pairs] = lua["ipairsForArray"].template get(); + globalStoreT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get(); + api["getGlobalVariables"] = [globalStore](sol::optional player) { + if (player.has_value() && player->ptr() != MWBase::Environment::get().getWorld()->getPlayerPtr()) + throw std::runtime_error("First argument must either be a player or be missing"); + + return globalStore; + }; return LuaUtil::makeReadOnly(api); } diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 90868387e5..13fa75e0ad 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -29,6 +29,12 @@ -- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the script if there is a separate instance for each player. Currently has no effect. -- @return #MWScript, #nil +--- +-- Returns mutable global variables. In multiplayer, these may be specific to the provided player. +-- @function [parent=#MWScriptFunctions] getGlobalVariables +-- @param openmw.core#GameObject player (optional) Will be used in multiplayer mode to get the globals if there is a separate instance for each player. Currently has no effect. +-- @return #list<#number> + --- -- Returns global mwscript with given recordId. Returns `nil` if the script doesn't exist or is not started. -- Currently there can be only one instance of each mwscript, but in multiplayer it will be possible to have a separate instance per player.