Scene-Graph.html (29501B)
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">Scene Graph</h1> 320 <h1 id="content-url" style='display:none;'>Guest-Articles/2021/Scene/Scene-Graph</h1> 321 <p> 322 In this article, we will talk about the <def>scene graph</def>. 323 A scene graph is not a class or an object, it's more like a pattern that allows you to create inheritance. 324 This pattern is used a lot in game engines. 325 For example, it is used in animation to manage bones. 326 If I move my arm, my hand needs to move too. 327 To obtain this result, I need a hierarchy with parents and children. 328 Thanks to it, my hand will be a child of my arm that is also a child of my body. 329 This hierarchy has the consequence of representing objects in global and local space. 330 Global space is based on position/rotation and scale based on the world and local space is based on its parent. 331 So, if I only move my hand, its local and global position will change but not the local and global position of my arm. 332 However, if I move my arm, its local and global position will change but also the global position of my hand. 333 Scene graphs can take a lot of forms: register or not the parent, list or contiguous buffer to store the children, flags... 334 Firstly, we will see a quick example of it. 335 In a second time, we will see simple optimizations to improve it. 336 So, talk less, code more ! ;D 337 </p> 338 339 <div class="box"> 340 <img src="/img/guest/2021/scene_graph/graph.png" width="400" style="display: inline" alt="Graph example"/> 341 <img src="/img/guest/2021/scene_graph/sceneGraph.png" width="400" style="display: inline" alt="Scene graph example in editor"/> 342 </div> 343 344 <p> 345 First of all, let’s create a transform. 346 Transform will contain local and global space information about our entities 347 </p> 348 349 <pre><code> 350 struct Transform 351 { 352 /*SPACE INFORMATION*/ 353 //Local space information 354 glm::vec3 pos = { 0.0f, 0.0f, 0.0f }; 355 glm::vec3 eulerRot = { 0.0f, 0.0f, 0.0f }; 356 glm::vec3 scale = { 1.0f, 1.0f, 1.0f }; 357 358 //Global space information concatenate in matrix 359 glm::mat4 modelMatrix = glm::mat4(1.0f); 360 }; 361 </code></pre> 362 363 <note> 364 In this example, we use the Euler angle system. 365 The Euler system is easy to use and understand but it contains a lot of undesirable effects: <a href="https://en.wikipedia.org/wiki/Gimbal_lock" target="_blank">Gimbal lock</a>, shortest angle between to angle thanks to lerp is wrong, cost and size... 366 To avoid it, we need to use <strong>quaternion</strong> but it needs to be hidden because it's hard to represent a rotation thanks to it. 367 To have a better understanding of this tutorial we will use the Euler angle but I invite you to do some research on quaternion. 368 </note> 369 370 <p> 371 Now we can represent local and global space information about an object. 372 We will make create a class called <funct>Entity</funct>. 373 This class will simply wrap the space information with a model for the visual demonstration. 374 </p> 375 376 <pre><code> 377 class Entity : public Model 378 { 379 public: 380 [...] 381 382 Transform transform; 383 384 // constructor, expects a filepath to a 3D model. 385 Entity(string const& path, bool gamma = false) : Model(path, gamma) 386 {} 387 }; 388 </code></pre> 389 <p> 390 The scene graph is still missing. So, let's add it into the Entity class. 391 </p> 392 <pre><code> 393 /*SCENE GRAPH*/ 394 std::list<std::unique_ptr<Entity>> children; 395 Entity* parent = nullptr; 396 </code></pre> 397 <p> 398 As we said, scene graphs can take a lot of forms: use std::vector instead of std::list, don't register its parent to simplify the size of the class... 399 In our example we used std::list. 400 We don't want our entity address to change at all. 401 My parents’ address always needs to be valid. 402 Then, I used std::unique_ptr because it's the cleanest approach to avoid a leak. 403 Memory leak appears happens if you allocate memory thanks to new or malloc and forget to call delete or free when datas disappears. 404 Information is still in memory but you cannot have access to it. 405 If you want more information about it, many tutorials talk about it more clearly. 406 </p> 407 408 <p> 409 <dl> 410 <dt> 411 We need now to create a function to add a child to our entity. 412 This function will be easy: 413 </dt> 414 <dd> 415 1: Add entity 416 </dd> 417 <dd> 418 2: Register parent of this new entity as the current entity 419 </dd> 420 </dl> 421 </p> 422 423 424 <pre><code> 425 template<typename... TArgs> 426 void addChild(const TArgs&... args) 427 { 428 children.emplace_back(std::make_unique<Entity>(args...)); 429 children.back()->parent = this; 430 } 431 </code></pre> 432 433 <note> 434 A variadic template allows us to create an entity with any constructor that you want to use without overloading the addChild function. 435 I used it in this example to awake your curiosity and show you a simple use of it. 436 </note> 437 438 <p> 439 Ok, perfect! It's almost done, courage! 440 Now we have scene graphs and space information but something is still missing. 441 When we want to send space information to our shader, we need a model matrix. 442 However, we don't see how to compute it thanks to our parents and our local information. 443 First, we need a function to create a local model matrix that represents an object in space based on its parent. 444 This function will be added to the transform class. 445 </p> 446 447 <pre><code> 448 glm::mat4 getLocalModelMatrix() 449 { 450 const glm::mat4 transformX = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 451 <function id='63'>glm::radians</function>(eulerRot.x), 452 glm::vec3(1.0f, 0.0f, 0.0f)); 453 const glm::mat4 transformY = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 454 <function id='63'>glm::radians</function>(eulerRot.y), 455 glm::vec3(0.0f, 1.0f, 0.0f)); 456 const glm::mat4 transformZ = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 457 <function id='63'>glm::radians</function>(eulerRot.z), 458 glm::vec3(0.0f, 0.0f, 1.0f)); 459 460 // Y * X * Z 461 const glm::mat4 roationMatrix = transformY * transformX * transformZ; 462 463 // translation * rotation * scale (also know as TRS matrix) 464 return <function id='55'>glm::translate</function>(glm::mat4(1.0f), pos) * 465 roationMatrix * 466 <function id='56'>glm::scale</function>(glm::mat4(1.0f), scale); 467 } 468 </code></pre> 469 470 <p> 471 To combine multiple model matrices, we need to multiply them. 472 So if I multiply the local model matrix of my hand with the model matrix of the world with arm and arm with hand, I will obtain the global matrix of my hand! 473 So, let's implement a function in entity class that will do it for us: 474 </p> 475 476 <pre><code> 477 void updateSelfAndChild() 478 { 479 if (parent) 480 modelMatrix = parent->modelMatrix * getLocalModelMatrix(); 481 else 482 modelMatrix = getLocalModelMatrix(); 483 484 for (auto&& child : children) 485 { 486 child->updateSelfAndChild(); 487 } 488 } 489 </code></pre> 490 491 <p> 492 Now, if I update the world, all of my entities contained will also be updated and our global model matrix will be computed. 493 Finally, we just need to add some code in the main function to add multiple moons with the same local distance and scale and code in the main loop to move the first entity. 494 </p> 495 496 <pre><code> 497 /*BEFOR THE MAIN LOOP*/ 498 499 // load entities 500 // ----------- 501 const char* pathStr = "resources/objects/planet/planet.obj"; 502 Entity ourEntity(FileSystem::getPath(pathStr)); 503 ourEntity.transform.pos.x = 10; 504 const float scale = 0.75; 505 ourEntity.transform.scale = { scale, scale, scale }; 506 507 { 508 Entity* lastEntity = &ourEntity; 509 510 for (unsigned int i = 0; i < 10; ++i) 511 { 512 lastEntity->addChild(FileSystem::getPath(pathStr)); 513 lastEntity = lastEntity->children.back().get(); 514 515 //Set tranform values 516 lastEntity->transform.pos.x = 10; 517 lastEntity->transform.scale = { scale, scale, scale }; 518 } 519 } 520 ourEntity.updateSelfAndChild(); 521 522 /*IN THE MAIN LOOP*/ 523 524 // draw our scene graph 525 Entity* lastEntity = &ourEntity; 526 while (lastEntity->children.size()) 527 { 528 ourShader.setMat4("model", lastEntity->transform.modelMatrix); 529 lastEntity->Draw(ourShader); 530 lastEntity = lastEntity->children.back().get(); 531 } 532 533 ourEntity.transform.eulerRot.y += 20 * deltaTime; 534 ourEntity.updateSelfAndChild(); 535 </code></pre> 536 537 <p> 538 We can see this result thanks to this code. 539 </p> 540 541 <img src="/img/guest/2021/scene_graph/result.png" alt="Graph example"/> 542 543 <h2>Optimization</h2> 544 545 <p> 546 Being pragmatic is essential to implement a new feature but now let's see how to optimize it. 547 Firstly, in the main loop, we always update the model matrix even if it doesn't move. 548 In programming, a pattern called a dirty flag can help us. 549 A dirty flag is a simple boolean called "isDirty" that allows us to know if an entity was moved during the previous frame. 550 So, if the user scales, rotates or translates it, we need to set this flag on. 551 Don't forget, the model matrix is based on the parent model matrix. 552 So if I change it, I also need to compute all its children's matrix. 553 This flag will be set off in the update function when the model matrix was recomputed. 554 </p> 555 556 <warning> 557 This pattern is very powerful but can be integrated easily only if you encapsulate your class correctly. 558 I haven’t done it to give you an example of rigid code without the possibility to evolve and change easily. 559 This code requires the programmer to code its functionality perfectly and is difficult to change, improve or optimize. 560 But in production, time is a precious resource and you sometimes need to implement the feature without optimizing. 561 One day, if your feature becomes the performance bottleneck, you need to change it easily. 562 Encapsulation, private/protected, getter/setter is C++ advantage that allows you to do it. 563 Encapsulation has its downsides too. 564 It can increase the size of your class and reduce its visibility. 565 It can also be laborious if you create a getter/setter for all your members. 566 An illarouse <a href="https://www.youtube.com/watch?v=-AQfQFcXac8" target="_blank">video</a> show the c++ limit but keep in mind this example and make do the balance in function of your situation. 567 </warning> 568 569 <p> 570 Let's remake do again transform function with encapsulation and dirty flag 571 </p> 572 573 <pre><code> 574 class Transform 575 { 576 protected: 577 //Local space information 578 glm::vec3 m_pos = { 0.0f, 0.0f, 0.0f }; 579 glm::vec3 m_eulerRot = { 0.0f, 0.0f, 0.0f }; //In degrees 580 glm::vec3 m_scale = { 1.0f, 1.0f, 1.0f }; 581 582 //Global space information concatenate in matrix 583 glm::mat4 m_modelMatrix = glm::mat4(1.0f); 584 585 //Dirty flag 586 bool m_isDirty = true; 587 588 protected: 589 glm::mat4 getLocalModelMatrix() 590 { 591 const glm::mat4 transformX = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 592 <function id='63'>glm::radians</function>(m_eulerRot.x), 593 glm::vec3(1.0f, 0.0f, 0.0f)); 594 const glm::mat4 transformY = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 595 <function id='63'>glm::radians</function>(m_eulerRot.y), 596 glm::vec3(0.0f, 1.0f, 0.0f)); 597 const glm::mat4 transformZ = <function id='57'>glm::rotate</function>(glm::mat4(1.0f), 598 <function id='63'>glm::radians</function>(m_eulerRot.z), 599 glm::vec3(0.0f, 0.0f, 1.0f)); 600 601 // Y * X * Z 602 const glm::mat4 roationMatrix = transformY * transformX * transformZ; 603 604 // translation * rotation * scale (also know as TRS matrix) 605 return <function id='55'>glm::translate</function>(glm::mat4(1.0f), m_pos) * 606 roationMatrix * 607 <function id='56'>glm::scale</function>(glm::mat4(1.0f), m_scale); 608 } 609 public: 610 611 void computeModelMatrix() 612 { 613 m_modelMatrix = getLocalModelMatrix(); 614 } 615 616 void computeModelMatrix(const glm::mat4& parentGlobalModelMatrix) 617 { 618 m_modelMatrix = parentGlobalModelMatrix * getLocalModelMatrix(); 619 } 620 621 void setLocalPosition(const glm::vec3& newPosition) 622 { 623 m_pos = newPosition; 624 m_isDirty = true; 625 } 626 627 [...] 628 629 const glm::vec3& getLocalPosition() 630 { 631 return m_pos; 632 } 633 634 [...] 635 636 const glm::mat4& getModelMatrix() 637 { 638 return m_modelMatrix; 639 } 640 641 bool isDirty() 642 { 643 return m_isDirty; 644 } 645 }; 646 647 648 class Entity : public Model 649 { 650 public: 651 //Scene graph 652 std::list<std::unique_ptr<Entity>> children; 653 Entity* parent = nullptr; 654 655 //Space information 656 Transform transform; 657 658 // constructor, expects a filepath to a 3D model. 659 Entity(string const& path, bool gamma = false) : Model(path, gamma) 660 {} 661 662 //Add child. Argument input is argument of any constructor that you create. 663 //By default you can use the default constructor and don't put argument input. 664 template<typename... TArgs> 665 void addChild(const TArgs&... args) 666 { 667 children.emplace_back(std::make_unique<Entity>(args...)); 668 children.back()->parent = this; 669 } 670 671 //Update transform if it was changed 672 void updateSelfAndChild() 673 { 674 if (!transform.isDirty()) 675 return; 676 677 forceUpdateSelfAndChild(); 678 } 679 680 //Force update of transform even if local space don't change 681 void forceUpdateSelfAndChild() 682 { 683 if (parent) 684 transform.computeModelMatrix(parent->transform.getModelMatrix()); 685 else 686 transform.computeModelMatrix(); 687 688 for (auto&& child : children) 689 { 690 child->forceUpdateSelfAndChild(); 691 } 692 } 693 }; 694 </code></pre> 695 696 <p> 697 Perfect! Another solution to improve performance can be memorizing the local Model matrix. 698 Thanks to it, we avoid recomputing all child local model matrices if the parent is moved. 699 This solution improves performance but transforms become heavier. 700 This size is really important for your hardware. 701 We will see why in the limitation subchapter. 702 You can find the code <a href="https://learnopengl.com/code_viewer_gh.php?code=src/8.guest/2021/1.scene/1.scene_graph/scene_graph.cpp" target="_blank">here</a>. 703 </p> 704 705 <h2>Limitation</h2> 706 707 <p> 708 Do you ever hear about <def>data oriented design</def> ? No ? Let's talk brevely about it ! 709 </p> 710 <p> 711 This design is based on how your hardware works. 712 In your computer, data is aligned into your memory. 713 When you use data like a variable, this data is sent to the cache. 714 The cache is a very fast memory but without a lot of space. 715 This memory is localized into your CPU. 716 For example, if you want to make a cake, you need to go to a supermarket (hard disk) to buy ingredients. 717 A supermarket is very big but is also very far from your home. 718 When you buy your ingredients, you store them in your fridge (RAM). 719 Your fridge contains ingredients that you need to live but I hope for you that you don't live only with cake ^^ 720 It is also small but near to your kitchen worktop. 721 And finally, your kitchen worktop (the cache) is small and can't contain all your ingredients but is very near to your preparation. 722 It's the same for the PC! 723 When you need to process data, your system will copy your data but also all datas after in cache (depending on your cache line size). 724 So, if all your datas are not contiguous in your memory, you will be as slow as if you need to take eggs one by one in your fridge to make your cake. 725 std::list is a noncontiguous array. 726 std::map will probably make the job better but inheritance cannot be aligned into your memory because it's a tree. 727 So, if you want to make RTS for example, with an independent unit, you probably don't need or don't want a scene graph to manage your entities. 728 </p> 729 730 <note> 731 std::map is tree and is aligned in memory but scene graph will jump into this memory and will be slower than don't use inheritance. 732 </note> 733 734 <p> 735 You know now what a Scene graph is and how to use it with its limitations. 736 In the next chapter, we will talk about frustum culling with a scene graph. 737 </p> 738 739 <h2>Additional resources</h2> 740 741 <ul> 742 <li><a href="https://walterkuppens.com/post/wtf-is-a-scene-graph/" target="_blank"> 743 walterkuppens article about scene graph</a>: An article by Walter Kuppens with another approach of the scene graph.</li> 744 745 <li><a href="https://webglfundamentals.org/webgl/lessons/webgl-scene-graph.html" target="_blank"> 746 webglfundamentals article and demonstration about scene graph</a>: An article with animated webGL code and animated demonstration</li> 747 </ul> 748 749 <author> 750 <strong>Article by: </strong>Six Jonathan<br> 751 <strong>Contact: </strong><a href="Six-Jonathan@orange.fr" target="_blank">e-mail</a><br> 752 <strong>Date: </strong> 09/2021<br> 753 <div> 754 <a href="https://github.com/Renardjojo"> 755 <svg height="32" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github v-align-middle"> 756 <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path> 757 </svg> 758 </a> 759 <a href="https://www.linkedin.com/in/jonathan-six-4553611a9/"> 760 <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 34 34" class="global-nav__logo"> 761 <path d="M34,2.5v29A2.5,2.5,0,0,1,31.5,34H2.5A2.5,2.5,0,0,1,0,31.5V2.5A2.5,2.5,0,0,1,2.5,0h29A2.5,2.5,0,0,1,34,2.5ZM10,13H5V29h5Zm.45-5.5A2.88,2.88,0,0,0,7.59,4.6H7.5a2.9,2.9,0,0,0,0,5.8h0a2.88,2.88,0,0,0,2.95-2.81ZM29,19.28c0-4.81-3.06-6.68-6.1-6.68a5.7,5.7,0,0,0-5.06,2.58H17.7V13H13V29h5V20.49a3.32,3.32,0,0,1,3-3.58h.19c1.59,0,2.77,1,2.77,3.52V29h5Z" fill="currentColor"></path> 762 </svg> 763 </a> 764 </div> 765 </author> 766 767 768 769 770 </div> 771 772 </main> 773 </body> 774 </html>