Browse Source

Merge branch 'master' into threaded-resource-load

Lasse Öörni 11 years ago
parent
commit
df67ccdac7

+ 7 - 7
.travis.yml

@@ -52,14 +52,14 @@ before_install:
     - bash -c "[ $ON_MASTER_COMMIT ]" && export RELEASE_TAG=$(git describe --tags --exact-match $TRAVIS_COMMIT 2>/dev/null) && bash -c "[ $RELEASE_TAG ]" && export PACKAGE_UPLOAD=1 || true
     - bash -c "[ $ON_MASTER_COMMIT ]" && export RELEASE_TAG=$(git describe --tags --exact-match $TRAVIS_COMMIT 2>/dev/null) && bash -c "[ $RELEASE_TAG ]" && export PACKAGE_UPLOAD=1 || true
     - bash -c "[ $ANDROID ]" && wget -q http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86_64.tar.bz2 && tar xjf *.bz2 && rm *.bz2 && ln -s android-ndk* android-ndk && export ANDROID_NDK=$(pwd)/android-ndk && bash -c "[ $PACKAGE_UPLOAD ]" && wget -q http://dl.google.com/android/android-sdk_r22.6.1-linux.tgz && tar xzf *.tgz && rm *.tgz && ln -s android-sdk* android-sdk && export ANDROID_SDK=$(pwd)/android-sdk || true
     - bash -c "[ $ANDROID ]" && wget -q http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86_64.tar.bz2 && tar xjf *.bz2 && rm *.bz2 && ln -s android-ndk* android-ndk && export ANDROID_NDK=$(pwd)/android-ndk && bash -c "[ $PACKAGE_UPLOAD ]" && wget -q http://dl.google.com/android/android-sdk_r22.6.1-linux.tgz && tar xzf *.tgz && rm *.tgz && ln -s android-sdk* android-sdk && export ANDROID_SDK=$(pwd)/android-sdk || true
     - bash -c "[ $RPI ]" && git clone --depth 1 https://github.com/raspberrypi/tools.git rpi-tools && export RASPI_TOOL=$(pwd)/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin && git clone --depth=1 https://github.com/urho3d/rpi-sysroot.git rpi-sysroot && export RASPI_ROOT=$(pwd)/rpi-sysroot || true
     - bash -c "[ $RPI ]" && git clone --depth 1 https://github.com/raspberrypi/tools.git rpi-tools && export RASPI_TOOL=$(pwd)/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin && git clone --depth=1 https://github.com/urho3d/rpi-sysroot.git rpi-sysroot && export RASPI_ROOT=$(pwd)/rpi-sysroot || true
-    - bash -c "( [ $SITE_UPDATE_ON_MASTER_COMMIT ] || [ $PACKAGE_UPLOAD ] )" && sudo add-apt-repository ppa:george-edison55/precise-backports -y || true
-    - sudo apt-get update -q -y
+    - bash -c "( [ $SITE_UPDATE_ON_MASTER_COMMIT ] || [ $PACKAGE_UPLOAD ] )" && travis_retry sudo add-apt-repository ppa:george-edison55/precise-backports -y || true
+    - travis_retry sudo apt-get update -q -y
 install:
 install:
