Browse Source

Refactor material shader parameter animations so that they happen in response to update events, the same as component/node attribute animations. This fixes material animations depending on visibility and therefore possibly going out of sync from other updates. Add possibility to associate material with scene to make it use the scene attribute update event instead of the global update event.

Lasse Öörni 11 years ago
parent
commit
3969b69b0d

+ 2 - 0
Bin/Data/LuaScripts/31_MaterialAnimation.lua

@@ -60,6 +60,8 @@ function CreateScene()
     specColorAnimation:SetKeyFrame(1.0, Variant(Color(1.0, 0.0, 0.0, 2.0)))
     specColorAnimation:SetKeyFrame(2.0, Variant(Color(1.0, 1.0, 0.0, 2.0)))
     specColorAnimation:SetKeyFrame(3.0, Variant(Color(0.1, 0.1, 0.1, 16.0)))
+    -- Optionally associate material with scene to make sure shader parameter animation respects scene time scale
+    mushroomMat.scene = scene_;
     mushroomMat:SetShaderParameterAnimation("MatSpecColor", specColorAnimation)
 
     local NUM_OBJECTS = 200

+ 2 - 0
Bin/Data/Scripts/31_MaterialAnimation.as

@@ -62,6 +62,8 @@ void CreateScene()
     specColorAnimation.SetKeyFrame(1.0f, Variant(Color(1.0f, 0.0f, 0.0f, 2.0f)));
     specColorAnimation.SetKeyFrame(2.0f, Variant(Color(1.0f, 1.0f, 0.0f, 2.0f)));
     specColorAnimation.SetKeyFrame(3.0f, Variant(Color(0.1f, 0.1f, 0.1f, 16.0f)));
+    // Optionally associate material with scene to make sure shader parameter animation respects scene time scale
+    mushroomMat.scene = scene_;
     mushroomMat.SetShaderParameterAnimation("MatSpecColor", specColorAnimation);
 
     const uint NUM_OBJECTS = 200;

+ 0 - 3
Source/Engine/Graphics/Batch.cpp

