add loop + vfxId params to world.vfx.spawn and add world.vfx.remove

This commit is contained in:
Sebastian Fieber 2025-01-05 20:32:02 +01:00
parent 710aa9ad4f
commit bc9df48dd2
10 changed files with 113 additions and 35 deletions

View file

@ -516,9 +516,12 @@ namespace MWBase
virtual void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) = 0;
virtual void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true)
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true,
std::string_view effectId = {}, bool loop = false)
= 0;
virtual void removeEffect(std::string_view effectId) = 0;
/// @see MWWorld::WeatherManager::isInStorm
virtual bool isInStorm() const = 0;

View file

@ -312,28 +312,34 @@ namespace MWLua
sol::table api(context.mLua->unsafeState(), sol::create);
auto world = MWBase::Environment::get().getWorld();
api["spawn"]
= [world, context](std::string_view model, const osg::Vec3f& worldPos, sol::optional<sol::table> options) {
if (options)
{
bool magicVfx = options->get_or("mwMagicVfx", true);
std::string texture = options->get_or<std::string>("particleTextureOverride", "");
float scale = options->get_or("scale", 1.f);
bool useAmbientLight = options->get_or("useAmbientLight", true);
context.mLuaManager->addAction(
[world, model = VFS::Path::Normalized(model), texture = std::move(texture), worldPos, scale,
magicVfx, useAmbientLight]() {
world->spawnEffect(model, texture, worldPos, scale, magicVfx, useAmbientLight);
},
"openmw.vfx.spawn");
}
else
{
context.mLuaManager->addAction([world, model = VFS::Path::Normalized(model),
worldPos]() { world->spawnEffect(model, "", worldPos, 1.f); },
"openmw.vfx.spawn");
}
};
api["remove"] = [world, context](std::string_view vfxId) {
context.mLuaManager->addAction([world, vfxId = vfxId] { world->removeEffect(vfxId); }, "openmw.vfx.remove");
};
api["spawn"] = [world, context](
std::string_view model, const osg::Vec3f& worldPos, sol::optional<sol::table> options) {
if (options)
{
bool magicVfx = options->get_or("mwMagicVfx", true);
std::string texture = options->get_or<std::string>("particleTextureOverride", "");
float scale = options->get_or("scale", 1.f);
std::string_view vfxId = options->get_or<std::string_view>("vfxId", "");
bool loop = options->get_or("loop", false);
bool useAmbientLight = options->get_or("useAmbientLight", true);
context.mLuaManager->addAction(
[world, model = VFS::Path::Normalized(model), texture = std::move(texture), worldPos, scale,
magicVfx, useAmbientLight, vfxId, loop]() {
world->spawnEffect(model, texture, worldPos, scale, magicVfx, useAmbientLight, vfxId, loop);
},
"openmw.vfx.spawn");
}
else
{
context.mLuaManager->addAction([world, model = VFS::Path::Normalized(model),
worldPos]() { world->spawnEffect(model, "", worldPos, 1.f); },
"openmw.vfx.spawn");
}
};
return api;
}

View file

@ -28,7 +28,8 @@ namespace MWRender
}
void EffectManager::addEffect(VFS::Path::NormalizedView model, std::string_view textureOverride,
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX, bool useAmbientLight)
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX, bool useAmbientLight, std::string_view effectId,
bool loop)
{
osg::ref_ptr<osg::Node> node = mResourceSystem->getSceneManager()->getInstance(model);
@ -36,6 +37,8 @@ namespace MWRender
Effect effect;
effect.mAnimTime = std::make_shared<EffectAnimationTime>();
effect.loop = loop;
effect.effectId = std::string(effectId);
SceneUtil::FindMaxControllerLengthVisitor findMaxLengthVisitor;
node->accept(findMaxLengthVisitor);
@ -67,17 +70,47 @@ namespace MWRender
mResourceSystem->getSceneManager()->setUpNormalsRTForStateSet(node->getOrCreateStateSet(), false);
std::lock_guard<std::mutex> lock(mEffectsMutex);
mEffects.push_back(std::move(effect));
}
void EffectManager::removeEffect(std::string_view effectId)
{
std::lock_guard<std::mutex> lock(mEffectsMutex);
mEffects.erase(std::remove_if(mEffects.begin(), mEffects.end(),
[effectId, this](Effect& effect) {
if (effectId.compare(effect.effectId) == 0)
{
mParentNode->removeChild(effect.mTransform);
return true;
}
return false;
}),
mEffects.end());
}
void EffectManager::update(float dt)
{
std::lock_guard<std::mutex> lock(mEffectsMutex);
mEffects.erase(std::remove_if(mEffects.begin(), mEffects.end(),
[dt, this](Effect& effect) {
bool remove = false;
effect.mAnimTime->addTime(dt);
const auto remove = effect.mAnimTime->getTime() >= effect.mMaxControllerLength;
if (remove)
mParentNode->removeChild(effect.mTransform);
if (effect.mAnimTime->getTime() >= effect.mMaxControllerLength)
{
if (effect.loop)
{
float remainder = effect.mAnimTime->getTime() - effect.mMaxControllerLength;
effect.mAnimTime->resetTime(remainder);
}
else
{
mParentNode->removeChild(effect.mTransform);
remove = true;
}
}
return remove;
}),
mEffects.end());
@ -85,6 +118,7 @@ namespace MWRender
void EffectManager::clear()
{
std::lock_guard<std::mutex> lock(mEffectsMutex);
for (const auto& effect : mEffects)
{
mParentNode->removeChild(effect.mTransform);

View file

@ -35,7 +35,10 @@ namespace MWRender
/// Add an effect. When it's finished playing, it will be removed automatically.
void addEffect(VFS::Path::NormalizedView model, std::string_view textureOverride,
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true, bool useAmbientLight = true);
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX = true, bool useAmbientLight = true,
std::string_view effectId = {}, bool loop = false);
void removeEffect(std::string_view effectId);
void update(float dt);
@ -45,11 +48,14 @@ namespace MWRender
private:
struct Effect
{
std::string effectId;
float mMaxControllerLength;
bool loop;
std::shared_ptr<EffectAnimationTime> mAnimTime;
osg::ref_ptr<osg::PositionAttitudeTransform> mTransform;
};
std::mutex mEffectsMutex;
std::vector<Effect> mEffects;
osg::ref_ptr<osg::Group> mParentNode;

View file

@ -1253,9 +1253,15 @@ namespace MWRender
}
void RenderingManager::spawnEffect(VFS::Path::NormalizedView model, std::string_view texture,
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX, bool useAmbientLight)
const osg::Vec3f& worldPosition, float scale, bool isMagicVFX, bool useAmbientLight, std::string_view effectId,
bool loop)
{
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX, useAmbientLight);
mEffectManager->addEffect(model, texture, worldPosition, scale, isMagicVFX, useAmbientLight, effectId, loop);
}
void RenderingManager::removeEffect(std::string_view effectId)
{
mEffectManager->removeEffect(effectId);
}
void RenderingManager::notifyWorldSpaceChanged()