-    - sudo apt-get install -q -y --no-install-recommends libasound2-dev
-    - bash -c "[ x$URHO3D_64BIT == 'x' ]" && sudo apt-get remove -q -y gvfs-daemons && sudo apt-get install -q -y libxrandr-dev:i386 libglapi-mesa:i386 libgl1-mesa-glx:i386 libgl1-mesa-dev:i386 libxext-dev:i386 libxrender-dev:i386 g++-multilib && export CMAKE_PREFIX_PATH=/usr/lib/i386-linux-gnu || true
-    - bash -c "( [ $SITE_UPDATE_ON_MASTER_COMMIT ] || [ $PACKAGE_UPLOAD ] )" && sudo apt-get install -q -y --no-install-recommends doxygen graphviz || true
-    - bash -c "[ $WINDOWS ]" && sudo apt-get install -q -y gcc-mingw-w64 gcc-mingw-w64-i686 gcc-mingw-w64-x86-64 g++-mingw-w64 g++-mingw-w64-i686 g++-mingw-w64-x86-64 binutils-mingw-w64 binutils-mingw-w64-i686 binutils-mingw-w64-x86-64 && export MINGW_PREFIX=/usr/bin/${ARCH}-w64-mingw32 || true
-    - bash -c "( [ $LINUX ] || [ $RPI ] ) && [ $PACKAGE_UPLOAD ]" && sudo apt-get install -q -y rpm || true
+    - travis_retry sudo apt-get install -q -y --no-install-recommends libasound2-dev
+    - bash -c "[ x$URHO3D_64BIT == 'x' ]" && travis_retry sudo apt-get remove -q -y gvfs-daemons && travis_retry sudo apt-get install -q -y libxrandr-dev:i386 libglapi-mesa:i386 libgl1-mesa-glx:i386 libgl1-mesa-dev:i386 libxext-dev:i386 libxrender-dev:i386 g++-multilib && export CMAKE_PREFIX_PATH=/usr/lib/i386-linux-gnu || true
+    - bash -c "( [ $SITE_UPDATE_ON_MASTER_COMMIT ] || [ $PACKAGE_UPLOAD ] )" && travis_retry sudo apt-get install -q -y --no-install-recommends doxygen graphviz || true
+    - bash -c "[ $WINDOWS ]" && travis_retry sudo apt-get install -q -y gcc-mingw-w64 gcc-mingw-w64-i686 gcc-mingw-w64-x86-64 g++-mingw-w64 g++-mingw-w64-i686 g++-mingw-w64-x86-64 binutils-mingw-w64 binutils-mingw-w64-i686 binutils-mingw-w64-x86-64 && export MINGW_PREFIX=/usr/bin/${ARCH}-w64-mingw32 || true
+    - bash -c "( [ $LINUX ] || [ $RPI ] ) && [ $PACKAGE_UPLOAD ]" && travis_retry sudo apt-get install -q -y rpm || true
     - bash -c "[ $ANDROID ] && [ $PACKAGE_UPLOAD ]" && (while :; do echo 'y'; sleep 1; done) |android-sdk/tools/android update sdk --no-ui --filter platform-tool,build-tools-19.0.3,android-19,system-image,extra-android-support || true
     - bash -c "[ $ANDROID ] && [ $PACKAGE_UPLOAD ]" && (while :; do echo 'y'; sleep 1; done) |android-sdk/tools/android update sdk --no-ui --filter platform-tool,build-tools-19.0.3,android-19,system-image,extra-android-support || true
     - bash -e /etc/init.d/xvfb start
     - bash -e /etc/init.d/xvfb start
 script: rake travis_ci
 script: rake travis_ci

+ 0 - 3
Bin/Data/Scripts/Editor/EditorMaterial.as

@@ -577,9 +577,6 @@ void PickMaterialTextureDone(StringHash eventType, VariantMap& eventData)
     String resourceName = eventData["FileName"].GetString();
     String resourceName = eventData["FileName"].GetString();
     Resource@ res = GetPickedResource(resourceName);
     Resource@ res = GetPickedResource(resourceName);
 
 
