1 /* 2 Copyright (c) 2017-2019 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.resource.scene; 29 30 import std.stdio; 31 import std.math; 32 import std.algorithm; 33 import std.traits; 34 import std.conv; 35 import std.path; 36 37 import dlib.core.memory; 38 39 import dlib.container.array; 40 import dlib.container.dict; 41 import dlib.math.vector; 42 import dlib.math.matrix; 43 import dlib.math.transformation; 44 import dlib.math.quaternion; 45 import dlib.image.color; 46 import dlib.image.image; 47 import dlib.image.unmanaged; 48 import dlib.image.io.png; 49 50 import dagon.core.libs; 51 import dagon.core.ownership; 52 import dagon.core.event; 53 import dagon.core.application; 54 55 import dagon.resource.asset; 56 import dagon.resource.textasset; 57 import dagon.resource.textureasset; 58 import dagon.resource.imageasset; 59 import dagon.resource.fontasset; 60 import dagon.resource.obj; 61 import dagon.resource.iqm; 62 import dagon.resource.packageasset; 63 import dagon.resource.props; 64 import dagon.resource.config; 65 66 import dagon.graphics.environment; 67 import dagon.graphics.rc; 68 import dagon.graphics.view; 69 import dagon.graphics.shapes; 70 import dagon.graphics.light; 71 import dagon.graphics.shadow; 72 import dagon.graphics.texture; 73 import dagon.graphics.material; 74 import dagon.graphics.particles; 75 import dagon.graphics.framebuffer; 76 import dagon.graphics.renderer; 77 import dagon.graphics.terrain; 78 79 import dagon.graphics.shader; 80 import dagon.graphics.shaders.standard; 81 import dagon.graphics.shaders.sky; 82 import dagon.graphics.shaders.particle; 83 import dagon.graphics.shaders.hud; 84 import dagon.graphics.shaders.decal; 85 import dagon.graphics.shaders.terrain; 86 87 import dagon.logics.entity; 88 89 class BaseScene: EventListener 90 { 91 SceneManager sceneManager; 92 AssetManager assetManager; 93 bool canRun = false; 94 bool releaseAtNextStep = false; 95 bool needToLoad = true; 96 97 this(SceneManager smngr) 98 { 99 super(smngr.eventManager, null); 100 sceneManager = smngr; 101 assetManager = New!AssetManager(eventManager); 102 } 103 104 ~this() 105 { 106 release(); 107 Delete(assetManager); 108 } 109 110 Configuration config() @property 111 { 112 return sceneManager.application.config; 113 } 114 115 // Set preload to true if you want to load the asset immediately 116 // before actual loading (e.g., to render a loading screen) 117 Asset addAsset(Asset asset, string filename, bool preload = false) 118 { 119 if (preload) 120 assetManager.preloadAsset(asset, filename); 121 else 122 assetManager.addAsset(asset, filename); 123 return asset; 124 } 125 126 void onAssetsRequest() 127 { 128 // Add your assets here 129 } 130 131 void onLoading(float percentage) 132 { 133 // Render your loading screen here 134 } 135 136 void onAllocate() 137 { 138 // Allocate your objects here 139 } 140 141 void onRelease() 142 { 143 // Release your objects here 144 } 145 146 void onStart() 147 { 148 // Do your (re)initialization here 149 } 150 151 void onEnd() 152 { 153 // Do your finalization here 154 } 155 156 void onUpdate(double dt) 157 { 158 // Do your animation and logics here 159 } 160 161 void onRender() 162 { 163 // Do your rendering here 164 } 165 166 void exitApplication() 167 { 168 generateUserEvent(DagonEvent.Exit); 169 } 170 171 void load() 172 { 173 if (needToLoad) 174 { 175 onAssetsRequest(); 176 float p = assetManager.nextLoadingPercentage; 177 178 assetManager.loadThreadSafePart(); 179 180 while(assetManager.isLoading) 181 { 182 sceneManager.application.beginRender(); 183 onLoading(p); 184 sceneManager.application.endRender(); 185 p = assetManager.nextLoadingPercentage; 186 } 187 188 bool loaded = assetManager.loadThreadUnsafePart(); 189 190 if (loaded) 191 { 192 onAllocate(); 193 canRun = true; 194 needToLoad = false; 195 } 196 else 197 { 198 writeln("Exiting due to error while loading assets"); 199 canRun = false; 200 eventManager.running = false; 201 } 202 } 203 else 204 { 205 canRun = true; 206 } 207 } 208 209 void release() 210 { 211 onRelease(); 212 clearOwnedObjects(); 213 assetManager.releaseAssets(); 214 needToLoad = true; 215 canRun = false; 216 } 217 218 void start() 219 { 220 if (canRun) 221 onStart(); 222 } 223 224 void end() 225 { 226 if (canRun) 227 onEnd(); 228 } 229 230 void update(double dt) 231 { 232 if (canRun) 233 { 234 processEvents(); 235 assetManager.updateMonitor(dt); 236 onUpdate(dt); 237 } 238 239 if (releaseAtNextStep) 240 { 241 end(); 242 release(); 243 244 releaseAtNextStep = false; 245 canRun = false; 246 } 247 } 248 249 void render() 250 { 251 if (canRun) 252 onRender(); 253 } 254 } 255 256 class SceneManager: Owner 257 { 258 SceneApplication application; 259 Dict!(BaseScene, string) scenesByName; 260 EventManager eventManager; 261 BaseScene currentScene; 262 263 this(EventManager emngr, SceneApplication app) 264 { 265 super(app); 266 application = app; 267 eventManager = emngr; 268 scenesByName = New!(Dict!(BaseScene, string)); 269 } 270 271 ~this() 272 { 273 foreach(i, s; scenesByName) 274 { 275 Delete(s); 276 } 277 Delete(scenesByName); 278 } 279 280 BaseScene addScene(BaseScene scene, string name) 281 { 282 scenesByName[name] = scene; 283 return scene; 284 } 285 286 void removeScene(string name) 287 { 288 Delete(scenesByName[name]); 289 scenesByName.remove(name); 290 } 291 292 void goToScene(string name, bool releaseCurrent = true) 293 { 294 if (currentScene && releaseCurrent) 295 { 296 currentScene.releaseAtNextStep = true; 297 } 298 299 BaseScene scene = scenesByName[name]; 300 301 writefln("Loading scene \"%s\"", name); 302 303 scene.load(); 304 currentScene = scene; 305 currentScene.start(); 306 307 writefln("Running...", name); 308 } 309 310 void update(double dt) 311 { 312 if (currentScene) 313 { 314 currentScene.update(dt); 315 } 316 } 317 318 void render() 319 { 320 if (currentScene) 321 { 322 currentScene.render(); 323 } 324 } 325 } 326 327 class SceneApplication: Application 328 { 329 SceneManager sceneManager; 330 UnmanagedImageFactory imageFactory; 331 SuperImage screenshotBuffer1; 332 SuperImage screenshotBuffer2; 333 Configuration config; 334 335 this(uint w, uint h, bool fullscreen, string windowTitle, string[] args) 336 { 337 super(w, h, fullscreen, windowTitle, args); 338 339 config = New!Configuration(this); 340 config.props.set(DPropType.Number, "mouseSensibility", "0.2"); 341 config.fromFile("settings.conf"); 342 config.props.set(DPropType.Number, "windowWidth", w.to!string); 343 config.props.set(DPropType.Number, "windowHeight", h.to!string); 344 config.props.set(DPropType.Number, "fullscreen", (cast(uint)fullscreen).to!string); 345 346 sceneManager = New!SceneManager(eventManager, this); 347 348 imageFactory = New!UnmanagedImageFactory(); 349 screenshotBuffer1 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8); 350 screenshotBuffer2 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8); 351 } 352 353 this(string windowTitle, string[] args) 354 { 355 config = New!Configuration(this); 356 if (!config.fromFile("settings.conf")) 357 { 358 writeln("Warning: no \"settings.conf\" found, using default configuration"); 359 config.props.set(DPropType.Number, "windowWidth", "1280"); 360 config.props.set(DPropType.Number, "windowHeight", "720"); 361 config.props.set(DPropType.Number, "fullscreen", "0"); 362 config.props.set(DPropType.Number, "mouseSensibility", "0.2"); 363 } 364 365 super( 366 config.props.windowWidth.toUInt, 367 config.props.windowHeight.toUInt, 368 config.props.fullscreen.toBool, 369 windowTitle, 370 args); 371 372 sceneManager = New!SceneManager(eventManager, this); 373 374 imageFactory = New!UnmanagedImageFactory(); 375 screenshotBuffer1 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8); 376 screenshotBuffer2 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8); 377 } 378 379 ~this() 380 { 381 Delete(imageFactory); 382 Delete(screenshotBuffer1); 383 Delete(screenshotBuffer2); 384 } 385 386 override void onUpdate(double dt) 387 { 388 sceneManager.update(dt); 389 } 390 391 override void onRender() 392 { 393 sceneManager.render(); 394 } 395 396 void saveScreenshot(string filename) 397 { 398 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 399 glReadPixels(0, 0, eventManager.windowWidth, eventManager.windowHeight, GL_RGB, GL_UNSIGNED_BYTE, screenshotBuffer1.data.ptr); 400 glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 401 402 foreach(y; 0..screenshotBuffer1.height) 403 foreach(x; 0..screenshotBuffer1.width) 404 { 405 screenshotBuffer2[x, y] = screenshotBuffer1[x, screenshotBuffer1.height - y]; 406 } 407 408 screenshotBuffer2.savePNG(filename); 409 } 410 } 411 412 class Scene: BaseScene 413 { 414 Renderer renderer; 415 Environment environment; 416 LightManager lightManager; 417 ParticleSystem particleSystem; 418 419 StandardShader standardShader; 420 SkyShader skyShader; 421 Material defaultSkyMaterial; 422 ParticleShader particleShader; 423 TerrainShader terrainShader; 424 Material defaultMaterial3D; 425 DecalShader decalShader; 426 Material defaultDecalMaterial; 427 View view; 428 429 DynamicArray!Entity _entities3D; 430 DynamicArray!Entity _entities2D; 431 Entities3D entities3Dflat; 432 Entities2D entities2Dflat; 433 434 Entities3DStatic entities3DStatic; 435 Entities3DDynamic entities3DDynamic; 436 437 DynamicArray!Entity decals; 438 ShapeBox decalShape; 439 440 ShapeQuad loadingProgressBar; 441 Entity eLoadingProgressBar; 442 HUDShader hudShader; 443 Material mLoadingProgressBar; 444 445 double timer = 0.0; 446 double fixedTimeStep = 1.0 / 60.0; 447 uint maxUpdatesPerFrame = 5; 448 449 LightSource mainSunLight; 450 451 this(SceneManager smngr) 452 { 453 super(smngr); 454 455 loadingProgressBar = New!ShapeQuad(assetManager); 456 eLoadingProgressBar = New!Entity(eventManager, assetManager); 457 eLoadingProgressBar.drawable = loadingProgressBar; 458 hudShader = New!HUDShader(assetManager); 459 mLoadingProgressBar = createMaterial(hudShader); 460 mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1); 461 eLoadingProgressBar.material = mLoadingProgressBar; 462 } 463 464 override void onAllocate() 465 { 466 environment = New!Environment(assetManager); 467 lightManager = New!LightManager(assetManager); 468 469 entities3Dflat = New!Entities3D(this, assetManager); 470 entities2Dflat = New!Entities2D(this, assetManager); 471 472 entities3DStatic = New!Entities3DStatic(this, assetManager); 473 entities3DDynamic = New!Entities3DDynamic(this, assetManager); 474 475 renderer = New!Renderer(this, assetManager); 476 477 standardShader = New!StandardShader(assetManager); 478 479 skyShader = New!SkyShader(assetManager); 480 defaultSkyMaterial = New!Material(skyShader, assetManager); 481 defaultSkyMaterial.depthWrite = false; 482 defaultSkyMaterial.culling = false; 483 484 terrainShader = New!TerrainShader(assetManager); 485 486 particleShader = New!ParticleShader(renderer.gbuffer, assetManager); 487 488 particleSystem = New!ParticleSystem(assetManager); 489 490 decalShader = New!DecalShader(renderer.gbuffer, assetManager); 491 defaultDecalMaterial = createDecalMaterial(); 492 decalShape = New!ShapeBox(Vector3f(1, 1, 1), assetManager); 493 494 defaultMaterial3D = createMaterial(); 495 496 timer = 0.0; 497 } 498 499 void sortEntities(ref DynamicArray!Entity entities) 500 { 501 size_t j = 0; 502 Entity tmp; 503 504 auto edata = entities.data; 505 506 foreach(i, v; edata) 507 { 508 j = i; 509 size_t k = i; 510 511 while (k < edata.length) 512 { 513 float b1 = edata[j].layer; 514 float b2 = edata[k].layer; 515 516 if (b2 < b1) 517 j = k; 518 519 k++; 520 } 521 522 tmp = edata[i]; 523 edata[i] = edata[j]; 524 edata[j] = tmp; 525 526 sortEntities(v.children); 527 } 528 } 529 530 auto asset(string filename, Args...)(Args args) 531 { 532 enum string e = filename.extension; 533 static if (e == ".txt" || e == ".TXT") 534 return addTextAsset(filename); 535 else static if (e == ".png" || e == ".PNG" || 536 e == ".jpg" || e == ".JPG" || 537 e == ".hdr" || e == ".HDR" || 538 e == ".bmp" || e == ".BMP" || 539 e == ".tga" || e == ".TGA") 540 return addTextureAsset(filename); 541 else static if (e == ".ttf" || e == ".TTF") 542 return addFontAsset(filename, args[0]); 543 else static if (e == ".obj" || e == ".OBJ") 544 return addOBJAsset(filename); 545 else static if (e == ".iqm" || e == ".IQM") 546 return addIQMAsset(filename); 547 else static if (e == ".asset" || e == ".ASSET") 548 return addPackageAsset(filename); 549 else 550 static assert(0, "Failed to detect asset type at compile time, call addAsset explicitly"); 551 } 552 553 TextAsset addTextAsset(string filename, bool preload = false) 554 { 555 TextAsset text; 556 if (assetManager.assetExists(filename)) 557 text = cast(TextAsset)assetManager.getAsset(filename); 558 else 559 { 560 text = New!TextAsset(assetManager); 561 addAsset(text, filename, preload); 562 } 563 return text; 564 } 565 566 TextureAsset addTextureAsset(string filename, bool preload = false) 567 { 568 TextureAsset tex; 569 if (assetManager.assetExists(filename)) 570 tex = cast(TextureAsset)assetManager.getAsset(filename); 571 else 572 { 573 tex = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetManager); 574 addAsset(tex, filename, preload); 575 } 576 return tex; 577 } 578 579 ImageAsset addImageAsset(string filename, bool preload = false) 580 { 581 ImageAsset img; 582 if (assetManager.assetExists(filename)) 583 img = cast(ImageAsset)assetManager.getAsset(filename); 584 else 585 { 586 img = New!ImageAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetManager); 587 addAsset(img, filename, preload); 588 } 589 return img; 590 } 591 592 version(NoFreetype) 593 { 594 pragma(msg, "Warning: Dagon is compiled without Freetype support, Scene.addFontAsset is not available"); 595 } 596 else 597 { 598 FontAsset addFontAsset(string filename, uint height, bool preload = false) 599 { 600 FontAsset font; 601 if (assetManager.assetExists(filename)) 602 font = cast(FontAsset)assetManager.getAsset(filename); 603 else 604 { 605 font = New!FontAsset(height, assetManager); 606 addAsset(font, filename, preload); 607 } 608 return font; 609 } 610 } 611 612 OBJAsset addOBJAsset(string filename, bool preload = false) 613 { 614 OBJAsset obj; 615 if (assetManager.assetExists(filename)) 616 obj = cast(OBJAsset)assetManager.getAsset(filename); 617 else 618 { 619 obj = New!OBJAsset(assetManager); 620 addAsset(obj, filename, preload); 621 } 622 return obj; 623 } 624 625 IQMAsset addIQMAsset(string filename, bool preload = false) 626 { 627 IQMAsset iqm; 628 if (assetManager.assetExists(filename)) 629 iqm = cast(IQMAsset)assetManager.getAsset(filename); 630 else 631 { 632 iqm = New!IQMAsset(assetManager); 633 addAsset(iqm, filename, preload); 634 } 635 return iqm; 636 } 637 638 PackageAsset addPackageAsset(string filename, bool preload = false) 639 { 640 PackageAsset pa; 641 if (assetManager.assetExists(filename)) 642 pa = cast(PackageAsset)assetManager.getAsset(filename); 643 else 644 { 645 pa = New!PackageAsset(this, assetManager); 646 addAsset(pa, filename, preload); 647 } 648 return pa; 649 } 650 651 Entity createEntity2D(Entity parent = null) 652 { 653 Entity e; 654 if (parent) 655 e = New!Entity(parent); 656 else 657 { 658 e = New!Entity(eventManager, assetManager); 659 _entities2D.append(e); 660 661 sortEntities(_entities2D); 662 } 663 664 return e; 665 } 666 667 Entity createEntity3D(Entity parent = null) 668 { 669 Entity e; 670 if (parent) 671 e = New!Entity(parent); 672 else 673 { 674 e = New!Entity(eventManager, assetManager); 675 _entities3D.append(e); 676 677 sortEntities(_entities3D); 678 } 679 680 e.material = defaultMaterial3D; 681 682 return e; 683 } 684 685 Entity addEntity3D(Entity e) 686 { 687 _entities3D.append(e); 688 sortEntities(_entities3D); 689 return e; 690 } 691 692 Entity createDecal() 693 { 694 auto decal = New!Entity(eventManager, assetManager); 695 decals.append(decal); 696 decal.material = defaultDecalMaterial; 697 decal.drawable = decalShape; 698 return decal; 699 } 700 701 Material createDecalMaterial() 702 { 703 return New!Material(decalShader, assetManager); 704 } 705 706 void deleteEntity(Entity e) 707 { 708 if (!_entities3D.removeFirst(e)) 709 _entities2D.removeFirst(e); 710 assetManager.deleteOwnedObject(e); 711 } 712 713 Entity createSky(Material mat = null) 714 { 715 Material matSky; 716 if (mat is null) 717 matSky = defaultSkyMaterial; 718 else 719 matSky = mat; 720 721 auto eSky = createEntity3D(); 722 eSky.layer = 0; 723 eSky.attach = Attach.Camera; 724 eSky.castShadow = false; 725 eSky.material = matSky; 726 // TODO: use box instead of sphere 727 eSky.drawable = New!ShapeBox(Vector3f(1.0f, 1.0f, 1.0f), assetManager); 728 //New!ShapeSphere(1.0f, 16, 8, true, assetManager); 729 eSky.scaling = Vector3f(100.0f, 100.0f, 100.0f); 730 return eSky; 731 } 732 733 Material createMaterial(Shader shader) 734 { 735 auto m = New!Material(shader, assetManager); 736 if (shader !is standardShader) 737 m.customShader = true; 738 return m; 739 } 740 741 Material createMaterial() 742 { 743 return createMaterial(standardShader); 744 } 745 746 Material createParticleMaterial(Shader shader = null) 747 { 748 if (shader is null) 749 shader = particleShader; 750 return New!Material(shader, assetManager); 751 } 752 753 Material createTerrainMaterial(Shader shader = null) 754 { 755 if (shader is null) 756 shader = terrainShader; 757 return New!Material(shader, assetManager); 758 } 759 760 deprecated("use Scene.createLightSphere instead") LightSource createLight(Vector3f position, Color4f color, float energy, float volumeRadius, float areaRadius = 0.0f) 761 { 762 return createLightSphere(position, color, energy, volumeRadius, areaRadius); 763 } 764 765 LightSource createLightSphere(Vector3f position, Color4f color, float energy, float volumeRadius, float areaRadius) 766 { 767 auto light = lightManager.addPointLight(position, color, energy, volumeRadius, areaRadius); 768 light.type = LightType.AreaSphere; 769 return light; 770 } 771 772 LightSource createLightTube(Vector3f position, Color4f color, float energy, float volumeRadius, float tubeRadius, Quaternionf rotation, float tubeLength) 773 { 774 auto light = lightManager.addPointLight(position, color, energy, volumeRadius, tubeRadius); 775 light.type = LightType.AreaTube; 776 light.rotation = rotation; 777 light.tubeLength = tubeLength; 778 return light; 779 } 780 781 LightSource createLightSun(Quaternionf rotation, Color4f color, float energy) 782 { 783 return lightManager.addSunLight(rotation, color, energy); 784 } 785 786 LightSource createLightSpot(Vector3f position, Color4f color, float energy, Quaternionf rotation, float outerCutoff, float innerCutoff, float volumeRadius) 787 { 788 return lightManager.addSpotLight(position, color, energy, rotation, outerCutoff, innerCutoff, volumeRadius); 789 } 790 791 void mainSun(LightSource sun) @property 792 { 793 mainSunLight = sun; 794 } 795 796 LightSource mainSun() @property 797 { 798 return mainSunLight; 799 } 800 801 override void onRelease() 802 { 803 _entities3D.free(); 804 _entities2D.free(); 805 decals.free(); 806 } 807 808 // TODO: move to separate class 809 override void onLoading(float percentage) 810 { 811 RenderingContext rc2d; 812 rc2d.init(eventManager, environment); 813 rc2d.projectionMatrix = orthoMatrix(0.0f, eventManager.windowWidth, 0.0f, eventManager.windowHeight, 0.0f, 100.0f); 814 815 glEnable(GL_SCISSOR_TEST); 816 glScissor(0, 0, eventManager.windowWidth, eventManager.windowHeight); 817 glViewport(0, 0, eventManager.windowWidth, eventManager.windowHeight); 818 glClearColor(0, 0, 0, 1); 819 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 820 821 float maxWidth = eventManager.windowWidth * 0.33f; 822 float x = (eventManager.windowWidth - maxWidth) * 0.5f; 823 float y = eventManager.windowHeight * 0.5f - 10; 824 float w = percentage * maxWidth; 825 826 glDisable(GL_DEPTH_TEST); 827 mLoadingProgressBar.diffuse = Color4f(0.1, 0.1, 0.1, 1); 828 eLoadingProgressBar.position = Vector3f(x, y, 0); 829 eLoadingProgressBar.scaling = Vector3f(maxWidth, 10, 1); 830 eLoadingProgressBar.update(1.0/60.0); 831 eLoadingProgressBar.render(&rc2d); 832 833 mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1); 834 eLoadingProgressBar.scaling = Vector3f(w, 10, 1); 835 eLoadingProgressBar.update(1.0/60.0); 836 eLoadingProgressBar.render(&rc2d); 837 } 838 839 override void onStart() 840 { 841 } 842 843 void onLogicsUpdate(double dt) 844 { 845 } 846 847 void fixedStepUpdate(bool logicsUpdate = true) 848 { 849 if (logicsUpdate) 850 { 851 if (view) 852 { 853 view.update(fixedTimeStep); 854 view.prepareRC(&renderer.rc3d); 855 } 856 857 renderer.rc3d.time += fixedTimeStep; 858 renderer.rc2d.time += fixedTimeStep; 859 860 if (mainSunLight) 861 { 862 if (mainSunLight.type == LightType.Sun && 863 mainSunLight.shadow && mainSunLight.shadowMap) 864 standardShader.shadowMap = cast(CascadedShadowMap)mainSunLight.shadowMap; 865 866 mainSunLight.rotation = environment.sunRotation; 867 mainSunLight.color = environment.sunColor; 868 mainSunLight.energy = environment.sunEnergy; 869 } 870 } 871 872 foreach(e; _entities3D) 873 e.update(fixedTimeStep); 874 875 foreach(e; _entities2D) 876 e.update(fixedTimeStep); 877 878 foreach(e; decals) 879 e.update(fixedTimeStep); 880 881 if (logicsUpdate) 882 { 883 particleSystem.update(fixedTimeStep); 884 onLogicsUpdate(fixedTimeStep); 885 } 886 887 environment.update(fixedTimeStep); 888 889 if (view) 890 lightManager.updateShadows(view, &renderer.rc3d, fixedTimeStep); 891 892 eventManager.resetUpDown(); 893 } 894 895 override void onUpdate(double dt) 896 { 897 foreach(e; _entities3D) 898 e.processEvents(); 899 900 foreach(e; _entities2D) 901 e.processEvents(); 902 903 int updateCount = 0; 904 905 timer += dt; 906 while (timer >= fixedTimeStep) 907 { 908 if (updateCount < maxUpdatesPerFrame) 909 fixedStepUpdate(); 910 911 timer -= fixedTimeStep; 912 updateCount++; 913 } 914 } 915 916 override void onRender() 917 { 918 renderer.render(); 919 } 920 } 921 922 alias Scene BaseScene3D; 923 924 925 interface EntityGroup 926 { 927 int opApply(scope int delegate(Entity) dg); 928 } 929 930 class Entities3D: Owner, EntityGroup 931 { 932 Scene scene; 933 934 this(Scene scene, Owner o) 935 { 936 super(o); 937 this.scene = scene; 938 } 939 940 int opApply(scope int delegate(Entity) dg) 941 { 942 int res = 0; 943 for(size_t i = 0; i < scene._entities3D.data.length; i++) 944 { 945 auto e = scene._entities3D.data[i]; 946 947 res = traverseEntitiesTree(e, dg); 948 if (res) 949 break; 950 } 951 return res; 952 } 953 954 protected int traverseEntitiesTree(Entity e, scope int delegate(Entity) dg) 955 { 956 int res = 0; 957 for(size_t i = 0; i < e.children.data.length; i++) 958 { 959 auto c = e.children.data[i]; 960 961 res = traverseEntitiesTree(c, dg); 962 if (res) 963 break; 964 } 965 966 if (res == 0) 967 res = dg(e); 968 969 return res; 970 } 971 } 972 973 class Entities2D: Owner, EntityGroup 974 { 975 Scene scene; 976 977 this(Scene scene, Owner o) 978 { 979 super(o); 980 this.scene = scene; 981 } 982 983 int opApply(scope int delegate(Entity) dg) 984 { 985 int res = 0; 986 for(size_t i = 0; i < scene._entities2D.data.length; i++) 987 { 988 auto e = scene._entities2D.data[i]; 989 990 res = traverseEntitiesTree(e, dg); 991 if (res) 992 break; 993 } 994 return res; 995 } 996 997 protected int traverseEntitiesTree(Entity e, scope int delegate(Entity) dg) 998 { 999 int res = 0; 1000 for(size_t i = 0; i < e.children.data.length; i++) 1001 { 1002 auto c = e.children.data[i]; 1003 1004 res = traverseEntitiesTree(c, dg); 1005 if (res) 1006 break; 1007 } 1008 1009 if (res == 0) 1010 res = dg(e); 1011 1012 return res; 1013 } 1014 } 1015 1016 class Entities3DDynamic: Owner, EntityGroup 1017 { 1018 Scene scene; 1019 1020 this(Scene scene, Owner o) 1021 { 1022 super(o); 1023 this.scene = scene; 1024 } 1025 1026 int opApply(scope int delegate(Entity) dg) 1027 { 1028 int res = 0; 1029 for(size_t i = 0; i < scene._entities3D.data.length; i++) 1030 { 1031 auto e = scene._entities3D.data[i]; 1032 1033 res = traverseEntitiesTree(e, dg); 1034 if (res) 1035 break; 1036 } 1037 return res; 1038 } 1039 1040 protected int traverseEntitiesTree(Entity e, scope int delegate(Entity) dg) 1041 { 1042 int res = 0; 1043 1044 if (e.dynamic) 1045 { 1046 for(size_t i = 0; i < e.children.data.length; i++) 1047 { 1048 auto c = e.children.data[i]; 1049 1050 res = traverseEntitiesTree(c, dg); 1051 if (res) 1052 break; 1053 } 1054 1055 if (res == 0) 1056 res = dg(e); 1057 } 1058 1059 return res; 1060 } 1061 } 1062 1063 1064 class Entities3DStatic: Owner, EntityGroup 1065 { 1066 Scene scene; 1067 1068 this(Scene scene, Owner o) 1069 { 1070 super(o); 1071 this.scene = scene; 1072 } 1073 1074 int opApply(scope int delegate(Entity) dg) 1075 { 1076 int res = 0; 1077 for(size_t i = 0; i < scene._entities3D.data.length; i++) 1078 { 1079 auto e = scene._entities3D.data[i]; 1080 1081 res = traverseEntitiesTree(e, dg); 1082 if (res) 1083 break; 1084 } 1085 return res; 1086 } 1087 1088 protected int traverseEntitiesTree(Entity e, scope int delegate(Entity) dg) 1089 { 1090 int res = 0; 1091 1092 if (!e.dynamic) 1093 { 1094 for(size_t i = 0; i < e.children.data.length; i++) 1095 { 1096 auto c = e.children.data[i]; 1097 1098 res = traverseEntitiesTree(c, dg); 1099 if (res) 1100 break; 1101 } 1102 1103 if (res == 0) 1104 res = dg(e); 1105 } 1106 1107 return res; 1108 } 1109 }