@@ -551,9 +551,6 @@ void Batch::Prepare(View* view, bool setModelTransform) const
     {
         if (graphics->NeedParameterUpdate(SP_MATERIAL, material_))
         {
-            // Update shader parameter animations
-            material_->UpdateShaderParameterAnimations();
-
             const HashMap<StringHash, MaterialShaderParameter>& parameters = material_->GetShaderParameters();
             for (HashMap<StringHash, MaterialShaderParameter>::ConstIterator i = parameters.Begin(); i != parameters.End(); ++i)
                 graphics->SetShaderParameter(i->first_, i->second_.value_);

+ 55 - 26
Source/Engine/Graphics/Material.cpp

@@ -22,6 +22,7 @@
 
 #include "Precompiled.h"
 #include "Context.h"
+#include "CoreEvents.h"
 #include "FileSystem.h"
 #include "Graphics.h"
 #include "Log.h"
@@ -29,6 +30,8 @@
 #include "Matrix3x4.h"
 #include "Profiler.h"
 #include "ResourceCache.h"
+#include "Scene.h"
+#include "SceneEvents.h"
 #include "Technique.h"
 #include "Texture2D.h"
 #include "TextureCube.h"
@@ -151,7 +154,7 @@ Material::Material(Context* context) :
     auxViewFrameNumber_(0),
     occlusion_(true),
     specular_(false),
-    animationFrameNumber_(0)
+    subscribed_(false)
 {
     ResetToDefaults();
 }
@@ -342,7 +345,6 @@ bool Material::Load(const XMLElement& source)
     if (depthBiasElem)
         SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
 
-    // Calculate memory use
     RefreshMemoryUse();
     CheckOcclusion();
     return true;
@@ -482,6 +484,7 @@ void Material::SetShaderParameterAnimation(const String& name, ValueAnimation* a
         
         StringHash nameHash(name);
         shaderParameterAnimationInfos_[nameHash] = new ShaderParameterAnimationInfo(this, name, animation, wrapMode, speed);
+        UpdateEventSubscription();
     }
     else
     {
@@ -489,6 +492,7 @@ void Material::SetShaderParameterAnimation(const String& name, ValueAnimation* a
         {
             StringHash nameHash(name);
             shaderParameterAnimationInfos_.Erase(nameHash);
+            UpdateEventSubscription();
         }
     }
 }
@@ -562,6 +566,15 @@ void Material::SetDepthBias(const BiasParameters& parameters)
     depthBias_.Validate();
 }
 
+void Material::SetScene(Scene* scene)
+{
+    UnsubscribeFromEvent(E_UPDATE);
+    UnsubscribeFromEvent(E_ATTRIBUTEANIMATIONUPDATE);
+    subscribed_ = false;
+    scene_ = scene;
+    UpdateEventSubscription();
+}
+
 void Material::RemoveShaderParameter(const String& name)
 {
     StringHash nameHash(name);
@@ -611,30 +624,6 @@ void Material::MarkForAuxView(unsigned frameNumber)
     auxViewFrameNumber_ = frameNumber;
 }
 
-void Material::UpdateShaderParameterAnimations()
-{
-    if (shaderParameterAnimationInfos_.Empty())
-        return;
-
-    Time* time = GetSubsystem<Time>();
-    if (time->GetFrameNumber() == animationFrameNumber_)
-        return;
-
-    animationFrameNumber_ = time->GetFrameNumber();
-    float timeStep = time->GetTimeStep();
-
-    Vector<String> finishedNames;
-    for (HashMap<StringHash, SharedPtr<ShaderParameterAnimationInfo> >::ConstIterator i = shaderParameterAnimationInfos_.Begin(); i != shaderParameterAnimationInfos_.End(); ++i)
-    {
-        if (i->second_->Update(timeStep))
-            finishedNames.Push(i->second_->GetName());
-    }
-
-    // Remove finished animation
-    for (unsigned i = 0; i < finishedNames.Size(); ++i)
-        SetShaderParameterAnimation(finishedNames[i], 0);
-}
-
 const TechniqueEntry& Material::GetTechniqueEntry(unsigned index) const
 {
     return index < techniques_.Size() ? techniques_[index] : noEntry;
@@ -680,6 +669,11 @@ float Material::GetShaderParameterAnimationSpeed(const String& name) const
     return info == 0 ? 0 : info->GetSpeed();
 }
 
+Scene* Material::GetScene() const
+{
+    return scene_;
+}
+
 String Material::GetTextureUnitName(TextureUnit unit)
 {
     return textureUnitNames[unit];
@@ -758,4 +752,39 @@ ShaderParameterAnimationInfo* Material::GetShaderParameterAnimationInfo(const St
     return i->second_;
 }
 
+void Material::UpdateEventSubscription()
+{
+    if (shaderParameterAnimationInfos_.Size() && !subscribed_)
+    {
+        if (scene_)
+            SubscribeToEvent(scene_, E_ATTRIBUTEANIMATIONUPDATE, HANDLER(Material, HandleAttributeAnimationUpdate));
+        else
+            SubscribeToEvent(E_UPDATE, HANDLER(Material, HandleAttributeAnimationUpdate));
+        subscribed_ = true;
+    }
+    else if (subscribed_)
+    {
+        UnsubscribeFromEvent(E_UPDATE);
+        UnsubscribeFromEvent(E_ATTRIBUTEANIMATIONUPDATE);
+        subscribed_ = false;
+    }
+}
+
+void Material::HandleAttributeAnimationUpdate(StringHash eventType, VariantMap& eventData)
+{
+    // Timestep parameter is same no matter what event is being listened to
+    float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
+
+    Vector<String> finishedNames;
+    for (HashMap<StringHash, SharedPtr<ShaderParameterAnimationInfo> >::ConstIterator i = shaderParameterAnimationInfos_.Begin(); i != shaderParameterAnimationInfos_.End(); ++i)
+    {
+        if (i->second_->Update(timeStep))
+            finishedNames.Push(i->second_->GetName());
+    }
+
+    // Remove finished animations
+    for (unsigned i = 0; i < finishedNames.Size(); ++i)
+        SetShaderParameterAnimation(finishedNames[i], 0);
+}
+
 }

+ 13 - 4
Source/Engine/Graphics/Material.h

@@ -33,6 +33,7 @@ namespace Urho3D
 
 class Material;
 class Pass;
+class Scene;
 class Technique;
 class Texture;
 class Texture2D;
@@ -137,6 +138,8 @@ public:
     void SetShadowCullMode(CullMode mode);
     /// Set depth bias.
     void SetDepthBias(const BiasParameters& parameters);
+    /// Associate the material with a scene to ensure that shader parameter animation happens in sync with scene update, respecting the scene time scale. If no scene is set, the global update events will be used.
+    void SetScene(Scene* scene);
     /// Remove shader parameter.
     void RemoveShaderParameter(const String& name);
     /// Reset all shader pointers.
@@ -147,8 +150,6 @@ public:
     void SortTechniques();
     /// Mark material for auxiliary view rendering.
     void MarkForAuxView(unsigned frameNumber);
-    /// Update shader parameter animations.
-    void UpdateShaderParameterAnimations();
 
     /// Return number of techniques.
     unsigned GetNumTechniques() const { return techniques_.Size(); }
@@ -186,6 +187,8 @@ public:
     bool GetOcclusion() const { return occlusion_; }
     /// Return whether should render specular.
     bool GetSpecular() const { return specular_; }
+    /// Return the scene associated with the material for shader parameter animation updates.
+    Scene* GetScene() const;
 
     /// Return name for texture unit.
     static String GetTextureUnitName(TextureUnit unit);
@@ -201,6 +204,10 @@ private:
     void RefreshMemoryUse();
     /// Return shader parameter animation info.
     ShaderParameterAnimationInfo* GetShaderParameterAnimationInfo(const String& name) const;
+    /// Update whether should be subscribed to scene or global update events for shader parameter animation.
+    void UpdateEventSubscription();
+    /// Update shader parameter animations.
+    void HandleAttributeAnimationUpdate(StringHash eventType, VariantMap& eventData);
 
     /// Techniques.
     Vector<TechniqueEntry> techniques_;
@@ -222,10 +229,12 @@ private:
     bool occlusion_;
     /// Specular lighting flag.
     bool specular_;
-    /// Last animation update frame number.
-    unsigned animationFrameNumber_;
+    /// Flag for whether is subscribed to animation updates.
+    bool subscribed_;
     /// XML file used while loading.
     SharedPtr<XMLFile> loadXMLFile_;
+    /// Associated scene for shader parameter animation updates.
+    WeakPtr<Scene> scene_;
 };
 
 }