-    Print("INDEX");
-    Print(resourcePickIndex);
-    
     if (res !is null && editMaterial !is null)
     if (res !is null && editMaterial !is null)
     {
     {
         BeginMaterialEdit();
         BeginMaterialEdit();

+ 9 - 1
Bin/Data/Scripts/Editor/EditorResourceBrowser.as

@@ -49,12 +49,14 @@ const int RESOURCE_TYPE_TEXTURE_ATLAS = 17;
 const int RESOURCE_TYPE_2D_PARTICLE_EFFECT = 18;
 const int RESOURCE_TYPE_2D_PARTICLE_EFFECT = 18;
 const int RESOURCE_TYPE_TEXTURE_3D = 19;
 const int RESOURCE_TYPE_TEXTURE_3D = 19;
 const int RESOURCE_TYPE_CUBEMAP = 20;
 const int RESOURCE_TYPE_CUBEMAP = 20;
+const int RESOURCE_TYPE_PARTICLEEMITTER = 21;
 
 
 const StringHash XML_TYPE_SCENE("scene");
 const StringHash XML_TYPE_SCENE("scene");
 const StringHash XML_TYPE_NODE("node");
 const StringHash XML_TYPE_NODE("node");
 const StringHash XML_TYPE_MATERIAL("material");
 const StringHash XML_TYPE_MATERIAL("material");
 const StringHash XML_TYPE_TECHNIQUE("technique");
 const StringHash XML_TYPE_TECHNIQUE("technique");
 const StringHash XML_TYPE_PARTICLEEFFECT("particleeffect");
 const StringHash XML_TYPE_PARTICLEEFFECT("particleeffect");
+const StringHash XML_TYPE_PARTICLEEMITTER("particleemitter");
 const StringHash XML_TYPE_TEXTURE("texture");
 const StringHash XML_TYPE_TEXTURE("texture");
 const StringHash XML_TYPE_ELEMENT("element");
 const StringHash XML_TYPE_ELEMENT("element");
 const StringHash XML_TYPE_ELEMENTS("elements");
 const StringHash XML_TYPE_ELEMENTS("elements");
@@ -226,7 +228,7 @@ void CreateResourceFilterUI()
 
 
     UIElement@ col1 = browserFilterWindow.GetChild("TypeFilterColumn1", true);
     UIElement@ col1 = browserFilterWindow.GetChild("TypeFilterColumn1", true);
     UIElement@ col2 = browserFilterWindow.GetChild("TypeFilterColumn2", true);
     UIElement@ col2 = browserFilterWindow.GetChild("TypeFilterColumn2", true);
-    for (int i=-2; i < 21; ++i)
+    for (int i=-2; i < 22; ++i)
     {
     {
         if (i == RESOURCE_TYPE_NOTSET)
         if (i == RESOURCE_TYPE_NOTSET)
             continue;
             continue;
@@ -1024,6 +1026,8 @@ int GetResourceType(StringHash fileType)
         return RESOURCE_TYPE_TECHNIQUE;
         return RESOURCE_TYPE_TECHNIQUE;
     else if(fileType == XML_TYPE_PARTICLEEFFECT)
     else if(fileType == XML_TYPE_PARTICLEEFFECT)
         return RESOURCE_TYPE_PARTICLEEFFECT;
         return RESOURCE_TYPE_PARTICLEEFFECT;
+    else if(fileType == XML_TYPE_PARTICLEEMITTER)
+        return RESOURCE_TYPE_PARTICLEEMITTER;
     else if(fileType == XML_TYPE_TEXTURE)
     else if(fileType == XML_TYPE_TEXTURE)
         return RESOURCE_TYPE_TEXTURE;
         return RESOURCE_TYPE_TEXTURE;
     else if(fileType == XML_TYPE_ELEMENT)
     else if(fileType == XML_TYPE_ELEMENT)
@@ -1221,6 +1225,8 @@ bool GetXmlType(String path, StringHash &out fileType, bool useCache = false)
             fileType = XML_TYPE_TECHNIQUE;
             fileType = XML_TYPE_TECHNIQUE;
         else if(type == XML_TYPE_PARTICLEEFFECT)
         else if(type == XML_TYPE_PARTICLEEFFECT)
             fileType = XML_TYPE_PARTICLEEFFECT;
             fileType = XML_TYPE_PARTICLEEFFECT;
+        else if(type == XML_TYPE_PARTICLEEMITTER)
+            fileType = XML_TYPE_PARTICLEEMITTER;
         else if(type == XML_TYPE_TEXTURE)
         else if(type == XML_TYPE_TEXTURE)
             fileType = XML_TYPE_TEXTURE;
             fileType = XML_TYPE_TEXTURE;
         else if(type == XML_TYPE_ELEMENT)
         else if(type == XML_TYPE_ELEMENT)
@@ -1277,6 +1283,8 @@ String ResourceTypeName(int resourceType)
         return "Render Technique";
         return "Render Technique";
     else if (resourceType == RESOURCE_TYPE_PARTICLEEFFECT)
     else if (resourceType == RESOURCE_TYPE_PARTICLEEFFECT)
         return "Particle Effect";
         return "Particle Effect";
+    else if (resourceType == RESOURCE_TYPE_PARTICLEEMITTER)
+        return "Particle Emitter";
     else if (resourceType == RESOURCE_TYPE_UIELEMENT)
     else if (resourceType == RESOURCE_TYPE_UIELEMENT)
         return "UI Element";
         return "UI Element";
     else if (resourceType == RESOURCE_TYPE_UIELEMENTS)
     else if (resourceType == RESOURCE_TYPE_UIELEMENTS)

+ 1 - 1
Docs/Reference.dox

@@ -432,7 +432,7 @@ The Start() and Stop() methods do not have direct counterparts in C++ components
 
 
 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.
 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. Due to an optimization mechanism for repeated scene graph updates, you need to "undirty" the scene node by for example reading its world position to get further transform changed notifications.
+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 instantiated script object, the ScriptInstance becomes the event receiver on the C++ side, and calls the specified handler method when the event arrives. If called from a function, the ScriptFile will be the event receiver and the handler must be a free function in the same script file. The third case is if the event is subscribed to from a script object that does not belong to a ScriptInstance. In that case the ScriptFile will create a proxy C++ object on demand to be able to forward the event to the script object.
 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 instantiated script object, the ScriptInstance becomes the event receiver on the C++ side, and calls the specified handler method when the event arrives. If called from a function, the ScriptFile will be the event receiver and the handler must be a free function in the same script file. The third case is if the event is subscribed to from a script object that does not belong to a ScriptInstance. In that case the ScriptFile will create a proxy C++ object on demand to be able to forward the event to the script object.
 
 

+ 5 - 0
Docs/ScriptAPI.dox

@@ -1331,6 +1331,11 @@ namespace Urho3D
 - %Shadow %Distance : float
 - %Shadow %Distance : float
 - %Animation %LOD %Bias : float
 - %Animation %LOD %Bias : float
 - %Is %Emitting : bool
 - %Is %Emitting : bool
+- %Max %Lights : int
+- %View %Mask : int
+- %Light %Mask : int
+- %Shadow %Mask : int
+- %Zone %Mask : int
 
 
 ### ParticleEmitter2D
 ### ParticleEmitter2D
 - %Particle %Effect : ResourceRef
 - %Particle %Effect : ResourceRef

+ 4 - 1
Source/Engine/Graphics/AnimatedModel.cpp

@@ -1134,10 +1134,13 @@ void AnimatedModel::UpdateAnimation(const FrameInfo& frame)
     // (first AnimatedModel in a node)
     // (first AnimatedModel in a node)
     if (isMaster_)
     if (isMaster_)
     {
     {
-        skeleton_.Reset();
+        skeleton_.ResetSilent();
         for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
         for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
             (*i)->Apply();
             (*i)->Apply();
         
         
+        // Skeleton reset and animations apply the node transforms "silently" to avoid repeated marking dirty. Mark dirty now
+        node_->MarkDirty();
+
         // Calculate new bone bounding box
         // Calculate new bone bounding box
         UpdateBoneBoundingBox();
         UpdateBoneBoundingBox();
     }
     }

+ 65 - 9
Source/Engine/Graphics/AnimationState.cpp

@@ -401,9 +401,9 @@ void AnimationState::ApplyToModel()
             continue;
             continue;
         
         
         if (Equals(finalWeight, 1.0f))
         if (Equals(finalWeight, 1.0f))
-            ApplyTrackFullWeight(stateTrack);
+            ApplyTrackFullWeightSilent(stateTrack);
         else
         else
-            ApplyTrackBlended(stateTrack, finalWeight);
+            ApplyTrackBlendedSilent(stateTrack, finalWeight);
     }
     }
 }
 }
 
 
