From f623f1f9b14da6eca60bbed22dbb9a4bdc7ada4a Mon Sep 17 00:00:00 2001 From: Kitty Barnett <develop@catznip.com> Date: Thu, 7 Jan 2021 22:26:18 +0100 Subject: [PATCH] Add additional sphere effects for the initial RFC release and make params tweenable for the fun of it --- indra/llmath/v4math.h | 13 +++ .../shaders/class1/deferred/rlvF.glsl | 87 ++++++++++++++++--- indra/newview/rlvcommon.h | 2 +- indra/newview/rlvdefines.h | 4 +- indra/newview/rlveffects.cpp | 80 ++++++++++++++--- indra/newview/rlveffects.h | 15 ++-- indra/newview/rlvhelper.cpp | 11 +++ 7 files changed, 177 insertions(+), 35 deletions(-) diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h index 623c8b20035..d1b5a9646bd 100644 --- a/indra/llmath/v4math.h +++ b/indra/llmath/v4math.h @@ -434,6 +434,19 @@ inline LLVector4 operator-(const LLVector4 &a) return LLVector4( -a.mV[VX], -a.mV[VY], -a.mV[VZ] ); } +// [RLVa:KB] - RlvBehaviourModifierCompMin/Max +inline bool operator<(const LLVector4& lhs, const LLVector4& rhs) +{ + return (lhs.mV[0] < rhs.mV[0] + || (lhs.mV[0] == rhs.mV[0] + && (lhs.mV[1] < rhs.mV[1] + || ((lhs.mV[1] == rhs.mV[1]) + && lhs.mV[2] < rhs.mV[2] + || ((lhs.mV[2] == rhs.mV[2]) + && lhs.mV[3] < rhs.mV[3]))))); +} +// [/RLVa:KB] + inline F32 dist_vec(const LLVector4 &a, const LLVector4 &b) { LLVector4 vec = a - b; diff --git a/indra/newview/app_settings/shaders/class1/deferred/rlvF.glsl b/indra/newview/app_settings/shaders/class1/deferred/rlvF.glsl index 6ad884446b8..8661ffe923c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/rlvF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/rlvF.glsl @@ -33,7 +33,7 @@ uniform int rlvEffectMode; // ESphereMode uniform vec4 rlvEffectParam1; // Sphere origin (in local coordinates) uniform vec4 rlvEffectParam2; // Min/max dist + min/max value uniform bvec2 rlvEffectParam3; // Min/max dist extend -uniform vec4 rlvEffectParam4; // Sphere color (not used for blur) +uniform vec4 rlvEffectParam4; // Sphere params (=color when using blend) uniform vec2 rlvEffectParam5; // Blur direction (not used for blend) #define SPHERE_ORIGIN rlvEffectParam1.xyz @@ -42,7 +42,7 @@ uniform vec2 rlvEffectParam5; // Blur direction (not used for blend) #define SPHERE_DISTEXTEND rlvEffectParam3 #define SPHERE_VALUEMIN rlvEffectParam2.x #define SPHERE_VALUEMAX rlvEffectParam2.z -#define SPHERE_COLOUR rlvEffectParam4.rgb +#define SPHERE_PARAMS rlvEffectParam4 #define BLUR_DIRECTION rlvEffectParam5.xy vec4 getPosition_d(vec2 pos_screen, float depth) @@ -57,33 +57,77 @@ vec4 getPosition_d(vec2 pos_screen, float depth) return pos; } -vec3 blur13(sampler2DRect image, vec2 uv, vec2 direction) +vec3 blur13(sampler2DRect source, vec2 tc, vec2 direction) { vec4 color = vec4(0.0); vec2 off1 = vec2(1.411764705882353) * direction; vec2 off2 = vec2(3.2941176470588234) * direction; vec2 off3 = vec2(5.176470588235294) * direction; - color += texture2D(image, uv) * 0.1964825501511404; + color += texture2DRect(source, tc) * 0.1964825501511404; - color += texture2D(image, uv + off1) * 0.2969069646728344; - color += texture2D(image, uv - off1) * 0.2969069646728344; + color += texture2DRect(source, tc + off1) * 0.2969069646728344; + color += texture2DRect(source, tc - off1) * 0.2969069646728344; - color += texture2D(image, uv + off2) * 0.09447039785044732; - color += texture2D(image, uv - off2) * 0.09447039785044732; + color += texture2DRect(source, tc + off2) * 0.09447039785044732; + color += texture2DRect(source, tc - off2) * 0.09447039785044732; - color += texture2D(image, uv + off3) * 0.010381362401148057; - color += texture2D(image, uv - off3) * 0.010381362401148057; + color += texture2DRect(source, tc + off3) * 0.010381362401148057; + color += texture2DRect(source, tc - off3) * 0.010381362401148057; return color.xyz; } +const float pi = 3.14159265; + +// http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html +vec3 blurVariable(sampler2DRect source, vec2 tc, float kernelSize, vec2 direction, float strength) { + float numBlurPixelsPerSide = float(kernelSize / 2); + + // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) + vec3 incrementalGaussian; + incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * strength); + incrementalGaussian.y = exp(-0.5 / (strength * strength)); + incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y; + + vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0); + float coefficientSum = 0.0; + + // Take the central sample first... + avgValue += texture2DRect(source, tc) * incrementalGaussian.x; + coefficientSum += incrementalGaussian.x; + incrementalGaussian.xy *= incrementalGaussian.yz; + + // Go through the remaining 8 vertical samples (4 on each side of the center) + for (float i = 1.0; i <= numBlurPixelsPerSide; i++) { + avgValue += texture2DRect(source, tc - i * direction) * incrementalGaussian.x; + avgValue += texture2DRect(source, tc + i * direction) * incrementalGaussian.x; + coefficientSum += 2.0 * incrementalGaussian.x; + incrementalGaussian.xy *= incrementalGaussian.yz; + } + + return (avgValue / coefficientSum).rgb; +} + +vec3 chromaticAberration(sampler2DRect source, vec2 tc, vec2 redDrift, vec2 blueDrift, float strength) +{ + vec3 sourceColor = texture2DRect(source, tc).rgb; + + // Sample the color components + vec3 driftColor; + driftColor.r = texture2DRect(source, tc + redDrift).r; + driftColor.g = sourceColor.g; + driftColor.b = texture2DRect(source, tc + blueDrift).b; + + // Adjust the strength of the effect + return mix(sourceColor, driftColor, strength); +} + void main() { vec2 fragTC = vary_fragcoord.st; float fragDepth = texture2DRect(depthMap, fragTC).x; vec3 fragPosLocal = getPosition_d(fragTC, fragDepth).xyz; - vec3 fragColor = texture2DRect(diffuseRect, fragTC).rgb; float distance = length(fragPosLocal.xyz - SPHERE_ORIGIN); // Linear non-branching interpolation of the strength of the sphere effect (replaces if/elseif/else for x < min, min <= x <= max and x > max) @@ -91,14 +135,31 @@ void main() effectStrength = mix(effectStrength, mix(0, SPHERE_VALUEMIN, SPHERE_DISTEXTEND.x), distance < SPHERE_DISTMIN); effectStrength = mix(effectStrength, mix(0, SPHERE_VALUEMAX, SPHERE_DISTEXTEND.y), distance > SPHERE_DISTMAX); + vec3 fragColor; switch (rlvEffectMode) { case 0: // Blend - fragColor = mix(fragColor, SPHERE_COLOUR, effectStrength); + fragColor = texture2DRect(diffuseRect, fragTC).rgb; + fragColor = mix(fragColor, SPHERE_PARAMS.rgb, effectStrength); break; - case 1: // Blur + case 1: // Blur (fixed) fragColor = blur13(diffuseRect, fragTC, effectStrength * BLUR_DIRECTION); break; + case 2: // Blur (variable) + fragColor = texture2DRect(diffuseRect, fragTC).rgb; + fragColor = mix(fragColor, blurVariable(diffuseRect, fragTC, SPHERE_PARAMS.x, BLUR_DIRECTION, effectStrength), effectStrength > 0); + break; + case 3: // ChromaticAberration + fragColor = chromaticAberration(diffuseRect, fragTC, SPHERE_PARAMS.xy, SPHERE_PARAMS.zw, effectStrength); + break; + case 4: // Pixelate + { + effectStrength = sign(effectStrength); + float pixelWidth = max(1, round(SPHERE_PARAMS.x * effectStrength)); float pixelHeight = max(1, round(SPHERE_PARAMS.y * effectStrength)); + fragTC = vec2(pixelWidth * floor(fragTC.x / pixelWidth), pixelHeight * floor(fragTC.y / pixelHeight)); + fragColor = texture2DRect(diffuseRect, fragTC).rgb; + } + break; } frag_color.rgb = fragColor; diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 82fca44f91b..9777ff9d0b6 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -56,7 +56,7 @@ class RlvObject; struct RlvException; typedef boost::variant<std::string, LLUUID, S32, ERlvBehaviour> RlvExceptionOption; -typedef boost::variant<int, float, bool, LLVector3, LLVector3d, LLUUID> RlvBehaviourModifierValue; +typedef boost::variant<int, float, bool, LLVector3, LLVector3d, LLVector4, LLUUID> RlvBehaviourModifierValue; class RlvGCTimer; diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index da9716b083e..690bda54fcf 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -286,12 +286,14 @@ enum class ERlvLocalBhvrModifier // @setsphere SphereMode, // The type of effect that will apply to any pixel that intersects with the sphere (e.g. blend, blur, ...) SphereOrigin, // The origin of the sphere can either be the avatar or the camera position - SphereColor, // [Blend only] Colour to mix with the actual pixel colour + SphereColor, // [Blend only] Colour to mix with the actual pixel colour (stored as params) + SphereParams, // Effect parameters (dependent on mode - see RlvSphereEffect) SphereDistMin, // Distance at which the effect starts and has weight minValue; e.g. for blend this would be colour = mix(colour, sphere_colour, min_alpha) SphereDistMax, // Distance at which the effect starts and has weight maxValue; e.g. for blend this would be colour = mix(colour, sphere_colour, max_alpha) SphereDistExtend, // Specifies the value beyond min dist or max dist (by default the sphere extends beyond max distance at max vlaue) SphereValueMin, // Value of the effect at minimum distance SphereValueMax, // Value of the effect at maximum distance + SphereTween, // Amount of seconds it takes to lerp from value A to value B Unknown, }; diff --git a/indra/newview/rlveffects.cpp b/indra/newview/rlveffects.cpp index 8b303847e38..454a9db38cc 100644 --- a/indra/newview/rlveffects.cpp +++ b/indra/newview/rlveffects.cpp @@ -183,19 +183,20 @@ void RlvOverlayEffect::run() const int c_SphereDefaultMode = 0; const int c_SphereDefaultOrigin = 0; -const float c_SphereDefaultColor[3] = { 0.0f, 0.0f, 0.0f }; +const float c_SphereDefaultColor[4] = { 0.0, 0.f, 0.f, 0.f }; const float c_SphereDefaultDistance = 0.0f; -const int c_SphereDefaultDistanceExtend = 0; +const int c_SphereDefaultDistanceExtend = 1; const float c_SphereDefaultAlpha = 1.0f; RlvSphereEffect::RlvSphereEffect(const LLUUID& idRlvObj) : LLVisualEffect(idRlvObj, EVisualEffect::RlvSphere, EVisualEffectType::PostProcessShader) , m_eMode((ESphereMode)c_SphereDefaultMode) , m_eOrigin((ESphereOrigin)c_SphereDefaultOrigin) - , m_Color(LLColor3(c_SphereDefaultColor)) + , m_Params(LLVector4(c_SphereDefaultColor)) , m_nDistanceMin(c_SphereDefaultDistance), m_nDistanceMax(c_SphereDefaultDistance) - , m_eDistExtend((ESphereDistExtend)0) + , m_eDistExtend((ESphereDistExtend)c_SphereDefaultDistanceExtend) , m_nValueMin(c_SphereDefaultAlpha), m_nValueMax(c_SphereDefaultAlpha) + , m_nTweenDuration(0.f) { if (RlvObject* pRlvObj = gRlvHandler.getObject(idRlvObj)) { @@ -207,7 +208,10 @@ RlvSphereEffect::RlvSphereEffect(const LLUUID& idRlvObj) LLVector3 vecColor; if (pRlvObj->getModifierValue<LLVector3>(ERlvLocalBhvrModifier::SphereColor, vecColor)) - m_Color = LLColor3(vecColor.mV); + m_Params = LLVector4(vecColor.mV[VX], vecColor.mV[VY], vecColor.mV[VZ], 1.0f); + LLVector4 vecParams; + if (pRlvObj->getModifierValue<LLVector4>(ERlvLocalBhvrModifier::SphereParams, vecParams)) + m_Params = vecParams; float nFloat; if (pRlvObj->getModifierValue<float>(ERlvLocalBhvrModifier::SphereDistMin, nFloat)) @@ -253,7 +257,11 @@ ERlvCmdRet RlvSphereEffect::onColorChanged(const LLUUID& idRlvObj, const boost:: { if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) { - pEffect->m_Color = LLColor3((newValue) ? boost::get<LLVector3>(newValue.value()).mV : c_SphereDefaultColor); + LLVector4 vecColor = (newValue) ? LLVector4(boost::get<LLVector3>(newValue.value()), 1.0f) : LLVector4(c_SphereDefaultColor); + if (!pEffect->m_nTweenDuration) + pEffect->m_Params = vecColor; + else + pEffect->m_Params.start(vecColor, pEffect->m_nTweenDuration); } return RLV_RET_SUCCESS; } @@ -263,7 +271,11 @@ ERlvCmdRet RlvSphereEffect::onDistMinChanged(const LLUUID& idRlvObj, const boost { if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) { - pEffect->m_nDistanceMin = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultDistance; + float nDistanceMin = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultDistance; + if (!pEffect->m_nTweenDuration) + pEffect->m_nDistanceMin = nDistanceMin; + else + pEffect->m_nDistanceMin.start(nDistanceMin, pEffect->m_nTweenDuration); } return RLV_RET_SUCCESS; } @@ -273,7 +285,11 @@ ERlvCmdRet RlvSphereEffect::onDistMaxChanged(const LLUUID& idRlvObj, const boost { if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) { - pEffect->m_nDistanceMax = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultDistance; + float nDistanceMax = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultDistance; + if (!pEffect->m_nTweenDuration) + pEffect->m_nDistanceMax = nDistanceMax; + else + pEffect->m_nDistanceMax.start(nDistanceMax, pEffect->m_nTweenDuration); } return RLV_RET_SUCCESS; } @@ -288,12 +304,40 @@ ERlvCmdRet RlvSphereEffect::onDistExtendChanged(const LLUUID& idRlvObj, const bo return RLV_RET_SUCCESS; } +// static +ERlvCmdRet RlvSphereEffect::onParamsChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue) +{ + if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) + { + LLVector4 params = LLVector4((newValue) ? boost::get<LLVector4>(newValue.value()).mV : c_SphereDefaultColor); + if (!pEffect->m_nTweenDuration) + pEffect->m_Params = params; + else + pEffect->m_Params.start(params, pEffect->m_nTweenDuration); + } + return RLV_RET_SUCCESS; +} + +// static +ERlvCmdRet RlvSphereEffect::onTweenDurationChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue) +{ + if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) + { + pEffect->m_nTweenDuration = (newValue) ? boost::get<float>(newValue.value()) : 0; + } + return RLV_RET_SUCCESS; +} + // static ERlvCmdRet RlvSphereEffect::onValueMinChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue) { if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) { - pEffect->m_nValueMin = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultAlpha; + float nValueMin = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultAlpha;; + if (!pEffect->m_nTweenDuration) + pEffect->m_nValueMin = nValueMin; + else + pEffect->m_nValueMin.start(nValueMin, pEffect->m_nTweenDuration); } return RLV_RET_SUCCESS; } @@ -303,7 +347,11 @@ ERlvCmdRet RlvSphereEffect::onValueMaxChanged(const LLUUID& idRlvObj, const boos { if (RlvSphereEffect* pEffect = dynamic_cast<RlvSphereEffect*>(LLVfxManager::instance().getEffect(idRlvObj))) { - pEffect->m_nValueMax = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultAlpha; + float nValueMax = (newValue) ? boost::get<float>(newValue.value()) : c_SphereDefaultAlpha; + if (!pEffect->m_nTweenDuration) + pEffect->m_nValueMax = nValueMax; + else + pEffect->m_nValueMax.start(nValueMax, pEffect->m_nTweenDuration); } return RLV_RET_SUCCESS; } @@ -332,16 +380,17 @@ void RlvSphereEffect::setShaderUniforms(LLGLSLShader* pShader, LLRenderTarget* p pShader->uniform4fv(LLShaderMgr::RLV_EFFECT_PARAM1, 1, posSphereOriginGl.v); // Pack min/max distance and alpha together - const glh::vec4f sphereParams(m_nValueMin, m_nDistanceMin, m_nValueMax, m_nDistanceMax); + float nDistMin = m_nDistanceMin.get(), nDistMax = m_nDistanceMax.get(); + const glh::vec4f sphereParams(m_nValueMin.get(), nDistMin, m_nValueMax.get(), (nDistMax >= nDistMin) ? nDistMax : nDistMin); pShader->uniform4fv(LLShaderMgr::RLV_EFFECT_PARAM2, 1, sphereParams.v); // Pass dist extend int eDistExtend = (int)m_eDistExtend; pShader->uniform2f(LLShaderMgr::RLV_EFFECT_PARAM3, eDistExtend & (int)ESphereDistExtend::Min, eDistExtend & (int)ESphereDistExtend::Max); - // Pass color - const glh::vec4f sphereColor(m_Color.mV, 1.0); - pShader->uniform4fv(LLShaderMgr::RLV_EFFECT_PARAM4, 1, sphereColor.v); + // Pass effect params + const glh::vec4f effectParams(m_Params.get().mV); + pShader->uniform4fv(LLShaderMgr::RLV_EFFECT_PARAM4, 1, effectParams.v); } void RlvSphereEffect::renderPass(LLGLSLShader* pShader) const @@ -397,9 +446,12 @@ void RlvSphereEffect::run() switch (m_eMode) { case ESphereMode::Blend: + case ESphereMode::ChromaticAberration: + case ESphereMode::Pixelate: renderPass(&gRlvSphereProgram); break; case ESphereMode::Blur: + case ESphereMode::BlurVariable: gRlvSphereProgram.uniform2f(LLShaderMgr::RLV_EFFECT_PARAM5, 1.f, 0.f); renderPass(&gRlvSphereProgram); gRlvSphereProgram.uniform2f(LLShaderMgr::RLV_EFFECT_PARAM5, 0.f, 1.f); diff --git a/indra/newview/rlveffects.h b/indra/newview/rlveffects.h index 4a4773d4481..283346c9f92 100644 --- a/indra/newview/rlveffects.h +++ b/indra/newview/rlveffects.h @@ -78,6 +78,8 @@ class RlvSphereEffect : public LLVisualEffect static ERlvCmdRet onDistMinChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); static ERlvCmdRet onDistMaxChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); static ERlvCmdRet onDistExtendChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); + static ERlvCmdRet onParamsChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); + static ERlvCmdRet onTweenDurationChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); static ERlvCmdRet onValueMinChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); static ERlvCmdRet onValueMaxChanged(const LLUUID& idRlvObj, const boost::optional<RlvBehaviourModifierValue> newValue); protected: @@ -88,17 +90,18 @@ class RlvSphereEffect : public LLVisualEffect * Member variables */ protected: - enum class ESphereMode { Blend = 0, SoftBlur, Blur, Count }; + enum class ESphereMode { Blend = 0, Blur, BlurVariable, ChromaticAberration, Pixelate, Count }; ESphereMode m_eMode; enum class ESphereOrigin { Avatar = 0, Camera, Count }; ESphereOrigin m_eOrigin; - LLColor3 m_Color; - float m_nDistanceMin; - float m_nDistanceMax; + LLTweenableValueLerp<LLVector4> m_Params; + LLTweenableValueLerp<float> m_nDistanceMin; + LLTweenableValueLerp<float> m_nDistanceMax; enum class ESphereDistExtend { Max = 0x01, Min = 0x02, Both = 0x03 }; ESphereDistExtend m_eDistExtend; - float m_nValueMin; - float m_nValueMax; + LLTweenableValueLerp<float> m_nValueMin; + LLTweenableValueLerp<float> m_nValueMax; + float m_nTweenDuration; }; // ==================================================================================== diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp index 817bce82d30..d56f0898f46 100644 --- a/indra/newview/rlvhelper.cpp +++ b/indra/newview/rlvhelper.cpp @@ -232,6 +232,8 @@ RlvBehaviourDictionary::RlvBehaviourDictionary() pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistMin, typeid(float), "distmin", &RlvSphereEffect::onDistMinChanged); pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistMax, typeid(float), "distmax", &RlvSphereEffect::onDistMaxChanged); pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereDistExtend, typeid(int), "distextend", &RlvSphereEffect::onDistExtendChanged); + pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereParams, typeid(LLVector4), "param", &RlvSphereEffect::onParamsChanged); + pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereTween, typeid(float), "tween", &RlvSphereEffect::onTweenDurationChanged); pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereValueMin, typeid(float), "valuemin", &RlvSphereEffect::onValueMinChanged); pSetSphereBhvr->addModifier(ERlvLocalBhvrModifier::SphereValueMax, typeid(float), "valuemax", &RlvSphereEffect::onValueMaxChanged); addEntry(pSetSphereBhvr); @@ -655,6 +657,15 @@ bool RlvBehaviourModifier::convertOptionValue(const std::string& optionValue, co return true; } } + else if (modType == typeid(LLVector4)) + { + LLVector4 vecOption; + if (4 == sscanf(optionValue.c_str(), "%f/%f/%f/%f", vecOption.mV + 0, vecOption.mV + 1, vecOption.mV + 2, vecOption.mV + 3)) + { + modValue = vecOption; + return true; + } + } else if (modType == typeid(LLUUID)) { LLUUID idOption; -- GitLab