1 module inochi2d.core.animation.animation; 2 import inochi2d.core; 3 import inochi2d.fmt.serialize; 4 import inmath; 5 import inmath.interpolate; 6 7 /** 8 An animation 9 */ 10 struct Animation { 11 public: 12 13 /** 14 The timestep of each frame 15 */ 16 float timestep = 0.0166; 17 18 /** 19 Whether the animation is additive. 20 21 Additive animations will not replace main animations, but add their data 22 on top of the running main animation 23 */ 24 bool additive; 25 26 /** 27 The weight of the animation 28 29 This is only relevant for additive animations 30 */ 31 float animationWeight; 32 33 /** 34 All of the animation lanes in this animation 35 */ 36 AnimationLane[] lanes; 37 38 /** 39 Length in frames 40 */ 41 int length; 42 43 /** 44 Time where the lead-in ends 45 */ 46 int leadIn = -1; 47 48 /** 49 Time where the lead-out starts 50 */ 51 int leadOut = -1; 52 53 54 void reconstruct(Puppet puppet) { 55 foreach(ref lane; lanes.dup) lane.reconstruct(puppet); 56 } 57 58 /** 59 Finalizes the animation 60 */ 61 void finalize(Puppet puppet) { 62 foreach(ref lane; lanes) lane.finalize(puppet); 63 } 64 65 /** 66 Serialization function 67 */ 68 void serialize(ref InochiSerializer serializer) { 69 auto obj = serializer.objectBegin(); 70 serializer.putKey("timestep"); 71 serializer.serializeValue(timestep); 72 serializer.putKey("additive"); 73 serializer.serializeValue(additive); 74 serializer.putKey("length"); 75 serializer.serializeValue(length); 76 serializer.putKey("timestep"); 77 serializer.serializeValue(timestep); 78 serializer.putKey("leadIn"); 79 serializer.serializeValue(leadIn); 80 serializer.putKey("leadOut"); 81 serializer.serializeValue(leadOut); 82 serializer.putKey("animationWeight"); 83 serializer.serializeValue(animationWeight); 84 85 serializer.putKey("lanes"); 86 auto state = serializer.arrayBegin; 87 foreach(lane; lanes) { 88 if (lane.paramRef.targetParam) { 89 serializer.elemBegin; 90 serializer.serializeValue(lane); 91 } 92 } 93 serializer.arrayEnd(state); 94 serializer.objectEnd(obj); 95 } 96 97 /** 98 Deserialization function 99 */ 100 SerdeException deserializeFromFghj(Fghj data) { 101 data["timestep"].deserializeValue(this.timestep); 102 data["additive"].deserializeValue(this.additive); 103 if (!data["animationWeight"].isEmpty) data["animationWeight"].deserializeValue(this.animationWeight); 104 data["length"].deserializeValue(this.length); 105 data["leadIn"].deserializeValue(this.leadIn); 106 data["leadOut"].deserializeValue(this.leadOut); 107 data["lanes"].deserializeValue(this.lanes); 108 return null; 109 } 110 } 111 112 struct AnimationParameterRef { 113 114 /** 115 A parameter to target 116 */ 117 Parameter targetParam; 118 119 /** 120 Target axis of the parameter 121 */ 122 int targetAxis; 123 124 } 125 126 /** 127 Animation Lane 128 */ 129 struct AnimationLane { 130 private: 131 uint refuuid; 132 133 public: 134 135 /** 136 Reference to parameter if any 137 */ 138 AnimationParameterRef* paramRef; 139 140 /** 141 Serialization function 142 */ 143 void serialize(ref InochiSerializer serializer) { 144 auto obj = serializer.objectBegin(); 145 serializer.putKey("interpolation"); 146 serializer.serializeValue(interpolation); 147 148 if (paramRef) { 149 serializer.putKey("uuid"); 150 serializer.putValue(paramRef.targetParam.uuid); 151 serializer.putKey("target"); 152 serializer.putValue(paramRef.targetAxis); 153 } 154 155 serializer.putKey("keyframes"); 156 serializer.serializeValue(frames); 157 158 serializer.putKey("merge_mode"); 159 serializer.serializeValue(mergeMode); 160 serializer.objectEnd(obj); 161 } 162 163 /** 164 Deserialization function 165 */ 166 SerdeException deserializeFromFghj(Fghj data) { 167 data["interpolation"].deserializeValue(this.interpolation); 168 data["uuid"].deserializeValue(refuuid); 169 170 this.paramRef = new AnimationParameterRef(null, 0); 171 data["target"].deserializeValue(this.paramRef.targetAxis); 172 173 data["keyframes"].deserializeValue(this.frames); 174 if (!data["merge_mode"].isEmpty) data["merge_mode"].deserializeValue(this.mergeMode); 175 return null; 176 } 177 178 /** 179 List of frames in the lane 180 */ 181 Keyframe[] frames; 182 183 /** 184 The interpolation between each frame in the lane 185 */ 186 InterpolateMode interpolation; 187 188 /** 189 Merging mode of the lane 190 */ 191 ParamMergeMode mergeMode = ParamMergeMode.Forced; 192 193 /** 194 Gets the interpolated state of a frame of animation 195 for this lane 196 */ 197 float get(float frame, bool snapSubframes=false) { 198 if (frames.length > 0) { 199 200 // If subframe snapping is turned on then we'll only run at the framerate 201 // of the animation, without any smooth interpolation on faster app rates. 202 if (snapSubframes) frame = floor(frame); 203 204 // Fallback if there's only 1 frame 205 if (frames.length == 1) return frames[0].value; 206 207 foreach(i; 0..frames.length) { 208 if (frames[i].frame < frame) continue; 209 210 // Fallback to not try to index frame -1 211 if (i == 0) return frames[0].value; 212 213 // Interpolation "time" 0->1 214 // Note we use floats here in case you're running the 215 // update step faster than the timestep of the animation 216 // This way it won't look choppy 217 float tonext = cast(float)frames[i].frame-frame; 218 float ilen = (cast(float)frames[i].frame-cast(float)frames[i-1].frame); 219 float t = 1-(tonext/ilen); 220 221 // Interpolation tension 0->1 222 float tension = frames[i].tension; 223 224 switch(interpolation) { 225 226 // Nearest - Snap to the closest frame 227 case InterpolateMode.Nearest: 228 return t > 0.5 ? frames[i].value : frames[i-1].value; 229 230 // Stepped - Snap to the current active keyframe 231 case InterpolateMode.Stepped: 232 return frames[i-1].value; 233 234 // Linear - Linearly interpolate between frame A and B 235 case InterpolateMode.Linear: 236 return lerp(frames[i-1].value, frames[i].value, t); 237 238 // Cubic - Smoothly in a curve between frame A and B 239 case InterpolateMode.Cubic: 240 float prev = frames[max(cast(ptrdiff_t)i-2, 0)].value; 241 float curr = frames[max(cast(ptrdiff_t)i-1, 0)].value; 242 float next1 = frames[min(cast(ptrdiff_t)i, frames.length-1)].value; 243 float next2 = frames[min(cast(ptrdiff_t)i+1, frames.length-1)].value; 244 245 // TODO: Switch formulae, catmullrom interpolation 246 return cubic(prev, curr, next1, next2, t); 247 248 // Bezier - Allows the user to specify beziér curves. 249 case InterpolateMode.Bezier: 250 // TODO: Switch formulae, Beziér curve 251 return lerp(frames[i-1].value, frames[i].value, clamp(hermite(0, 2*tension, 1, 2*tension, t), 0, 1)); 252 253 default: assert(0); 254 } 255 } 256 return frames[$-1].value; 257 } 258 259 // Fallback, no values. 260 // Ideally we won't even call this function 261 // if there's nothing to do. 262 return 0; 263 } 264 265 void reconstruct(Puppet puppet) { } 266 267 void finalize(Puppet puppet) { 268 if (paramRef) paramRef.targetParam = puppet.findParameter(refuuid); 269 } 270 271 /** 272 Updates the order of the keyframes 273 */ 274 void updateFrames() { 275 import std.algorithm.sorting : sort; 276 import std.algorithm.mutation : SwapStrategy; 277 sort!((a, b) => a.frame < b.frame, SwapStrategy.stable)(frames); 278 } 279 } 280 281 /** 282 A keyframe 283 */ 284 struct Keyframe { 285 /** 286 The frame at which this frame occurs 287 */ 288 int frame; 289 290 /** 291 The value of the parameter at the given frame 292 */ 293 float value; 294 295 /** 296 Interpolation tension for cubic/inout 297 */ 298 float tension = 0.5; 299 }