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 }