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 }