@@ -470,7 +470,63 @@ void AnimationState::ApplyTrackFullWeight(AnimationStateTrack& stateTrack)
     }
     }
 }
 }
 
 
-void AnimationState::ApplyTrackBlended(AnimationStateTrack& stateTrack, float weight)
+void AnimationState::ApplyTrackFullWeightSilent(AnimationStateTrack& stateTrack)
+{
+    const AnimationTrack* track = stateTrack.track_;
+    Node* node = stateTrack.node_;
+
+    if (track->keyFrames_.Empty() || !node)
+        return;
+
+    unsigned& frame = stateTrack.keyFrame_;
+    track->GetKeyFrameIndex(time_, frame);
+
+    // Check if next frame to interpolate to is valid, or if wrapping is needed (looping animation only)
+    unsigned nextFrame = frame + 1;
+    bool interpolate = true;
+    if (nextFrame >= track->keyFrames_.Size())
+    {
+        if (!looped_)
+        {
+            nextFrame = frame;
+            interpolate = false;
+        }
+        else
+            nextFrame = 0;
+    }
+
+    const AnimationKeyFrame* keyFrame = &track->keyFrames_[frame];
+    unsigned char channelMask = track->channelMask_;
+
+    if (!interpolate)
+    {
+        // No interpolation, full weight
+        if (channelMask & CHANNEL_POSITION)
+            node->SetPositionSilent(keyFrame->position_);
+        if (channelMask & CHANNEL_ROTATION)
+            node->SetRotationSilent(keyFrame->rotation_);
+        if (channelMask & CHANNEL_SCALE)
+            node->SetScaleSilent(keyFrame->scale_);
+    }
+    else
+    {
+        const AnimationKeyFrame* nextKeyFrame = &track->keyFrames_[nextFrame];
+        float timeInterval = nextKeyFrame->time_ - keyFrame->time_;
+        if (timeInterval < 0.0f)
+            timeInterval += animation_->GetLength();
+        float t = timeInterval > 0.0f ? (time_ - keyFrame->time_) / timeInterval : 1.0f;
+
+        // Interpolation, full weight
+        if (channelMask & CHANNEL_POSITION)
+            node->SetPositionSilent(keyFrame->position_.Lerp(nextKeyFrame->position_, t));
+        if (channelMask & CHANNEL_ROTATION)
+            node->SetRotationSilent(keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t));
+        if (channelMask & CHANNEL_SCALE)
+            node->SetScaleSilent(keyFrame->scale_.Lerp(nextKeyFrame->scale_, t));
+    }
+}
+
+void AnimationState::ApplyTrackBlendedSilent(AnimationStateTrack& stateTrack, float weight)
 {
 {
     const AnimationTrack* track = stateTrack.track_;
     const AnimationTrack* track = stateTrack.track_;
     Node* node = stateTrack.node_;
     Node* node = stateTrack.node_;
@@ -502,11 +558,11 @@ void AnimationState::ApplyTrackBlended(AnimationStateTrack& stateTrack, float we
     {
     {
         // No interpolation, blend between old transform & animation
         // No interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
-            node->SetPosition(node->GetPosition().Lerp(keyFrame->position_, weight));
+            node->SetPositionSilent(node->GetPosition().Lerp(keyFrame->position_, weight));
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotation(node->GetRotation().Slerp(keyFrame->rotation_, weight));
+            node->SetRotationSilent(node->GetRotation().Slerp(keyFrame->rotation_, weight));
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
-            node->SetScale(node->GetScale().Lerp(keyFrame->scale_, weight));
+            node->SetScaleSilent(node->GetScale().Lerp(keyFrame->scale_, weight));
     }
     }
     else
     else
     {
     {
@@ -519,17 +575,17 @@ void AnimationState::ApplyTrackBlended(AnimationStateTrack& stateTrack, float we
         // Interpolation, blend between old transform & animation
         // Interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
         if (channelMask & CHANNEL_POSITION)
         {
         {
-            node->SetPosition(node->GetPosition().Lerp(
+            node->SetPositionSilent(node->GetPosition().Lerp(
                 keyFrame->position_.Lerp(nextKeyFrame->position_, t), weight));
                 keyFrame->position_.Lerp(nextKeyFrame->position_, t), weight));
         }
         }
         if (channelMask & CHANNEL_ROTATION)
         if (channelMask & CHANNEL_ROTATION)
         {
         {
-            node->SetRotation(node->GetRotation().Slerp(
+            node->SetRotationSilent(node->GetRotation().Slerp(
                 keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t), weight));
                 keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t), weight));
         }
         }
         if (channelMask & CHANNEL_SCALE)
         if (channelMask & CHANNEL_SCALE)
         {
         {
-            node->SetScale(node->GetScale().Lerp(
+            node->SetScaleSilent(node->GetScale().Lerp(
                 keyFrame->scale_.Lerp(nextKeyFrame->scale_, t), weight));
                 keyFrame->scale_.Lerp(nextKeyFrame->scale_, t), weight));
         }
         }
     }
     }

+ 6 - 4
Source/Engine/Graphics/AnimationState.h

@@ -125,15 +125,17 @@ public:
     void Apply();
     void Apply();
     
     
 private:
 private:
-    /// Apply animation to a skeleton.
+    /// Apply animation to a skeleton. Transform changes are applied silently, so the model needs to dirty its root model afterward.
     void ApplyToModel();
     void ApplyToModel();
     /// Apply animation to a scene node hierarchy.
     /// Apply animation to a scene node hierarchy.
     void ApplyToNodes();
     void ApplyToNodes();
     /// Apply animation track to a scene node, full weight.
     /// Apply animation track to a scene node, full weight.
     void ApplyTrackFullWeight(AnimationStateTrack& stateTrack);
     void ApplyTrackFullWeight(AnimationStateTrack& stateTrack);
-    /// Apply animation track to a scene node, blended with current node transform.
-    void ApplyTrackBlended(AnimationStateTrack& stateTrack, float weight);
-    
+    /// Apply animation track to a scene node, full weight. Apply transform changes silently without marking the node dirty.
+    void ApplyTrackFullWeightSilent(AnimationStateTrack& stateTrack);
+    /// Apply animation track to a scene node, blended with current node transform. Apply transform changes silently without marking the node dirty.
+    void ApplyTrackBlendedSilent(AnimationStateTrack& stateTrack, float weight);
+
     /// Animated model (model mode.)
     /// Animated model (model mode.)
     WeakPtr<AnimatedModel> model_;
     WeakPtr<AnimatedModel> model_;
     /// Root scene node (node hierarchy mode.)
     /// Root scene node (node hierarchy mode.)

+ 1 - 0
Source/Engine/Graphics/ParticleEmitter.cpp

@@ -68,6 +68,7 @@ void ParticleEmitter::RegisterObject(Context* context)
     ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Emitting", emitting_, true, AM_FILE);
     ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Is Emitting", emitting_, true, AM_FILE);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Period Timer", periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Period Timer", periodTimer_, 0.0f, AM_FILE | AM_NOEDIT);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Timer", emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Emission Timer", emissionTimer_, 0.0f, AM_FILE | AM_NOEDIT);
+    COPY_BASE_ATTRIBUTES(ParticleEmitter, Drawable);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Particles", GetParticlesAttr, SetParticlesAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_VARIANTVECTOR, "Billboards", GetBillboardsAttr, SetBillboardsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
 }
 }

