1 /*
2 Copyright (c) 2019 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.logics.tween;
29 
30 import dlib.math.vector;
31 import dlib.math.quaternion;
32 import dlib.math.easing;
33 import dlib.math.interpolation;
34 import dlib.math.utils;
35 import dlib.image.color;
36 import dagon.logics.entity;
37 
38 enum TweenDataType
39 {
40     Float,
41     Vector,
42     Color
43 }
44 
45 enum TweenType
46 {
47     Unknown = 0,
48     Position,
49     Rotation,
50     Scaling,
51     Color,
52     Alpha,
53 }
54 
55 enum Easing
56 {
57     Linear,
58     QuadIn,
59     QuadOut,
60     QuadInOut,
61     BackIn,
62     BackOut,
63     BackInOut,
64     BounceOut
65 }
66 
67 struct Tween
68 {
69     TweenDataType dataType;
70     TweenType type = TweenType.Unknown;
71     Easing easing;
72     bool active = false;
73     Entity entity;
74     double duration;
75     double time = 0.0f;
76     uint repeat = 1;
77     uint repeatCounter;
78     bool isPlaying = true;
79 
80     union
81     {
82         Color4f fromColor;
83         Vector3f fromVector;
84         float fromFloat;
85     }
86 
87     union
88     {
89         Color4f toColor;
90         Vector3f toVector;
91         float toFloat;
92     }
93 
94     this(Entity entity, TweenType type, Vector3f start, Vector3f end, double duration, Easing easing = Easing.Linear)
95     {
96         this.dataType = TweenDataType.Vector;
97         this.type = type;
98         this.easing = easing;
99         this.active = true;
100         this.entity = entity;
101         this.duration = duration;
102         this.time = 0.0f;
103         this.fromVector = start;
104         this.toVector = end;
105         this.repeatCounter = 0;
106         this.isPlaying = true;
107     }
108 
109     this(Entity entity, TweenType type, Color4f start, Color4f end, double duration, Easing easing = Easing.Linear)
110     {
111         this.dataType = TweenDataType.Color;
112         this.type = type;
113         this.easing = easing;
114         this.active = true;
115         this.entity = entity;
116         this.duration = duration;
117         this.time = 0.0f;
118         this.fromColor = start;
119         this.toColor = end;
120         this.repeatCounter = 0;
121         this.isPlaying = true;
122     }
123 
124     this(Entity entity, TweenType type, float start, float end, double duration, Easing easing = Easing.Linear)
125     {
126         this.dataType = TweenDataType.Float;
127         this.type = type;
128         this.easing = easing;
129         this.active = true;
130         this.entity = entity;
131         this.duration = duration;
132         this.time = 0.0f;
133         this.fromFloat = start;
134         this.toFloat = end;
135         this.repeatCounter = 0;
136         this.isPlaying = true;
137     }
138 
139     void pause()
140     {
141         isPlaying = false;
142     }
143 
144     void play()
145     {
146         isPlaying = true;
147     }
148 
149     void kill()
150     {
151         active = false;
152         time = 0.0;
153         repeatCounter = 0;
154         isPlaying = false;
155     }
156 
157     void restart()
158     {
159         time = 0.0;
160     }
161 
162     void update(double dt)
163     {
164         if (active && entity && isPlaying)
165         {
166             time += dt;
167             float t;
168 
169             if (time > duration)
170             {
171                 time = 0.0;
172                 t = 0.0f;
173                 if (repeat >= 0)
174                 {
175                     repeatCounter++;
176                     if (repeatCounter >= repeat)
177                     {
178                         repeatCounter = 0;
179                         active = false;
180                         t = 1.0f;
181                     }
182                 }
183             }
184             else
185             {
186                 t = time / duration;
187             }
188 
189             applyTween(t);
190         }
191     }
192 
193     void applyTween(float t)
194     {
195         if (type == TweenType.Position)
196             entity.position = lerp(fromVector, toVector, ease(t));
197         else if (type == TweenType.Rotation)
198             entity.angles = lerp(fromVector, toVector, ease(t));
199         else if (type == TweenType.Scaling)
200             entity.scaling = lerp(fromVector, toVector, ease(t));
201     }
202 
203     float ease(float t)
204     {
205         if (easing == Easing.Linear) return t;
206         else if (easing == Easing.QuadIn) return easeInQuad(t);
207         else if (easing == Easing.QuadOut) return easeOutQuad(t);
208         else if (easing == Easing.QuadInOut) return easeInOutQuad(t);
209         else if (easing == Easing.BackIn) return easeInBack(t);
210         else if (easing == Easing.BackOut) return easeOutBack(t);
211         else if (easing == Easing.BackInOut) return easeInOutBack(t);
212         else if (easing == Easing.BounceOut) return easeOutBounce(t);
213         else return t;
214     }
215 }