1 /*
2 Copyright (c) 2019 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.render.shaders.decal;
29 
30 import std.stdio;
31 import std.math;
32 
33 import dlib.core.memory;
34 import dlib.core.ownership;
35 import dlib.math.vector;
36 import dlib.math.matrix;
37 import dlib.math.transformation;
38 import dlib.math.interpolation;
39 import dlib.image.color;
40 import dlib.text.str;
41 
42 import dagon.core.bindings;
43 import dagon.graphics.material;
44 import dagon.graphics.shader;
45 import dagon.graphics.state;
46 import dagon.render.gbuffer;
47 
48 class DecalShader: Shader
49 {
50     String vs, fs;
51 
52     GBuffer gbuffer;
53 
54     this(GBuffer gbuffer, Owner owner)
55     {
56         vs = Shader.load("data/__internal/shaders/Decal/Decal.vert.glsl");
57         fs = Shader.load("data/__internal/shaders/Decal/Decal.frag.glsl");
58 
59         auto prog = New!ShaderProgram(vs, fs, this);
60         super(prog, owner);
61 
62         this.gbuffer = gbuffer;
63     }
64 
65     ~this()
66     {
67         vs.free();
68         fs.free();
69     }
70 
71     override void bindParameters(GraphicsState* state)
72     {
73         auto idiffuse = "diffuse" in state.material.inputs;
74         auto inormal = "normal" in state.material.inputs;
75         auto iheight = "height" in state.material.inputs;
76         auto iparallax = "parallax" in state.material.inputs;
77         auto ipbr = "pbr" in state.material.inputs;
78         auto iroughness = "roughness" in state.material.inputs;
79         auto imetallic = "metallic" in state.material.inputs;
80         auto ispecularity = "specularity" in state.material.inputs;
81         auto itextureScale = "textureScale" in state.material.inputs;
82         auto iemission = "emission" in state.material.inputs;
83         auto ienergy = "energy" in state.material.inputs;
84         auto ioutputColor = "outputColor" in state.material.inputs;
85         auto ioutputNormal = "outputNormal" in state.material.inputs;
86         auto ioutputPBR = "outputPBR" in state.material.inputs;
87         auto ioutputEmission = "outputEmission" in state.material.inputs;
88 
89         int parallaxMethod = iparallax.asInteger;
90         if (parallaxMethod > ParallaxOcclusionMapping)
91             parallaxMethod = ParallaxOcclusionMapping;
92         if (parallaxMethod < 0)
93             parallaxMethod = 0;
94 
95         setParameter("modelViewMatrix", state.modelViewMatrix);
96         setParameter("projectionMatrix", state.projectionMatrix);
97         setParameter("invProjectionMatrix", state.invProjectionMatrix);
98         setParameter("normalMatrix", state.normalMatrix);
99         setParameter("viewMatrix", state.viewMatrix);
100         setParameter("invViewMatrix", state.invViewMatrix);
101         setParameter("invModelMatrix", state.invModelMatrix);
102         setParameter("resolution", state.resolution);
103 
104         setParameter("opacity", state.opacity);
105         setParameter("textureScale", itextureScale.asVector2f);
106 
107         glActiveTexture(GL_TEXTURE0);
108         glBindTexture(GL_TEXTURE_2D, gbuffer.depthTexture);
109         setParameter("depthTexture", cast(int)0);
110 
111         // Diffuse
112         if (idiffuse.texture)
113         {
114             glActiveTexture(GL_TEXTURE1);
115             idiffuse.texture.bind();
116             setParameter("diffuseTexture", cast(int)1);
117             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorTexture");
118         }
119         else
120         {
121             setParameter("diffuseVector", idiffuse.asVector4f);
122             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorValue");
123         }
124 
125         if (!ioutputColor.asBool)
126         {
127             glColorMaski(0, 0, 0, 0, 0);
128         }
129 
130         // Normal/height
131         bool haveHeightMap = inormal.texture !is null;
132         if (haveHeightMap)
133             haveHeightMap = inormal.texture.image.channels == 4;
134 
135         if (!haveHeightMap)
136         {
137             if (inormal.texture is null)
138             {
139                 if (iheight.texture !is null) // we have height map, but no normal map
140                 {
141                     Color4f color = Color4f(0.5f, 0.5f, 1.0f, 0.0f); // default normal pointing upwards
142                     inormal.texture = state.material.makeTexture(color, iheight.texture);
143                     haveHeightMap = true;
144                 }
145             }
146             else
147             {
148                 if (iheight.texture !is null) // we have both normal and height maps
149                 {
150                     inormal.texture = state.material.makeTexture(inormal.texture, iheight.texture);
151                     haveHeightMap = true;
152                 }
153             }
154         }
155 
156         if (inormal.texture)
157         {
158             setParameter("normalTexture", 2);
159             setParameterSubroutine("normal", ShaderType.Fragment, "normalMap");
160 
161             glActiveTexture(GL_TEXTURE2);
162             inormal.texture.bind();
163         }
164         else
165         {
166             setParameter("normalVector", state.material.normal.asVector3f);
167             setParameterSubroutine("normal", ShaderType.Fragment, "normalValue");
168         }
169 
170         if (!ioutputNormal.asBool)
171         {
172             glColorMaski(1, 0, 0, 0, 0);
173         }
174 
175         // Height and parallax
176         // TODO: make these material properties
177         float parallaxScale = 0.03f;
178         float parallaxBias = -0.01f;
179         setParameter("parallaxScale", parallaxScale);
180         setParameter("parallaxBias", parallaxBias);
181 
182         if (haveHeightMap)
183         {
184             setParameterSubroutine("height", ShaderType.Fragment, "heightMap");
185         }
186         else
187         {
188             float h = 0.0f; //-parallaxBias / parallaxScale;
189             setParameter("heightScalar", h);
190             setParameterSubroutine("height", ShaderType.Fragment, "heightValue");
191             parallaxMethod = ParallaxNone;
192         }
193 
194         if (parallaxMethod == ParallaxSimple)
195             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxSimple");
196         else if (parallaxMethod == ParallaxOcclusionMapping)
197             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxOcclusionMapping");
198         else
199             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxNone");
200 
201         // PBR
202         if (ipbr is null)
203         {
204             state.material.setInput("pbr", 0.0f);
205             ipbr = "pbr" in state.material.inputs;
206         }
207         if (ipbr.texture is null)
208         {
209             ipbr.texture = state.material.makeTexture(*iroughness, *imetallic, *ispecularity, materialInput(0.0f));
210         }
211         glActiveTexture(GL_TEXTURE3);
212         ipbr.texture.bind();
213         setParameter("pbrTexture", 3);
214 
215         if (iroughness.texture is null)
216         {
217             setParameterSubroutine("roughness", ShaderType.Fragment, "roughnessValue");
218 
219             if (iroughness.type == MaterialInputType.Float)
220                 setParameter("roughnessScalar", iroughness.asFloat);
221             else if (iroughness.type == MaterialInputType.Bool)
222                 setParameter("roughnessScalar", cast(float)iroughness.asBool);
223             else if (iroughness.type == MaterialInputType.Integer)
224                 setParameter("roughnessScalar", cast(float)iroughness.asInteger);
225             else if (iroughness.type == MaterialInputType.Vec2)
226                 setParameter("roughnessScalar", iroughness.asVector2f.r);
227             else if (iroughness.type == MaterialInputType.Vec3)
228                 setParameter("roughnessScalar", iroughness.asVector3f.r);
229             else if (iroughness.type == MaterialInputType.Vec4)
230                 setParameter("roughnessScalar", iroughness.asVector4f.r);
231         }
232         else
233         {
234             setParameterSubroutine("roughness", ShaderType.Fragment, "roughnessMap");
235         }
236 
237         if (imetallic.texture is null)
238         {
239             setParameterSubroutine("metallic", ShaderType.Fragment, "metallicValue");
240 
241             if (imetallic.type == MaterialInputType.Float)
242                 setParameter("metallicScalar", imetallic.asFloat);
243             else if (imetallic.type == MaterialInputType.Bool)
244                 setParameter("metallicScalar", cast(float)imetallic.asBool);
245             else if (imetallic.type == MaterialInputType.Integer)
246                 setParameter("metallicScalar", cast(float)imetallic.asInteger);
247             else if (imetallic.type == MaterialInputType.Vec2)
248                 setParameter("metallicScalar", imetallic.asVector2f.r);
249             else if (imetallic.type == MaterialInputType.Vec3)
250                 setParameter("metallicScalar", imetallic.asVector3f.r);
251             else if (imetallic.type == MaterialInputType.Vec4)
252                 setParameter("metallicScalar", imetallic.asVector4f.r);
253         }
254         else
255         {
256             setParameterSubroutine("metallic", ShaderType.Fragment, "metallicMap");
257         }
258 
259         if (ispecularity.texture is null)
260         {
261             setParameterSubroutine("specularity", ShaderType.Fragment, "specularityValue");
262 
263             if (ispecularity.type == MaterialInputType.Float)
264                 setParameter("specularityScalar", ispecularity.asFloat);
265             else if (ispecularity.type == MaterialInputType.Bool)
266                 setParameter("specularityScalar", cast(float)ispecularity.asBool);
267             else if (ispecularity.type == MaterialInputType.Integer)
268                 setParameter("specularityScalar", cast(float)ispecularity.asInteger);
269             else if (ispecularity.type == MaterialInputType.Vec2)
270                 setParameter("specularityScalar", ispecularity.asVector2f.r);
271             else if (ispecularity.type == MaterialInputType.Vec3)
272                 setParameter("specularityScalar", ispecularity.asVector3f.r);
273             else if (ispecularity.type == MaterialInputType.Vec4)
274                 setParameter("specularityScalar", ispecularity.asVector4f.r);
275         }
276         else
277         {
278             setParameterSubroutine("specularity", ShaderType.Fragment, "specularityMap");
279         }
280 
281         if (!ioutputPBR.asBool)
282         {
283             glColorMaski(2, 0, 0, 0, 0);
284         }
285 
286         // Emission
287         if (iemission.texture)
288         {
289             glActiveTexture(GL_TEXTURE4);
290             iemission.texture.bind();
291             setParameter("emissionTexture", cast(int)4);
292             setParameterSubroutine("emission", ShaderType.Fragment, "emissionMap");
293         }
294         else
295         {
296             setParameter("emissionVector", iemission.asVector4f);
297             setParameterSubroutine("emission", ShaderType.Fragment, "emissionValue");
298         }
299         setParameter("energy", ienergy.asFloat);
300 
301         if (!ioutputEmission.asBool)
302         {
303             glColorMaski(3, 0, 0, 0, 0);
304         }
305 
306         glActiveTexture(GL_TEXTURE0);
307 
308         super.bindParameters(state);
309     }
310 
311     override void unbindParameters(GraphicsState* state)
312     {
313         super.unbindParameters(state);
314 
315         glColorMaski(0, 1, 1, 1, 1);
316         glColorMaski(1, 1, 1, 1, 1);
317         glColorMaski(2, 1, 1, 1, 1);
318         glColorMaski(3, 1, 1, 1, 1);
319 
320         glActiveTexture(GL_TEXTURE0);
321         glBindTexture(GL_TEXTURE_2D, 0);
322 
323         glActiveTexture(GL_TEXTURE1);
324         glBindTexture(GL_TEXTURE_2D, 0);
325 
326         glActiveTexture(GL_TEXTURE2);
327         glBindTexture(GL_TEXTURE_2D, 0);
328 
329         glActiveTexture(GL_TEXTURE3);
330         glBindTexture(GL_TEXTURE_2D, 0);
331 
332         glActiveTexture(GL_TEXTURE4);
333         glBindTexture(GL_TEXTURE_2D, 0);
334 
335         glActiveTexture(GL_TEXTURE0);
336     }
337 }