+ 10 - 0
Source/Engine/Graphics/Skeleton.cpp

@@ -129,6 +129,16 @@ void Skeleton::Reset()
     }
     }
 }
 }
 
 
+void Skeleton::ResetSilent()
+{
+    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
+    {
+        if (i->animated_ && i->node_)
+            i->node_->SetTransformSilent(i->initialPosition_, i->initialRotation_, i->initialScale_);
+    }
+}
+
+
 Bone* Skeleton::GetRootBone()
 Bone* Skeleton::GetRootBone()
 {
 {
     return GetBone(rootBoneIndex_);
     return GetBone(rootBoneIndex_);

+ 3 - 0
Source/Engine/Graphics/Skeleton.h

@@ -114,6 +114,9 @@ public:
     /// Return bone by name hash.
     /// Return bone by name hash.
     Bone* GetBone(StringHash boneNameHash);
     Bone* GetBone(StringHash boneNameHash);
     
     
+    /// Reset all animating bones to initial positions without marking the nodes dirty. Requires the node dirtying to be performed later.
+    void ResetSilent();
+
 private:
 private:
     /// Bones.
     /// Bones.
     Vector<Bone> bones_;
     Vector<Bone> bones_;

+ 7 - 3
Source/Engine/Scene/Node.cpp

@@ -589,9 +589,6 @@ void Node::SetOwner(Connection* owner)
 
 
 void Node::MarkDirty()
 void Node::MarkDirty()
 {
 {
-    if (dirty_)
-        return;
-
     dirty_ = true;
     dirty_ = true;
 
 
     // Notify listener components first, then mark child nodes
     // Notify listener components first, then mark child nodes
@@ -1503,6 +1500,13 @@ unsigned Node::GetNumPersistentComponents() const
     return ret;
     return ret;
 }
 }
 
 
+void Node::SetTransformSilent(const Vector3& position, const Quaternion& rotation, const Vector3& scale)
+{
+    position_ = position;
+    rotation_ = rotation;
+    scale_ = scale;
+}
+
 void Node::OnAttributeAnimationAdded()
 void Node::OnAttributeAnimationAdded()
 {
 {
     if (attributeAnimationInfos_.Size() == 1)
     if (attributeAnimationInfos_.Size() == 1)

+ 8 - 0
Source/Engine/Scene/Node.h

@@ -449,6 +449,14 @@ public:
     unsigned GetNumPersistentChildren() const;
     unsigned GetNumPersistentChildren() const;
     /// Calculate number of non-temporary components.
     /// Calculate number of non-temporary components.
     unsigned GetNumPersistentComponents() const;
     unsigned GetNumPersistentComponents() const;
+    /// Set position in parent space silently without marking the node & child nodes dirty. Used by animation code.
+    void SetPositionSilent(const Vector3& position) { position_ = position; }
+    /// Set position in parent space silently without marking the node & child nodes dirty. Used by animation code.
+    void SetRotationSilent(const Quaternion& rotation) { rotation_ = rotation; }
+    /// Set scale in parent space silently without marking the node & child nodes dirty. Used by animation code.
+    void SetScaleSilent(const Vector3& scale) { scale_ = scale; }
+    /// Set local transform silently without marking the node & child nodes dirty. Used by animation code.
+    void SetTransformSilent(const Vector3& position, const Quaternion& rotation, const Vector3& scale);
 
 
 protected:
 protected:
     /// Handle attribute animation added.
     /// Handle attribute animation added.

+ 38 - 28
Source/Engine/Urho2D/AnimatedSprite2D.cpp

@@ -294,7 +294,8 @@ void AnimatedSprite2D::OnWorldBoundingBoxUpdate()
             continue;
             continue;
 
 
         StaticSprite2D* staticSprite = timelineNodes_[i]->GetComponent<StaticSprite2D>();
         StaticSprite2D* staticSprite = timelineNodes_[i]->GetComponent<StaticSprite2D>();
-        worldBoundingBox_.Merge(staticSprite->GetWorldBoundingBox());
+        if (staticSprite)
+            worldBoundingBox_.Merge(staticSprite->GetWorldBoundingBox());
     }
     }
 
 
     boundingBox_ = worldBoundingBox_.Transformed(node_->GetWorldTransform().Inverse());
     boundingBox_ = worldBoundingBox_.Transformed(node_->GetWorldTransform().Inverse());
@@ -338,35 +339,37 @@ void AnimatedSprite2D::SetAnimation(Animation2D* animation, LoopMode2D loopMode)
     for (unsigned i = 0; i < animation_->GetNumTimelines(); ++i)
     for (unsigned i = 0; i < animation_->GetNumTimelines(); ++i)
     {
     {
         const Timeline2D& timeline = animation->GetTimeline(i);
         const Timeline2D& timeline = animation->GetTimeline(i);
-        // Just handle OT_SPRITE type timeline
-        if (timeline.type_ == OT_SPRITE)
-        {
-            SharedPtr<Node> timelineNode(rootNode_->GetChild(timeline.name_));
-            StaticSprite2D* staticSprite = 0;
+        SharedPtr<Node> timelineNode(rootNode_->GetChild(timeline.name_));
 
 
-            if (timelineNode)
-            {
-                // Enable timeline node
-                timelineNode->SetEnabled(true);
-                // Get StaticSprite2D component
+        StaticSprite2D* staticSprite = 0;
+
+        if (timelineNode)
+        {
+            // Enable timeline node
+            timelineNode->SetEnabled(true);
+            // Get StaticSprite2D component
+            if (timeline.type_ == OT_SPRITE)
                 staticSprite = timelineNode->GetComponent<StaticSprite2D>();
                 staticSprite = timelineNode->GetComponent<StaticSprite2D>();
-            }
-            else
-            {
-                // Create new timeline node
-                timelineNode = rootNode_->CreateChild(timeline.name_, LOCAL);
-                // Create StaticSprite2D component
+        }
+        else
+        {
+            // Create new timeline node
+            timelineNode = rootNode_->CreateChild(timeline.name_, LOCAL);
+            // Create StaticSprite2D component
+            if (timeline.type_ == OT_SPRITE)
                 staticSprite = timelineNode->CreateComponent<StaticSprite2D>();
                 staticSprite = timelineNode->CreateComponent<StaticSprite2D>();
-            }
+        }
 
 
+        if (staticSprite)
+        {
             staticSprite->SetLayer(layer_);
             staticSprite->SetLayer(layer_);
             staticSprite->SetBlendMode(blendMode_);
             staticSprite->SetBlendMode(blendMode_);
             staticSprite->SetFlip(flipX_, flipY_);
             staticSprite->SetFlip(flipX_, flipY_);
             staticSprite->SetUseHotSpot(true);
             staticSprite->SetUseHotSpot(true);
-
-            timelineNodes_[i] = timelineNode;
         }
         }
 
 
+        timelineNodes_[i] = timelineNode;
+
         timelineTransformInfos_[i].parent_ = timeline.parent_;
         timelineTransformInfos_[i].parent_ = timeline.parent_;
     }
     }
 
 
@@ -423,10 +426,13 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
             if (timelineNode)
             if (timelineNode)
             {
             {
                 StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
                 StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
-                staticSprite->SetSprite(currKey.sprite_);
-                staticSprite->SetHotSpot(currKey.hotSpot_.Lerp(nextKey.hotSpot_, t));
-                float alpha = Lerp(currKey.alpha_, nextKey.alpha_, t);
-                staticSprite->SetColor(Color(color_.r_, color_.g_, color_.b_, color_.a_ * alpha));
+                if (staticSprite)
+                {
+                    staticSprite->SetSprite(currKey.sprite_);
+                    staticSprite->SetHotSpot(currKey.hotSpot_.Lerp(nextKey.hotSpot_, t));
+                    float alpha = Lerp(currKey.alpha_, nextKey.alpha_, t);
+                    staticSprite->SetColor(Color(color_.r_, color_.g_, color_.b_, color_.a_ * alpha));
+                }
             }
             }
         }
         }
         else
         else
