Use memory pools to allocate nodes, renderers, materials and animations
[platform/core/uifw/dali-core.git] / dali / internal / update / animation / scene-graph-animation.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/update/animation/scene-graph-animation.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath> // fmod
23
24 // INTERNAL INCLUDES
25 #include <dali/internal/common/memory-pool-object-allocator.h>
26 #include <dali/internal/render/common/performance-monitor.h>
27
28 namespace //Unnamed namespace
29 {
30 //Memory pool used to allocate new animations. Memory used by this pool will be released when shutting down DALi
31 Dali::Internal::MemoryPoolObjectAllocator<Dali::Internal::SceneGraph::Animation> gAnimationMemoryPool;
32 }
33
34 namespace Dali
35 {
36
37 namespace Internal
38 {
39
40 namespace SceneGraph
41 {
42
43 Animation* Animation::New( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, EndAction endAction, EndAction disconnectAction )
44 {
45   return new ( gAnimationMemoryPool.AllocateRawThreadSafe() ) Animation( durationSeconds, speedFactor, playRange, isLooping, endAction, disconnectAction );
46 }
47
48 Animation::Animation( float durationSeconds, float speedFactor, const Vector2& playRange, bool isLooping, Dali::Animation::EndAction endAction, Dali::Animation::EndAction disconnectAction )
49 : mDurationSeconds(durationSeconds),
50   mSpeedFactor( speedFactor ),
51   mLooping(isLooping),
52   mEndAction(endAction),
53   mDisconnectAction(disconnectAction),
54   mState(Stopped),
55   mElapsedSeconds(playRange.x*mDurationSeconds),
56   mPlayCount(0),
57   mPlayRange( playRange )
58 {
59 }
60
61 Animation::~Animation()
62 {
63 }
64
65 void Animation::operator delete( void* ptr )
66 {
67   gAnimationMemoryPool.FreeThreadSafe( static_cast<Animation*>( ptr ) );
68 }
69
70 void Animation::SetDuration(float durationSeconds)
71 {
72   mDurationSeconds = durationSeconds;
73 }
74
75 void Animation::SetLooping(bool looping)
76 {
77   mLooping = looping;
78 }
79
80 void Animation::SetEndAction(Dali::Animation::EndAction action)
81 {
82   mEndAction = action;
83 }
84
85 void Animation::SetDisconnectAction(Dali::Animation::EndAction action)
86 {
87   if ( mDisconnectAction != action )
88   {
89     mDisconnectAction = action;
90
91     for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
92     {
93       (*iter)->SetDisconnectAction( action );
94     }
95   }
96 }
97
98 void Animation::SetPlayRange( const Vector2& range )
99 {
100   mPlayRange = range;
101
102   //Make sure mElapsedSeconds is within the new range
103   mElapsedSeconds = Dali::Clamp(mElapsedSeconds, mPlayRange.x*mDurationSeconds , mPlayRange.y*mDurationSeconds );
104 }
105
106 void Animation::Play()
107 {
108   mState = Playing;
109
110   if ( mSpeedFactor < 0.0f && mElapsedSeconds <= mPlayRange.x*mDurationSeconds )
111   {
112     mElapsedSeconds = mPlayRange.y * mDurationSeconds;
113   }
114
115   SetAnimatorsActive( true );
116 }
117
118 void Animation::PlayFrom( float progress )
119 {
120   //If the animation is already playing this has no effect
121   if( mState != Playing )
122   {
123     mElapsedSeconds = progress * mDurationSeconds;
124     mState = Playing;
125
126     SetAnimatorsActive( true );
127   }
128 }
129
130 void Animation::Pause()
131 {
132   if (mState == Playing)
133   {
134     mState = Paused;
135   }
136 }
137
138 void Animation::Bake(BufferIndex bufferIndex, EndAction action)
139 {
140   if( action == Dali::Animation::BakeFinal )
141   {
142     if( mSpeedFactor > 0.0f )
143     {
144       mElapsedSeconds = mPlayRange.y*mDurationSeconds + Math::MACHINE_EPSILON_1; // Force animation to reach it's end
145     }
146     else
147     {
148       mElapsedSeconds = mPlayRange.x*mDurationSeconds - Math::MACHINE_EPSILON_1; //Force animation to reach it's beginning
149     }
150   }
151
152   UpdateAnimators( bufferIndex, true/*bake the final result*/, true /*animation finished*/ );
153 }
154
155 void Animation::SetAnimatorsActive( bool active )
156 {
157   for ( AnimatorIter iter = mAnimators.Begin(), endIter = mAnimators.End(); iter != endIter; ++iter )
158   {
159     (*iter)->SetActive( active );
160   }
161 }
162
163 bool Animation::Stop(BufferIndex bufferIndex)
164 {
165   bool animationFinished(false);
166
167   if (mState == Playing || mState == Paused)
168   {
169     animationFinished = true; // The actor-thread should be notified of this
170
171     if( mEndAction != Dali::Animation::Discard )
172     {
173       Bake( bufferIndex, mEndAction );
174
175       // Animators are automatically set to inactive in Bake
176     }
177     else
178     {
179       SetAnimatorsActive( false );
180     }
181
182     // The animation has now been played to completion
183     ++mPlayCount;
184   }
185
186   mElapsedSeconds = mPlayRange.x*mDurationSeconds;
187   mState = Stopped;
188
189   return animationFinished;
190 }
191
192 void Animation::OnDestroy(BufferIndex bufferIndex)
193 {
194   if (mState == Playing || mState == Paused)
195   {
196     if (mEndAction != Dali::Animation::Discard)
197     {
198       Bake( bufferIndex, mEndAction );
199
200       // Animators are automatically set to inactive in Bake
201     }
202     else
203     {
204       SetAnimatorsActive( false );
205     }
206   }
207
208   mState = Destroyed;
209 }
210
211 void Animation::AddAnimator( AnimatorBase* animator )
212 {
213   animator->ConnectToSceneGraph();
214   animator->SetDisconnectAction( mDisconnectAction );
215
216   mAnimators.PushBack( animator );
217 }
218
219 bool Animation::Update(BufferIndex bufferIndex, float elapsedSeconds)
220 {
221   if (mState == Stopped || mState == Destroyed)
222   {
223     // Short circuit when animation isn't running
224     return false;
225   }
226
227   // The animation must still be applied when Paused/Stopping
228   if (mState == Playing)
229   {
230     mElapsedSeconds += elapsedSeconds * mSpeedFactor;
231   }
232
233   Vector2 playRangeSeconds = mPlayRange * mDurationSeconds;
234   if (mLooping)
235   {
236     if (mElapsedSeconds > playRangeSeconds.y )
237     {
238       mElapsedSeconds = playRangeSeconds.x + fmod(mElapsedSeconds, playRangeSeconds.y);
239     }
240     else if( mElapsedSeconds < playRangeSeconds.x )
241     {
242       mElapsedSeconds = playRangeSeconds.y - fmod(mElapsedSeconds, playRangeSeconds.y);
243     }
244   }
245
246   const bool animationFinished(mState == Playing                                                &&
247                               (( mSpeedFactor > 0.0f && mElapsedSeconds > playRangeSeconds.y )  ||
248                                ( mSpeedFactor < 0.0f && mElapsedSeconds < playRangeSeconds.x ))
249                               );
250
251   UpdateAnimators(bufferIndex, animationFinished && (mEndAction != Dali::Animation::Discard), animationFinished);
252
253   if (animationFinished)
254   {
255     // The animation has now been played to completion
256     ++mPlayCount;
257
258     mElapsedSeconds = playRangeSeconds.x;
259     mState = Stopped;
260   }
261
262   return animationFinished;
263 }
264
265 void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
266 {
267   float elapsedSecondsClamped = Clamp( mElapsedSeconds, mPlayRange.x * mDurationSeconds,mPlayRange.y * mDurationSeconds );
268
269   //Loop through all animators
270   bool applied(true);
271   for ( AnimatorIter iter = mAnimators.Begin(); iter != mAnimators.End(); )
272   {
273     AnimatorBase *animator = *iter;
274
275     if( animator->Orphan() )
276     {
277       //Remove animators whose PropertyOwner has been destroyed
278       iter = mAnimators.Erase(iter);
279     }
280     else
281     {
282       if( animator->IsEnabled() )
283       {
284         const float initialDelay(animator->GetInitialDelay());
285         if (elapsedSecondsClamped >= initialDelay || mSpeedFactor < 0.0f )
286         {
287           // Calculate a progress specific to each individual animator
288           float progress(1.0f);
289           const float animatorDuration = animator->GetDuration();
290           if (animatorDuration > 0.0f) // animators can be "immediate"
291           {
292             progress = Clamp((elapsedSecondsClamped - initialDelay) / animatorDuration, 0.0f , 1.0f );
293           }
294           animator->Update(bufferIndex, progress, bake);
295         }
296         applied = true;
297       }
298       else
299       {
300         applied = false;
301       }
302
303       if ( animationFinished )
304       {
305         animator->SetActive( false );
306       }
307
308       if (applied)
309       {
310         INCREASE_COUNTER(PerformanceMonitor::ANIMATORS_APPLIED);
311       }
312
313       ++iter;
314     }
315   }
316
317 }
318
319 } // namespace SceneGraph
320
321 } // namespace Internal
322
323 } // namespace Dali