1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.resource.scene;
29 
30 import std.stdio;
31 import std.math;
32 import std.algorithm;
33 
34 import dlib.core.memory;
35 
36 import dlib.container.array;
37 import dlib.container.dict;
38 import dlib.math.vector;
39 import dlib.math.matrix;
40 import dlib.math.transformation;
41 import dlib.image.color;
42 
43 import derelict.opengl;
44 
45 import dagon.core.ownership;
46 import dagon.core.event;
47 import dagon.core.application;
48 import dagon.resource.asset;
49 import dagon.resource.textasset;
50 import dagon.resource.textureasset;
51 import dagon.resource.fontasset;
52 import dagon.resource.obj;
53 import dagon.resource.iqm;
54 import dagon.resource.packageasset;
55 import dagon.graphics.environment;
56 import dagon.graphics.rc;
57 import dagon.graphics.view;
58 import dagon.graphics.shapes;
59 import dagon.graphics.clustered;
60 import dagon.graphics.shadow;
61 import dagon.graphics.texture;
62 import dagon.graphics.materials.generic;
63 import dagon.graphics.materials.standard;
64 import dagon.graphics.materials.sky;
65 import dagon.graphics.materials.hud;
66 import dagon.graphics.framebuffer;
67 import dagon.graphics.postproc;
68 import dagon.graphics.filters.fxaa;
69 import dagon.graphics.filters.lens;
70 import dagon.graphics.filters.hdrprepass;
71 import dagon.graphics.filters.hdr;
72 import dagon.graphics.filters.blur;
73 import dagon.graphics.filters.finalizer;
74 import dagon.logics.entity;
75 
76 class BaseScene: EventListener
77 {
78     SceneManager sceneManager;
79     AssetManager assetManager;
80     bool canRun = false;
81     bool releaseAtNextStep = false;
82     bool needToLoad = true;
83 
84     this(SceneManager smngr)
85     {
86         super(smngr.eventManager, null);
87         sceneManager = smngr;
88         assetManager = New!AssetManager(eventManager);
89     }
90 
91     ~this()
92     {
93         release();
94         Delete(assetManager);
95     }
96 
97     // Set preload to true if you want to load the asset immediately
98     // before actual loading (e.g., to render a loading screen)
99 
100     Asset addAsset(Asset asset, string filename, bool preload = false)
101     {
102         if (preload)
103             assetManager.preloadAsset(asset, filename);
104         else
105             assetManager.addAsset(asset, filename);
106         return asset;
107     }
108 
109     void onAssetsRequest()
110     {
111         // Add your assets here
112     }
113 
114     void onLoading(float percentage)
115     {
116         // Render your loading screen here
117     }
118 
119     void onAllocate()
120     {
121         // Allocate your objects here
122     }
123 
124     void onRelease()
125     {
126         // Release your objects here
127     }
128 
129     void onStart()
130     {
131         // Do your (re)initialization here
132     }
133 
134     void onEnd()
135     {
136         // Do your finalization here
137     }
138 
139     void onUpdate(double dt)
140     {
141         // Do your animation and logics here
142     }
143 
144     void onRender()
145     {
146         // Do your rendering here
147     }
148 
149     void exitApplication()
150     {
151         generateUserEvent(DagonEvent.Exit);
152     }
153 
154     void load()
155     {
156         if (needToLoad)
157         {
158             onAssetsRequest();
159             float p = assetManager.nextLoadingPercentage;
160 
161             assetManager.loadThreadSafePart();
162 
163             while(assetManager.isLoading)
164             {
165                 sceneManager.application.beginRender();
166                 onLoading(p);
167                 sceneManager.application.endRender();
168                 p = assetManager.nextLoadingPercentage;
169             }
170 
171             bool loaded = assetManager.loadThreadUnsafePart();
172             
173             if (loaded)
174             {
175                 onAllocate();
176                 canRun = true;
177                 needToLoad = false;
178             }
179             else
180             {
181                 writeln("Exiting due to error while loading assets");
182                 canRun = false;
183                 eventManager.running = false;
184             }
185         }
186         else
187         {
188             canRun = true;
189         }
190     }
191 
192     void release()
193     {
194         onRelease();
195         clearOwnedObjects();
196         assetManager.releaseAssets();
197         needToLoad = true;
198         canRun = false;
199     }
200 
201     void start()
202     {
203         if (canRun)
204             onStart();
205     }
206 
207     void end()
208     {
209         if (canRun)
210             onEnd();
211     }
212 
213     void update(double dt)
214     {
215         if (canRun)
216         {
217             processEvents();
218             assetManager.updateMonitor(dt);
219             onUpdate(dt);
220         }
221 
222         if (releaseAtNextStep)
223         {
224             end();
225             release();
226 
227             releaseAtNextStep = false;
228             canRun = false;
229         }
230     }
231 
232     void render()
233     {
234         if (canRun)
235             onRender();
236     }
237 }
238 
239 class SceneManager: Owner
240 {
241     SceneApplication application;
242     Dict!(BaseScene, string) scenesByName;
243     EventManager eventManager;
244     BaseScene currentScene;
245 
246     this(EventManager emngr, SceneApplication app)
247     {
248         super(app);
249         application = app;
250         eventManager = emngr;
251         scenesByName = New!(Dict!(BaseScene, string));
252     }
253 
254     ~this()
255     {
256         foreach(i, s; scenesByName)
257         {
258             Delete(s);
259         }
260         Delete(scenesByName);
261     }
262 
263     BaseScene addScene(BaseScene scene, string name)
264     {
265         scenesByName[name] = scene;
266         return scene;
267     }
268 
269     void removeScene(string name)
270     {
271         Delete(scenesByName[name]);
272         scenesByName.remove(name);
273     }
274 
275     void goToScene(string name, bool releaseCurrent = true)
276     {
277         if (currentScene && releaseCurrent)
278         {
279             currentScene.releaseAtNextStep = true;
280         }
281 
282         BaseScene scene = scenesByName[name];
283         
284         writefln("Loading scene \"%s\"", name);
285         
286         scene.load();
287         currentScene = scene;
288         currentScene.start();
289         
290         writefln("Running...", name);
291     }
292 
293     void update(double dt)
294     {
295         if (currentScene)
296         {
297             currentScene.update(dt);
298         }
299     }
300 
301     void render()
302     {
303         if (currentScene)
304         {
305             currentScene.render();
306         }
307     } 
308 }
309 
310 class SceneApplication: Application
311 {
312     SceneManager sceneManager;
313 
314     this(uint w, uint h, bool fullscreen, string windowTitle, string[] args)
315     {
316         super(w, h, fullscreen, windowTitle, args);
317 
318         sceneManager = New!SceneManager(eventManager, this);
319     }
320     
321     override void onUpdate(double dt)
322     {
323         sceneManager.update(dt);
324     }
325     
326     override void onRender()
327     {
328         sceneManager.render();
329     }
330 }
331 
332 class Scene: BaseScene
333 {
334     Environment environment;
335     
336     ClusteredLightManager lightManager;
337     CascadedShadowMap shadowMap;
338 
339     StandardBackend defaultMaterialBackend;
340     GenericMaterial defaultMaterial3D;
341     
342     SkyBackend skyMaterialBackend;
343 
344     RenderingContext rc3d; 
345     RenderingContext rc2d; 
346     View view;
347     
348     Framebuffer sceneFramebuffer;
349     PostFilterHDR hdrFilter;
350     
351     Framebuffer hdrPrepassFramebuffer;
352     PostFilterHDRPrepass hdrPrepassFilter;
353     
354     Framebuffer hblurredFramebuffer;
355     Framebuffer vblurredFramebuffer;
356     PostFilterBlur hblur;
357     PostFilterBlur vblur;
358     
359     PostFilterFXAA fxaaFilter;
360     PostFilterLensDistortion lensFilter;
361     
362     PostFilterFinalizer finalizerFilter;
363     
364     struct HDRSettings
365     {
366         BaseScene3D scene;
367         
368         void tonemapper(Tonemapper f) @property
369         {
370             scene.hdrFilter.tonemapFunction = f;
371         }
372         
373         Tonemapper tonemapper() @property
374         {
375             return scene.hdrFilter.tonemapFunction;
376         }
377         
378         
379         void minLuminance(float l) @property
380         {
381             scene.hdrFilter.minLuminance = l;
382         }
383         
384         float minLuminance() @property
385         {
386             return scene.hdrFilter.minLuminance;
387         }
388         
389         
390         void maxLuminance(float l) @property
391         {
392             scene.hdrFilter.maxLuminance = l;
393         }
394         
395         float maxLuminance() @property
396         {
397             return scene.hdrFilter.maxLuminance;
398         }
399         
400         
401         void keyValue(float k) @property
402         {
403             scene.hdrFilter.keyValue = k;
404         }
405         
406         float keyValue() @property
407         {
408             return scene.hdrFilter.keyValue;
409         }
410         
411         
412         void adaptationSpeed(float s) @property
413         {
414             scene.hdrFilter.adaptationSpeed = s;
415         }
416         
417         float adaptationSpeed() @property
418         {
419             return scene.hdrFilter.adaptationSpeed;
420         }
421     }
422 
423     struct GlowSettings
424     {
425         BaseScene3D scene;
426         uint radius;
427     
428         void enabled(bool mode) @property
429         {
430             scene.hblur.enabled = mode;
431             scene.vblur.enabled = mode;
432             scene.hdrPrepassFilter.glowEnabled = mode;
433         }
434         
435         bool enabled() @property
436         {
437             return scene.hdrPrepassFilter.glowEnabled;
438         }
439         
440         
441         void brightness(float b) @property
442         {
443             scene.hdrPrepassFilter.glowBrightness = b;
444         }
445         
446         float brightness() @property
447         {
448             return scene.hdrPrepassFilter.glowBrightness;
449         }
450     }
451     
452     struct MotionBlurSettings
453     {
454         BaseScene3D scene;
455         
456         void enabled(bool mode) @property
457         {
458             scene.hdrFilter.mblurEnabled = mode;
459         }
460         
461         bool enabled() @property
462         {
463             return scene.hdrFilter.mblurEnabled;
464         }
465         
466         
467         void samples(uint s) @property
468         {
469             scene.hdrFilter.motionBlurSamples = s;
470         }
471         
472         uint samples() @property
473         {
474             return scene.hdrFilter.motionBlurSamples;
475         }
476         
477         
478         void shutterSpeed(float s) @property
479         {
480             scene.hdrFilter.shutterSpeed = s;
481             scene.hdrFilter.shutterFps = 1.0 / s;
482         }
483         
484         float shutterSpeed() @property
485         {
486             return scene.hdrFilter.shutterSpeed;
487         }
488     }
489     
490     struct LUTSettings
491     {
492         BaseScene3D scene;
493         
494         void texture(Texture tex) @property
495         {
496             scene.hdrFilter.colorTable = tex;
497         }
498         
499         Texture texture() @property
500         {
501             return scene.hdrFilter.colorTable;
502         }
503     }
504     
505     struct VignetteSettings
506     {
507         BaseScene3D scene;
508         
509         void texture(Texture tex) @property
510         {
511             scene.hdrFilter.vignette = tex;
512         }
513         
514         Texture texture() @property
515         {
516             return scene.hdrFilter.vignette;
517         }
518     }
519     
520     struct AASettings
521     {
522         BaseScene3D scene;
523         
524         void enabled(bool mode) @property
525         {
526             scene.fxaaFilter.enabled = mode;
527         }
528         
529         bool enabled() @property
530         {
531             return scene.fxaaFilter.enabled;
532         }
533     }
534     
535     struct LensSettings
536     {
537         BaseScene3D scene;
538         
539         void enabled(bool mode) @property
540         {
541             scene.lensFilter.enabled = mode;
542         }
543         
544         bool enabled() @property
545         {
546             return scene.lensFilter.enabled;
547         }
548         
549         void scale(float s) @property
550         {
551             scene.lensFilter.scale = s;
552         }
553         
554         float scale() @property
555         {
556             return scene.lensFilter.scale;
557         }
558         
559         
560         void dispersion(float d) @property
561         {
562             scene.lensFilter.dispersion = d;
563         }
564         
565         float dispersion() @property
566         {
567             return scene.lensFilter.dispersion;
568         }
569     }
570     
571     HDRSettings hdr;
572     MotionBlurSettings motionBlur;
573     GlowSettings glow;
574     LUTSettings lut;
575     VignetteSettings vignette;
576     AASettings antiAliasing;
577     LensSettings lensDistortion;
578     
579     DynamicArray!PostFilter postFilters;
580 
581     DynamicArray!Entity entities3D;
582     DynamicArray!Entity entities2D;
583     
584     ShapeQuad loadingProgressBar;
585     Entity eLoadingProgressBar;
586     HUDMaterialBackend hudMaterialBackend;
587     GenericMaterial mLoadingProgressBar;
588 
589     double timer = 0.0;
590     double fixedTimeStep = 1.0 / 60.0;
591 
592     this(SceneManager smngr)
593     {
594         super(smngr);
595         
596         rc3d.init(eventManager, environment);
597         rc3d.projectionMatrix = perspectiveMatrix(60.0f, eventManager.aspectRatio, 0.1f, 1000.0f);
598 
599         rc2d.init(eventManager, environment);
600         rc2d.projectionMatrix = orthoMatrix(0.0f, eventManager.windowWidth, 0.0f, eventManager.windowHeight, 0.0f, 100.0f);
601 
602         loadingProgressBar = New!ShapeQuad(assetManager);
603         eLoadingProgressBar = New!Entity(eventManager, assetManager);
604         eLoadingProgressBar.drawable = loadingProgressBar;
605         hudMaterialBackend = New!HUDMaterialBackend(assetManager);
606         mLoadingProgressBar = createMaterial(hudMaterialBackend);
607         mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1);
608         eLoadingProgressBar.material = mLoadingProgressBar;
609     }
610     
611     void sortEntities(ref DynamicArray!Entity entities)
612     {
613         size_t j = 0;
614         Entity tmp;
615 
616         auto edata = entities.data;
617 
618         foreach(i, v; edata)
619         {
620             j = i;
621             size_t k = i;
622 
623             while (k < edata.length)
624             {
625                 float b1 = edata[j].layer;
626                 float b2 = edata[k].layer;
627                 
628                 if (b2 < b1)
629                     j = k;
630                 
631                 k++;
632             }
633 
634             tmp = edata[i];
635             edata[i] = edata[j];
636             edata[j] = tmp;
637 
638             sortEntities(v.children);
639         }
640     }
641     
642     TextAsset addTextAsset(string filename, bool preload = false)
643     {
644         TextAsset text;
645         if (assetManager.assetExists(filename))
646             text = cast(TextAsset)assetManager.getAsset(filename);
647         else
648         {
649             text = New!TextAsset(assetManager);
650             addAsset(text, filename, preload);
651         }
652         return text;
653     }
654 
655     TextureAsset addTextureAsset(string filename, bool preload = false)
656     {
657         TextureAsset tex;
658         if (assetManager.assetExists(filename))
659             tex = cast(TextureAsset)assetManager.getAsset(filename);
660         else
661         {
662             tex = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetManager);
663             addAsset(tex, filename, preload);
664         }
665         return tex;
666     }
667 
668     FontAsset addFontAsset(string filename, uint height, bool preload = false)
669     {
670         FontAsset font;
671         if (assetManager.assetExists(filename))
672             font = cast(FontAsset)assetManager.getAsset(filename);
673         else
674         {
675             font = New!FontAsset(height, assetManager);
676             addAsset(font, filename, preload);
677         }
678         return font;
679     }
680     
681     OBJAsset addOBJAsset(string filename, bool preload = false)
682     {
683         OBJAsset obj;
684         if (assetManager.assetExists(filename))
685             obj = cast(OBJAsset)assetManager.getAsset(filename);
686         else
687         {
688             obj = New!OBJAsset(assetManager);
689             addAsset(obj, filename, preload);
690         }
691         return obj;
692     }
693     
694     IQMAsset addIQMAsset(string filename, bool preload = false)
695     {
696         IQMAsset iqm;
697         if (assetManager.assetExists(filename))
698             iqm = cast(IQMAsset)assetManager.getAsset(filename);
699         else
700         {
701             iqm = New!IQMAsset(assetManager);
702             addAsset(iqm, filename, preload);
703         }
704         return iqm;
705     }
706     
707     PackageAsset addPackageAsset(string filename, bool preload = false)
708     {
709         PackageAsset pa;
710         if (assetManager.assetExists(filename))
711             pa = cast(PackageAsset)assetManager.getAsset(filename);
712         else
713         {
714             pa = New!PackageAsset(this, assetManager);
715             addAsset(pa, filename, preload);
716         }
717         return pa;
718     }
719 
720     Entity createEntity2D(Entity parent = null)
721     {
722         Entity e;
723         if (parent)
724             e = New!Entity(parent);
725         else
726         {
727             e = New!Entity(eventManager, assetManager);
728             entities2D.append(e);
729             
730             sortEntities(entities2D);
731         }
732         
733         return e;
734     }
735     
736     Entity createEntity3D(Entity parent = null)
737     {
738         Entity e;
739         if (parent)
740             e = New!Entity(parent);
741         else
742         {
743             e = New!Entity(eventManager, assetManager);
744             entities3D.append(e);
745             
746             sortEntities(entities3D);
747         }
748         
749         e.material = defaultMaterial3D;
750         
751         return e;
752     }
753     
754     Entity addEntity3D(Entity e)
755     {
756         entities3D.append(e); 
757         sortEntities(entities3D);
758         return e;
759     }
760     
761     Entity createSky()
762     {
763         auto matSky = createMaterial(skyMaterialBackend);
764         matSky.depthWrite = false;
765     
766         auto eSky = createEntity3D();
767         eSky.layer = 0;
768         eSky.attach = Attach.Camera;
769         eSky.castShadow = false;
770         eSky.material = matSky;
771         eSky.drawable = New!ShapeSphere(1.0f, 16, 8, true, assetManager); //aSphere.mesh;
772         eSky.scaling = Vector3f(100.0f, 100.0f, 100.0f);
773         sortEntities(entities3D);
774         return eSky;
775     }
776     
777     GenericMaterial createMaterial(GenericMaterialBackend backend = null)
778     {
779         if (backend is null)
780             backend = defaultMaterialBackend;
781         return New!GenericMaterial(backend, assetManager);
782     }
783     
784     LightSource createLight(Vector3f position, Color4f color, float energy, float volumeRadius, float areaRadius = 0.0f)
785     {
786         return lightManager.addLight(position, color, energy, volumeRadius, areaRadius);
787     }
788     
789     override void onAllocate()
790     {    
791         environment = New!Environment(assetManager);
792         
793         lightManager = New!ClusteredLightManager(200.0f, 100, assetManager);
794         defaultMaterialBackend = New!StandardBackend(lightManager, assetManager);
795         
796         skyMaterialBackend = New!SkyBackend(assetManager);
797         
798         shadowMap = New!CascadedShadowMap(1024, this, 10, 30, 200, -100, 100, assetManager);
799         defaultMaterialBackend.shadowMap = shadowMap;
800         
801         defaultMaterial3D = createMaterial();
802         
803         sceneFramebuffer = New!Framebuffer(eventManager.windowWidth, eventManager.windowHeight, true, true, assetManager);
804         
805         hdr.scene = this;
806         motionBlur.scene = this;
807         glow.scene = this;
808         glow.radius = 3;
809         lut.scene = this;
810         vignette.scene = this;
811         antiAliasing.scene = this;
812         lensDistortion.scene = this;
813         
814         hblurredFramebuffer = New!Framebuffer(eventManager.windowWidth / 2, eventManager.windowHeight / 2, true, false, assetManager);
815         hblur = New!PostFilterBlur(true, sceneFramebuffer, hblurredFramebuffer, assetManager);
816         
817         vblurredFramebuffer = New!Framebuffer(eventManager.windowWidth / 2, eventManager.windowHeight / 2, true, false, assetManager);
818         vblur = New!PostFilterBlur(false, hblurredFramebuffer, vblurredFramebuffer, assetManager);
819         
820         hdrPrepassFramebuffer = New!Framebuffer(eventManager.windowWidth, eventManager.windowHeight, true, false, assetManager);
821         hdrPrepassFilter = New!PostFilterHDRPrepass(sceneFramebuffer, hdrPrepassFramebuffer, assetManager);
822         hdrPrepassFilter.blurredTexture = vblurredFramebuffer.colorTexture;
823         postFilters.append(hdrPrepassFilter);
824         
825         hdrFilter = New!PostFilterHDR(hdrPrepassFramebuffer, null, assetManager);
826         hdrFilter.velocityTexture = sceneFramebuffer.velocityTexture;
827         postFilters.append(hdrFilter);
828         
829         fxaaFilter = New!PostFilterFXAA(null, null, assetManager);
830         postFilters.append(fxaaFilter);
831         fxaaFilter.enabled = false;
832         
833         lensFilter = New!PostFilterLensDistortion(null, null, assetManager);
834         postFilters.append(lensFilter);
835         lensFilter.enabled = false;
836         
837         finalizerFilter = New!PostFilterFinalizer(null, null, assetManager);
838     }
839     
840     PostFilter addFilter(PostFilter f)
841     {
842         postFilters.append(f);
843         return f;
844     }
845     
846     override void onRelease()
847     {
848         entities3D.free();
849         entities2D.free();
850         
851         postFilters.free();
852     }
853     
854     override void onLoading(float percentage)
855     {
856         glEnable(GL_SCISSOR_TEST);
857         glScissor(0, 0, eventManager.windowWidth, eventManager.windowHeight);
858         glViewport(0, 0, eventManager.windowWidth, eventManager.windowHeight);
859         glClearColor(0, 0, 0, 1);
860         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
861         
862         float maxWidth = eventManager.windowWidth * 0.33f;
863         float x = (eventManager.windowWidth - maxWidth) * 0.5f;
864         float y = eventManager.windowHeight * 0.5f - 10;
865         float w = percentage * maxWidth;
866         
867         glDisable(GL_DEPTH_TEST);
868         mLoadingProgressBar.diffuse = Color4f(0.1, 0.1, 0.1, 1);
869         eLoadingProgressBar.position = Vector3f(x, y, 0);
870         eLoadingProgressBar.scaling = Vector3f(maxWidth, 10, 1);
871         eLoadingProgressBar.update(1.0/60.0);
872         eLoadingProgressBar.render(&rc2d);
873         
874         mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1);
875         eLoadingProgressBar.scaling = Vector3f(w, 10, 1);
876         eLoadingProgressBar.update(1.0/60.0);
877         eLoadingProgressBar.render(&rc2d);
878     }
879 
880     override void onStart()
881     {
882         rc3d.initPerspective(eventManager, environment, 60.0f, 0.1f, 1000.0f);
883         rc2d.initOrtho(eventManager, environment, 0.0f, 100.0f);
884 
885         timer = 0.0;
886     }
887 
888     void onLogicsUpdate(double dt)
889     {
890     }
891 
892     override void onUpdate(double dt)
893     {
894         foreach(e; entities3D)
895             e.processEvents();
896 
897         foreach(e; entities2D)
898             e.processEvents();
899 
900         timer += dt;
901         if (timer >= fixedTimeStep)
902         {
903             timer -= fixedTimeStep;
904 
905             if (view)
906             {
907                 view.update(fixedTimeStep);
908                 view.prepareRC(&rc3d);
909             }
910             
911             rc3d.time += fixedTimeStep;
912             rc2d.time += fixedTimeStep;
913 
914             foreach(e; entities3D)
915                 e.update(fixedTimeStep);
916 
917             foreach(e; entities2D)
918                 e.update(fixedTimeStep);
919                 
920             onLogicsUpdate(fixedTimeStep);
921             
922             environment.update(fixedTimeStep);
923             
924             if (view) // TODO: allow to turn this off
925             {
926                 Vector3f cameraDirection = -view.invViewMatrix.forward;
927                 cameraDirection.y = 0.0f;
928                 cameraDirection = cameraDirection.normalized;
929                 
930                 shadowMap.area1.position = view.cameraPosition + cameraDirection * (shadowMap.projSize1  * 0.5f - 1.0f);
931                 shadowMap.area2.position = view.cameraPosition + cameraDirection * shadowMap.projSize2 * 0.5f;
932                 shadowMap.area3.position = view.cameraPosition + cameraDirection * shadowMap.projSize3 * 0.5f;
933             }
934             
935             shadowMap.update(&rc3d, fixedTimeStep);
936             
937             lightManager.update(&rc3d);
938         }
939     }
940     
941     void renderShadows(RenderingContext* rc)
942     {
943         shadowMap.render(rc);
944     }
945 
946     void renderEntities3D(RenderingContext* rc)
947     {
948         glEnable(GL_DEPTH_TEST);
949         foreach(e; entities3D)
950             e.render(rc);
951     }
952 
953     void renderEntities2D(RenderingContext* rc)
954     {
955         glDisable(GL_DEPTH_TEST);
956         foreach(e; entities2D)
957             e.render(rc);
958     }
959     
960     void prepareViewport(Framebuffer b = null)
961     {
962         glEnable(GL_SCISSOR_TEST);
963         if (b)
964         {
965             glScissor(0, 0, b.width, b.height);
966             glViewport(0, 0, b.width, b.height);
967         }
968         else
969         {
970             glScissor(0, 0, eventManager.windowWidth, eventManager.windowHeight);
971             glViewport(0, 0, eventManager.windowWidth, eventManager.windowHeight);
972         }
973         if (environment)
974             glClearColor(environment.backgroundColor.r, environment.backgroundColor.g, environment.backgroundColor.b, environment.backgroundColor.a);
975     }
976     
977     void renderBlur(uint iterations)
978     {
979         RenderingContext rcTmp;
980         
981         foreach(i; 1..iterations+1)
982         {
983             hblur.outputBuffer.bind();
984             rcTmp.initOrtho(eventManager, environment, hblur.outputBuffer.width, hblur.outputBuffer.height, 0.0f, 100.0f);
985             prepareViewport(hblur.outputBuffer);
986             hblur.radius = i;
987             hblur.render(&rcTmp);
988             hblur.outputBuffer.unbind();
989             
990             vblur.outputBuffer.bind();
991             rcTmp.initOrtho(eventManager, environment, vblur.outputBuffer.width, vblur.outputBuffer.height, 0.0f, 100.0f);
992             prepareViewport(vblur.outputBuffer);
993             vblur.radius = i;
994             vblur.render(&rcTmp);
995             vblur.outputBuffer.unbind();
996             
997             hblur.inputBuffer = vblur.outputBuffer;
998         }
999         
1000         hblur.inputBuffer = sceneFramebuffer;
1001     }
1002 
1003     override void onRender()
1004     {
1005         renderShadows(&rc3d);
1006 
1007         sceneFramebuffer.bind();
1008         prepareViewport();        
1009         sceneFramebuffer.clearBuffers();       
1010         renderEntities3D(&rc3d);
1011         sceneFramebuffer.unbind();
1012 
1013         sceneFramebuffer.genLuminanceMipmaps();
1014         float lum = sceneFramebuffer.averageLuminance();
1015         if (!isNaN(lum))
1016         {
1017             float newExposure = hdrFilter.keyValue * (1.0f / clamp(lum, hdrFilter.minLuminance, hdrFilter.maxLuminance));
1018             
1019             float exposureDelta = newExposure - hdrFilter.exposure;
1020             hdrFilter.exposure += exposureDelta * hdrFilter.adaptationSpeed * eventManager.deltaTime;
1021         }
1022         
1023         if (hdrPrepassFilter.glowEnabled)
1024             renderBlur(glow.radius);
1025         
1026         RenderingContext rcTmp;
1027         Framebuffer nextInput = sceneFramebuffer;
1028         
1029         hdrPrepassFilter.perspectiveMatrix = rc3d.projectionMatrix;
1030         
1031         foreach(i, f; postFilters.data)
1032         if (f.enabled)
1033         {
1034             if (f.outputBuffer is null)
1035                 f.outputBuffer = New!Framebuffer(eventManager.windowWidth, eventManager.windowHeight, false, false, assetManager);
1036                 
1037             if (f.inputBuffer is null)
1038                 f.inputBuffer = nextInput;
1039                 
1040             nextInput = f.outputBuffer;
1041             
1042             f.outputBuffer.bind();
1043             rcTmp.initOrtho(eventManager, environment, f.outputBuffer.width, f.outputBuffer.height, 0.0f, 100.0f);
1044             prepareViewport(f.outputBuffer);
1045             f.render(&rcTmp);
1046             f.outputBuffer.unbind();
1047         }
1048         
1049         prepareViewport();
1050         finalizerFilter.inputBuffer = nextInput;
1051         finalizerFilter.render(&rc2d);
1052 
1053         renderEntities2D(&rc2d);
1054     }
1055 }
1056 
1057 alias Scene BaseScene3D;