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