1 /++ 2 A module for managing textures. Not to be confused 3 with $(HREF image.html#Image, Image), which directly lie in RAM and can always 4 be changed, unlike textures, which need to initialize or update texture data. 5 6 Macros: 7 LREF = <a href="#$1">$1</a> 8 HREF = <a href="$1">$2</a> 9 10 Authors: $(HREF https://github.com/TodNaz,TodNaz) 11 Copyright: Copyright (c) 2020 - 2021, TodNaz. 12 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 13 +/ 14 module tida.texture; 15 16 import std.traits; 17 import tida.gl; 18 import tida.drawable; 19 import tida.render; 20 21 enum MinFilter = GL_TEXTURE_MIN_FILTER; /// Min Filter 22 enum MagFilter = GL_TEXTURE_MAG_FILTER; /// Mag Filter 23 24 enum WrapS = GL_TEXTURE_WRAP_S; 25 enum WrapT = GL_TEXTURE_WRAP_T; 26 27 /// Texture filter method 28 enum TextureFilter 29 { 30 Nearest = GL_NEAREST, /// Nearest method 31 Linear = GL_LINEAR /// Linear method 32 } 33 34 /// Texture wrap method 35 enum TextureWrap 36 { 37 ClampToEdge = GL_CLAMP_TO_EDGE, /// Clamp to Edge method 38 Repeat = GL_REPEAT, /// Repeat method 39 MirroredRepeat = GL_MIRRORED_REPEAT /// Mirrored repeat method 40 } 41 42 /// Default texture params filter 43 enum DefaultParams = [ 44 MinFilter, TextureFilter.Nearest, 45 MagFilter, TextureFilter.Nearest 46 ]; 47 48 /++ 49 Texture initialization structure. 50 51 Example: 52 --- 53 Image image = new Image().load("default.png"); 54 TextureInfo tinfo; 55 tinfo.width = image.width; 56 tinfo.height = image.height; 57 tinfo.data = image.bytes!(PixelFormat.RGBA); 58 ... 59 --- 60 +/ 61 struct TextureInfo 62 { 63 uint width; /// Texture width 64 uint height; /// Texture height 65 ubyte[] data; /// Texture image data 66 int[] params = DefaultParams; /// Texture initialization parametr's. 67 } 68 69 /++ 70 A texture object for manipulating its data, properties, and parameters. 71 +/ 72 class Texture : IDrawable, IDrawableEx, ITarget 73 { 74 import tida.vertgen; 75 import tida.shader; 76 import tida.vector; 77 import tida.color; 78 import tida.matrix; 79 import tida.shape; 80 81 private: 82 uint glid = 0; 83 uint _width = 0; 84 uint _height = 0; 85 uint type = GL_TEXTURE_2D; 86 int activeid = GL_TEXTURE0; 87 uint fbo; 88 uint rbo; 89 90 public: 91 VertexInfo!float vertexInfo; /++ Information about the vertices of the 92 texture being drawn. +/ 93 ShapeType drawType; 94 95 @trusted: 96 /// Texture width 97 @property uint width() inout 98 { 99 return _width; 100 } 101 102 /// Texture height 103 @property uint height() inout 104 { 105 return _height; 106 } 107 108 /// The ID of the texture in the open graphics library. 109 @property uint id() inout 110 { 111 return glid; 112 } 113 114 /++ 115 Edits texture parameters based on an array of parameters. 116 117 Params: 118 parametrs = Array of parametrs. 119 +/ 120 void editParametrs(int[] parametrs) 121 { 122 for (int i = 0; i < parametrs.length; i += 2) 123 { 124 glTexParameteri(type, parametrs[i], parametrs[i + 1]); 125 } 126 } 127 128 /++ 129 Initializes the texture from the input data (if the texture was previously 130 initialized, the data will be updated). 131 132 Params: 133 information = Texture information structure. 134 +/ 135 void initializeFromData(int format)(TextureInfo information) 136 { 137 import tida.color : fromFormat; 138 139 type = GL_TEXTURE_2D; 140 _width = information.width; 141 _height = information.height; 142 143 ubyte[] data = information.data.fromFormat!(format, PixelFormat.RGBA); 144 145 if (glid == 0) 146 { 147 glGenTextures(1, &glid); 148 glBindTexture(GL_TEXTURE_2D, glid); 149 editParametrs(information.params); 150 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, 151 GL_UNSIGNED_BYTE, cast(void*) data); 152 glBindTexture(GL_TEXTURE_2D, 0); 153 } else 154 { 155 glBindTexture(GL_TEXTURE_2D, glid); 156 editParametrs(information.params); 157 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, GL_RGBA, 158 GL_UNSIGNED_BYTE, cast(void*) data); 159 glBindTexture(GL_TEXTURE_2D, 0); 160 } 161 } 162 163 void initializeArrayFromData(int format)(TextureInfo[] informations) 164 { 165 import tida.color : fromFormat; 166 167 _width = informations[0].width; 168 _height = informations[1].height; 169 type = GL_TEXTURE_2D_ARRAY_EXT; 170 171 ubyte[] data; 172 173 foreach (e; informations) 174 { 175 data ~= e.data.fromFormat!(format, PixelFormat.RGBA); 176 } 177 178 if (glid == 0) 179 { 180 glGenTextures(1, &glid); 181 glBindTexture(type, glid); 182 183 glTexStorage3D(type, 1, GL_RGBA, _width, _height, cast(int) informations.length); 184 glPixelStorei(GL_UNPACK_ROW_LENGTH, cast(int) (_width * informations.length)); 185 glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, cast(int) (_height * informations.length)); 186 187 for (size_t i = 0; i < informations.length; ++i) 188 { 189 glTexSubImage3D(type, 190 0, 0, 0, 191 cast(int) i, 192 _width, 193 _height, 194 1, 195 GL_RGBA, 196 GL_UNSIGNED_BYTE, 197 cast(void*) (data.ptr + (i * (_width * _height) * 4))); 198 } 199 200 glTexParameteri(type, GL_TEXTURE_BASE_LEVEL, 0); 201 editParametrs(informations[0].params); 202 203 glBindTexture(type, 0); 204 } 205 } 206 207 void inActive(int id) 208 { 209 activeid = GL_TEXTURE0 + id; 210 } 211 212 /// Bind the texture to the current render cycle. 213 void bind() 214 { 215 glActiveTexture(activeid); 216 glBindTexture(type, glid); 217 } 218 219 /// Unbind the texture to the current render cycle. 220 void unbind() 221 { 222 glBindTexture(type, 0); 223 } 224 225 enum deprecatedVertex = 226 "#version 130 227 in vec2 position; 228 in vec2 texCoord; 229 230 uniform mat4 projection; 231 uniform mat4 model; 232 233 out vec2 fragTexCoord; 234 235 void main() 236 { 237 gl_Position = projection * model * vec4(position, 0.0, 1.0); 238 fragTexCoord = texCoord; 239 } 240 "; 241 242 enum deprecatedFragment = 243 "#version 130 244 in vec2 fragTexCoord; 245 246 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 247 uniform sampler2D texture; 248 249 void main() 250 { 251 gl_FragColor = texture2D(texture, fragTexCoord) * color; 252 } 253 "; 254 255 enum modernVertex = 256 "#version 330 core 257 layout(location = 0) in vec2 position; 258 layout(location = 1) in vec2 texCoord; 259 260 uniform mat4 projection; 261 uniform mat4 model; 262 263 out vec2 fragTexCoord; 264 265 void main() 266 { 267 gl_Position = projection * model * vec4(position, 0.0, 1.0); 268 fragTexCoord = texCoord; 269 } 270 "; 271 272 enum modernFragment = 273 "#version 330 core 274 in vec2 fragTexCoord; 275 276 uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0); 277 uniform sampler2D ctexture; 278 279 out vec4 fragColor; 280 281 void main() 282 { 283 fragColor = texture(ctexture, fragTexCoord) * color; 284 } 285 "; 286 287 /++ 288 Initializes the shader for rendering the texture 289 (if no shader was specified in the current rendering, then the default 290 shader is taken). 291 292 Params: 293 render = Renderer object. 294 +/ 295 Shader!Program initShader(IRenderer render) 296 { 297 if (render.currentShader !is null) 298 return render.currentShader; 299 300 if (render.getShader("DefaultImage") is null) 301 { 302 Shader!Program program = new Shader!Program(); 303 304 string vsource, fsource; 305 bool isModern = (cast(GLRender) render).isModern; 306 307 if (isModern) 308 { 309 vsource = modernVertex; 310 fsource = modernFragment; 311 } else 312 { 313 vsource = deprecatedVertex; 314 fsource = deprecatedFragment; 315 } 316 317 Shader!Vertex vertex = new Shader!Vertex(); 318 vertex.bindSource(vsource); 319 320 Shader!Fragment fragment = new Shader!Fragment(); 321 fragment.bindSource(fsource); 322 323 program.attach(vertex); 324 program.attach(fragment); 325 program.link(); 326 327 render.setShader("DefaultImage", program); 328 329 return program; 330 } 331 332 return render.getShader("DefaultImage"); 333 } 334 335 void clear(Color!ubyte color) @trusted 336 { 337 import std.algorithm : fill; 338 339 Color!ubyte[] data = new Color!ubyte[](width * height); 340 data.fill(color); 341 342 bind(); 343 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, _width, _height, GL_RGBA, 344 GL_UNSIGNED_BYTE, cast(void*) data); 345 unbind(); 346 } 347 348 void generateFrameBuffer() @trusted 349 { 350 glGenFramebuffers(1, &fbo); 351 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 352 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glid, 0); 353 glBindFramebuffer(GL_FRAMEBUFFER, 0); 354 } 355 356 void destroy() 357 { 358 glDeleteTextures(1, &glid); 359 } 360 361 ~this() 362 { 363 this.destroy(); 364 } 365 366 override: 367 void bind(IRenderer render) @trusted 368 { 369 glBindFramebuffer(GL_FRAMEBUFFER, fbo); 370 } 371 372 void unbind(IRenderer render) @trusted 373 { 374 glBindFramebuffer(GL_FRAMEBUFFER, 0); 375 } 376 377 void drawning(IRenderer render) @trusted 378 { 379 return; 380 } 381 382 void draw(IRenderer renderer, Vecf position) 383 { 384 Shader!Program shader = this.initShader(renderer); 385 386 mat4 proj = (cast(GLRender) renderer).projection; 387 mat4 model = identity(); 388 389 model = mulmat(model, renderer.currentModelMatrix); 390 model = translate(model, position.x, position.y, 0.0f); 391 392 vertexInfo.bind(); 393 if (vertexInfo.elements !is null) 394 vertexInfo.elements.bind(); 395 396 shader.using(); 397 398 glActiveTexture(GL_TEXTURE0); 399 bind(); 400 401 shader.enableVertex("position"); 402 shader.enableVertex("texCoord"); 403 404 if (shader.getUniformLocation("projection") != -1) 405 shader.setUniform("projection", proj); 406 407 if (shader.getUniformLocation("model") != -1) 408 shader.setUniform("model", model); 409 410 if (shader.getUniformLocation("color") != -1) 411 shader.setUniform("color", rgba(255, 255, 255, 255)); 412 413 if (shader.getUniformLocation("size") != -1) 414 shader.setUniform("size", vec!float(width, height)); 415 416 debug (GLError) checkGLError(); 417 418 vertexInfo.draw (drawType); 419 420 if (vertexInfo.elements !is null) 421 vertexInfo.elements.unbind(); 422 vertexInfo.unbind(); 423 unbind(); 424 425 renderer.resetShader(); 426 renderer.resetModelMatrix(); 427 } 428 429 void drawEx(IRenderer renderer, 430 Vecf position, 431 float angle, 432 Vecf center, 433 Vecf size, 434 ubyte alpha, 435 Color!ubyte color = rgb(255, 255, 255)) 436 { 437 Shader!Program shader = this.initShader(renderer); 438 439 Vecf scaleFactor; 440 if (!size.isVecfNaN) 441 scaleFactor = size / vecf(width, height); 442 else 443 scaleFactor = vecf(1.0f, 1.0f); 444 445 if (center.isVecfNaN) 446 center = (vecf(width, height) * scaleFactor) / 2; 447 448 mat4 proj = (cast(GLRender) renderer).projection; 449 mat4 model = identity(); 450 451 model = mulmat(model, renderer.currentModelMatrix); 452 model = scale(model, scaleFactor.x, scaleFactor.y, 1.0f); 453 454 model = translate(model, -center.x, -center.y, 0.0f); 455 model = rotateMat(model, -angle, 0.0f, 0.0f, 1.0f); 456 model = translate(model, center.x, center.y, 0.0f); 457 458 model = translate(model, position.x, position.y, 0.0f); 459 460 vertexInfo.bind(); 461 if (vertexInfo.elements !is null) 462 vertexInfo.elements.bind(); 463 464 shader.using(); 465 466 glActiveTexture(GL_TEXTURE0); 467 bind(); 468 469 shader.enableVertex("position"); 470 shader.enableVertex("texCoord"); 471 472 if (shader.getUniformLocation("projection") != -1) 473 shader.setUniform("projection", proj); 474 475 if (shader.getUniformLocation("model") != -1) 476 shader.setUniform("model", model); 477 478 if (shader.getUniformLocation("color") != -1) 479 shader.setUniform("color", rgba(color.r, color.g, color.b, alpha)); 480 481 if (shader.getUniformLocation("size") != -1) 482 shader.setUniform("size", vec!float(width, height)); 483 484 debug (GLError) checkGLError(); 485 486 vertexInfo.draw (drawType); 487 488 if (vertexInfo.elements !is null) 489 vertexInfo.elements.unbind(); 490 vertexInfo.unbind(); 491 unbind(); 492 493 renderer.resetShader(); 494 renderer.resetModelMatrix(); 495 } 496 }