1 /*
2 Copyright (c) 2017-2018 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.materials.generic;
29 
30 import std.stdio;
31 import std.algorithm;
32 
33 import dlib.core.memory;
34 import dlib.math.vector;
35 import dlib.image.color;
36 import dlib.image.image;
37 import dlib.image.unmanaged;
38 import dlib.image.render.shapes;
39 import derelict.opengl;
40 import dagon.core.ownership;
41 import dagon.graphics.material;
42 import dagon.graphics.texture;
43 import dagon.graphics.rc;
44 
45 interface GenericMaterialBackend
46 {
47     final bool boolProp(GenericMaterial mat, string prop)
48     {
49         auto p = prop in mat.inputs;
50         bool res = false;
51         if (p.type == MaterialInputType.Bool ||
52             p.type == MaterialInputType.Integer)
53         {
54             res = p.asBool;
55         }
56         return res;
57     }
58     
59     final int intProp(GenericMaterial mat, string prop)
60     {
61         auto p = prop in mat.inputs;
62         int res = 0;
63         if (p.type == MaterialInputType.Bool ||
64             p.type == MaterialInputType.Integer)
65         {
66             res = p.asInteger;
67         }
68         else if (p.type == MaterialInputType.Float)
69         {
70             res = cast(int)p.asFloat;
71         }
72         return res;
73     }
74     
75     final Texture makeOnePixelTexture(Material mat, Color4f color)
76     {
77         auto img = New!UnmanagedImageRGBA8(8, 8);
78         img.fillColor(color);
79         auto tex = New!Texture(img, mat);
80         return tex;
81     }
82     
83     final void packAlphaToTexture(Texture rgb, Texture alpha)
84     {
85         SuperImage rgbaImg = New!UnmanagedImageRGBA8(rgb.width, rgb.height);
86         foreach(y; 0..rgb.height)
87         foreach(x; 0..rgb.width)
88         {
89             Color4f col = rgb.image[x, y];
90             col.a = alpha.image[x, y].r;
91             rgbaImg[x, y] = col;
92         }
93             
94         rgb.release();
95         rgb.createFromImage(rgbaImg);
96     }
97     
98     final void packAlphaToTexture(Texture rgb, float alpha)
99     {
100         SuperImage rgbaImg = New!UnmanagedImageRGBA8(rgb.width, rgb.height);
101         foreach(y; 0..rgb.height)
102         foreach(x; 0..rgb.width)
103         {
104             Color4f col = rgb.image[x, y];
105             col.a = alpha;
106             rgbaImg[x, y] = col;
107         }
108             
109         rgb.release();
110         rgb.createFromImage(rgbaImg);
111     }
112     
113     final Texture makeTextureFrom(Material mat, MaterialInput r, MaterialInput g, MaterialInput b, MaterialInput a)
114     {
115         uint width = 8;
116         uint height = 8;
117         
118         if (r.texture !is null)
119         {
120             width = max(width, r.texture.width);
121             height = max(height, r.texture.height);
122         }
123         
124         if (g.texture !is null)
125         {
126             width = max(width, g.texture.width);
127             height = max(height, g.texture.height);
128         }
129         
130         if (b.texture !is null)
131         {
132             width = max(width, b.texture.width);
133             height = max(height, b.texture.height);
134         }
135         
136         if (a.texture !is null)
137         {
138             width = max(width, a.texture.width);
139             height = max(height, a.texture.height);
140         }
141         
142         SuperImage img = New!UnmanagedImageRGBA8(width, height);
143         
144         foreach(y; 0..img.height)
145         foreach(x; 0..img.width)
146         {
147             Color4f col = Color4f(0, 0, 0, 0);
148             
149             float u = cast(float)x / cast(float)img.width;
150             float v = cast(float)y / cast(float)img.height;
151             
152             col.r = r.sample(u, v).r;
153             col.g = g.sample(u, v).r;
154             col.b = b.sample(u, v).r;
155             col.a = a.sample(u, v).r;
156             
157             img[x, y] = col;
158         }
159         
160         auto tex = New!Texture(img, mat);
161         return tex;
162     }
163     
164     void bind(GenericMaterial mat, RenderingContext* rc);
165     void unbind(GenericMaterial mat, RenderingContext* rc);
166 }
167 
168 enum int None = 0;
169 
170 enum int ShadowFilterNone = 0;
171 enum int ShadowFilterPCF = 1;
172 
173 enum int ParallaxNone = 0;
174 enum int ParallaxSimple = 1;
175 enum int ParallaxOcclusionMapping = 2;
176 
177 enum int Opaque = 0;
178 enum int Transparent = 1;
179 enum int Additive = 2;
180 
181 class GenericMaterial: Material
182 {
183     protected GenericMaterialBackend _backend;
184 
185     this(GenericMaterialBackend backend, Owner o)
186     {
187         super(o);
188 
189         setInput("diffuse", Color4f(0.8f, 0.8f, 0.8f, 1.0f));
190         setInput("specular", Color4f(1.0f, 1.0f, 1.0f, 1.0f));
191         setInput("shadeless", false);
192         setInput("emission", Color4f(0.0f, 0.0f, 0.0f, 1.0f));
193         setInput("energy", 1.0f);
194         setInput("transparency", 1.0f);
195         setInput("roughness", 0.5f);
196         setInput("metallic", 0.0f);
197         setInput("normal", Vector3f(0.0f, 0.0f, 1.0f));
198         setInput("height", 0.0f);
199         setInput("parallax", ParallaxNone);
200         setInput("parallaxScale", 0.03f);
201         setInput("parallaxBias", -0.01f);
202         setInput("shadowsEnabled", true);
203         setInput("shadowFilter", ShadowFilterPCF);
204         setInput("fogEnabled", true);
205         setInput("blending", Opaque);
206         setInput("culling", true);
207         setInput("colorWrite", true);
208         setInput("depthWrite", true);
209         setInput("particleColor", Color4f(1.0f, 1.0f, 1.0f, 1.0f));
210 
211         _backend = backend;
212     }
213 
214     GenericMaterialBackend backend()
215     {
216         return _backend;
217     }
218 
219     void backend(GenericMaterialBackend b)
220     {
221         _backend = b;
222     }
223 
224     override void bind(RenderingContext* rc)
225     {
226         auto iblending = "blending" in inputs;
227         auto iculling = "culling" in inputs;
228         auto icolorWrite = "colorWrite" in inputs;
229         auto idepthWrite = "depthWrite" in inputs;
230         
231         if (iblending.asInteger == Transparent)
232         {
233             glEnablei(GL_BLEND, 0);
234             glEnablei(GL_BLEND, 2);
235             glBlendFunci(0, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
236             glBlendFunci(2, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
237         }
238         else if (iblending.asInteger == Additive)
239         {
240             glEnablei(GL_BLEND, 0);
241             glEnablei(GL_BLEND, 2);
242             glBlendFunci(0, GL_SRC_ALPHA, GL_ONE);
243             glBlendFunci(2, GL_SRC_ALPHA, GL_ONE);
244         }
245         
246         if (iculling.asBool)
247         {
248             glEnable(GL_CULL_FACE);
249         }
250         
251         if (!icolorWrite.asBool)
252         {
253             glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
254         }
255         
256         if (!idepthWrite.asBool)
257         {
258             glDepthMask(GL_FALSE);
259         }
260     
261         if (_backend)
262             _backend.bind(this, rc);
263     }
264 
265     override void unbind(RenderingContext* rc)
266     {
267         auto icolorWrite = "colorWrite" in inputs;
268         auto idepthWrite = "depthWrite" in inputs;
269         
270         if (_backend)
271             _backend.unbind(this, rc);
272             
273         if (!idepthWrite.asBool && rc.depthPass)
274         {
275             glDepthMask(GL_TRUE);
276         }
277             
278         if (!icolorWrite.asBool && rc.colorPass)
279         {
280             glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
281         }
282         
283         glDisable(GL_CULL_FACE);
284         
285         glDisablei(GL_BLEND, 0);
286         glDisablei(GL_BLEND, 2);
287     }
288 }
289 
290 abstract class GLSLMaterialBackend: Owner, GenericMaterialBackend
291 {
292     string vertexShaderSrc();
293     string fragmentShaderSrc();
294     
295     GLuint shaderProgram;
296     GLuint vertexShader;
297     GLuint fragmentShader;
298     
299     this(Owner o)
300     {
301         super(o);
302         
303         const(char*)pvs = vertexShaderSrc().ptr;
304         const(char*)pfs = fragmentShaderSrc().ptr;
305         
306         char[1000] infobuffer = 0;
307         int infobufferlen = 0;
308 
309         vertexShader = glCreateShader(GL_VERTEX_SHADER);
310         glShaderSource(vertexShader, 1, &pvs, null);
311         glCompileShader(vertexShader);
312         GLint success = 0;
313         glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
314         if (!success)
315         {
316             GLint logSize = 0;
317             glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logSize);
318             glGetShaderInfoLog(vertexShader, 999, &logSize, infobuffer.ptr);
319             writeln("Error in vertex shader:");
320             writeln(infobuffer[0..logSize]);
321         }
322 
323         fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
324         glShaderSource(fragmentShader, 1, &pfs, null);
325         glCompileShader(fragmentShader);
326         success = 0;
327         glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
328         if (!success)
329         {
330             GLint logSize = 0;
331             glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logSize);
332             glGetShaderInfoLog(fragmentShader, 999, &logSize, infobuffer.ptr);
333             writeln("Error in fragment shader:");
334             writeln(infobuffer[0..logSize]);
335         }
336 
337         shaderProgram = glCreateProgram();
338         glAttachShader(shaderProgram, vertexShader);
339         glAttachShader(shaderProgram, fragmentShader);
340         glLinkProgram(shaderProgram);
341     }
342     
343     void bind(GenericMaterial mat, RenderingContext* rc)
344     {
345         glUseProgram(shaderProgram);
346     }
347     
348     void unbind(GenericMaterial mat, RenderingContext* rc)
349     {
350         glUseProgram(0);
351     }
352 }