@@ -438,9 +444,12 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
             if (timelineNode)
             if (timelineNode)
             {
             {
                 StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
                 StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
-                staticSprite->SetSprite(currKey.sprite_);
-                staticSprite->SetHotSpot(currKey.hotSpot_);
-                staticSprite->SetColor(Color(color_.r_, color_.g_, color_.b_, color_.a_ * currKey.alpha_));
+                if (staticSprite)
+                {
+                    staticSprite->SetSprite(currKey.sprite_);
+                    staticSprite->SetHotSpot(currKey.hotSpot_);
+                    staticSprite->SetColor(Color(color_.r_, color_.g_, color_.b_, color_.a_ * currKey.alpha_));
+                }
             }
             }
         }
         }
     }
     }
@@ -502,7 +511,8 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
 
 
             // Update sprite's z order
             // Update sprite's z order
             StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
             StaticSprite2D* staticSprite = timelineNode->GetComponent<StaticSprite2D>();
-            staticSprite->SetOrderInLayer(orderInLayer_ + ref->zIndex_);
+            if (staticSprite)
+                staticSprite->SetOrderInLayer(orderInLayer_ + ref->zIndex_);
         }
         }
     }
     }
 
 

+ 5 - 0
cmake_macosx.sh

@@ -51,4 +51,9 @@ if [ "$1" == "-DIOS=1" -a -e $BUILD/CMakeScripts/install_postBuildPhase.makeDebu
     sed -i '' 's/$(EFFECTIVE_PLATFORM_NAME)//g' $BUILD/CMakeScripts/install_postBuildPhase.make*
     sed -i '' 's/$(EFFECTIVE_PLATFORM_NAME)//g' $BUILD/CMakeScripts/install_postBuildPhase.make*
 fi
 fi
 
 
+# Set Xcode build settings to skip dSYM file generation for Debug configuration (other configurations still use the default dwarf-with-dsym)
+if [ "$1" == "-DIOS=1" -a -e $BUILD/*.xcodeproj/project.pbxproj ] && perl -v >/dev/null 2>&1; then
+    perl -i -pe 'BEGIN {$/=undef} s/(Begin XCBuildConfiguration.*?Debug.*?Settings = {\n)/\1DEBUG_INFORMATION_FORMAT = dwarf;\n/s' $BUILD/*.xcodeproj/project.pbxproj
+fi
+
 # vi: set ts=4 sw=4 expandtab:
 # vi: set ts=4 sw=4 expandtab: