فهرست منبع

Merge branch 'master' into threaded-resource-load

Lasse Öörni 11 سال پیش
والد
کامیت
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 "[ $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 "( [ $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:
-    - 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 -e /etc/init.d/xvfb start
 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();
     Resource@ res = GetPickedResource(resourceName);
 
-    Print("INDEX");
-    Print(resourcePickIndex);
-    
     if (res !is null && editMaterial !is null)
     {
         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_TEXTURE_3D = 19;
 const int RESOURCE_TYPE_CUBEMAP = 20;
+const int RESOURCE_TYPE_PARTICLEEMITTER = 21;
 
 const StringHash XML_TYPE_SCENE("scene");
 const StringHash XML_TYPE_NODE("node");
 const StringHash XML_TYPE_MATERIAL("material");
 const StringHash XML_TYPE_TECHNIQUE("technique");
 const StringHash XML_TYPE_PARTICLEEFFECT("particleeffect");
+const StringHash XML_TYPE_PARTICLEEMITTER("particleemitter");
 const StringHash XML_TYPE_TEXTURE("texture");
 const StringHash XML_TYPE_ELEMENT("element");
 const StringHash XML_TYPE_ELEMENTS("elements");
@@ -226,7 +228,7 @@ void CreateResourceFilterUI()
 
     UIElement@ col1 = browserFilterWindow.GetChild("TypeFilterColumn1", 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)
             continue;
@@ -1024,6 +1026,8 @@ int GetResourceType(StringHash fileType)
         return RESOURCE_TYPE_TECHNIQUE;
     else if(fileType == XML_TYPE_PARTICLEEFFECT)
         return RESOURCE_TYPE_PARTICLEEFFECT;
+    else if(fileType == XML_TYPE_PARTICLEEMITTER)
+        return RESOURCE_TYPE_PARTICLEEMITTER;
     else if(fileType == XML_TYPE_TEXTURE)
         return RESOURCE_TYPE_TEXTURE;
     else if(fileType == XML_TYPE_ELEMENT)
@@ -1221,6 +1225,8 @@ bool GetXmlType(String path, StringHash &out fileType, bool useCache = false)
             fileType = XML_TYPE_TECHNIQUE;
         else if(type == XML_TYPE_PARTICLEEFFECT)
             fileType = XML_TYPE_PARTICLEEFFECT;
+        else if(type == XML_TYPE_PARTICLEEMITTER)
+            fileType = XML_TYPE_PARTICLEEMITTER;
         else if(type == XML_TYPE_TEXTURE)
             fileType = XML_TYPE_TEXTURE;
         else if(type == XML_TYPE_ELEMENT)
@@ -1277,6 +1283,8 @@ String ResourceTypeName(int resourceType)
         return "Render Technique";
     else if (resourceType == RESOURCE_TYPE_PARTICLEEFFECT)
         return "Particle Effect";
+    else if (resourceType == RESOURCE_TYPE_PARTICLEEMITTER)
+        return "Particle Emitter";
     else if (resourceType == RESOURCE_TYPE_UIELEMENT)
         return "UI Element";
     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.
 
-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.
 

+ 5 - 0
Docs/ScriptAPI.dox

@@ -1331,6 +1331,11 @@ namespace Urho3D
 - %Shadow %Distance : float
 - %Animation %LOD %Bias : float
 - %Is %Emitting : bool
+- %Max %Lights : int
+- %View %Mask : int
+- %Light %Mask : int
+- %Shadow %Mask : int
+- %Zone %Mask : int
 
 ### ParticleEmitter2D
 - %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)
     if (isMaster_)
     {
-        skeleton_.Reset();
+        skeleton_.ResetSilent();
         for (Vector<SharedPtr<AnimationState> >::Iterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
             (*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
         UpdateBoneBoundingBox();
     }

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

@@ -401,9 +401,9 @@ void AnimationState::ApplyToModel()
             continue;
         
         if (Equals(finalWeight, 1.0f))
-            ApplyTrackFullWeight(stateTrack);
+            ApplyTrackFullWeightSilent(stateTrack);
         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_;
     Node* node = stateTrack.node_;
@@ -502,11 +558,11 @@ void AnimationState::ApplyTrackBlended(AnimationStateTrack& stateTrack, float we
     {
         // No interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
-            node->SetPosition(node->GetPosition().Lerp(keyFrame->position_, weight));
+            node->SetPositionSilent(node->GetPosition().Lerp(keyFrame->position_, weight));
         if (channelMask & CHANNEL_ROTATION)
-            node->SetRotation(node->GetRotation().Slerp(keyFrame->rotation_, weight));
+            node->SetRotationSilent(node->GetRotation().Slerp(keyFrame->rotation_, weight));
         if (channelMask & CHANNEL_SCALE)
-            node->SetScale(node->GetScale().Lerp(keyFrame->scale_, weight));
+            node->SetScaleSilent(node->GetScale().Lerp(keyFrame->scale_, weight));
     }
     else
     {
@@ -519,17 +575,17 @@ void AnimationState::ApplyTrackBlended(AnimationStateTrack& stateTrack, float we
         // Interpolation, blend between old transform & animation
         if (channelMask & CHANNEL_POSITION)
         {
-            node->SetPosition(node->GetPosition().Lerp(
+            node->SetPositionSilent(node->GetPosition().Lerp(
                 keyFrame->position_.Lerp(nextKeyFrame->position_, t), weight));
         }
         if (channelMask & CHANNEL_ROTATION)
         {
-            node->SetRotation(node->GetRotation().Slerp(
+            node->SetRotationSilent(node->GetRotation().Slerp(
                 keyFrame->rotation_.Slerp(nextKeyFrame->rotation_, t), weight));
         }
         if (channelMask & CHANNEL_SCALE)
         {
-            node->SetScale(node->GetScale().Lerp(
+            node->SetScaleSilent(node->GetScale().Lerp(
                 keyFrame->scale_.Lerp(nextKeyFrame->scale_, t), weight));
         }
     }

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

@@ -125,15 +125,17 @@ public:
     void Apply();
     
 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();
     /// Apply animation to a scene node hierarchy.
     void ApplyToNodes();
     /// Apply animation track to a scene node, full weight.
     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.)
     WeakPtr<AnimatedModel> model_;
     /// 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_FLOAT, "Period Timer", periodTimer_, 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, "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()
 {
     return GetBone(rootBoneIndex_);

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

@@ -114,6 +114,9 @@ public:
     /// Return bone by name hash.
     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:
     /// Bones.
     Vector<Bone> bones_;

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

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

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

@@ -449,6 +449,14 @@ public:
     unsigned GetNumPersistentChildren() const;
     /// Calculate number of non-temporary components.
     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:
     /// Handle attribute animation added.

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

@@ -294,7 +294,8 @@ void AnimatedSprite2D::OnWorldBoundingBoxUpdate()
             continue;
 
         StaticSprite2D* staticSprite = timelineNodes_[i]->GetComponent<StaticSprite2D>();
-        worldBoundingBox_.Merge(staticSprite->GetWorldBoundingBox());
+        if (staticSprite)
+            worldBoundingBox_.Merge(staticSprite->GetWorldBoundingBox());
     }
 
     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)
     {
         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>();
-            }
-            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>();
-            }
+        }
 
+        if (staticSprite)
+        {
             staticSprite->SetLayer(layer_);
             staticSprite->SetBlendMode(blendMode_);
             staticSprite->SetFlip(flipX_, flipY_);
             staticSprite->SetUseHotSpot(true);
-
-            timelineNodes_[i] = timelineNode;
         }
 
+        timelineNodes_[i] = timelineNode;
+
         timelineTransformInfos_[i].parent_ = timeline.parent_;
     }
 
@@ -423,10 +426,13 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
             if (timelineNode)
             {
                 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
@@ -438,9 +444,12 @@ void AnimatedSprite2D::UpdateAnimation(float timeStep)
             if (timelineNode)
             {
                 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
             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*
 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: