1 /* 2 Copyright (c) 2017-2018 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.animmodel; 29 30 import dlib.core.memory; 31 import dlib.math.vector; 32 import dlib.math.matrix; 33 import derelict.opengl; 34 import dagon.core.interfaces; 35 import dagon.core.ownership; 36 import dagon.graphics.texture; 37 import dagon.graphics.mesh; 38 39 interface AnimatedModel 40 { 41 void calcBindPose(AnimationFrameData* data); 42 void calcFrame(uint f1, uint f2, float t, AnimationFrameData* data); 43 void blendFrame(uint f1, uint f2, float t, AnimationFrameData* data, float blendFactor); 44 Vector3f[] getVertices(); 45 Vector3f[] getNormals(); 46 Vector2f[] getTexcoords(); 47 uint[3][] getTriangles(); 48 AnimationFacegroup[] getFacegroups(); 49 size_t numBones(); 50 bool getAnimation(string name, AnimationData* data); 51 uint numAnimationFrames(); 52 } 53 54 struct AnimationFacegroup 55 { 56 size_t firstTriangle; 57 size_t numTriangles; 58 Texture texture; 59 string textureName; 60 } 61 62 struct AnimationData 63 { 64 uint firstFrame; 65 uint numFrames; 66 float framerate; 67 } 68 69 struct AnimationFrameData 70 { 71 Vector3f[] vertices; 72 Vector3f[] normals; 73 Vector2f[] texcoords; 74 uint[3][] tris; 75 Matrix4x4f[] frame; 76 } 77 78 struct ActorState 79 { 80 uint currentFrame = 0; 81 uint nextFrame = 1; 82 float t = 0.0f; 83 } 84 85 class Actor: Owner, Drawable 86 { 87 AnimatedModel model; 88 AnimationFrameData frameData; 89 AnimationData animation; 90 AnimationData nextAnimation; 91 bool hasNextAnimation = false; 92 float blendFactor = 0.0f; 93 ActorState state; 94 ActorState nextState; 95 bool playing = false; 96 float defaultFramerate = 24.0f; 97 float speed = 1.0f; 98 bool swapZY = true; 99 100 GLuint vao = 0; 101 GLuint vbo = 0; 102 GLuint nbo = 0; 103 GLuint tbo = 0; 104 GLuint eao = 0; 105 106 this(AnimatedModel m, Owner owner) 107 { 108 super(owner); 109 model = m; 110 111 if (model.getVertices().length) 112 frameData.vertices = New!(Vector3f[])(model.getVertices().length); 113 if (model.getNormals().length) 114 frameData.normals = New!(Vector3f[])(model.getNormals().length); 115 if (model.getTexcoords().length) 116 frameData.texcoords = model.getTexcoords(); // no need to make a copy, texcoords don't change frame to frame 117 if (model.getTriangles().length) 118 frameData.tris = model.getTriangles(); // no need to make a copy, indices don't change frame to frame 119 if (model.numBones()) 120 frameData.frame = New!(Matrix4x4f[])(model.numBones()); 121 122 model.calcBindPose(&frameData); 123 124 switchToFullSequence(); 125 126 foreach(ref v; frameData.vertices) 127 v = Vector3f(0, 0, 0); 128 129 foreach(ref n; frameData.normals) 130 n = Vector3f(0, 0, 0); 131 132 glGenBuffers(1, &vbo); 133 glBindBuffer(GL_ARRAY_BUFFER, vbo); 134 glBufferData(GL_ARRAY_BUFFER, frameData.vertices.length * float.sizeof * 3, frameData.vertices.ptr, GL_DYNAMIC_DRAW); 135 136 glGenBuffers(1, &nbo); 137 glBindBuffer(GL_ARRAY_BUFFER, nbo); 138 glBufferData(GL_ARRAY_BUFFER, frameData.normals.length * float.sizeof * 3, frameData.normals.ptr, GL_DYNAMIC_DRAW); 139 140 glGenBuffers(1, &tbo); 141 glBindBuffer(GL_ARRAY_BUFFER, tbo); 142 glBufferData(GL_ARRAY_BUFFER, frameData.texcoords.length * float.sizeof * 2, frameData.texcoords.ptr, GL_DYNAMIC_DRAW); 143 144 glGenBuffers(1, &eao); 145 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 146 glBufferData(GL_ELEMENT_ARRAY_BUFFER, frameData.tris.length * uint.sizeof * 3, frameData.tris.ptr, GL_DYNAMIC_DRAW); 147 148 glGenVertexArrays(1, &vao); 149 glBindVertexArray(vao); 150 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao); 151 152 glEnableVertexAttribArray(VertexAttrib.Vertices); 153 glBindBuffer(GL_ARRAY_BUFFER, vbo); 154 glVertexAttribPointer(VertexAttrib.Vertices, 3, GL_FLOAT, GL_FALSE, 0, null); 155 156 glEnableVertexAttribArray(VertexAttrib.Normals); 157 glBindBuffer(GL_ARRAY_BUFFER, nbo); 158 glVertexAttribPointer(VertexAttrib.Normals, 3, GL_FLOAT, GL_FALSE, 0, null); 159 160 glEnableVertexAttribArray(VertexAttrib.Texcoords); 161 glBindBuffer(GL_ARRAY_BUFFER, tbo); 162 glVertexAttribPointer(VertexAttrib.Texcoords, 2, GL_FLOAT, GL_FALSE, 0, null); 163 164 glBindBuffer(GL_ARRAY_BUFFER, 0); 165 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 166 167 glBindVertexArray(0); 168 } 169 170 ~this() 171 { 172 if (frameData.vertices.length) Delete(frameData.vertices); 173 if (frameData.normals.length) Delete(frameData.normals); 174 if (frameData.frame.length) Delete(frameData.frame); 175 } 176 177 void switchToBindPose() 178 { 179 model.calcBindPose(&frameData); 180 playing = false; 181 } 182 183 void switchToAnimation(string name) 184 { 185 model.getAnimation(name, &animation); 186 state.currentFrame = animation.firstFrame; 187 state.nextFrame = state.currentFrame + 1; 188 state.t = 0.0f; 189 } 190 191 void switchToAnimationSmooth(string name, float smooth) 192 { 193 model.getAnimation(name, &nextAnimation); 194 hasNextAnimation = true; 195 nextState.currentFrame = nextAnimation.firstFrame; 196 nextState.nextFrame = nextState.currentFrame + 1; 197 nextState.t = 0.0f; 198 } 199 200 void switchToSequence(uint startFrame, uint endFrame) 201 { 202 //model.getAnimation(name, &nextAnimation); 203 animation.firstFrame = startFrame; 204 animation.numFrames = endFrame - startFrame; 205 state.currentFrame = animation.firstFrame; 206 state.nextFrame = state.currentFrame + 1; 207 state.t = 0.0f; 208 } 209 210 void switchToSequenceSmooth(uint startFrame, uint endFrame, float smooth) 211 { 212 //model.getAnimation(name, &nextAnimation); 213 nextAnimation.firstFrame = startFrame; 214 nextAnimation.numFrames = endFrame - startFrame; 215 hasNextAnimation = true; 216 nextState.currentFrame = nextAnimation.firstFrame; 217 nextState.nextFrame = nextState.currentFrame + 1; 218 nextState.t = 0.0f; 219 } 220 221 void switchToFullSequence() 222 { 223 switchToAnimation(""); 224 animation.framerate = defaultFramerate; 225 state.currentFrame = animation.firstFrame; 226 state.nextFrame = state.currentFrame + 1; 227 state.t = 0.0f; 228 } 229 230 void play() 231 { 232 playing = true; 233 } 234 235 void pause() 236 { 237 playing = false; 238 } 239 240 void update(double dt) 241 { 242 if (!playing) 243 return; 244 245 model.calcFrame(state.currentFrame, state.nextFrame, state.t, &frameData); 246 247 state.t += defaultFramerate * dt * speed; //animation.framerate 248 249 if (state.t >= 1.0f) 250 { 251 state.t = 0.0f; 252 state.currentFrame++; 253 state.nextFrame++; 254 255 if (state.currentFrame == animation.firstFrame + animation.numFrames - 1) 256 { 257 state.nextFrame = animation.firstFrame; 258 } 259 else if (state.currentFrame == animation.firstFrame + animation.numFrames) 260 { 261 state.currentFrame = animation.firstFrame; 262 state.nextFrame = state.currentFrame + 1; 263 } 264 } 265 266 if (hasNextAnimation) 267 { 268 model.blendFrame(nextState.currentFrame, nextState.nextFrame, nextState.t, &frameData, blendFactor); 269 nextState.t += defaultFramerate * dt * speed; //nextAnimation.framerate 270 blendFactor += dt; // TODO: time multiplier 271 272 if (nextState.t >= 1.0f) 273 { 274 nextState.t = 0.0f; 275 nextState.currentFrame++; 276 nextState.nextFrame++; 277 278 if (nextState.currentFrame == nextAnimation.numFrames - 1) 279 { 280 nextState.nextFrame = nextAnimation.firstFrame; 281 } 282 else if (nextState.currentFrame == nextAnimation.numFrames) 283 { 284 nextState.currentFrame = nextAnimation.firstFrame; 285 nextState.nextFrame = nextState.currentFrame + 1; 286 } 287 } 288 289 if (blendFactor >= 1.0f) 290 { 291 blendFactor = 0.0f; 292 hasNextAnimation = false; 293 animation = nextAnimation; 294 state = nextState; 295 } 296 } 297 298 glBindBuffer(GL_ARRAY_BUFFER, vbo); 299 glBufferSubData(GL_ARRAY_BUFFER, 0, frameData.vertices.length * float.sizeof * 3, frameData.vertices.ptr); 300 glBindBuffer(GL_ARRAY_BUFFER, nbo); 301 glBufferSubData(GL_ARRAY_BUFFER, 0, frameData.normals.length * float.sizeof * 3, frameData.normals.ptr); 302 glBindBuffer(GL_ARRAY_BUFFER, 0); 303 } 304 305 void render(RenderingContext* rc) 306 { 307 //glDisable(GL_CULL_FACE); 308 glBindVertexArray(vao); 309 foreach(ref fg; model.getFacegroups) 310 { 311 glActiveTexture(GL_TEXTURE0); 312 if (fg.texture) 313 fg.texture.bind(); 314 glDrawElements(GL_TRIANGLES, cast(uint)(3 * fg.numTriangles), GL_UNSIGNED_INT, &frameData.tris[fg.firstTriangle]); 315 if (fg.texture) 316 fg.texture.unbind(); 317 } 318 glBindVertexArray(0); 319 } 320 }