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 }