Browse Source

Added TransformChanged() script method to ScriptInstance & LuaScriptInstance. Called when node transform changes (hooks to OnMarkedDirty() on C++ side)
Ensure that LuaScriptInstance listens only to its own scene's & physics world's update events.
Changed LuaScriptInstance to use IsEnabledEffective() to make sure it gets disabled if the node is disabled.

Lasse Öörni 12 years ago
parent
commit
c3870201b9

+ 16 - 12
Docs/Reference.dox

@@ -388,13 +388,16 @@ The following methods that implement the component behaviour will be checked for
 - void WriteNetworkUpdate(Serializer&)
 - void ReadNetworkUpdate(Deserializer&)
 - void ApplyAttributes()
+- void TransformChanged()
 
 The update methods above correspond to the variable timestep scene update and post-update, and the fixed timestep physics world update and post-update. The application-wide update events are not handled by default.
 
-The Start() and Stop() methods do not have direct counterparts in C++ components. Start() is called just after the script object has been created. Stop() is called just before the script object is destroyed. This happens when the ScriptInstance is destroyed, or if the script class is changed. 
+The Start() and Stop() methods do not have direct counterparts in C++ components. Start() is called just after the script object has been created. Stop() is called just before the script object is destroyed. This happens when the ScriptInstance is destroyed, or if the script class is changed.
 
 When a scene node hierarchy with script objects is instantiated (such as when loading a scene) any child nodes may not have been created yet when Start() is executed, and can thus not be relied upon for initialization. The DelayedStart() method can be used in this case instead: if defined, it is called immediately before any of the Update() calls.
 
+TransformChanged() is called whenever the scene node transform changes, similar to C++ components' OnMarkedDirty() function.
+
 Subscribing to \ref Events "events" in script behaves differently depending on whether \ref Object::SubscribeToEvent "SubscribeToEvent()" is called from a script object's method, or from a procedural script function. If called from an object method, the ScriptInstance becomes the event receiver on the C++ side, and forwards the events to the script object. If called from a function, the ScriptFile will be the event receiver. Note that object-based event handling only works when the script object in question is attached to a ScriptInstance component, as a C++ side proxy is always needed. If you simply create a new free-standing object in script, it will not be able to subscribe to events.
 
 The script object's enabled state can be controlled through the \ref ScriptInstance::SetEnabled "SetEnabled()" function. When disabled, the scripted update methods or event handlers will not be called. This can be used to reduce CPU load in a large or densely populated scene.
@@ -552,17 +555,18 @@ After instantiation, functions can be \ref LuaScriptInstance::ExecuteFunction "c
 
 Like their AngelScript counterparts, script object classes can define functions which are automatically called by LuaScriptInstance for operations like initialization, scene update, or load/save. These functions are listed below. Refer to the \ref Scripting "AngelScript scripting" page for details.
 
-- void Start()
-- void Stop()
-- void Update(timeStep)
-- void PostUpdate(timeStep)
-- void FixedUpdate(timeStep)
-- void FixedPostUpdate(timeStep)
-- void Save(serializer)
-- void Load(deserializer)
-- void WriteNetworkUpdate(serializer)
-- void ReadNetworkUpdate(deserializer)
-- void ApplyAttributes()
+- Start()
+- Stop()
+- Update(timeStep)
+- PostUpdate(timeStep)
+- FixedUpdate(timeStep)
+- FixedPostUpdate(timeStep)
+- Save(serializer)
+- Load(deserializer)
+- WriteNetworkUpdate(serializer)
+- ReadNetworkUpdate(deserializer)
+- ApplyAttributes()
+- TransformChanged()
 
 \section LuaScripting_Events Event handling
 

+ 24 - 2
Source/Engine/Script/ScriptInstance.cpp

@@ -54,7 +54,8 @@ static const char* methodDeclarations[] = {
     "void Save(Serializer&)",
     "void ReadNetworkUpdate(Deserializer&)",
     "void WriteNetworkUpdate(Serializer&)",
-    "void ApplyAttributes()"
+    "void ApplyAttributes()",
+    "void TransformChanged()"
 };
 
 extern const char* UI_CATEGORY;
@@ -430,6 +431,19 @@ PODVector<unsigned char> ScriptInstance::GetScriptNetworkDataAttr() const
     }
 }
 
+void ScriptInstance::OnMarkedDirty(Node* node)
+{
+    // Script functions are not safe from worker threads
+    Scene* scene = GetScene();
+    if (scene && scene->IsThreadedUpdate())
+    {
+        scene->DelayedMarkedDirty(this);
+        return;
+    }
+    
+    if (scriptObject_ && methods_[METHOD_TRANSFORMCHANGED])
+        scriptFile_->Execute(scriptObject_, methods_[METHOD_TRANSFORMCHANGED]);
+}
 
 void ScriptInstance::CreateObject()
 {
@@ -467,6 +481,8 @@ void ScriptInstance::ReleaseObject()
         exceptions.Push(E_RELOADSTARTED);
         exceptions.Push(E_RELOADFINISHED);
         UnsubscribeFromAllEventsExcept(exceptions, false);
+        if (node_)
+            node_->RemoveListener(this);
         subscribed_ = false;
         subscribedPostFixed_ = false;
         
@@ -596,7 +612,7 @@ void ScriptInstance::UpdateEventSubscription()
     Scene* scene = GetScene();
     if (!scene)
     {
-        LOGERROR("Node is detached from scene, can not subscribe script object to update events");
+        LOGWARNING("Node is detached from scene, can not subscribe script object to update events");
         return;
     }
     
@@ -632,6 +648,9 @@ void ScriptInstance::UpdateEventSubscription()
             
             subscribedPostFixed_ = true;
         }
+        
+        if (methods_[METHOD_TRANSFORMCHANGED])
+            node_->AddListener(this);
     }
     else
     {
@@ -654,6 +673,9 @@ void ScriptInstance::UpdateEventSubscription()
             
             subscribedPostFixed_ = false;
         }
+        
+        if (methods_[METHOD_TRANSFORMCHANGED])
+            node_->RemoveListener(this);
     }
 }
 

+ 5 - 0
Source/Engine/Script/ScriptInstance.h

@@ -49,6 +49,7 @@ enum ScriptInstanceMethod
     METHOD_READNETWORKUPDATE,
     METHOD_WRITENETWORKUPDATE,
     METHOD_APPLYATTRIBUTES,
+    METHOD_TRANSFORMCHANGED,
     MAX_SCRIPT_METHODS
 };
 
@@ -142,6 +143,10 @@ public:
     /// Get script network serialization attribute by calling a script function.
     PODVector<unsigned char> GetScriptNetworkDataAttr() const;
     
+protected:
+    /// Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
+    
 private:
     /// (Re)create the script object and check for supported methods if successfully created.
     void CreateObject();

+ 53 - 20
Source/Extras/LuaScript/LuaScriptInstance.cpp

@@ -30,7 +30,10 @@
 #include "LuaScriptInstance.h"
 #include "MemoryBuffer.h"
 #include "PhysicsEvents.h"
+#include "PhysicsWorld.h"
 #include "ResourceCache.h"
+#include "Scene.h"
+#include "SceneEvents.h"
 #include "ToluaUrho3DEx.h"
 #include "ProcessUtils.h"
 #include "VectorBuffer.h"
@@ -59,7 +62,8 @@ static const char* scriptObjectMethodNames[] = {
     "Save",
     "ReadNetworkUpdate",
     "WriteNetworkUpdate",
-    "ApplyAttributes"
+    "ApplyAttributes",
+    "TransformChanged"
 };
 
 LuaScriptInstance::LuaScriptInstance(Context* context) :
@@ -100,7 +104,7 @@ void LuaScriptInstance::ApplyAttributes()
 
 void LuaScriptInstance::OnSetEnabled()
 {
-    if (enabled_)
+    if (IsEnabledEffective())
         SubscribeToScriptMethodEvents();
     else
         UnsubscribeFromScriptMethodEvents();
@@ -308,43 +312,72 @@ PODVector<unsigned char> LuaScriptInstance::GetScriptNetworkDataAttr() const
     return buf.GetBuffer();
 }
 
+void LuaScriptInstance::OnMarkedDirty(Node* node)
+{
+    // Script functions are not safe from worker threads
+    Scene* scene = GetScene();
+    if (scene && scene->IsThreadedUpdate())
+    {
+        scene->DelayedMarkedDirty(this);
+        return;
+    }
+    
+    LuaFunction* function = scriptObjectMethods_[LSOM_TRANSFORMCHANGED];
+    if (function && function->BeginCall(this))
+    {
+        function->EndCall();
+    }
+}
+
 void LuaScriptInstance::FindScriptObjectMethodRefs()
 {
     for (unsigned i = 0; i < MAX_LUA_SCRIPT_OBJECT_METHODS; ++i)
         scriptObjectMethods_[i] = GetScriptObjectFunction(scriptObjectMethodNames[i]);
 
-    if (enabled_)
+    if (IsEnabledEffective())
         SubscribeToScriptMethodEvents();
 }
 
 void LuaScriptInstance::SubscribeToScriptMethodEvents()
 {
-    if (scriptObjectMethods_[LSOM_UPDATE])
-        SubscribeToEvent(E_UPDATE, HANDLER(LuaScriptInstance, HandleUpdate));
+    Scene* scene = GetScene();
+    PhysicsWorld* physicsWorld = scene ? scene->GetComponent<PhysicsWorld>() : 0;
+    
+    if (scene && scriptObjectMethods_[LSOM_UPDATE])
+        SubscribeToEvent(scene, E_SCENEUPDATE, HANDLER(LuaScriptInstance, HandleUpdate));
 
-    if (scriptObjectMethods_[LSOM_POSTUPDATE])
-        SubscribeToEvent(E_POSTUPDATE, HANDLER(LuaScriptInstance, HandlePostUpdate));
+    if (scene && scriptObjectMethods_[LSOM_POSTUPDATE])
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(LuaScriptInstance, HandlePostUpdate));
 
-    if (scriptObjectMethods_[LSOM_FIXEDUPDATE])
-        SubscribeToEvent(E_PHYSICSPRESTEP, HANDLER(LuaScriptInstance, HandleFixedUpdate));
+    if (physicsWorld && scriptObjectMethods_[LSOM_FIXEDUPDATE])
+        SubscribeToEvent(physicsWorld, E_PHYSICSPRESTEP, HANDLER(LuaScriptInstance, HandleFixedUpdate));
 
-    if (scriptObjectMethods_[LSOM_FIXEDPOSTUPDATE])
-        SubscribeToEvent(E_PHYSICSPOSTSTEP, HANDLER(LuaScriptInstance, HandlePostFixedUpdate));
+    if (physicsWorld && scriptObjectMethods_[LSOM_FIXEDPOSTUPDATE])
+        SubscribeToEvent(physicsWorld, E_PHYSICSPOSTSTEP, HANDLER(LuaScriptInstance, HandlePostFixedUpdate));
+    
+    if (node_ && scriptObjectMethods_[LSOM_TRANSFORMCHANGED])
+        node_->AddListener(this);
 }
 
 void LuaScriptInstance::UnsubscribeFromScriptMethodEvents()
 {
-    if (scriptObjectMethods_[LSOM_UPDATE])
-        UnsubscribeFromEvent(E_UPDATE);
+    Scene* scene = GetScene();
+    PhysicsWorld* physicsWorld = scene ? scene->GetComponent<PhysicsWorld>() : 0;
+    
+    if (scene && scriptObjectMethods_[LSOM_UPDATE])
+        UnsubscribeFromEvent(scene, E_SCENEUPDATE);
 
-    if (scriptObjectMethods_[LSOM_POSTUPDATE])
-        UnsubscribeFromEvent(E_POSTUPDATE);
+    if (scene && scriptObjectMethods_[LSOM_POSTUPDATE])
+        UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
 
-    if (scriptObjectMethods_[LSOM_FIXEDUPDATE])
-        UnsubscribeFromEvent(E_PHYSICSPRESTEP);
+    if (physicsWorld && scriptObjectMethods_[LSOM_FIXEDUPDATE])
+        UnsubscribeFromEvent(physicsWorld, E_PHYSICSPRESTEP);
 
-    if (scriptObjectMethods_[LSOM_FIXEDPOSTUPDATE])
-        UnsubscribeFromEvent(E_PHYSICSPOSTSTEP);
+    if (physicsWorld && scriptObjectMethods_[LSOM_FIXEDPOSTUPDATE])
+        UnsubscribeFromEvent(physicsWorld, E_PHYSICSPOSTSTEP);
+    
+    if (node_ && scriptObjectMethods_[LSOM_TRANSFORMCHANGED])
+        node_->RemoveListener(this);
 }
 
 void LuaScriptInstance::HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -427,7 +460,7 @@ void LuaScriptInstance::ReleaseObject()
     if (scriptObjectRef_ == LUA_REFNIL)
         return;
 
-    if (enabled_)
+    if (IsEnabledEffective())
         UnsubscribeFromScriptMethodEvents();
 
     // Unref script object.

+ 5 - 0
Source/Extras/LuaScript/LuaScriptInstance.h

@@ -46,6 +46,7 @@ enum LuaScriptObjectMethod
     LSOM_READNETWORKUPDATE,
     LSOM_WRITENETWORKUPDATE,
     LSOM_APPLYATTRIBUTES,
+    LSOM_TRANSFORMCHANGED,
     MAX_LUA_SCRIPT_OBJECT_METHODS
 };
 
@@ -105,6 +106,10 @@ public:
     /// Return script object's funcition.
     LuaFunction* GetScriptObjectFunction(const String& functionName);
 
+protected:
+    /// Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
+
 private:
     /// Find script object method refs.
     void FindScriptObjectMethodRefs();