+ 3 - 0
Source/Engine/LuaScript/pkgs/Graphics/Material.pkg

@@ -20,6 +20,7 @@ class Material : public Resource
     void SetCullMode(CullMode mode);
     void SetShadowCullMode(CullMode mode);
     void SetDepthBias(const BiasParameters& parameters);
+    void SetScene(Scene* scene);
     void RemoveShaderParameter(const String name);
     void ReleaseShaders();
     
@@ -47,12 +48,14 @@ class Material : public Resource
     unsigned GetAuxViewFrameNumber() const;
     bool GetOcclusion() const;
     bool GetSpecular() const;
+    Scene* GetScene() const;
     
     tolua_readonly tolua_property__get_set CullMode cullMode;
     tolua_readonly tolua_property__get_set CullMode shadowCullMode;
     tolua_readonly tolua_property__get_set unsigned auxViewFrameNumber;
     tolua_readonly tolua_property__get_set bool occlusion;
     tolua_readonly tolua_property__get_set bool specular;
+    tolua_property__get_set Scene* scene;
 };
 
 ${

+ 2 - 0
Source/Engine/Script/GraphicsAPI.cpp

@@ -684,6 +684,8 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Material", "CullMode get_shadowCullMode() const", asMETHOD(Material, GetShadowCullMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void set_depthBias(const BiasParameters&in)", asMETHOD(Material, SetDepthBias), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "const BiasParameters& get_depthBias() const", asMETHOD(Material, GetDepthBias), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Material", "void set_scene(Scene@+)", asMETHOD(Material, SetScene), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Material", "Scene@+ get_scene() const", asMETHOD(Material, GetScene), asCALL_THISCALL);
     
     engine->RegisterGlobalFunction("String GetTextureUnitName(TextureUnit)", asFUNCTION(Material::GetTextureUnitName), asCALL_CDECL);
 }

+ 2 - 0
Source/Samples/31_MaterialAnimation/MaterialAnimation.cpp

@@ -108,6 +108,8 @@ void MaterialAnimation::CreateScene()
     specColorAnimation->SetKeyFrame(1.0f, Color(1.0f, 0.0f, 0.0f, 2.0f));
     specColorAnimation->SetKeyFrame(2.0f, Color(1.0f, 1.0f, 0.0f, 2.0f));
     specColorAnimation->SetKeyFrame(3.0f, Color(0.1f, 0.1f, 0.1f, 16.0f));
+    // Optionally associate material with scene to make sure shader parameter animation respects scene time scale
+    mushroomMat->SetScene(scene_);
     mushroomMat->SetShaderParameterAnimation("MatSpecColor", specColorAnimation);
 
     const unsigned NUM_OBJECTS = 200;