1 /**
2 * Copyright: Copyright Auburn Sounds 2015-2017.
3 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
4 * Authors:   Guillaume Piolat
5 */
6 module dplug.gui.pbrbackgroundgui;
7 
8 import gfm.math.box;
9 import dplug.core.nogc;
10 import dplug.core.file;
11 import dplug.graphics.color;
12 import dplug.graphics.image;
13 import dplug.graphics.view;
14 import dplug.graphics.drawex;
15 import dplug.window.window;
16 import dplug.gui.graphics;
17 
18 /// PBRBackgroundGUI provides a PBR background loaded from PNG or JPEG images.
19 /// It's very practical while in development because it let's you reload the six
20 /// images used with the press of ENTER.
21 /// The path of each of these images (given as a template parameter) must be
22 /// in your "stringImportPaths" settings.
23 class PBRBackgroundGUI(string baseColorPath, 
24                        string emissivePath, 
25                        string materialPath,
26                        string physicalPath,
27                        string depthPath,
28                        string skyboxPath,
29                        string absoluteGfxDirectory // for UI development only
30                        ) : GUIGraphics
31 {
32 public:
33 nothrow:
34 @nogc:
35 
36     this(int width, int height)
37     {
38         super(width, height);
39         auto basecolorData = cast(ubyte[])(import(baseColorPath));
40         auto emissiveData = cast(ubyte[])(import(emissivePath));        
41         auto materialData = cast(ubyte[])(import(materialPath));
42         auto physicalData = cast(ubyte[])(import(physicalPath));
43         auto depthData = cast(ubyte[])(import(depthPath));
44         auto skyboxData = cast(ubyte[])(import(skyboxPath));
45         loadImages(basecolorData, emissiveData, materialData, physicalData, depthData, skyboxData);
46     } 
47 
48     ~this()
49     {
50         freeImages();
51     }
52 
53     // Development purposes. 
54     // In debug mode, pressing ENTER reload the backgrounds
55     debug
56     {
57         override bool onKeyDown(Key key)
58         {
59             if (super.onKeyDown(key))
60                 return true;
61 
62             if (key == Key.enter)
63             {
64                 reloadImagesAtRuntime();
65                 return true;
66             }
67 
68             return false;
69         }
70     }
71 
72     override void reflow(box2i availableSpace)
73     {
74         _position = availableSpace;
75     }
76 
77     override void onDraw(ImageRef!RGBA diffuseMap, ImageRef!L16 depthMap, ImageRef!RGBA materialMap, box2i[] dirtyRects)
78     {
79         // Just blit backgrounds into dirtyRects.
80         foreach(dirtyRect; dirtyRects)
81         {
82             auto croppedDiffuseIn = _diffuse.crop(dirtyRect);
83             auto croppedDiffuseOut = diffuseMap.crop(dirtyRect);
84 
85             auto croppedDepthIn = _depth.crop(dirtyRect);
86             auto croppedDepthOut = depthMap.crop(dirtyRect);
87 
88             auto croppedMaterialIn = _material.crop(dirtyRect);
89             auto croppedMaterialOut = materialMap.crop(dirtyRect);
90 
91             croppedDiffuseIn.blitTo(croppedDiffuseOut);
92             croppedDepthIn.blitTo(croppedDepthOut);
93             croppedMaterialIn.blitTo(croppedMaterialOut);
94         }
95     }
96 
97 private:
98 
99     // CTFE used here so we are allowed to use ~
100     static immutable string baseColorPathAbs = absoluteGfxDirectory ~ baseColorPath;
101     static immutable string emissivePathAbs = absoluteGfxDirectory ~ emissivePath;
102     static immutable string materialPathAbs = absoluteGfxDirectory ~ materialPath;
103     static immutable string physicalPathAbs = absoluteGfxDirectory ~ physicalPath;
104     static immutable string depthPathAbs = absoluteGfxDirectory ~ depthPath;
105     static immutable string skyboxPathAbs = absoluteGfxDirectory ~ skyboxPath;
106 
107 
108     OwnedImage!RGBA _diffuse;
109     OwnedImage!RGBA _material;
110     OwnedImage!L16 _depth;
111 
112     void freeImages()
113     {
114         if (_diffuse)
115             _diffuse.destroyFree();
116         if (_depth)
117             _depth.destroyFree();
118         if (_material)
119             _material.destroyFree();
120     }
121 
122     // Reloads images for UI development, avoid long compile round trips
123     // This saves up hours.
124     void reloadImagesAtRuntime()
125     {
126         // reading images with an absolute path since we don't know 
127         // which is the current directory from the host
128         ubyte[] basecolorData = readFile(baseColorPathAbs);
129         ubyte[] emissiveData = readFile(emissivePathAbs);
130         ubyte[] materialData = readFile(materialPathAbs);
131         ubyte[] physicalData = readFile(physicalPathAbs);
132         ubyte[] depthData = readFile(depthPathAbs);
133         ubyte[] skyboxData = readFile(skyboxPathAbs);
134 
135         if (basecolorData && emissiveData && materialData
136             && physicalData && depthData && skyboxData) // all valid?
137         {
138             // Reload images from disk and update the UI
139             freeImages();
140             loadImages(basecolorData, emissiveData, materialData, physicalData, depthData, skyboxData);
141             setDirtyWhole();
142         }
143 
144         // Release copy of file contents
145         freeSlice(basecolorData);
146         freeSlice(emissiveData);
147         freeSlice(materialData);
148         freeSlice(physicalData);
149         freeSlice(depthData);
150         freeSlice(skyboxData);
151     }
152 
153     void loadImages(ubyte[] basecolorData, ubyte[] emissiveData,
154                     ubyte[] materialData, ubyte[] physicalData,
155                     ubyte[] depthData, ubyte[] skyboxData)
156     {
157         _diffuse = loadImageSeparateAlpha(basecolorData, emissiveData);
158         _material = loadImageSeparateAlpha(materialData, physicalData);
159         OwnedImage!RGBA depthRGBA = loadOwnedImage(depthData);
160         scope(exit) depthRGBA.destroyFree();
161         assert(_diffuse.w == _material.w);
162         assert(_diffuse.h == _material.h);
163         assert(_diffuse.w == depthRGBA.w);
164         assert(_diffuse.h == depthRGBA.h);
165 
166         int width = depthRGBA.w;
167         int height = depthRGBA.h;
168 
169         _depth = mallocEmplace!(OwnedImage!L16)(width, height);
170 
171         for (int j = 0; j < height; ++j)
172         {
173             RGBA[] inDepth = depthRGBA.scanline(j);
174             L16[] outDepth = _depth.scanline(j);
175             for (int i = 0; i < width; ++i)
176             {
177                 RGBA v = inDepth[i];
178 
179                 float d = 0.5f + 257 * (v.g + v.r + v.b) / 3;
180 
181                 outDepth[i].l = cast(ushort)(d);
182             }
183         }
184 
185         OwnedImage!RGBA skybox = loadOwnedImage(skyboxData);
186         context.setSkybox(skybox);
187     }
188 }