LearnOpenGL

Translation in progress of learnopengl.com.
git clone https://git.mtkn.jp/LearnOpenGL
Log | Files | Refs

Skeletal-Animation.html (51243B)


      1 <!DOCTYPE html>
      2 <html lang="ja"> 
      3 <head>
      4     <meta charset="utf-8"/>
      5     <title>LearnOpenGL</title>
      6     <link rel="shortcut icon" type="image/ico" href="/favicon.ico"  />
      7 	<link rel="stylesheet" href="../static/style.css" />
      8 	<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script>
      9 	<script src="/static/functions.js"></script>
     10 </head>
     11 <body>
     12 	<nav>
     13 <ol>
     14 	<li id="Introduction">
     15 		<a href="https://learnopengl.com/Introduction">はじめに</a>
     16 	</li>
     17 	<li id="Getting-started">
     18 		<span class="closed">入門</span>
     19 		<ol>
     20 			<li id="Getting-started/OpenGL">
     21 				<a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a>
     22 			</li>
     23 			<li id="Getting-started/Creating-a-window">
     24 				<a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a>
     25 			</li>
     26 			<li id="Getting-started/Hello-Window">
     27 				<a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a>
     28 			</li>
     29 			<li id="Getting-started/Hello-Triangle">
     30 				<a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a>
     31 			</li>
     32 			<li id="Getting-started/Shaders">
     33 				<a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a>
     34 			</li>
     35 			<li id="Getting-started/Textures">
     36 				<a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a>
     37 			</li>
     38 			<li id="Getting-started/Transformations">
     39 				<a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a>
     40 			</li>
     41 			<li id="Getting-started/Coordinate-Systems">
     42 				<a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a>
     43 			</li>
     44 			<li id="Getting-started/Camera">
     45 				<a href="https://learnopengl.com/Getting-started/Camera">カメラ</a>
     46 			</li>
     47 			<li id="Getting-started/Review">
     48 				<a href="https://learnopengl.com/Getting-started/Review">まとめ</a>
     49 			</li>
     50 		</ol>
     51 	</li>
     52 	<li id="Lighting">
     53 		<span class="closed">Lighting </span>
     54 		<ol>
     55 			<li id="Lighting/Colors">
     56 				<a href="https://learnopengl.com/Lighting/Colors">Colors </a>
     57 			</li>
     58 			<li id="Lighting/Basic-Lighting">
     59 				<a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a>
     60 			</li>
     61 			<li id="Lighting/Materials">
     62 				<a href="https://learnopengl.com/Lighting/Materials">Materials </a>
     63 			</li>
     64 			<li id="Lighting/Lighting-maps">
     65 				<a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a>
     66 			</li>
     67 			<li id="Lighting/Light-casters">
     68 				<a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a>
     69 			</li>
     70 			<li id="Lighting/Multiple-lights">
     71 				<a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a>
     72 			</li>
     73 			<li id="Lighting/Review">
     74 				<a href="https://learnopengl.com/Lighting/Review">Review </a>
     75 			</li>
     76 		</ol>
     77 	</li>
     78 	<li id="Model-Loading">
     79 		<span class="closed">Model Loading </span>
     80 		<ol>
     81 			<li id="Model-Loading/Assimp">
     82 				<a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a>
     83 			</li>
     84 			<li id="Model-Loading/Mesh">
     85 				<a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a>
     86 			</li>
     87 			<li id="Model-Loading/Model">
     88 				<a href="https://learnopengl.com/Model-Loading/Model">Model </a>
     89 			</li>
     90 		</ol>
     91 	</li>
     92 	<li id="Advanced-OpenGL">
     93 		<span class="closed">Advanced OpenGL </span>
     94 		<ol>
     95 			<li id="Advanced-OpenGL/Depth-testing">
     96 				<a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a>
     97 			</li>
     98 			<li id="Advanced-OpenGL/Stencil-testing">
     99 				<a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a>
    100 			</li>
    101 			<li id="Advanced-OpenGL/Blending">
    102 				<a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a>
    103 			</li>
    104 			<li id="Advanced-OpenGL/Face-culling">
    105 				<a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a>
    106 			</li>
    107 			<li id="Advanced-OpenGL/Framebuffers">
    108 				<a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a>
    109 			</li>
    110 			<li id="Advanced-OpenGL/Cubemaps">
    111 				<a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a>
    112 			</li>
    113 			<li id="Advanced-OpenGL/Advanced-Data">
    114 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a>
    115 			</li>
    116 			<li id="Advanced-OpenGL/Advanced-GLSL">
    117 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a>
    118 			</li>
    119 			<li id="Advanced-OpenGL/Geometry-Shader">
    120 				<a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a>
    121 			</li>
    122 			<li id="Advanced-OpenGL/Instancing">
    123 				<a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a>
    124 			</li>
    125 			<li id="Advanced-OpenGL/Anti-Aliasing">
    126 				<a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a>
    127 			</li>
    128 		</ol>
    129 	</li>
    130 	<li id="Advanced-Lighting">
    131 		<span class="closed">Advanced Lighting </span>
    132 		<ol>
    133 			<li id="Advanced-Lighting/Advanced-Lighting">
    134 				<a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a>
    135 			</li>
    136 			<li id="Advanced-Lighting/Gamma-Correction">
    137 				<a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a>
    138 			</li>
    139 			<li id="Advanced-Lighting/Shadows">
    140 				<span class="closed">Shadows </span>
    141 				<ol>
    142 					<li id="Advanced-Lighting/Shadows/Shadow-Mapping">
    143 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a>
    144 					</li>
    145 					<li id="Advanced-Lighting/Shadows/Point-Shadows">
    146 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a>
    147 					</li>
    148 				</ol>
    149 			</li>
    150 			<li id="Advanced-Lighting/Normal-Mapping">
    151 				<a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a>
    152 			</li>
    153 			<li id="Advanced-Lighting/Parallax-Mapping">
    154 				<a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a>
    155 			</li>
    156 			<li id="Advanced-Lighting/HDR">
    157 				<a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a>
    158 			</li>
    159 			<li id="Advanced-Lighting/Bloom">
    160 				<a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a>
    161 			</li>
    162 			<li id="Advanced-Lighting/Deferred-Shading">
    163 				<a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a>
    164 			</li>
    165 			<li id="Advanced-Lighting/SSAO">
    166 				<a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a>
    167 			</li>
    168 		</ol>
    169 	</li>
    170 	<li id="PBR">
    171 		<span class="closed">PBR </span>
    172 		<ol>
    173 			<li id="PBR/Theory">
    174 				<a href="https://learnopengl.com/PBR/Theory">Theory </a>
    175 			</li>
    176 			<li id="PBR/Lighting">
    177 				<a href="https://learnopengl.com/PBR/Lighting">Lighting </a>
    178 			</li>
    179 			<li id="PBR/IBL">
    180 				<span class="closed">IBL </span>
    181 				<ol>
    182 					<li id="PBR/IBL/Diffuse-irradiance">
    183 						<a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a>
    184 					</li>
    185 					<li id="PBR/IBL/Specular-IBL">
    186 						<a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a>
    187 					</li>
    188 				</ol>
    189 			</li>
    190 		</ol>
    191 	</li>
    192 	<li id="In-Practice">
    193 		<span class="closed">In Practice </span>
    194 		<ol>
    195 			<li id="In-Practice/Debugging">
    196 				<a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a>
    197 			</li>
    198 			<li id="In-Practice/Text-Rendering">
    199 				<a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a>
    200 			</li>
    201 			<li id="In-Practice/2D-Game">
    202 				<span class="closed">2D Game </span>
    203 				<ol>
    204 					<li id="In-Practice/2D-Game/Breakout">
    205 						<a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a>
    206 					</li>
    207 					<li id="In-Practice/2D-Game/Setting-up">
    208 						<a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a>
    209 					</li>
    210 					<li id="In-Practice/2D-Game/Rendering-Sprites">
    211 						<a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a>
    212 					</li>
    213 					<li id="In-Practice/2D-Game/Levels">
    214 						<a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a>
    215 					</li>
    216 					<li id="In-Practice/2D-Game/Collisions">
    217 						<span class="closed">Collisions </span>
    218 						<ol>
    219 							<li id="In-Practice/2D-Game/Collisions/Ball">
    220 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a>
    221 							</li>
    222 							<li id="In-Practice/2D-Game/Collisions/Collision-detection">
    223 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a>
    224 							</li>
    225 							<li id="In-Practice/2D-Game/Collisions/Collision-resolution">
    226 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a>
    227 							</li>
    228 						</ol>
    229 					</li>
    230 					<li id="In-Practice/2D-Game/Particles">
    231 						<a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a>
    232 					</li>
    233 					<li id="In-Practice/2D-Game/Postprocessing">
    234 						<a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a>
    235 					</li>
    236 					<li id="In-Practice/2D-Game/Powerups">
    237 						<a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a>
    238 					</li>
    239 					<li id="In-Practice/2D-Game/Audio">
    240 						<a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a>
    241 					</li>
    242 					<li id="In-Practice/2D-Game/Render-text">
    243 						<a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a>
    244 					</li>
    245 					<li id="In-Practice/2D-Game/Final-thoughts">
    246 						<a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a>
    247 					</li>
    248 				</ol>
    249 			</li>
    250 		</ol>
    251 	</li>
    252 	<li id="Guest-Articles">
    253 		<span class="closed">Guest Articles </span>
    254 		<ol>
    255 			<li id="Guest-Articles/How-to-publish">
    256 				<a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a>
    257 			</li>
    258 			<li id="Guest-Articles/2020">
    259 				<span class="closed">2020 </span>
    260 				<ol>
    261 					<li id="Guest-Articles/2020/OIT">
    262 						<span class="closed">OIT </span>
    263 						<ol>
    264 							<li id="Guest-Articles/2020/OIT/Introduction">
    265 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a>
    266 							</li>
    267 							<li id="Guest-Articles/2020/OIT/Weighted-Blended">
    268 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a>
    269 							</li>
    270 						</ol>
    271 					</li>
    272 					<li id="Guest-Articles/2020/Skeletal-Animation">
    273 						<a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a>
    274 					</li>
    275 				</ol>
    276 			</li>
    277 			<li id="Guest-Articles/2021">
    278 				<span class="closed">2021 </span>
    279 				<ol>
    280 					<li id="Guest-Articles/2021/CSM">
    281 						<a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a>
    282 					</li>
    283 					<li id="Guest-Articles/2021/Scene">
    284 						<span class="closed">Scene </span>
    285 						<ol>
    286 							<li id="Guest-Articles/2021/Scene/Scene-Graph">
    287 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a>
    288 							</li>
    289 							<li id="Guest-Articles/2021/Scene/Frustum-Culling">
    290 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a>
    291 							</li>
    292 						</ol>
    293 					</li>
    294 					<li id="Guest-Articles/2021/Tessellation">
    295 						<span class="closed">Tessellation </span>
    296 						<ol>
    297 							<li id="Guest-Articles/2021/Tessellation/Height-map">
    298 								<a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a>
    299 							</li>
    300 						</ol>
    301 					</li>
    302 				</ol>
    303 			</li>
    304 		</ol>
    305 	</li>
    306 	<li id="Code-repository">
    307 		<a href="https://learnopengl.com/Code-repository">Code repository </a>
    308 	</li>
    309 	<li id="Translations">
    310 		<a href="https://learnopengl.com/Translations">Translations </a>
    311 	</li>
    312 	<li id="About">
    313 		<a href="https://learnopengl.com/About">About </a>
    314 	</li>
    315 </ol>
    316 	</nav>
    317 	<main>
    318     <div id="content">
    319     <h1 id="content-title">Skeletal Animation</h1>
    320 <h1 id="content-url" style='display:none;'>Guest-Articles/2020/Skeletal-Animation</h1>
    321 <p>3D Animations can bring our games to life. Objects in 3D world like humans and &amp; 
    322 	animals feel more organic when they move their limbs to do certain things like walking, running &amp; attacking. 
    323 	This tutorial is about Skeletal animation which you all have been waiting for. We will first understand the concept thoroughly and then understand the data 
    324 	we need to animate a 3D model using Assimp. I'd recommend you to finish the <a href="https://learnopengl.com/Model-Loading/Assimp">Model Loading</a> chapter of this saga as this tutorial code continues from there. You can still understand the concept and implement it in your way. So let's get started.</p>
    325 	
    326 	
    327 	<h3>Interpolation</h3>
    328 <p>To understand how animation works at basic level we need to understand the concept of Interpolation. 
    329 	Interpolation can be defined as something happening over time. Like an enemy moving from point A to point B in time T i.e Translation happening over time . 
    330 	A gun turret smoothly rotates to face the target i.e Rotation happening over time and a tree is scaling up from size A to size B in time T i.e Scaling happening over time.</p>
    331 <p>A simple interpolation equation used for Translation and Scale looks like this..</p>
    332 <p style="text-align: center;"><strong>a = a * (1 - t) + b * t </strong></p>
    333 <p>It is known as as Linear Interpolation equation or Lerp. For Rotation we cannot use Vector. 
    334 		The reason for that is if we went ahead and tried to use the linear interpolation equation on a vector of X(Pitch),Y(Yaw) & Z(Roll), the interpolation won't be linear. 
    335 		You will encounter 
    336 		weird issues like The 
    337 		Gimbal Lock(See references section below to learn about it). To avoid this issue we use Quaternion for rotations. 
    338 		Quaternion provides something called The Spherical Interpolation or 
    339 		Slerp equation which gives the same result as Lerp but for two rotations A & B. 
    340 		I won't be able to explain how the equation works because its out of the scope for now. You can surely checkout references section below to 
    341 		understand The Quaternion. 
    342 </p>
    343 <h3>Components of An Animated Model : Skin, Bones and Keyframes</h3>
    344 <p>The whole process of an animation starts with the addition of the first component which is The Skin in a software like blender or Maya. 
    345 	Skin is nothing but meshes which add visual aspect to the model to tell the viewer how it looks like. 
    346 	But If you want to move any mesh then just like the real world, you need to add Bones. You can see the images below to understand how it looks in software like blender....</p>
    347 <p>&nbsp;</p>
    348 <img src="/img/guest/2020/skeletal_animation/skin.png" alt="skin" width="300" height="300"  class="clean"> <img src="/img/guest/2020/skeletal_animation/bones.png" alt="bones" width="300" height="300"class="clean"> <img src="/img/guest/2020/skeletal_animation/merged.png" alt="skin and bones" width="300" height="300"class="clean">
    349 <p>These bones are usually added in hierarchical fashion for characters like humans &amp; animals and the reason is pretty obvious. We want parent-child relationship among limbs. 
    350 	For example, If we move our right shoulder then our right bicep, forearm, hand and fingers should move as well. This is how the hierarchy looks like....</p>
    351 <p>&nbsp;</p>
    352   
    353   <p>
    354     <img src="/img/guest/2020/skeletal_animation/parent_child.png"  alt="" width="853" height="425"/></p>
    355   
    356 <p>In the above diagram if you grab the hip bone and move it, all limbs will be affected by its movement.</p>
    357 <p>At this point, we are ready to create KeyFrames for an animation. Keyframes are poses at different point of time in an animation. We will interpolate between 
    358 	these Keyframes to go from one pose to another pose smoothly in our code. Below you can see how poses are created for a simple 4 frame jump animation...</p>
    359   <p><img src="/img/guest/2020/skeletal_animation/poses.gif" width="300px" class="clean" alt=""/> <img src="/img/guest/2020/skeletal_animation/interpolating.gif" class="clean" width="300px" alt="Interpolation bw frames" hspace="20"/></p>
    360 <p>&nbsp;</p>
    361 <h3>How Assimp holds animation data</h3>
    362 <p>We are almost there to the code part but first we need to understand how assimp holds imported animation data. Look at the diagram below..</p>
    363   <p><img src="/img/guest/2020/skeletal_animation/assimp1.jpeg" alt="" width="710" height="800"/></p>
    364 <p>Just like in the <a href="https://learnopengl.com/Model-Loading/Assimp">Model Loading</a> chapter, we will start with the <code>aiScene</code> pointer 
    365 	which holds a pointer to the root node and look what do we have here, an array of Animations.
    366 	 This array of <code>aiAnimation</code> contains the general information like duration of an animation represented here as 
    367 	 <code>mDuration</code> and then we have a <code>mTicksPerSecond</code> variable, which controls how fast 
    368 	 we should interpolate between frames. If you remember from the last section that an animation has keyframes. 
    369 	 Similary, an <code>aiAnimation</code> contains an <code>aiNodeAnim</code> array called Channels. 
    370 	 This array of contains all bones and their keyframes which are going to be engaged in an animation.
    371 
    372 	 An <code>aiNodeAnim</code> contains name of the bone and you 
    373 	 will find 3 types of keys to interpolate between here, Translation,Rotation &amp; Scale.</p>
    374 
    375 <p>Alright, there's one last thing we need to understand and we are good to go for writing some code.</p>
    376 
    377 <p>&nbsp;</p>
    378 <h3>Influence of multiple bones on vertices</h3>
    379 <p>When we curl our forearm and we see our biceps muscle pop up. We can also say that forearm bone transformation is affecting vertices on our biceps. 
    380 	Similary, there could be multiple bones affecting a single vertex in a mesh. 
    381 	For characters like solid metal robots all forearm vertices will only be affected by forearm bone but for characters like humans, animals etc, there could be
    382 	 upto 4 bones which can affect a vertex.&nbsp; Let's see how assimp stores that information...</p>
    383 <p>&nbsp;</p>
    384   <p><img src="/img/guest/2020/skeletal_animation/assimp2.jpeg" alt="" width="760" height="860"/></p>
    385 <p>&nbsp;</p>
    386 <p>We start with the <code>aiScene</code> pointer again which contains an array of all aiMeshes. 
    387 	Each <code>aiMesh</code> object has an array of <code>aiBone</code> which contains the information like 
    388 	how much influence this <code>aiBone</code> will have on set of vertices on the mesh. 
    389 	aiBone contains the name of the bone, an array of <code>aiVertexWeight</code> which basically 
    390 	tells us how much influence this <code>aiBone</code> will have on what vertices on the mesh.  
    391 	Now we have one more member of <code>aiBone</code> which is offsetMatrix. It's a 4x4 matrix
    392 	used to transform vertices from model space to their bone space. 
    393 	 You can see this in action in images below....</p>
    394 	 
    395 		  <img src="/img/guest/2020/skeletal_animation/mesh_space.png" class="clean" alt="Mesh Space" style="width:50%">
    396 		  <img src="/img/guest/2020/skeletal_animation/bone_space.png" class="clean" alt="Bone Space" style="width:50%">
    397 	 <p>
    398 		 When vertices are in bone space they will be transformed relative to their bone
    399 		  as they are supposed to. You will soon see this in action
    400 		 in code.
    401 	 </p>
    402             
    403 <h3>Finally! Let's code.</h3>
    404 <p>Thank you for making it this far. We will start with directly looking at the end result which is our final vertex 
    405 	shader code. This will give us good sense what we need at the end.. </p>
    406             
    407 <pre><code>#version 430 core
    408 
    409 layout(location = 0) in vec3 pos;
    410 layout(location = 1) in vec3 norm;
    411 layout(location = 2) in vec2 tex;
    412 layout(location = 3) in ivec4 boneIds; 
    413 layout(location = 4) in vec4 weights;
    414 
    415 uniform mat4 projection;
    416 uniform mat4 view;
    417 uniform mat4 model;
    418 
    419 const int MAX_BONES = 100;
    420 const int MAX_BONE_INFLUENCE = 4;
    421 uniform mat4 finalBonesMatrices[MAX_BONES];
    422 
    423 out vec2 TexCoords;
    424 
    425 void main()
    426 {
    427     vec4 totalPosition = vec4(0.0f);
    428     for(int i = 0 ; i &lt; MAX_BONE_INFLUENCE ; i++)
    429     {
    430         if(boneIds[i] == -1) 
    431             continue;
    432         if(boneIds[i] &gt;= MAX_JOINTS) 
    433         {
    434             totalPosition = vec4(pos,1.0f);
    435             break;
    436         }
    437         vec4 localPosition = finalBoneMatrices[boneIds[i]] * vec4(pos,1.0f);
    438         totalPosition += localPosition * weights[i];
    439         vec3 localNormal = mat3(finalBoneMatrices[boneIds[i]]) * norm;
    440    }
    441 	
    442     mat4 viewModel = view * model;
    443     gl_Position =  projection * viewModel * totalPosition;
    444 	TexCoords = tex;
    445 }
    446 </code></pre>
    447             
    448 <p>
    449   Fragment shader remains the same from the <a href="https://learnopengl.com/Model-Loading/Model">model loading</a> chapter. 
    450 	Starting from the top you see two new attributes layout declaration. 
    451 	First <code>boneIds</code> and second is <code>weights</code>. we also have 
    452 	a uniform array <code>finalBonesMatrices</code> which stores transformations of all bones.
    453 	 &nbsp;&nbsp;<code>boneIds</code> contains indices which are used to read the <code>finalBonesMatrices</code>
    454 	 array and apply those transformation to <code>pos</code> vertex with their respective weights
    455 	 stored in <code> weights </code> array. This happens inside <code> for </code> loop above.
    456 	   Now let's add support in our <code>Mesh</code> class for bone weights first..
    457 </p>
    458             
    459 <pre><code>#define MAX_BONE_INFLUENCE 4
    460 
    461 struct Vertex {
    462     // position
    463     glm::vec3 Position;
    464     // normal
    465     glm::vec3 Normal;
    466     // texCoords
    467     glm::vec2 TexCoords;
    468     
    469     //bone indexes which will influence this vertex
    470     int m_BoneIDs[MAX_BONE_INFLUENCE];
    471 
    472     //weights from each bone
    473     float m_Weights[MAX_BONE_INFLUENCE];
    474 };
    475 </code></pre>
    476             
    477 <p>
    478   We have added two new attributes for the <code>Vertex</code>, just like we saw in our vertex shader. 
    479 	Now's let's load them in GPU buffers just like other attributes in our <code>Mesh::setupMesh </code> function...
    480             </p>
    481             
    482 <pre><code>class Mesh
    483 {
    484     ...
    485     
    486     void setupMesh()
    487     {
    488         ...
    489         
    490         // ids
    491         <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(3);
    492         glVertexAttribIPointer(3, 4, GL_INT, sizeof(Vertex), 
    493                                (void*)offsetof(Vertex, m_BoneIDs));
    494 
    495         // weights
    496         <function id='29'><function id='60'>glEnable</function>VertexAttribArray</function>(4);
    497         <function id='30'>glVertexAttribPointer</function>(4, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), 
    498                               (void*)offsetof(Vertex, m_Weights));
    499         
    500         ...
    501     }
    502     ...
    503 }
    504 </code></pre>
    505 <p>Just like before, except now we have added 3 and 4 layout location ids for <code>boneIds</code> and <code>weights</code>. One imporant thing to notice here is how we are passing data for <code>boneIds</code>. We are using <code>glVertexAttribIPointer</code> and we passed GL_INT as third parameter.&nbsp;</p>
    506 <p>Now we can extract the bone-weight information from the assimp data structure. Let's make some changes in Model class...</p>
    507 
    508 <pre><code>struct BoneInfo
    509 {
    510     /*id is index in finalBoneMatrices*/
    511     int id;
    512 
    513     /*offset matrix transforms vertex from model space to bone space*/
    514     glm::mat4 offset;
    515 
    516 };
    517 </code></pre>
    518 
    519 <p> This <code> BoneInfo </code> will store our offset matrix and also a unique id which will 
    520 	be used as an index to store it in <code>finalBoneMatrices</code> array we saw earlier in our shader.
    521 Now we will add bone weight extraction support in <code>Model</code>... </p>
    522             
    523 <pre><code>class Model 
    524 {
    525 private:
    526     ...
    527     std::map&lt;string, BoneInfo&gt; m_BoneInfoMap; //
    528 	int m_BoneCounter = 0;
    529     
    530     ...
    531     void SetVertexBoneDataToDefault(Vertex&amp; vertex)
    532     {
    533         for (int i = 0; i &lt; MAX_BONE_WEIGHTS; i++)
    534         {
    535             vertex.m_BoneIDs[i] = -1;
    536             vertex.m_Weights[i] = 0.0f;
    537         }
    538     }
    539 
    540     Mesh processMesh(aiMesh* mesh, const aiScene* scene)
    541     {
    542         vector vertices;
    543         vector indices;
    544         vector textures;
    545 
    546         for (unsigned int i = 0; i &lt; mesh-&gt;mNumVertices; i++)
    547         {
    548             Vertex vertex;
    549             
    550             SetVertexBoneDataToDefault(vertex);
    551 
    552             vertex.Position = AssimpGLMHelpers::GetGLMVec(mesh-&gt;mVertices[i]);
    553             vertex.Normal = AssimpGLMHelpers::GetGLMVec(mesh-&gt;mNormals[i]);
    554 			
    555             if (mesh-&gt;mTextureCoords[0])
    556             {
    557                 glm::vec2 vec;
    558                 vec.x = mesh-&gt;mTextureCoords[0][i].x;
    559                 vec.y = mesh-&gt;mTextureCoords[0][i].y;
    560                 vertex.TexCoords = vec;
    561             }
    562             else
    563                 vertex.TexCoords = glm::vec2(0.0f, 0.0f);
    564 
    565             vertices.push_back(vertex);
    566         }
    567         ...
    568         ExtractBoneWeightForVertices(vertices,mesh,scene);
    569 
    570         return Mesh(vertices, indices, textures);
    571     }
    572 
    573     void SetVertexBoneData(Vertex&amp; vertex, int boneID, float weight)
    574     {
    575         for (int i = 0; i &lt; MAX_BONE_WEIGHTS; ++i)
    576         {
    577             if (vertex.m_BoneIDs[i] &lt; 0)
    578             {
    579                 vertex.m_Weights[i] = weight;
    580                 vertex.m_BoneIDs[i] = boneID;
    581                 break;
    582             }
    583         }
    584     }
    585 
    586     void ExtractBoneWeightForVertices(std::vector&amp; vertices, aiMesh* mesh, 
    587                                       const aiScene* scene)
    588     {
    589         for (int boneIndex = 0; boneIndex &lt; mesh-&gt;mNumBones; ++boneIndex)
    590         {
    591             int boneID = -1;
    592             std::string boneName = mesh-&gt;mBones[boneIndex]-&gt;mName.C_Str();
    593             if (m_BoneInfoMap.find(boneName) == m_BoneInfoMap.end())
    594             {
    595                 BoneInfo newBoneInfo;
    596                 newBoneInfo.id = m_BoneCounter;
    597                 newBoneInfo.offset = AssimpGLMHelpers::
    598                     ConvertMatrixToGLMFormat(mesh-&gt;mBones[boneIndex]-&gt;mOffsetMatrix);
    599                 m_BoneInfoMap[boneName] = newBoneInfo;
    600                 boneID = m_BoneCounter;
    601                 m_BoneCounter++;
    602             }
    603             else
    604             {
    605                 boneID = m_BoneInfoMap[boneName].id;
    606             }
    607             assert(boneID != -1);
    608             auto weights = mesh-&gt;mBones[boneIndex]-&gt;mWeights;
    609             int numWeights = mesh-&gt;mBones[boneIndex]-&gt;mNumWeights;
    610 
    611             for (int weightIndex = 0; weightIndex &lt; numWeights; ++weightIndex)
    612             {
    613                 int vertexId = weights[weightIndex].mVertexId;
    614                 float weight = weights[weightIndex].mWeight;
    615                 assert(vertexId &lt;= vertices.size());
    616                 SetVertexBoneData(vertices[vertexId], boneID, weight);
    617             }
    618         }
    619     }
    620     .......
    621 };
    622 </code></pre>
    623             
    624 <p>We start by declaring a map <code>m_BoneInfoMap</code> and a counter <code>m_BoneCounter</code> 
    625 	which will be incremented as soon as we read a new bone.
    626 	 we saw in the diagram earlier that each <code>aiMesh</code> contains all 
    627 	 aiBones which are associated with the <code>aiMesh</code>. 
    628 	 The whole process of the bone-weight extraction starts from the 
    629 	 <code> processMesh </code> 
    630 	function. For each loop iteration we are setting <code>m_BoneIDs</code> and <code>m_Weights</code> to 
    631 	their default values 
    632 	by calling function <code>SetVertexBoneDataToDefault</code>. 
    633 	Just before the <code>processMesh</code> function ends, we call the 
    634 	<code>ExtractBoneWeightData</code>. In the <code>ExtractBoneWeightData</code> we run 
    635 	a for loop for each <code>aiBone</code> and check if this bone already exists in the <code>m_BoneInfoMap</code>. 
    636 	If we couldn't find it then it's considered a new bone and we create new <code>BoneInfo</code> 
    637 	with an id and store its associated <code>mOffsetMatrix</code> to it. Then we store this new <code>BoneInfo</code>
    638 	 in <code>m_BoneInfoMap</code> and then we increment the <code>m_BoneCounter</code> counter to create 
    639 	 an id for next bone. In case we find the bone name in <code>m_BoneInfoMap</code> then 
    640 	 that means this bone affects vertices of mesh out of 
    641 	 its scope. So we take it's Id and proceed further to know which vertices it affects. </p>
    642 	 
    643 	 <p> One thing to notice that we are calling <code>AssimpGLMHelpers::ConvertMatrixToGLMFormat</code>. 
    644 	 Assimp store its matrix data in different format than GLM so this function just gives us our matrix in GLM format.
    645     </p>
    646 	<p>We have extracted the offsetMatrix for the bone and now we will simply iterate its <code>aiVertexWeight</code>array 
    647 		and extract all vertices indices which will be influenced by this bone along with their 
    648 		respective weights and call <code>SetVertexBoneData</code> to fill up <code>Vertex.boneIds</code> and <code>Vertex.weights</code> with extracted information. </p>
    649 		
    650 		<p>Phew! You deserve a coffee break at this point. </p>  
    651 
    652 <h3>Bone,Animation &amp; Animator classes</h3>
    653 <p>Here's high level view of classes..</p>
    654 
    655               <p><img src="/img/guest/2020/skeletal_animation/bird_eye_view.png" class="clean" alt="" width="700"/></p>
    656 
    657 <p>  Let us remind ourselves what we are trying to achieve. For each rendering frame we want to interpolate all bones in heirarchy smoothly and get their final transformations matrices which will be supplied to shader
    658 	uniform <code>finalBonesMatrices</code>.
    659 	Here's what each class does... 
    660 
    661 	<p><b>Bone</b> : A single bone which reads all keyframes data from <code>aiNodeAnim</code>. It will also interpolate between its keys i.e Translation,Scale & Rotation based on the current animation time. </p>
    662 	<p><b>AssimpNodeData</b> : This struct will help us to isolate our <code><b>Animation</b> from Assimp. </code> </p>
    663 	<p><b>Animation</b> : An asset which reads data from aiAnimation and create a heirarchical record of <code><b>Bone</b></code>s </p>
    664 	<p><b>Animator</b> : This will read the heirarchy of <code>AssimpNodeData</code>, 
    665 		Interpolate all bones in a recursive manner and then prepare final bone transformation matrices for us that we need.  
    666 	
    667 </p>
    668 
    669 <p>
    670    Here's the code for <code>Bone</code>...
    671 
    672    <pre><code>struct KeyPosition
    673 {
    674     glm::vec3 position;
    675     float timeStamp;
    676 };
    677 
    678 struct KeyRotation
    679 {
    680     glm::quat orientation;
    681     float timeStamp;
    682 };
    683 
    684 struct KeyScale
    685 {
    686     glm::vec3 scale;
    687     float timeStamp;
    688 };
    689 
    690 class Bone
    691 {
    692 private:
    693   std::vector&lt;KeyPosition&gt; m_Positions;
    694   std::vector&lt;KeyRotation&gt; m_Rotations;
    695   std::vector&lt;KeyScale&gt; m_Scales;
    696   int m_NumPositions;
    697   int m_NumRotations;
    698   int m_NumScalings;
    699 	
    700   glm::mat4 m_LocalTransform;
    701   std::string m_Name;
    702   int m_ID;
    703 public:
    704   /*reads keyframes from aiNodeAnim*/
    705   Bone(const std::string& name, int ID, const aiNodeAnim* channel)
    706     :
    707     m_Name(name),
    708     m_ID(ID),
    709     m_LocalTransform(1.0f)
    710     {
    711         m_NumPositions = channel->mNumPositionKeys;
    712 
    713         for (int positionIndex = 0; positionIndex &lt; m_NumPositions; ++positionIndex)
    714         {
    715             aiVector3D aiPosition = channel->mPositionKeys[positionIndex].mValue;
    716             float timeStamp = channel->mPositionKeys[positionIndex].mTime;
    717             KeyPosition data;
    718             data.position = AssimpGLMHelpers::GetGLMVec(aiPosition);
    719             data.timeStamp = timeStamp;
    720             m_Positions.push_back(data);
    721         }
    722 
    723         m_NumRotations = channel->mNumRotationKeys;
    724         for (int rotationIndex = 0; rotationIndex &lt; m_NumRotations; ++rotationIndex)
    725         {
    726             aiQuaternion aiOrientation = channel->mRotationKeys[rotationIndex].mValue;
    727             float timeStamp = channel->mRotationKeys[rotationIndex].mTime;
    728             KeyRotation data;
    729             data.orientation = AssimpGLMHelpers::GetGLMQuat(aiOrientation);
    730             data.timeStamp = timeStamp;
    731             m_Rotations.push_back(data);
    732         }
    733 
    734         m_NumScalings = channel->mNumScalingKeys;
    735         for (int keyIndex = 0; keyIndex &lt; m_NumScalings; ++keyIndex)
    736         {
    737             aiVector3D scale = channel->mScalingKeys[keyIndex].mValue;
    738             float timeStamp = channel->mScalingKeys[keyIndex].mTime;
    739             KeyScale data;
    740             data.scale = AssimpGLMHelpers::GetGLMVec(scale);
    741             data.timeStamp = timeStamp;
    742             m_Scales.push_back(data);
    743         }
    744     }
    745 	
    746     /* Interpolates b/w positions,rotations & scaling keys based on the curren time of the 
    747     animation and prepares the local transformation matrix by combining all keys tranformations */
    748     void Update(float animationTime)
    749     {
    750         glm::mat4 translation = InterpolatePosition(animationTime);
    751         glm::mat4 rotation = InterpolateRotation(animationTime);
    752         glm::mat4 scale = InterpolateScaling(animationTime);
    753         m_LocalTransform = translation * rotation * scale;
    754     }
    755 
    756     glm::mat4 GetLocalTransform() { return m_LocalTransform; }
    757     std::string GetBoneName() const { return m_Name; }
    758     int GetBoneID() { return m_ID; }
    759 	
    760     /* Gets the current index on mKeyPositions to interpolate to based on the current 
    761     animation time */
    762     int GetPositionIndex(float animationTime)
    763     {
    764         for (int index = 0; index &lt; m_NumPositions - 1; ++index)
    765         {
    766             if (animationTime &lt; m_Positions[index + 1].timeStamp)
    767                 return index;
    768         }
    769         assert(0);
    770     }
    771     
    772     /* Gets the current index on mKeyRotations to interpolate to based on the current 
    773     animation time */
    774     int GetRotationIndex(float animationTime)
    775     {
    776         for (int index = 0; index &lt; m_NumRotations - 1; ++index)
    777         {
    778             if (animationTime &lt; m_Rotations[index + 1].timeStamp)
    779                 return index;
    780         }
    781         assert(0);
    782     }
    783 
    784     /* Gets the current index on mKeyScalings to interpolate to based on the current 
    785     animation time */
    786     int GetScaleIndex(float animationTime)
    787     {
    788         for (int index = 0; index &lt; m_NumScalings - 1; ++index)
    789         {
    790             if (animationTime &lt; m_Scales[index + 1].timeStamp)
    791                 return index;
    792         }
    793         assert(0);
    794     }
    795 private:
    796 
    797     /* Gets normalized value for Lerp & Slerp*/
    798     float GetScaleFactor(float lastTimeStamp, float nextTimeStamp, float animationTime)
    799     {
    800         float scaleFactor = 0.0f;
    801         float midWayLength = animationTime - lastTimeStamp;
    802         float framesDiff = nextTimeStamp - lastTimeStamp;
    803         scaleFactor = midWayLength / framesDiff;
    804         return scaleFactor;
    805     }
    806 
    807     /* figures out which position keys to interpolate b/w and performs the interpolation 
    808     and returns the translation matrix */
    809     glm::mat4 InterpolatePosition(float animationTime)
    810     {
    811         if (1 == m_NumPositions)
    812             return <function id='55'>glm::translate</function>(glm::mat4(1.0f), m_Positions[0].position);
    813 
    814         int p0Index = GetPositionIndex(animationTime);
    815         int p1Index = p0Index + 1;
    816         float scaleFactor = GetScaleFactor(m_Positions[p0Index].timeStamp,
    817                             m_Positions[p1Index].timeStamp, animationTime);
    818         glm::vec3 finalPosition = glm::mix(m_Positions[p0Index].position, 
    819                                            m_Positions[p1Index].position
    820 			, scaleFactor);
    821         return <function id='55'>glm::translate</function>(glm::mat4(1.0f), finalPosition);
    822     }
    823 
    824     /* figures out which rotations keys to interpolate b/w and performs the interpolation 
    825     and returns the rotation matrix */
    826     glm::mat4 InterpolateRotation(float animationTime)
    827     {
    828         if (1 == m_NumRotations)
    829         {
    830             auto rotation = glm::normalize(m_Rotations[0].orientation);
    831             return glm::toMat4(rotation);
    832         }
    833 
    834         int p0Index = GetRotationIndex(animationTime);
    835         int p1Index = p0Index + 1;
    836         float scaleFactor = GetScaleFactor(m_Rotations[p0Index].timeStamp,
    837                             m_Rotations[p1Index].timeStamp, animationTime);
    838         glm::quat finalRotation = glm::slerp(m_Rotations[p0Index].orientation,
    839                                   m_Rotations[p1Index].orientation, scaleFactor);
    840         finalRotation = glm::normalize(finalRotation);
    841         return glm::toMat4(finalRotation);
    842     }
    843 
    844     /* figures out which scaling keys to interpolate b/w and performs the interpolation 
    845     and returns the scale matrix */
    846     glm::mat4 Bone::InterpolateScaling(float animationTime)
    847     {
    848         if (1 == m_NumScalings)
    849             return <function id='56'>glm::scale</function>(glm::mat4(1.0f), m_Scales[0].scale);
    850 
    851         int p0Index = GetScaleIndex(animationTime);
    852         int p1Index = p0Index + 1;
    853         float scaleFactor = GetScaleFactor(m_Scales[p0Index].timeStamp,
    854                             m_Scales[p1Index].timeStamp, animationTime);
    855         glm::vec3 finalScale = glm::mix(m_Scales[p0Index].scale, 
    856                                m_Scales[p1Index].scale, scaleFactor);
    857         return <function id='56'>glm::scale</function>(glm::mat4(1.0f), finalScale);
    858     }	
    859 };
    860 </code></pre>
    861   
    862    <p>
    863 
    864 	We start by creating 3 structs for our key types. Each struct holds a value and a time stamp. Timestamp tells us at what point of an animation we need to interpolate to its value. 
    865 	<code>Bone</code> has a constructor which reads from <code>aiNodeAnim</code> and stores keys and their timestamps to <code>mPositionKeys, mRotationKeys & mScalingKeys </code>. The main interpolation process 
    866 	starts from <code>Update(float animationTime)</code> which gets called every frame. This function calls respective interpolation functions for all key types and combines all final interpolation results
    867 	and store it to a 4x4 Matrix <code>m_LocalTransform</code>. The interpolations functions for translation & scale keys are similar but for rotation we are using <code>Slerp</code> to interpolate between quaternions.
    868 	Both <code>Lerp</code> & <code>Slerp</code> takes 3 arguments. First argument takes last key, second argument takes next key and third argument takes value of range 0-1,we call it scale factor here. 
    869 	Let's see how we calculate this scale factor in function <code>GetScaleFactor</code>...
    870 
    871      <p><img src="/img/guest/2020/skeletal_animation/scale_factor.png" alt="skin"/></p>
    872 
    873    <p>In code...</p>
    874 
    875 	<p><b> 	float midWayLength = animationTime - lastTimeStamp; </b></p>
    876 	<p><b> float framesDiff = nextTimeStamp - lastTimeStamp;</b></p>
    877 		<p><b> scaleFactor = midWayLength / framesDiff; </b></p>
    878 	<p></p>     
    879    </p>
    880   
    881     Let's move on to <code><b>Animation</b></code> class now...
    882   
    883 <pre><code>struct AssimpNodeData
    884 {
    885     glm::mat4 transformation;
    886     std::string name;
    887     int childrenCount;
    888     std::vector&lt;AssimpNodeData&gt; children;
    889 };
    890 
    891 class Animation
    892 {
    893 public:
    894     Animation() = default;
    895 
    896     Animation(const std::string& animationPath, Model* model)
    897     {
    898         Assimp::Importer importer;
    899         const aiScene* scene = importer.ReadFile(animationPath, aiProcess_Triangulate);
    900         assert(scene && scene->mRootNode);
    901         auto animation = scene->mAnimations[0];
    902         m_Duration = animation->mDuration;
    903         m_TicksPerSecond = animation->mTicksPerSecond;
    904         ReadHeirarchyData(m_RootNode, scene->mRootNode);
    905         ReadMissingBones(animation, *model);
    906     }
    907 
    908     ~Animation()
    909     {
    910     }
    911 
    912     Bone* FindBone(const std::string& name)
    913     {
    914         auto iter = std::find_if(m_Bones.begin(), m_Bones.end(),
    915             [&](const Bone& Bone)
    916             {
    917                 return Bone.GetBoneName() == name;
    918             }
    919         );
    920         if (iter == m_Bones.end()) return nullptr;
    921         else return &(*iter);
    922     }
    923 
    924 	
    925     inline float GetTicksPerSecond() { return m_TicksPerSecond; }
    926 
    927     inline float GetDuration() { return m_Duration;}
    928 
    929     inline const AssimpNodeData& GetRootNode() { return m_RootNode; }
    930 
    931     inline const std::map&lt;std::string,BoneInfo&gt;& GetBoneIDMap() 
    932     { 
    933         return m_BoneInfoMap;
    934     }
    935 
    936 private:
    937     void ReadMissingBones(const aiAnimation* animation, Model& model)
    938     {
    939         int size = animation->mNumChannels;
    940 
    941         auto& boneInfoMap = model.GetBoneInfoMap();//getting m_BoneInfoMap from Model class
    942         int& boneCount = model.GetBoneCount(); //getting the m_BoneCounter from Model class
    943 
    944         //reading channels(bones engaged in an animation and their keyframes)
    945         for (int i = 0; i &lt; size; i++)
    946         {
    947             auto channel = animation->mChannels[i];
    948             std::string boneName = channel->mNodeName.data;
    949 
    950             if (boneInfoMap.find(boneName) == boneInfoMap.end())
    951             {
    952                 boneInfoMap[boneName].id = boneCount;
    953                 boneCount++;
    954             }
    955             m_Bones.push_back(Bone(channel->mNodeName.data,
    956                               boneInfoMap[channel->mNodeName.data].id, channel));
    957 		}
    958 
    959         m_BoneInfoMap = boneInfoMap;
    960     }
    961 
    962     void ReadHeirarchyData(AssimpNodeData& dest, const aiNode* src)
    963     {
    964         assert(src);
    965 
    966         dest.name = src->mName.data;
    967         dest.transformation = AssimpGLMHelpers::ConvertMatrixToGLMFormat(src->mTransformation);
    968         dest.childrenCount = src->mNumChildren;
    969 
    970         for (int i = 0; i &lt; src->mNumChildren; i++)
    971         {
    972             AssimpNodeData newData;
    973             ReadHeirarchyData(newData, src->mChildren[i]);
    974             dest.children.push_back(newData);
    975         }
    976     }
    977     float m_Duration;
    978     int m_TicksPerSecond;
    979     std::vector&lt;Bone&gt; m_Bones;
    980     AssimpNodeData m_RootNode;
    981     std::map&lt;std::string, BoneInfo&gt; m_BoneInfoMap;
    982 };
    983 </code></pre>
    984 
    985   <p> Here, creation of an  <code>Animation</code> object starts with a constructor. It takes two arguments. First, path to the animation file & second parameter is the <code>Model</code> for this animation. 
    986 You will see later ahead why we need this <code>Model</code> reference here. We then create an <code>Assimp::Importer</code> to read the animation file, followed by an <code>assert</code> check which will throw
    987 an error if animation could not be found. Then we read general animation data like how long is this animation which is <code>mDuration</code> and the animation speed represented by <code>mTicksPerSecond</code>.
    988 We then call <code>ReadHeirarchyData</code> which replicates <code>aiNode</code> heirarchy of Assimp and creates heirarchy of <code>AssimpNodeData</code>. 
    989 </p>
    990 
    991 <p> Then we call a function called <code>ReadMissingBones</code>. I had to write this function because sometimes when I loaded FBX model separately, it had some bones missing and I found those missing bones in
    992 the animation file. This function reads the missing bones information and stores their information in <code>m_BoneInfoMap</code> of <code>Model</code> and saves a reference of <code>m_BoneInfoMap</code> locally in
    993 the m_BoneInfoMap.</p> 
    994 
    995 <p>And we have our animation ready. Now let's look at our final stage, The Animator class...</p>
    996 
    997 <pre><code>class Animator
    998 {	
    999 public:
   1000     Animator::Animator(Animation* Animation)
   1001     {
   1002         m_CurrentTime = 0.0;
   1003         m_CurrentAnimation = currentAnimation;
   1004 
   1005         m_FinalBoneMatrices.reserve(100);
   1006 
   1007         for (int i = 0; i &lt; 100; i++)
   1008             m_FinalBoneMatrices.push_back(glm::mat4(1.0f));
   1009     }
   1010 	
   1011     void Animator::UpdateAnimation(float dt)
   1012     {
   1013         m_DeltaTime = dt;
   1014         if (m_CurrentAnimation)
   1015         {
   1016             m_CurrentTime += m_CurrentAnimation->GetTicksPerSecond() * dt;
   1017             m_CurrentTime = fmod(m_CurrentTime, m_CurrentAnimation->GetDuration());
   1018             CalculateBoneTransform(&m_CurrentAnimation->GetRootNode(), glm::mat4(1.0f));
   1019         }
   1020     }
   1021 	
   1022     void Animator::PlayAnimation(Animation* pAnimation)
   1023     {
   1024         m_CurrentAnimation = pAnimation;
   1025         m_CurrentTime = 0.0f;
   1026     }
   1027 	
   1028     void Animator::CalculateBoneTransform(const AssimpNodeData* node, glm::mat4 parentTransform)
   1029     {
   1030         std::string nodeName = node->name;
   1031         glm::mat4 nodeTransform = node->transformation;
   1032 	
   1033         Bone* Bone = m_CurrentAnimation->FindBone(nodeName);
   1034 	
   1035         if (Bone)
   1036         {
   1037             Bone->Update(m_CurrentTime);
   1038             nodeTransform = Bone->GetLocalTransform();
   1039         }
   1040 	
   1041         glm::mat4 globalTransformation = parentTransform * nodeTransform;
   1042 	
   1043         auto boneInfoMap = m_CurrentAnimation->GetBoneIDMap();
   1044         if (boneInfoMap.find(nodeName) != boneInfoMap.end())
   1045         {
   1046             int index = boneInfoMap[nodeName].id;
   1047             glm::mat4 offset = boneInfoMap[nodeName].offset;
   1048             m_FinalBoneMatrices[index] = globalTransformation * offset;
   1049         }
   1050 	
   1051         for (int i = 0; i &lt; node->childrenCount; i++)
   1052             CalculateBoneTransform(&node->children[i], globalTransformation);
   1053     }
   1054 	
   1055     std::vector&lt;glm::mat4&gt; GetFinalBoneMatrices() 
   1056     { 
   1057         return m_FinalBoneMatrices;  
   1058     }
   1059 		
   1060 private:
   1061     std::vector&lt;glm::mat4&gt; m_FinalBoneMatrices;
   1062     Animation* m_CurrentAnimation;
   1063     float m_CurrentTime;
   1064     float m_DeltaTime;	
   1065 };
   1066 </code></pre>
   1067 
   1068 <p>
   1069 
   1070 	<code>Animator</code> constructor takes an animation to play and
   1071 	 then it proceeds to reset the animation time <code>m_CurrentTime</code> to 0. 
   1072 	 It also initializes <code>m_FinalBoneMatrices</code> 
   1073 	which is a <code>std::vector&lt;glm::mat4&gt;</code>. 
   1074 	 The main point of attention here is <code>UpdateAnimation(float deltaTime)</code> function.
   1075 	  It advances the <code>m_CurrentTime</code> with rate of 
   1076 	<code>m_TicksPerSecond</code> and then calls the <code>CalculateBoneTransform</code> function.
   1077 	 We will pass two arguments in the start, first is the <code>m_RootNode</code> of <code>m_CurrentAnimation</code>
   1078 	and second is an identity matrix passed as <code>parentTransform</code> This function then check if <code>m_RootNode</code>s bone is engaged in this animation by finding it in <code>m_Bones</code> array of <code>Animation</code>. 
   1079 	If bone is found then it calls <code>Bone.Update()</code> function which interpolates all bones and return local bone transform matrix to
   1080 	 <code>nodeTransform</code>. 
   1081 	 But this is local space matrix and will move bone around origin if passed in shaders. So we multiply this <code>nodeTransform</code> with <code>parentTransform</code> and 
   1082 	 we store the result in <code>globalTransformation</code>. This would be enough but vertices are still in default model space. 
   1083 	 we find offset matrix in <code>m_BoneInfoMap</code> and then multiply it 
   1084 		with <code>globalTransfromMatrix</code>. 
   1085 		We will also get the id index which will be used to write final transformation of this bone to m_FinalBoneMatrices. 
   1086 	 </p>
   1087 
   1088 	 <p>
   1089 		 Finally! we call <code>CalculateBoneTransform</code> for each child nodes of this node and pass <code>globalTransformation</code> as <code>parentTransform</code>. 
   1090 		 We break this recursive loop when there will no children
   1091 		 left to process further. 
   1092 	 </p>
   1093 
   1094 </p>
   1095 </p>
   1096 
   1097 <h3> Let's Animate</h3>
   1098 
   1099 <p>
   1100 Fruit of our hardwork is finally here! Here's how we will play the animation in <code>main.cpp</code> ...
   1101 </p>
   1102 
   1103 <pre><code>int main()
   1104 {
   1105     ...
   1106 	
   1107     Model ourModel(FileSystem::getPath("resources/objects/vampire/dancing_vampire.dae"));
   1108     Animation danceAnimation(FileSystem::getPath(
   1109             "resources/objects/vampire/dancing_vampire.dae"), &ourModel);
   1110     Animator animator(&danceAnimation);
   1111 
   1112     // draw in wireframe
   1113     //<function id='43'>glPolygonMode</function>(GL_FRONT_AND_BACK, GL_LINE);
   1114 
   1115     // render loop
   1116     // -----------
   1117     while (!<function id='14'>glfwWindowShouldClose</function>(window))
   1118     {
   1119         // per-frame time logic
   1120         // --------------------
   1121         float currentFrame = <function id='47'>glfwGetTime</function>();
   1122         deltaTime = currentFrame - lastFrame;
   1123         lastFrame = currentFrame;
   1124 
   1125         // input
   1126         // -----
   1127         processInput(window);
   1128         animator.UpdateAnimation(deltaTime);
   1129 		
   1130         // render
   1131         // ------
   1132         <function id='13'><function id='10'>glClear</function>Color</function>(0.05f, 0.05f, 0.05f, 1.0f);
   1133         <function id='10'>glClear</function>(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   1134 
   1135         // don't forget to enable shader before setting uniforms
   1136         ourShader.use();
   1137 
   1138         // view/projection transformations
   1139         glm::mat4 projection = <function id='58'>glm::perspective</function>(<function id='63'>glm::radians</function>(camera.Zoom), 
   1140             (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
   1141         glm::mat4 view = camera.GetViewMatrix();
   1142         ourShader.setMat4("projection", projection);
   1143         ourShader.setMat4("view", view);
   1144 
   1145         auto transforms = animator.GetFinalBoneMatrices();
   1146         for (int i = 0; i &lt; transforms.size(); ++i)
   1147             ourShader.setMat4("finalBonesTransformations[" + std::to_string(i) + "]", 
   1148                               transforms[i]);
   1149 
   1150         // render the loaded model
   1151         glm::mat4 model = glm::mat4(1.0f);
   1152         model = <function id='55'>glm::translate</function>(model, glm::vec3(0.0f, -0.4f, 0.0f)); 
   1153         model = <function id='56'>glm::scale</function>(model, glm::vec3(.5f, .5f, .5f));
   1154         ourShader.setMat4("model", model);
   1155         ourModel.Draw(ourShader);
   1156 
   1157         // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
   1158         // -------------------------------------------------------------------------------
   1159         <function id='24'>glfwSwapBuffers</function>(window);
   1160         <function id='23'>glfwPollEvents</function>();
   1161     }
   1162 
   1163     // glfw: terminate, clearing all previously allocated GLFW resources.
   1164     // ------------------------------------------------------------------
   1165     <function id='25'>glfwTerminate</function>();
   1166     return 0;
   1167 }
   1168 </code></pre>
   1169     
   1170 <p>
   1171 
   1172 	We start with loading our <code>Model</code> which will setup bone weight data for the shader and then create an <code>Animation</code> by giving it the path.
   1173 	Then we create our <code>Animator</code> object by passing it the created <code>Animation</code>. In render loop we then update our <code>Animator</code>, take the 
   1174 	final bone transformations and give it to shaders. Here's the output we all have been waiting for...
   1175 
   1176 </p>
   1177 
   1178 <img src="/img/guest/2020/skeletal_animation/output.gif" alt="output">
   1179 
   1180   <p> Download the model used <a href="/data/models/vampire.zip">here.</a> Note that animations
   1181 and meshes are baked in single DAE(collada) file. You can find the full source code <a href="/code_viewer_gh.php?code=src/8.guest/2020/skeletal_animation/skeletal_animation.cpp" target="_blank">here</a>.
   1182 	
   1183 <h3>Further reading</h3>
   1184     <ul>
   1185 		<li><a href="http://www.songho.ca/math/quaternion/quaternion.html" target="_blank">
   1186 			Quaternions</a>: An article by songho to understand quaternions in depth.</li>
   1187 			<li><a href="http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html" target="_blank">
   1188 				Skeletal Animation with Assimp</a>: An article by OGL Dev.</li>
   1189 				<li><a href="https://youtu.be/f3Cr8Yx3GGA" target="_blank">
   1190 					Skeletal Animation with Java</a>: A fantastic youtube playlist by Thin Matrix.</li>
   1191 					<li><a href="https://www.gamasutra.com/view/feature/131686/rotating_objects_using_quaternions.php" target="_blank">
   1192 						Why Quaternions should be used for Rotation</a>: An awesome gamasutra article.</li>
   1193 			
   1194     </ul>
   1195 
   1196 
   1197 
   1198 <author>
   1199   <strong>Article by: </strong>Ankit Singh Kushwah<br/>
   1200   <strong>Contact: </strong><a href="mailto:eklavyagames@gmail.com" target="_blank">e-mail</a>
   1201 </author>       
   1202 
   1203     </div>
   1204     
   1205 	</main>
   1206 </body>
   1207 </html>