1 /** 2 Copyright: Guillaume Piolat 2015-2017. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 4 */ 5 module gui; 6 7 import std.math; 8 9 import dplug.core; 10 import dplug.math; 11 import dplug.gui; 12 import dplug.pbrwidgets; 13 import dplug.client; 14 import dplug.flatwidgets; 15 import dplug.wren; 16 import leveldisplay; 17 import main; 18 19 20 // Plugin GUI, based on PBRBackgroundGUI. 21 // If you don't want to use PBR, you not inherit from it. 22 class DistortGUI : PBRBackgroundGUI!("basecolor.jpg", "emissive.png", "material.png", 23 "depth.png", "skybox.jpg", 24 25 // In development, enter here the absolute path to the gfx directory. 26 // This allows to reload background images at debug-time with the press of ENTER. 27 `/home/myuser/my/path/to/Dplug/examples/distort/gfx/`) 28 { 29 public: 30 nothrow: 31 @nogc: 32 33 this(DistortClient client) 34 { 35 _client = client; 36 37 static immutable float[7] ratios = [0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f]; 38 super( makeSizeConstraintsDiscrete(620, 330, ratios) ); 39 40 // Note: PBRCompositor default lighting might change in a future version (increase of light to allow white plastics). 41 // So we keep the value. 42 PBRCompositor comp = cast(PBRCompositor)compositor; 43 comp.light1Color = vec3f(0.26, 0.24, 0.22f) * 0.98f; 44 comp.light2Dir = vec3f(-0.5f, 1.0f, 0.23f).normalized; 45 comp.light2Color = vec3f(0.36, 0.38f, 0.40) * 1.148; 46 comp.light3Dir = vec3f(0.0f, 1.0f, 0.1f).normalized; 47 comp.light3Color = vec3f(0.2f, 0.2f, 0.2f) * 0.84f; 48 comp.ambientLight = 0.042f; 49 comp.skyboxAmount = 0.56f; 50 51 // Sets the number of pixels recomputed around dirtied controls. 52 // This is a tradeoff between Emissive light accuracy and speed. 53 // A typical good value is 20, and this is the default, as this is 54 // what `PBRCompositor` needs for the emissive pass. 55 setUpdateMargin(20); // TODO: put this in reflow, depends on scale factor 56 57 // All resources are bundled as a string import. 58 // You can avoid resource compilers that way. 59 // The only cost is that each resource is in each binary, this creates overhead with 60 _font = mallocNew!Font(cast(ubyte[])( import("VeraBd.ttf") )); 61 62 // Builds the UI hierarchy 63 // Meanwhile, we hardcode each position. 64 65 _knobImageData = loadKnobImage( import("imageknob.png") ); 66 addChild(_imageKnob = mallocNew!UIImageKnob(context(), _knobImageData, cast(FloatParameter) _client.param(paramBias))); 67 68 // Add procedural knobs 69 addChild(_driveKnob = mallocNew!UIKnob(context(), cast(FloatParameter) _client.param(paramDrive))); 70 71 // Add sliders 72 addChild(_inputSlider = mallocNew!UISlider(context(), cast(FloatParameter) _client.param(paramInput))); 73 74 addChild(_outputSlider = mallocNew!UISlider(context(), cast(FloatParameter) _client.param(paramOutput))); 75 76 // Add switch 77 addChild(_onOffSwitch = mallocNew!UIOnOffSwitch(context(), cast(BoolParameter) _client.param(paramOnOff))); 78 79 80 // Add bargraphs 81 addChild(_inputLevel = mallocNew!UILevelDisplay(context())); 82 addChild(_outputLevel = mallocNew!UILevelDisplay(context())); 83 84 // Add resizer corner 85 addChild(_resizer = mallocNew!UIWindowResizer(context())); 86 87 // Global color correction. 88 // Very useful at the end of the UI creating process. 89 // As the sole Raw-only widget it is always on top and doesn't need zOrder adjustment. 90 { 91 mat3x4!float colorCorrectionMatrix = mat3x4!float(- 0.07f, 1.0f , 1.15f, 0.03f, 92 + 0.01f, 0.93f, 1.16f, 0.08f, 93 + 0.0f , 1.0f , 1.10f, -0.01f); 94 addChild(_colorCorrection = mallocNew!UIColorCorrection(context())); 95 _colorCorrection.setLiftGammaGainContrastRGB(colorCorrectionMatrix); 96 } 97 98 // Enable all things Wren 99 mixin(fieldIdentifiersAreIDs!DistortGUI); // Each UIElement in this object receives its identifier as runtime ID, ie. _inputSlider receives ID "_inputSlider" 100 context.enableWrenSupport(); 101 //debug 102 // context.wrenSupport.addModuleFileWatch("plugin", `/my/absolute/path/to/plugin.wren`); // debug => live reload, enter absolute path here 103 //else 104 context.wrenSupport.addModuleSource("plugin", import("plugin.wren")); // no debug => static scripts 105 context.wrenSupport.registerScriptExports!DistortGUI; // Note: for now, only UIElement should be @ScriptExport 106 context.wrenSupport.callCreateUI(); 107 } 108 109 override void onAnimate(double dt, double time) 110 { 111 context.wrenSupport.callReflowWhenScriptsChange(dt); 112 } 113 114 ~this() 115 { 116 // Note: UI widgets are owned by the UI and don't need to be destroyed manually 117 // However some of the resources they consumed aren't owned by them, but borrowed. 118 _font.destroyFree(); 119 _knobImageData.destroyFree(); 120 context.disableWrenSupport(); 121 122 version(Dplug_ProfileUI) 123 { 124 writeFile(`/home/myuser/plugin-trace.json`, context.profiler.toBytes()); 125 browseNoGC("https://ui.perfetto.dev/"); // A webtool to read that trace 126 } 127 } 128 129 override void reflow() 130 { 131 super.reflow(); 132 context.wrenSupport.callReflow(); 133 } 134 135 void sendFeedbackToUI(float* inputRMS, float* outputRMS, int frames, float sampleRate) 136 { 137 _inputLevel.sendFeedbackToUI(inputRMS, frames, sampleRate); 138 _outputLevel.sendFeedbackToUI(outputRMS, frames, sampleRate); 139 } 140 141 private: 142 DistortClient _client; 143 144 // Resources 145 Font _font; 146 KnobImage _knobImageData; 147 148 // Widgets can be exported to Wren. This allow styling through a plugin.wren script. 149 @ScriptExport 150 { 151 UISlider _inputSlider; 152 UIKnob _driveKnob; 153 UISlider _outputSlider; 154 UIOnOffSwitch _onOffSwitch; 155 UILevelDisplay _inputLevel, _outputLevel; 156 UIColorCorrection _colorCorrection; 157 UIImageKnob _imageKnob; 158 UIWindowResizer _resizer; 159 } 160 } 161