View file

@ -195,7 +195,10 @@ namespace MWRender
SkyManager* getSkyManager();
void spawnEffect(VFS::Path::NormalizedView model, std::string_view texture, const osg::Vec3f& worldPosition,
float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true);
float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true, std::string_view effectId = {},
bool loop = false);
void removeEffect(std::string_view effectId);
/// Clear all savegame-specific data
void clear();

View file

@ -3689,9 +3689,15 @@ namespace MWWorld
}
void World::spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
const osg::Vec3f& worldPos, float scale, bool isMagicVFX, bool useAmbientLight)
const osg::Vec3f& worldPos, float scale, bool isMagicVFX, bool useAmbientLight, std::string_view effectId,
bool loop)
{
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX, useAmbientLight);
mRendering->spawnEffect(model, textureOverride, worldPos, scale, isMagicVFX, useAmbientLight, effectId, loop);
}
void World::removeEffect(std::string_view effectId)
{
mRendering->removeEffect(effectId);
}
struct ResetActorsVisitor

View file

@ -603,8 +603,10 @@ namespace MWWorld
void spawnBloodEffect(const MWWorld::Ptr& ptr, const osg::Vec3f& worldPosition) override;
void spawnEffect(VFS::Path::NormalizedView model, const std::string& textureOverride,
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true,
bool useAmbientLight = true) override;
const osg::Vec3f& worldPos, float scale = 1.f, bool isMagicVFX = true, bool useAmbientLight = true,
std::string_view effectId = {}, bool loop = false) override;
void removeEffect(std::string_view effectId) override;
/// @see MWWorld::WeatherManager::isInStorm
bool isInStorm() const override;

View file

@ -7,5 +7,6 @@ return {
SetGameTimeScale = function(scale) world.setGameTimeScale(scale) end,
SetSimulationTimeScale = function(scale) world.setSimulationTimeScale(scale) end,
SpawnVfx = function(data) world.vfx.spawn(data.model, data.position, data.options) end,
RemoveVfx = function(vfxId) world.vfx.remove(vfxId) end,
},
}

View file

@ -196,6 +196,8 @@
-- * `particleTextureOverride` - Name of a particle texture that should override this effect's default texture. (default: "")
-- * `scale` - A number that scales the size of the vfx (Default: 1)
-- * `useAmbientLighting` - boolean, vfx get a white ambient light attached in Morrowind. If false don't attach this. (default: 1)
-- * `loop` - boolean, if true the effect will loop until removed (default: 0).
-- * `vfxId` - a string ID that can be used to remove the effect later, using #remove. (Default: "").
--
-- @usage -- Spawn a sanctuary effect near the player
-- local effect = core.magic.effects.records[core.magic.EFFECT_TYPE.Sanctuary]
@ -204,4 +206,13 @@
-- core.sendGlobalEvent('SpawnVfx', {model = model, position = pos})
--
---
-- Remove a VFX with the given vfxId. Best invoked through the RemoveVfx global event
-- @function [parent=#VFX] remove
-- @param #string vfxId the vfxId of the vfx to remove.
--
-- @usage -- Remove the vfx with vfxId "myvfx"
-- core.sendGlobalEvent('RemoveVfx', "myvfx")
--
return nil