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.graphics.shaders.decal;
29 
30 import std.stdio;
31 import std.math;
32 
33 import dlib.core.memory;
34 import dlib.math.vector;
35 import dlib.math.matrix;
36 import dlib.math.transformation;
37 import dlib.image.color;
38 
39 import dagon.core.libs;
40 import dagon.core.ownership;
41 import dagon.graphics.rc;
42 import dagon.graphics.shader;
43 import dagon.graphics.gbuffer;
44 import dagon.graphics.material;
45 
46 class DecalShader: Shader
47 {
48     string vs = import("Decal.vs");
49     string fs = import("Decal.fs");
50 
51     GBuffer gbuffer;
52 
53     this(GBuffer gbuffer, Owner o)
54     {
55         auto myProgram = New!ShaderProgram(vs, fs, this);
56         super(myProgram, o);
57         this.gbuffer = gbuffer;
58     }
59 
60     override void bind(RenderingContext* rc)
61     {
62         auto idiffuse = "diffuse" in rc.material.inputs;
63         auto inormal = "normal" in rc.material.inputs;
64         auto iheight = "height" in rc.material.inputs;
65         auto iparallax = "parallax" in rc.material.inputs;
66         auto ipbr = "pbr" in rc.material.inputs;
67         auto iroughness = "roughness" in rc.material.inputs;
68         auto imetallic = "metallic" in rc.material.inputs;
69         auto iemission = "emission" in rc.material.inputs;
70         auto ienergy = "energy" in rc.material.inputs;
71         auto ioutputColor = "outputColor" in rc.material.inputs;
72         auto ioutputNormal = "outputNormal" in rc.material.inputs;
73         auto ioutputPBR = "outputPBR" in rc.material.inputs;
74         auto ioutputEmission = "outputEmission" in rc.material.inputs;
75         
76         int parallaxMethod = iparallax.asInteger;
77         if (parallaxMethod > ParallaxOcclusionMapping)
78             parallaxMethod = ParallaxOcclusionMapping;
79         if (parallaxMethod < 0)
80             parallaxMethod = 0;
81 
82         //setParameter("viewMatrix", rc.viewMatrix);
83         setParameter("modelViewMatrix", rc.modelViewMatrix);
84         setParameter("projectionMatrix", rc.projectionMatrix);
85         setParameter("invViewMatrix", rc.invViewMatrix);
86         setParameter("invModelMatrix", rc.invModelMatrix);
87         setParameter("viewSize", Vector2f(gbuffer.width, gbuffer.height));
88 
89         glActiveTexture(GL_TEXTURE0);
90         glBindTexture(GL_TEXTURE_2D, gbuffer.positionTexture);
91         setParameter("positionTexture", cast(int)0);
92 
93         // Diffuse
94         if (idiffuse.texture)
95         {
96             glActiveTexture(GL_TEXTURE1);
97             idiffuse.texture.bind();
98             setParameter("diffuseTexture", cast(int)1);
99             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorTexture");
100         }
101         else
102         {
103             setParameter("diffuseVector", rc.material.diffuse.asVector4f);
104             setParameterSubroutine("diffuse", ShaderType.Fragment, "diffuseColorValue");
105         }
106         
107         if (!ioutputColor.asBool)
108         {
109             glColorMaski(0, 0, 0, 0, 0);
110         }
111         
112         // Normal/height
113         bool haveHeightMap = inormal.texture !is null;
114         if (haveHeightMap)
115             haveHeightMap = inormal.texture.image.channels == 4;
116 
117         if (!haveHeightMap)
118         {
119             if (inormal.texture is null)
120             {
121                 if (iheight.texture !is null) // we have height map, but no normal map
122                 {
123                     Color4f color = Color4f(0.5f, 0.5f, 1.0f, 0.0f); // default normal pointing upwards
124                     inormal.texture = rc.material.makeTexture(color, iheight.texture);
125                     haveHeightMap = true;
126                 }
127             }
128             else
129             {
130                 if (iheight.texture !is null) // we have both normal and height maps
131                 {
132                     inormal.texture = rc.material.makeTexture(inormal.texture, iheight.texture);
133                     haveHeightMap = true;
134                 }
135             }
136         }
137         
138         if (inormal.texture)
139         {
140             setParameter("normalTexture", cast(int)2);
141             setParameterSubroutine("normal", ShaderType.Fragment, "normalMap");
142 
143             glActiveTexture(GL_TEXTURE2);
144             inormal.texture.bind();
145         }
146         else
147         {
148             setParameter("normalVector", rc.material.normal.asVector3f);
149             setParameterSubroutine("normal", ShaderType.Fragment, "normalValue");
150         }
151         
152         if (!ioutputNormal.asBool)
153         {
154             glColorMaski(2, 0, 0, 0, 0);
155         }
156         
157         // Height and parallax
158 
159         // TODO: make these material properties
160         float parallaxScale = 0.03f;
161         float parallaxBias = -0.01f;
162         setParameter("parallaxScale", parallaxScale);
163         setParameter("parallaxBias", parallaxBias);
164 
165         if (haveHeightMap)
166         {
167             setParameterSubroutine("height", ShaderType.Fragment, "heightMap");
168         }
169         else
170         {
171             float h = 0.0f; //-parallaxBias / parallaxScale;
172             setParameter("heightScalar", h);
173             setParameterSubroutine("height", ShaderType.Fragment, "heightValue");
174             parallaxMethod = ParallaxNone;
175         }
176 
177         if (parallaxMethod == ParallaxSimple)
178             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxSimple");
179         else if (parallaxMethod == ParallaxOcclusionMapping)
180             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxOcclusionMapping");
181         else
182             setParameterSubroutine("parallax", ShaderType.Fragment, "parallaxNone");
183 
184         // PBR
185         // TODO: pass solid values as uniforms, make subroutine for each mode
186         if (ipbr is null)
187         {
188             rc.material.setInput("pbr", 0.0f);
189             ipbr = "pbr" in rc.material.inputs;
190         }
191 
192         if (ipbr.texture is null)
193             ipbr.texture = rc.material.makeTexture(*iroughness, *imetallic, materialInput(0.0f), materialInput(0.0f));
194         glActiveTexture(GL_TEXTURE3);
195         ipbr.texture.bind();
196         setParameter("pbrTexture", 3);
197         
198         if (!ioutputPBR.asBool)
199         {
200             glColorMaski(1, 0, 0, 0, 0);
201         }
202         
203         // Emission
204         if (iemission.texture)
205         {
206             glActiveTexture(GL_TEXTURE4);
207             iemission.texture.bind();
208 
209             setParameter("emissionTexture", 4);
210             setParameterSubroutine("emission", ShaderType.Fragment, "emissionMap");
211         }
212         else
213         {
214             setParameter("emissionVector", rc.material.emission.asVector4f);
215             setParameterSubroutine("emission", ShaderType.Fragment, "emissionValue");
216         }
217 
218         setParameter("emissionEnergy", ienergy.asFloat);
219         
220         if (!ioutputEmission.asBool)
221         {
222             glColorMaski(3, 0, 0, 0, 0);
223         }
224 
225         glActiveTexture(GL_TEXTURE0);
226 
227         super.bind(rc);
228     }
229 
230     override void unbind(RenderingContext* rc)
231     {
232         super.unbind(rc);
233         
234         glColorMaski(0, 1, 1, 1, 1);
235         glColorMaski(1, 1, 1, 1, 1);
236         glColorMaski(2, 1, 1, 1, 1);
237         glColorMaski(3, 1, 1, 1, 1);
238 
239         glActiveTexture(GL_TEXTURE0);
240         glBindTexture(GL_TEXTURE_2D, 0);
241 
242         glActiveTexture(GL_TEXTURE1);
243         glBindTexture(GL_TEXTURE_2D, 0);
244         
245         glActiveTexture(GL_TEXTURE2);
246         glBindTexture(GL_TEXTURE_2D, 0);
247         
248         glActiveTexture(GL_TEXTURE3);
249         glBindTexture(GL_TEXTURE_2D, 0);
250         
251         glActiveTexture(GL_TEXTURE4);
252         glBindTexture(GL_TEXTURE_2D, 0);
253 
254         glActiveTexture(GL_TEXTURE0);
255     }
256 }