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