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 import std.traits;
34 
35 import dlib.core.memory;
36 
37 import dlib.container.array;
38 import dlib.container.dict;
39 import dlib.math.vector;
40 import dlib.math.matrix;
41 import dlib.math.transformation;
42 import dlib.image.color;
43 import dlib.image.image;
44 import dlib.image.unmanaged;
45 import dlib.image.io.png;
46 
47 import dagon.core.libs;
48 import dagon.core.ownership;
49 import dagon.core.event;
50 import dagon.core.application;
51 
52 import dagon.resource.asset;
53 import dagon.resource.textasset;
54 import dagon.resource.textureasset;
55 import dagon.resource.fontasset;
56 import dagon.resource.obj;
57 import dagon.resource.iqm;
58 import dagon.resource.packageasset;
59 
60 import dagon.graphics.environment;
61 import dagon.graphics.rc;
62 import dagon.graphics.view;
63 import dagon.graphics.shapes;
64 import dagon.graphics.light;
65 import dagon.graphics.shadow;
66 import dagon.graphics.texture;
67 import dagon.graphics.particles;
68 import dagon.graphics.framebuffer;
69 import dagon.graphics.renderer;
70 import dagon.graphics.postproc;
71 
72 import dagon.graphics.material;
73 
74 import dagon.graphics.shader;
75 import dagon.graphics.shaders.standard;
76 import dagon.graphics.shaders.sky;
77 import dagon.graphics.shaders.particle;
78 import dagon.graphics.shaders.hud;
79 
80 import dagon.graphics.filters.fxaa;
81 import dagon.graphics.filters.lens;
82 import dagon.graphics.filters.hdrprepass;
83 import dagon.graphics.filters.hdr;
84 import dagon.graphics.filters.blur;
85 import dagon.graphics.filters.finalizer;
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     // Set preload to true if you want to load the asset immediately
111     // before actual loading (e.g., to render a loading screen)
112     Asset addAsset(Asset asset, string filename, bool preload = false)
113     {
114         if (preload)
115             assetManager.preloadAsset(asset, filename);
116         else
117             assetManager.addAsset(asset, filename);
118         return asset;
119     }
120 
121     void onAssetsRequest()
122     {
123         // Add your assets here
124     }
125 
126     void onLoading(float percentage)
127     {
128         // Render your loading screen here
129     }
130 
131     void onAllocate()
132     {
133         // Allocate your objects here
134     }
135 
136     void onRelease()
137     {
138         // Release your objects here
139     }
140 
141     void onStart()
142     {
143         // Do your (re)initialization here
144     }
145 
146     void onEnd()
147     {
148         // Do your finalization here
149     }
150 
151     void onUpdate(double dt)
152     {
153         // Do your animation and logics here
154     }
155 
156     void onRender()
157     {
158         // Do your rendering here
159     }
160 
161     void exitApplication()
162     {
163         generateUserEvent(DagonEvent.Exit);
164     }
165 
166     void load()
167     {
168         if (needToLoad)
169         {
170             onAssetsRequest();
171             float p = assetManager.nextLoadingPercentage;
172 
173             assetManager.loadThreadSafePart();
174 
175             while(assetManager.isLoading)
176             {
177                 sceneManager.application.beginRender();
178                 onLoading(p);
179                 sceneManager.application.endRender();
180                 p = assetManager.nextLoadingPercentage;
181             }
182 
183             bool loaded = assetManager.loadThreadUnsafePart();
184 
185             if (loaded)
186             {
187                 onAllocate();
188                 canRun = true;
189                 needToLoad = false;
190             }
191             else
192             {
193                 writeln("Exiting due to error while loading assets");
194                 canRun = false;
195                 eventManager.running = false;
196             }
197         }
198         else
199         {
200             canRun = true;
201         }
202     }
203 
204     void release()
205     {
206         onRelease();
207         clearOwnedObjects();
208         assetManager.releaseAssets();
209         needToLoad = true;
210         canRun = false;
211     }
212 
213     void start()
214     {
215         if (canRun)
216             onStart();
217     }
218 
219     void end()
220     {
221         if (canRun)
222             onEnd();
223     }
224 
225     void update(double dt)
226     {
227         if (canRun)
228         {
229             processEvents();
230             assetManager.updateMonitor(dt);
231             onUpdate(dt);
232         }
233 
234         if (releaseAtNextStep)
235         {
236             end();
237             release();
238 
239             releaseAtNextStep = false;
240             canRun = false;
241         }
242     }
243 
244     void render()
245     {
246         if (canRun)
247             onRender();
248     }
249 }
250 
251 class SceneManager: Owner
252 {
253     SceneApplication application;
254     Dict!(BaseScene, string) scenesByName;
255     EventManager eventManager;
256     BaseScene currentScene;
257 
258     this(EventManager emngr, SceneApplication app)
259     {
260         super(app);
261         application = app;
262         eventManager = emngr;
263         scenesByName = New!(Dict!(BaseScene, string));
264     }
265 
266     ~this()
267     {
268         foreach(i, s; scenesByName)
269         {
270             Delete(s);
271         }
272         Delete(scenesByName);
273     }
274 
275     BaseScene addScene(BaseScene scene, string name)
276     {
277         scenesByName[name] = scene;
278         return scene;
279     }
280 
281     void removeScene(string name)
282     {
283         Delete(scenesByName[name]);
284         scenesByName.remove(name);
285     }
286 
287     void goToScene(string name, bool releaseCurrent = true)
288     {
289         if (currentScene && releaseCurrent)
290         {
291             currentScene.releaseAtNextStep = true;
292         }
293 
294         BaseScene scene = scenesByName[name];
295 
296         writefln("Loading scene \"%s\"", name);
297 
298         scene.load();
299         currentScene = scene;
300         currentScene.start();
301 
302         writefln("Running...", name);
303     }
304 
305     void update(double dt)
306     {
307         if (currentScene)
308         {
309             currentScene.update(dt);
310         }
311     }
312 
313     void render()
314     {
315         if (currentScene)
316         {
317             currentScene.render();
318         }
319     }
320 }
321 
322 class SceneApplication: Application
323 {
324     SceneManager sceneManager;
325     UnmanagedImageFactory imageFactory;
326     SuperImage screenshotBuffer1;
327     SuperImage screenshotBuffer2;
328 
329     this(uint w, uint h, bool fullscreen, string windowTitle, string[] args)
330     {
331         super(w, h, fullscreen, windowTitle, args);
332 
333         sceneManager = New!SceneManager(eventManager, this);
334 
335         imageFactory = New!UnmanagedImageFactory();
336         screenshotBuffer1 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8);
337         screenshotBuffer2 = imageFactory.createImage(eventManager.windowWidth, eventManager.windowHeight, 3, 8);
338     }
339 
340     ~this()
341     {
342         Delete(imageFactory);
343         Delete(screenshotBuffer1);
344         Delete(screenshotBuffer2);
345     }
346 
347     override void onUpdate(double dt)
348     {
349         sceneManager.update(dt);
350     }
351 
352     override void onRender()
353     {
354         sceneManager.render();
355     }
356 
357     void saveScreenshot(string filename)
358     {
359         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
360         glReadPixels(0, 0, eventManager.windowWidth, eventManager.windowHeight, GL_RGB, GL_UNSIGNED_BYTE, screenshotBuffer1.data.ptr);
361         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
362 
363         foreach(y; 0..screenshotBuffer1.height)
364         foreach(x; 0..screenshotBuffer1.width)
365         {
366             screenshotBuffer2[x, y] = screenshotBuffer1[x, screenshotBuffer1.height - y];
367         }
368 
369         screenshotBuffer2.savePNG(filename);
370     }
371 }
372 
373 // TODO: Renderer class
374 class Scene: BaseScene
375 {
376     Renderer renderer;
377 
378     Environment environment;
379 
380     LightManager lightManager;
381     ParticleSystem particleSystem;
382 
383 	StandardShader standardShader;
384     SkyShader skyShader;
385     ParticleShader particleShader;
386     Material defaultMaterial3D;
387 
388     RenderingContext rc3d;
389     RenderingContext rc2d;
390     View view;
391 
392     PostFilterHDR hdrFilter;
393 
394     Framebuffer hdrPrepassFramebuffer;
395     PostFilterHDRPrepass hdrPrepassFilter;
396 
397     Framebuffer hblurredFramebuffer;
398     Framebuffer vblurredFramebuffer;
399     PostFilterBlur hblur;
400     PostFilterBlur vblur;
401 
402     PostFilterFXAA fxaaFilter;
403     PostFilterLensDistortion lensFilter;
404 
405     PostFilterFinalizer finalizerFilter;
406 
407 	//TODO: move post-processing to a separate class
408     struct SSAOSettings
409     {
410         BaseScene3D scene;
411 
412         void enabled(bool mode) @property
413         {
414             scene.renderer.deferredEnvPass.shader.enableSSAO = mode;
415         }
416 
417         bool enabled() @property
418         {
419             return scene.renderer.deferredEnvPass.shader.enableSSAO;
420         }
421 
422         void samples(int s) @property
423         {
424             scene.renderer.deferredEnvPass.shader.ssaoSamples = s;
425         }
426 
427         int samples() @property
428         {
429             return scene.renderer.deferredEnvPass.shader.ssaoSamples;
430         }
431 
432         void radius(float r) @property
433         {
434             scene.renderer.deferredEnvPass.shader.ssaoRadius = r;
435         }
436 
437         float radius() @property
438         {
439             return scene.renderer.deferredEnvPass.shader.ssaoRadius;
440         }
441 
442         void power(float p) @property
443         {
444             scene.renderer.deferredEnvPass.shader.ssaoPower = p;
445         }
446 
447         float power() @property
448         {
449             return scene.renderer.deferredEnvPass.shader.ssaoPower;
450         }
451 
452         //TODO: other SSAO parameters
453     }
454 
455     struct HDRSettings
456     {
457         BaseScene3D scene;
458 
459         void tonemapper(Tonemapper f) @property
460         {
461             scene.hdrFilter.tonemapFunction = f;
462         }
463 
464         Tonemapper tonemapper() @property
465         {
466             return scene.hdrFilter.tonemapFunction;
467         }
468 
469 
470         void exposure(float ex) @property
471         {
472             scene.hdrFilter.exposure = ex;
473         }
474 
475         float exposure() @property
476         {
477             return scene.hdrFilter.exposure;
478         }
479 
480 
481         void autoExposure(bool mode) @property
482         {
483             scene.hdrFilter.autoExposure = mode;
484         }
485 
486         bool autoExposure() @property
487         {
488             return scene.hdrFilter.autoExposure;
489         }
490 
491 
492         void minLuminance(float l) @property
493         {
494             scene.hdrFilter.minLuminance = l;
495         }
496 
497         float minLuminance() @property
498         {
499             return scene.hdrFilter.minLuminance;
500         }
501 
502 
503         void maxLuminance(float l) @property
504         {
505             scene.hdrFilter.maxLuminance = l;
506         }
507 
508         float maxLuminance() @property
509         {
510             return scene.hdrFilter.maxLuminance;
511         }
512 
513 
514         void keyValue(float k) @property
515         {
516             scene.hdrFilter.keyValue = k;
517         }
518 
519         float keyValue() @property
520         {
521             return scene.hdrFilter.keyValue;
522         }
523 
524 
525         void adaptationSpeed(float s) @property
526         {
527             scene.hdrFilter.adaptationSpeed = s;
528         }
529 
530         float adaptationSpeed() @property
531         {
532             return scene.hdrFilter.adaptationSpeed;
533         }
534     }
535 
536     struct GlowSettings
537     {
538         BaseScene3D scene;
539         uint radius;
540 
541         void enabled(bool mode) @property
542         {
543             scene.hblur.enabled = mode;
544             scene.vblur.enabled = mode;
545             scene.hdrPrepassFilter.glowEnabled = mode;
546         }
547 
548         bool enabled() @property
549         {
550             return scene.hdrPrepassFilter.glowEnabled;
551         }
552 
553 
554         void brightness(float b) @property
555         {
556             scene.hdrPrepassFilter.glowBrightness = b;
557         }
558 
559         float brightness() @property
560         {
561             return scene.hdrPrepassFilter.glowBrightness;
562         }
563     }
564 
565     struct MotionBlurSettings
566     {
567         BaseScene3D scene;
568 
569         void enabled(bool mode) @property
570         {
571             scene.hdrFilter.mblurEnabled = mode;
572         }
573 
574         bool enabled() @property
575         {
576             return scene.hdrFilter.mblurEnabled;
577         }
578 
579 
580         void samples(uint s) @property
581         {
582             scene.hdrFilter.motionBlurSamples = s;
583         }
584 
585         uint samples() @property
586         {
587             return scene.hdrFilter.motionBlurSamples;
588         }
589 
590 
591         void shutterSpeed(float s) @property
592         {
593             scene.hdrFilter.shutterSpeed = s;
594             scene.hdrFilter.shutterFps = 1.0 / s;
595         }
596 
597         float shutterSpeed() @property
598         {
599             return scene.hdrFilter.shutterSpeed;
600         }
601     }
602 
603     struct LUTSettings
604     {
605         BaseScene3D scene;
606 
607         void texture(Texture tex) @property
608         {
609             scene.hdrFilter.colorTable = tex;
610         }
611 
612         Texture texture() @property
613         {
614             return scene.hdrFilter.colorTable;
615         }
616     }
617 
618     struct VignetteSettings
619     {
620         BaseScene3D scene;
621 
622         void texture(Texture tex) @property
623         {
624             scene.hdrFilter.vignette = tex;
625         }
626 
627         Texture texture() @property
628         {
629             return scene.hdrFilter.vignette;
630         }
631     }
632 
633     struct AASettings
634     {
635         BaseScene3D scene;
636 
637         void enabled(bool mode) @property
638         {
639             scene.fxaaFilter.enabled = mode;
640         }
641 
642         bool enabled() @property
643         {
644             return scene.fxaaFilter.enabled;
645         }
646     }
647 
648     struct LensSettings
649     {
650         BaseScene3D scene;
651 
652         void enabled(bool mode) @property
653         {
654             scene.lensFilter.enabled = mode;
655         }
656 
657         bool enabled() @property
658         {
659             return scene.lensFilter.enabled;
660         }
661 
662         void scale(float s) @property
663         {
664             scene.lensFilter.scale = s;
665         }
666 
667         float scale() @property
668         {
669             return scene.lensFilter.scale;
670         }
671 
672 
673         void dispersion(float d) @property
674         {
675             scene.lensFilter.dispersion = d;
676         }
677 
678         float dispersion() @property
679         {
680             return scene.lensFilter.dispersion;
681         }
682     }
683 
684     SSAOSettings ssao;
685     HDRSettings hdr;
686     MotionBlurSettings motionBlur;
687     GlowSettings glow;
688     LUTSettings lut;
689     VignetteSettings vignette;
690     AASettings antiAliasing;
691     LensSettings lensDistortion;
692 
693     DynamicArray!PostFilter postFilters;
694 
695     DynamicArray!Entity entities3D;
696     DynamicArray!Entity entities2D;
697 
698     ShapeQuad loadingProgressBar;
699     Entity eLoadingProgressBar;
700     HUDShader hudShader;
701     Material mLoadingProgressBar;
702 
703     double timer = 0.0;
704     double fixedTimeStep = 1.0 / 60.0;
705 
706     this(SceneManager smngr)
707     {
708         super(smngr);
709 
710         rc3d.init(eventManager, environment);
711         rc3d.projectionMatrix = perspectiveMatrix(60.0f, eventManager.aspectRatio, 0.1f, 1000.0f);
712 
713         rc2d.init(eventManager, environment);
714         rc2d.projectionMatrix = orthoMatrix(0.0f, eventManager.windowWidth, 0.0f, eventManager.windowHeight, 0.0f, 100.0f);
715 
716         loadingProgressBar = New!ShapeQuad(assetManager);
717         eLoadingProgressBar = New!Entity(eventManager, assetManager);
718         eLoadingProgressBar.drawable = loadingProgressBar;
719         hudShader = New!HUDShader(assetManager);
720         mLoadingProgressBar = createMaterial(hudShader);
721         mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1);
722         eLoadingProgressBar.material = mLoadingProgressBar;
723     }
724 
725     void sortEntities(ref DynamicArray!Entity entities)
726     {
727         size_t j = 0;
728         Entity tmp;
729 
730         auto edata = entities.data;
731 
732         foreach(i, v; edata)
733         {
734             j = i;
735             size_t k = i;
736 
737             while (k < edata.length)
738             {
739                 float b1 = edata[j].layer;
740                 float b2 = edata[k].layer;
741 
742                 if (b2 < b1)
743                     j = k;
744 
745                 k++;
746             }
747 
748             tmp = edata[i];
749             edata[i] = edata[j];
750             edata[j] = tmp;
751 
752             sortEntities(v.children);
753         }
754     }
755 
756     TextAsset addTextAsset(string filename, bool preload = false)
757     {
758         TextAsset text;
759         if (assetManager.assetExists(filename))
760             text = cast(TextAsset)assetManager.getAsset(filename);
761         else
762         {
763             text = New!TextAsset(assetManager);
764             addAsset(text, filename, preload);
765         }
766         return text;
767     }
768 
769     TextureAsset addTextureAsset(string filename, bool preload = false)
770     {
771         TextureAsset tex;
772         if (assetManager.assetExists(filename))
773             tex = cast(TextureAsset)assetManager.getAsset(filename);
774         else
775         {
776             tex = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, assetManager);
777             addAsset(tex, filename, preload);
778         }
779         return tex;
780     }
781 
782     FontAsset addFontAsset(string filename, uint height, bool preload = false)
783     {
784         FontAsset font;
785         if (assetManager.assetExists(filename))
786             font = cast(FontAsset)assetManager.getAsset(filename);
787         else
788         {
789             font = New!FontAsset(height, assetManager);
790             addAsset(font, filename, preload);
791         }
792         return font;
793     }
794 
795     OBJAsset addOBJAsset(string filename, bool preload = false)
796     {
797         OBJAsset obj;
798         if (assetManager.assetExists(filename))
799             obj = cast(OBJAsset)assetManager.getAsset(filename);
800         else
801         {
802             obj = New!OBJAsset(assetManager);
803             addAsset(obj, filename, preload);
804         }
805         return obj;
806     }
807 
808     IQMAsset addIQMAsset(string filename, bool preload = false)
809     {
810         IQMAsset iqm;
811         if (assetManager.assetExists(filename))
812             iqm = cast(IQMAsset)assetManager.getAsset(filename);
813         else
814         {
815             iqm = New!IQMAsset(assetManager);
816             addAsset(iqm, filename, preload);
817         }
818         return iqm;
819     }
820 
821     PackageAsset addPackageAsset(string filename, bool preload = false)
822     {
823         PackageAsset pa;
824         if (assetManager.assetExists(filename))
825             pa = cast(PackageAsset)assetManager.getAsset(filename);
826         else
827         {
828             pa = New!PackageAsset(this, assetManager);
829             addAsset(pa, filename, preload);
830         }
831         return pa;
832     }
833 
834     Entity createEntity2D(Entity parent = null)
835     {
836         Entity e;
837         if (parent)
838             e = New!Entity(parent);
839         else
840         {
841             e = New!Entity(eventManager, assetManager);
842             entities2D.append(e);
843 
844             sortEntities(entities2D);
845         }
846 
847         return e;
848     }
849 
850     Entity createEntity3D(Entity parent = null)
851     {
852         Entity e;
853         if (parent)
854             e = New!Entity(parent);
855         else
856         {
857             e = New!Entity(eventManager, assetManager);
858             entities3D.append(e);
859 
860             sortEntities(entities3D);
861         }
862 
863         e.material = defaultMaterial3D;
864 
865         return e;
866     }
867 
868     Entity addEntity3D(Entity e)
869     {
870         entities3D.append(e);
871         sortEntities(entities3D);
872         return e;
873     }
874 
875     Entity createSky(Material mat = null)
876     {
877         Material matSky;
878         if (mat is null)
879         {
880             matSky = New!Material(skyShader, assetManager);
881             matSky.depthWrite = false;
882         }
883         else
884         {
885             matSky = mat;
886         }
887 
888         auto eSky = createEntity3D();
889         eSky.layer = 0;
890         eSky.attach = Attach.Camera;
891         eSky.castShadow = false;
892         eSky.material = matSky;
893         // TODO: use box instead of sphere
894         eSky.drawable = New!ShapeSphere(1.0f, 16, 8, true, assetManager);
895         eSky.scaling = Vector3f(100.0f, 100.0f, 100.0f);
896         sortEntities(entities3D);
897         return eSky;
898     }
899 
900     Material createMaterial(Shader shader)
901     {
902         auto m = New!Material(shader, assetManager);
903         if (shader !is standardShader)
904             m.customShader = true;
905         return m;
906     }
907 
908     Material createMaterial()
909     {
910         return createMaterial(standardShader);
911     }
912 
913     Material createParticleMaterial(Shader shader = null)
914     {
915         if (shader is null)
916             shader = particleShader;
917         return New!Material(shader, assetManager);
918     }
919 
920     LightSource createLight(Vector3f position, Color4f color, float energy, float volumeRadius, float areaRadius = 0.0f)
921     {
922         return lightManager.addLight(position, color, energy, volumeRadius, areaRadius);
923     }
924 
925     override void onAllocate()
926     {
927         environment = New!Environment(assetManager);
928         lightManager = New!LightManager(assetManager);
929 
930         renderer = New!Renderer(this, assetManager);
931 
932 		standardShader = New!StandardShader(assetManager);
933         standardShader.shadowMap = renderer.shadowMap;
934         skyShader = New!SkyShader(assetManager);
935         particleShader = New!ParticleShader(renderer.gbuffer, assetManager);
936 
937         particleSystem = New!ParticleSystem(assetManager);
938 
939         defaultMaterial3D = createMaterial();
940 
941         ssao.scene = this;
942         hdr.scene = this;
943         motionBlur.scene = this;
944         glow.scene = this;
945         glow.radius = 3;
946         lut.scene = this;
947         vignette.scene = this;
948         antiAliasing.scene = this;
949         lensDistortion.scene = this;
950 
951         hblurredFramebuffer = New!Framebuffer(renderer.gbuffer, eventManager.windowWidth / 2, eventManager.windowHeight / 2, true, false, assetManager);
952         hblur = New!PostFilterBlur(true, renderer.sceneFramebuffer, hblurredFramebuffer, assetManager);
953 
954         vblurredFramebuffer = New!Framebuffer(renderer.gbuffer, eventManager.windowWidth / 2, eventManager.windowHeight / 2, true, false, assetManager);
955         vblur = New!PostFilterBlur(false, hblurredFramebuffer, vblurredFramebuffer, assetManager);
956 
957         hdrPrepassFramebuffer = New!Framebuffer(renderer.gbuffer, eventManager.windowWidth, eventManager.windowHeight, true, false, assetManager);
958         hdrPrepassFilter = New!PostFilterHDRPrepass(renderer.sceneFramebuffer, hdrPrepassFramebuffer, assetManager);
959         hdrPrepassFilter.blurredTexture = vblurredFramebuffer.currentColorTexture;
960         postFilters.append(hdrPrepassFilter);
961 
962         hdrFilter = New!PostFilterHDR(hdrPrepassFramebuffer, null, assetManager);
963         hdrFilter.velocityTexture = renderer.gbuffer.velocityTexture;
964         postFilters.append(hdrFilter);
965 
966         fxaaFilter = New!PostFilterFXAA(null, null, assetManager);
967         postFilters.append(fxaaFilter);
968         fxaaFilter.enabled = false;
969 
970         lensFilter = New!PostFilterLensDistortion(null, null, assetManager);
971         postFilters.append(lensFilter);
972         lensFilter.enabled = false;
973 
974         finalizerFilter = New!PostFilterFinalizer(null, null, assetManager);
975 
976         rc3d.initPerspective(eventManager, environment, 60.0f, 0.1f, 1000.0f);
977         rc2d.initOrtho(eventManager, environment, 0.0f, 100.0f);
978 
979         timer = 0.0;
980     }
981 
982     PostFilter addFilter(PostFilter f)
983     {
984         postFilters.append(f);
985         return f;
986     }
987 
988     override void onRelease()
989     {
990         entities3D.free();
991         entities2D.free();
992 
993         postFilters.free();
994     }
995 
996     override void onLoading(float percentage)
997     {
998         glEnable(GL_SCISSOR_TEST);
999         glScissor(0, 0, eventManager.windowWidth, eventManager.windowHeight);
1000         glViewport(0, 0, eventManager.windowWidth, eventManager.windowHeight);
1001         glClearColor(0, 0, 0, 1);
1002         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1003 
1004         float maxWidth = eventManager.windowWidth * 0.33f;
1005         float x = (eventManager.windowWidth - maxWidth) * 0.5f;
1006         float y = eventManager.windowHeight * 0.5f - 10;
1007         float w = percentage * maxWidth;
1008 
1009         glDisable(GL_DEPTH_TEST);
1010         mLoadingProgressBar.diffuse = Color4f(0.1, 0.1, 0.1, 1);
1011         eLoadingProgressBar.position = Vector3f(x, y, 0);
1012         eLoadingProgressBar.scaling = Vector3f(maxWidth, 10, 1);
1013         eLoadingProgressBar.update(1.0/60.0);
1014         eLoadingProgressBar.render(&rc2d);
1015 
1016         mLoadingProgressBar.diffuse = Color4f(1, 1, 1, 1);
1017         eLoadingProgressBar.scaling = Vector3f(w, 10, 1);
1018         eLoadingProgressBar.update(1.0/60.0);
1019         eLoadingProgressBar.render(&rc2d);
1020     }
1021 
1022     override void onStart()
1023     {
1024     }
1025 
1026     void onLogicsUpdate(double dt)
1027     {
1028     }
1029 
1030     void fixedStepUpdate()
1031     {
1032         if (view)
1033         {
1034             view.update(fixedTimeStep);
1035             view.prepareRC(&rc3d);
1036         }
1037 
1038         rc3d.time += fixedTimeStep;
1039         rc2d.time += fixedTimeStep;
1040 
1041         foreach(e; entities3D)
1042             e.update(fixedTimeStep);
1043 
1044         foreach(e; entities2D)
1045             e.update(fixedTimeStep);
1046 
1047         particleSystem.update(fixedTimeStep);
1048 
1049         onLogicsUpdate(fixedTimeStep);
1050 
1051         environment.update(fixedTimeStep);
1052 
1053         if (view) // TODO: allow to turn this off
1054         {
1055             Vector3f cameraDirection = -view.invViewMatrix.forward;
1056             cameraDirection.y = 0.0f;
1057             cameraDirection = cameraDirection.normalized;
1058 
1059             renderer.shadowMap.area1.position = view.cameraPosition + cameraDirection * (renderer.shadowMap.projSize1  * 0.5f - 1.0f);
1060             renderer.shadowMap.area2.position = view.cameraPosition + cameraDirection * renderer.shadowMap.projSize2 * 0.5f;
1061             renderer.shadowMap.area3.position = view.cameraPosition + cameraDirection * renderer.shadowMap.projSize3 * 0.5f;
1062         }
1063 
1064         renderer.shadowMap.update(&rc3d, fixedTimeStep);
1065     }
1066 
1067     override void onUpdate(double dt)
1068     {
1069         foreach(e; entities3D)
1070             e.processEvents();
1071 
1072         foreach(e; entities2D)
1073             e.processEvents();
1074 
1075         timer += dt;
1076         if (timer >= fixedTimeStep)
1077         {
1078             timer -= fixedTimeStep;
1079             fixedStepUpdate();
1080         }
1081     }
1082 
1083     void renderBackgroundEntities3D(RenderingContext* rc)
1084     {
1085         glEnable(GL_DEPTH_TEST);
1086         foreach(e; entities3D)
1087             if (e.layer <= 0)
1088                 e.render(rc);
1089     }
1090 
1091     void renderOpaqueEntities3D(RenderingContext* rc)
1092     {
1093         glEnable(GL_DEPTH_TEST);
1094         RenderingContext rcLocal = *rc;
1095         rcLocal.ignoreTransparentEntities = true;
1096         foreach(e; entities3D)
1097         {
1098             if (e.layer > 0)
1099                 e.render(&rcLocal);
1100         }
1101     }
1102 
1103     void renderTransparentEntities3D(RenderingContext* rc)
1104     {
1105         glEnable(GL_DEPTH_TEST);
1106         RenderingContext rcLocal = *rc;
1107         rcLocal.ignoreOpaqueEntities = true;
1108         foreach(e; entities3D)
1109         {
1110             if (e.layer > 0)
1111                 e.render(&rcLocal);
1112         }
1113     }
1114 
1115     void renderEntities3D(RenderingContext* rc)
1116     {
1117         glEnable(GL_DEPTH_TEST);
1118         foreach(e; entities3D)
1119             e.render(rc);
1120     }
1121 
1122     void renderEntities2D(RenderingContext* rc)
1123     {
1124         glDisable(GL_DEPTH_TEST);
1125         foreach(e; entities2D)
1126             e.render(rc);
1127     }
1128 
1129     void renderBlur(uint iterations)
1130     {
1131         RenderingContext rcTmp;
1132 
1133         foreach(i; 1..iterations+1)
1134         {
1135             hblur.outputBuffer.bind();
1136             rcTmp.initOrtho(eventManager, environment, hblur.outputBuffer.width, hblur.outputBuffer.height, 0.0f, 100.0f);
1137             renderer.prepareViewport(hblur.outputBuffer);
1138             hblur.radius = i;
1139             hblur.render(&rcTmp);
1140             hblur.outputBuffer.unbind();
1141 
1142             vblur.outputBuffer.bind();
1143             rcTmp.initOrtho(eventManager, environment, vblur.outputBuffer.width, vblur.outputBuffer.height, 0.0f, 100.0f);
1144             renderer.prepareViewport(vblur.outputBuffer);
1145             vblur.radius = i;
1146             vblur.render(&rcTmp);
1147             vblur.outputBuffer.unbind();
1148 
1149             hblur.inputBuffer = vblur.outputBuffer;
1150         }
1151 
1152         hblur.inputBuffer = renderer.sceneFramebuffer;
1153     }
1154 
1155     override void onRender()
1156     {
1157         renderer.render(&rc3d);
1158 
1159         if (hdrFilter.autoExposure)
1160         {
1161             renderer.sceneFramebuffer.genLuminanceMipmaps();
1162             float lum = renderer.sceneFramebuffer.averageLuminance();
1163 
1164             if (!isNaN(lum))
1165             {
1166                 float newExposure = hdrFilter.keyValue * (1.0f / clamp(lum, hdrFilter.minLuminance, hdrFilter.maxLuminance));
1167 
1168                 float exposureDelta = newExposure - hdrFilter.exposure;
1169                 hdrFilter.exposure += exposureDelta * hdrFilter.adaptationSpeed * eventManager.deltaTime;
1170             }
1171         }
1172 
1173         if (hdrPrepassFilter.glowEnabled)
1174             renderBlur(glow.radius);
1175 
1176         RenderingContext rcTmp;
1177         Framebuffer nextInput = renderer.sceneFramebuffer;
1178 
1179         hdrPrepassFilter.perspectiveMatrix = rc3d.projectionMatrix;
1180 
1181         foreach(i, f; postFilters.data)
1182         if (f.enabled)
1183         {
1184             if (f.outputBuffer is null)
1185                 f.outputBuffer = New!Framebuffer(renderer.gbuffer, eventManager.windowWidth, eventManager.windowHeight, false, false, assetManager);
1186 
1187             if (f.inputBuffer is null)
1188                 f.inputBuffer = nextInput;
1189 
1190             nextInput = f.outputBuffer;
1191 
1192             f.outputBuffer.bind();
1193             rcTmp.initOrtho(eventManager, environment, f.outputBuffer.width, f.outputBuffer.height, 0.0f, 100.0f);
1194             renderer.prepareViewport(f.outputBuffer);
1195             f.render(&rcTmp);
1196             f.outputBuffer.unbind();
1197         }
1198 
1199         renderer.prepareViewport();
1200         finalizerFilter.inputBuffer = nextInput;
1201         finalizerFilter.render(&rc2d);
1202 
1203         renderEntities2D(&rc2d);
1204     }
1205 }
1206 
1207 alias Scene BaseScene3D;