1 /*
2 Copyright (c) 2019-2020 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.graphics.tween;
29 
30 import dlib.math.vector;
31 import dlib.math.quaternion;
32 import dlib.math.interpolation;
33 import dlib.math.utils;
34 import dlib.image.color;
35 import dagon.graphics.entity;
36 
37 enum TweenDataType
38 {
39     Float,
40     Vector,
41     Color
42 }
43 
44 enum TweenType
45 {
46     Unknown = 0,
47     Position,
48     Rotation,
49     Scaling,
50     Color,
51     Alpha,
52 }
53 
54 enum Easing
55 {
56     Linear,
57     QuadIn,
58     QuadOut,
59     QuadInOut,
60     BackIn,
61     BackOut,
62     BackInOut,
63     BounceOut
64 }
65 
66 struct Tween
67 {
68     TweenDataType dataType;
69     TweenType type = TweenType.Unknown;
70     Easing easing;
71     bool active = false;
72     Entity entity;
73     double duration;
74     double time = 0.0f;
75     uint repeat = 1;
76     uint repeatCounter;
77     bool isPlaying = true;
78 
79     union
80     {
81         Color4f fromColor;
82         Vector3f fromVector;
83         float fromFloat;
84     }
85 
86     union
87     {
88         Color4f toColor;
89         Vector3f toVector;
90         float toFloat;
91     }
92 
93     this(Entity entity, TweenType type, Vector3f start, Vector3f end, double duration, Easing easing = Easing.Linear)
94     {
95         this.dataType = TweenDataType.Vector;
96         this.type = type;
97         this.easing = easing;
98         this.active = true;
99         this.entity = entity;
100         this.duration = duration;
101         this.time = 0.0f;
102         this.fromVector = start;
103         this.toVector = end;
104         this.repeatCounter = 0;
105         this.isPlaying = true;
106     }
107 
108     this(Entity entity, TweenType type, Color4f start, Color4f end, double duration, Easing easing = Easing.Linear)
109     {
110         this.dataType = TweenDataType.Color;
111         this.type = type;
112         this.easing = easing;
113         this.active = true;
114         this.entity = entity;
115         this.duration = duration;
116         this.time = 0.0f;
117         this.fromColor = start;
118         this.toColor = end;
119         this.repeatCounter = 0;
120         this.isPlaying = true;
121     }
122 
123     this(Entity entity, TweenType type, float start, float end, double duration, Easing easing = Easing.Linear)
124     {
125         this.dataType = TweenDataType.Float;
126         this.type = type;
127         this.easing = easing;
128         this.active = true;
129         this.entity = entity;
130         this.duration = duration;
131         this.time = 0.0f;
132         this.fromFloat = start;
133         this.toFloat = end;
134         this.repeatCounter = 0;
135         this.isPlaying = true;
136     }
137 
138     void pause()
139     {
140         isPlaying = false;
141     }
142 
143     void play()
144     {
145         isPlaying = true;
146     }
147 
148     void kill()
149     {
150         active = false;
151         time = 0.0;
152         repeatCounter = 0;
153         isPlaying = false;
154     }
155 
156     void restart()
157     {
158         time = 0.0;
159     }
160 
161     void update(double dt)
162     {
163         if (active && entity && isPlaying)
164         {
165             time += dt;
166             float t;
167 
168             if (time > duration)
169             {
170                 time = 0.0;
171                 t = 0.0f;
172                 if (repeat >= 0)
173                 {
174                     repeatCounter++;
175                     if (repeatCounter >= repeat)
176                     {
177                         repeatCounter = 0;
178                         active = false;
179                         t = 1.0f;
180                     }
181                 }
182             }
183             else
184             {
185                 t = time / duration;
186             }
187 
188             applyTween(t);
189         }
190     }
191 
192     void applyTween(float t)
193     {
194         if (type == TweenType.Position)
195             entity.position = lerp(fromVector, toVector, ease(t));
196         else if (type == TweenType.Rotation)
197             entity.angles = lerp(fromVector, toVector, ease(t));
198         else if (type == TweenType.Scaling)
199             entity.scaling = lerp(fromVector, toVector, ease(t));
200     }
201 
202     float ease(float t)
203     {
204         if (easing == Easing.Linear) return t;
205         else if (easing == Easing.QuadIn) return easeInQuad(t);
206         else if (easing == Easing.QuadOut) return easeOutQuad(t);
207         else if (easing == Easing.QuadInOut) return easeInOutQuad(t);
208         else if (easing == Easing.BackIn) return easeInBack(t);
209         else if (easing == Easing.BackOut) return easeOutBack(t);
210         else if (easing == Easing.BackInOut) return easeInOutBack(t);
211         else if (easing == Easing.BounceOut) return easeOutBounce(t);
212         else return t;
213     }
214 }