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.math;
10 import dplug.gui;
11 import dplug.pbrwidgets;
12 import dplug.client;
13 import dplug.flatwidgets;
14 import leveldisplay;
15 import main;
16 
17 // Plugin GUI, based on PBRBackgroundGUI.
18 // If you don't want to use PBR, you not inherit from it.
19 class DistortGUI : PBRBackgroundGUI!("basecolor.jpg", "emissive.png", "material.png",
20                                      "depth.png", "skybox.jpg",
21 
22                                      // Enter here the absolute path to the gfx directory.
23                                      // This will allow to reload images at debug-time with the press of ENTER.
24                                      `C:\Users\myuser\Products\distort\gfx\`)
25 {
26 public:
27 nothrow:
28 @nogc:
29 
30     this(DistortClient client)
31     {
32         _client = client;
33 
34 
35         static immutable float[7] ratios = [0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f];
36         super( makeSizeConstraintsDiscrete(620, 330, ratios) );
37 
38         // Note: PBRCompositor default lighting might change in a future version (increase of light to allow white plastics).
39         //       So we keep the value.
40         PBRCompositor comp = cast(PBRCompositor)compositor;
41         comp.light1Color = vec3f(0.26, 0.24, 0.22f) * 0.98f;
42         comp.light2Dir = vec3f(-0.5f, 1.0f, 0.23f).normalized;
43         comp.light2Color = vec3f(0.36, 0.38f, 0.40) * 1.148;
44         comp.light3Dir = vec3f(0.0f, 1.0f, 0.1f).normalized;
45         comp.light3Color = vec3f(0.2f, 0.2f, 0.2f) * 0.84f;
46         comp.ambientLight = 0.042f;
47         comp.skyboxAmount = 0.56f;
48 
49         // Sets the number of pixels recomputed around dirtied controls.
50         // This is a tradeoff between Emissive light accuracy and speed.
51         // A typical good value is 20, and this is the default, as this is
52         // what `PBRCompositor` needs for the emissive pass.
53         setUpdateMargin(20);
54 
55         // All resources are bundled as a string import.
56         // You can avoid resource compilers that way.
57         // The only cost is that each resource is in each binary, this creates overhead with
58         _font = mallocNew!Font(cast(ubyte[])( import("VeraBd.ttf") ));
59 
60         // Builds the UI hierarchy
61         // Meanwhile, we hardcode each position.
62 
63         RGBA litTrailDiffuse = RGBA(151, 119, 255, 100);
64         RGBA unlitTrailDiffuse = RGBA(81, 54, 108, 0);
65 
66         _knobImageData = loadKnobImage( import("imageknob.png") );
67         addChild(_imageKnob = mallocNew!UIImageKnob(context(), _knobImageData, cast(FloatParameter) _client.param(paramBias)));
68         _imageKnob.hasTrail = false; // no trail by default
69 
70         // Add procedural knobs
71         addChild(_driveKnob = mallocNew!UIKnob(context(), cast(FloatParameter) _client.param(paramDrive)));
72         _driveKnob.knobRadius = 0.65f;
73         _driveKnob.knobDiffuse = RGBA(255, 255, 238, 0);
74         _driveKnob.knobMaterial = RGBA(0, 255, 128, 255);
75         _driveKnob.numLEDs = 15;
76         _driveKnob.litTrailDiffuse = litTrailDiffuse;
77         _driveKnob.unlitTrailDiffuse = unlitTrailDiffuse;
78         _driveKnob.LEDDiffuseLit = RGBA(40, 40, 40, 100);
79         _driveKnob.LEDDiffuseUnlit = RGBA(40, 40, 40, 0);
80         _driveKnob.LEDRadiusMin = 0.06f;
81         _driveKnob.LEDRadiusMax = 0.06f;
82 
83         // Add sliders
84         addChild(_inputSlider = mallocNew!UISlider(context(), cast(FloatParameter) _client.param(paramInput)));
85         _inputSlider.litTrailDiffuse = litTrailDiffuse;
86         _inputSlider.unlitTrailDiffuse = unlitTrailDiffuse;
87 
88         addChild(_outputSlider = mallocNew!UISlider(context(), cast(FloatParameter) _client.param(paramOutput)));
89         _outputSlider.litTrailDiffuse = litTrailDiffuse;
90         _outputSlider.unlitTrailDiffuse = unlitTrailDiffuse;
91 
92         // Add switch
93         addChild(_onOffSwitch = mallocNew!UIOnOffSwitch(context(), cast(BoolParameter) _client.param(paramOnOff)));
94         _onOffSwitch.diffuseOn = litTrailDiffuse;
95         _onOffSwitch.diffuseOff = unlitTrailDiffuse;
96 
97         // Add bargraphs
98         addChild(_inputLevel = mallocNew!UILevelDisplay(context()));
99         addChild(_outputLevel = mallocNew!UILevelDisplay(context()));
100 
101         // Add resizer corner
102         addChild(_resizer = mallocNew!UIWindowResizer(context()));
103 
104         // Global color correction.
105         // Very useful at the end of the UI creating process.
106         // As the sole Raw-only widget it is always on top and doesn't need zOrder adjustment.
107         {
108             mat3x4!float colorCorrectionMatrix = mat3x4!float(- 0.07f, 1.0f , 1.15f, 0.03f,
109                                                               + 0.01f, 0.93f, 1.16f, 0.08f,
110                                                               + 0.0f , 1.0f , 1.10f, -0.01f);
111             addChild(_colorCorrection = mallocNew!UIColorCorrection(context()));
112             _colorCorrection.setLiftGammaGainContrastRGB(colorCorrectionMatrix);
113         }
114     }
115 
116     ~this()
117     {
118         // Note: UI widgets are owned by the UI and don't need to be destroyed manually
119         //       However some of the resources they consumed aren't owned by them, but borrowed.
120         _font.destroyFree();
121         _knobImageData.destroyFree();
122     }
123 
124     override void reflow()
125     {
126         super.reflow();
127         int W = position.width;
128         int H = position.height;
129         float S = W / cast(float)(context.getDefaultUIWidth());
130         _imageKnob.position = rectangle(517, 176, 46, 46).scaleByFactor(S);
131         _inputSlider.position = rectangle(190, 132, 30, 130).scaleByFactor(S);
132         _outputSlider.position = rectangle(410, 132, 30, 130).scaleByFactor(S);
133         _onOffSwitch.position = rectangle(90, 177, 30, 40).scaleByFactor(S);
134         _driveKnob.position = rectangle(250, 140, 120, 120).scaleByFactor(S);
135         _inputLevel.position = rectangle(150, 132, 30, 130).scaleByFactor(S);
136         _outputLevel.position = rectangle(450, 132, 30, 130).scaleByFactor(S);
137 
138         _colorCorrection.position = rectangle(0, 0, W, H);
139         _resizer.position = rectangle(W-30, H-30, 30, 30);
140     }
141 
142     void sendFeedbackToUI(float* inputRMS, float* outputRMS, int frames, float sampleRate)
143     {
144         _inputLevel.sendFeedbackToUI(inputRMS, frames, sampleRate);
145         _outputLevel.sendFeedbackToUI(outputRMS, frames, sampleRate);
146     }
147 
148 private:
149     DistortClient _client;
150     UISlider _inputSlider;
151     UIKnob _driveKnob;
152     UISlider _outputSlider;
153     UIOnOffSwitch _onOffSwitch;
154     UILevelDisplay _inputLevel, _outputLevel;
155     UIColorCorrection _colorCorrection;
156     Font _font;
157     KnobImage _knobImageData;
158     UIImageKnob _imageKnob;
159     UIWindowResizer _resizer;
160 }