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 82 public: 83 VertexInfo!float vertexInfo; /++ Information about the vertices of the 84 texture being drawn. +/ 85 86 @trusted: 87 /// Texture width 88 @property uint width() inout 89 { 90 return _width; 91 } 92 93 /// Texture height 94 @property uint height() inout 95 { 96 return _height; 97 } 98 99 /// The ID of the texture in the open graphics library. 100 @property uint id() inout 101 { 102 return glid; 103 } 104 105 /++ 106 Edits texture parameters based on an array of parameters. 107 108 Params: 109 parametrs = Array of parametrs. 110 +/ 111 void editParametrs(int[] parametrs) 112 { 113 for (int i = 0; i < parametrs.length; i += 2) 114 { 115 glTexParameteri(GL_TEXTURE_2D, parametrs[i], parametrs[i + 1]); 116 } 117 } 118 119 /++ 120 Initializes the texture from the input data (if the texture was previously 121 initialized, the data will be updated). 122 123 Params: 124 information = Texture information structure. 125 +/ 126 void initializeFromData(int format)(TextureInfo information) 127 { 128 import tida.color : fromFormat; 129 130 _width = information.width; 131 _height = information.height; 132 133 ubyte[] data = information.data.fromFormat!(format, PixelFormat.RGBA); 134 135 if (glid == 0) 136 { 137 glGenTextures(1, &glid); 138 glBindTexture(GL_TEXTURE_2D, glid); 139 editParametrs(information.params); 140 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, 141 GL_UNSIGNED_BYTE, cast(void*) data); 142 glBindTexture(GL_TEXTURE_2D, 0); 143 } else 144 { 145 glBindTexture(GL_TEXTURE_2D, glid); 146 editParametrs(information.params); 147 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _width, _height, GL_RGBA, 148 GL_UNSIGNED_BYTE, cast(void*) data); 149 glBindTexture(GL_TEXTURE_2D, 0); 150 } 151 } 152 153 /// Bind the texture to the current render cycle. 154 void bind() 155 { 156 glActiveTexture(GL_TEXTURE0); 157 glBindTexture(GL_TEXTURE_2D, glid); 158 } 159 160 /// Unbind the texture to the current render cycle. 161 static void unbind() 162 { 163 glBindTexture(GL_TEXTURE_2D, 0); 164 } 165 166 enum deprecatedVertex = 167 "#version 130 168 in vec2 position; 169 in vec2 texCoord; 170 171 uniform mat4 projection; 172 uniform mat4 model; 173 174 out vec2 fragTexCoord; 175 176 void main() 177 { 178 gl_Position = projection * model * vec4(position, 0.0, 1.0); 179 fragTexCoord = texCoord; 180 } 181 "; 182 183 enum deprecatedFragment = 184 "#version 130 185 in vec2 fragTexCoord; 186 187 uniform vec4 color = vec4(1.0f, 1.0f, 1.0f, 1.0f); 188 uniform sampler2D texture; 189 190 void main() 191 { 192 gl_FragColor = texture2D(texture, fragTexCoord) * color; 193 } 194 "; 195 196 enum modernVertex = 197 "#version 330 core 198 layout(location = 0) in vec2 position; 199 layout(location = 1) in vec2 texCoord; 200 201 uniform mat4 projection; 202 uniform mat4 model; 203 204 out vec2 fragTexCoord; 205 206 void main() 207 { 208 gl_Position = projection * model * vec4(position, 0.0, 1.0); 209 fragTexCoord = texCoord; 210 } 211 "; 212 213 enum modernFragment = 214 "#version 330 core 215 in vec2 fragTexCoord; 216 217 uniform vec4 color = vec4(1.0, 1.0, 1.0, 1.0); 218 uniform sampler2D ctexture; 219 220 out vec4 fragColor; 221 222 void main() 223 { 224 fragColor = texture(ctexture, fragTexCoord) * color; 225 } 226 "; 227 228 /++ 229 Initializes the shader for rendering the texture 230 (if no shader was specified in the current rendering, then the default 231 shader is taken). 232 233 Params: 234 render = Renderer object. 235 +/ 236 Shader!Program initShader(IRenderer render) 237 { 238 if (render.currentShader !is null) 239 return render.currentShader; 240 241 if (render.getShader("DefaultImage") is null) 242 { 243 Shader!Program program = new Shader!Program(); 244 245 string vsource, fsource; 246 bool isModern = (cast(GLRender) render).isModern; 247 248 if (isModern) 249 { 250 vsource = modernVertex; 251 fsource = modernFragment; 252 } else 253 { 254 vsource = deprecatedVertex; 255 fsource = deprecatedFragment; 256 } 257 258 Shader!Vertex vertex = new Shader!Vertex(); 259 vertex.bindSource(vsource); 260 261 Shader!Fragment fragment = new Shader!Fragment(); 262 fragment.bindSource(fsource); 263 264 program.attach(vertex); 265 program.attach(fragment); 266 program.link(); 267 268 render.setShader("DefaultImage", program); 269 270 return program; 271 } 272 273 return render.getShader("DefaultImage"); 274 } 275 276 void destroy() 277 { 278 glDeleteTextures(1, &glid); 279 } 280 281 ~this() 282 { 283 this.destroy(); 284 } 285 286 override: 287 void draw(IRenderer renderer, Vecf position) 288 { 289 Shader!Program shader = this.initShader(renderer); 290 291 vertexInfo.bindVertexArray(); 292 vertexInfo.bindBuffer(); 293 if (vertexInfo.idElementArray != 0) vertexInfo.bindElementBuffer(); 294 295 shader.enableVertex("position"); 296 vertexInfo.vertexAttribPointer(shader.getAttribLocation("position"), 4); 297 298 shader.enableVertex("texCoord"); 299 vertexInfo.textureAttribPointer(shader.getAttribLocation("texCoord"), 4); 300 vertexInfo.unbindBuffer(); 301 302 mat4 proj = (cast(GLRender) renderer).projection; 303 mat4 model = identity(); 304 305 model = mulmat(model, renderer.currentModelMatrix); 306 model = translate(model, position.x, position.y, 0.0f); 307 308 shader.using(); 309 310 bind(); 311 312 if (shader.getUniformLocation("projection") != -1) 313 shader.setUniform("projection", proj); 314 315 316 if (shader.getUniformLocation("model") != -1) 317 shader.setUniform("model", model); 318 319 if (shader.getUniformLocation("color") != -1) 320 shader.setUniform("color", rgba(255, 255, 255, 255)); 321 322 vertexInfo.draw(vertexInfo.shapeinfo.type); 323 324 vertexInfo.unbindBuffer(); 325 if (vertexInfo.idElementArray != 0) vertexInfo.unbindElementBuffer(); 326 vertexInfo.unbindVertexArray(); 327 unbind(); 328 329 renderer.resetShader(); 330 renderer.resetModelMatrix(); 331 } 332 333 void drawEx(IRenderer renderer, 334 Vecf position, 335 float angle, 336 Vecf center, 337 Vecf size, 338 ubyte alpha, 339 Color!ubyte color = rgb(255, 255, 255)) 340 { 341 Shader!Program shader = this.initShader(renderer); 342 343 Vecf scaleFactor; 344 if (!size.isVecfNaN) 345 scaleFactor = size / vecf(width, height); 346 else 347 scaleFactor = vecf(1.0f, 1.0f); 348 349 if (center.isVecfNaN) 350 center = (vecf(width, height) * scaleFactor) / 2; 351 352 vertexInfo.bindVertexArray(); 353 vertexInfo.bindBuffer(); 354 if (vertexInfo.idElementArray != 0) vertexInfo.bindElementBuffer(); 355 356 shader.enableVertex("position"); 357 vertexInfo.vertexAttribPointer(shader.getAttribLocation("position"), 4); 358 359 shader.enableVertex("texCoord"); 360 vertexInfo.textureAttribPointer(shader.getAttribLocation("texCoord"), 4); 361 vertexInfo.unbindBuffer(); 362 363 mat4 proj = (cast(GLRender) renderer).projection; 364 mat4 model = identity(); 365 366 model = mulmat(model, renderer.currentModelMatrix); 367 model = scale(model, scaleFactor.x, scaleFactor.y, 1.0f); 368 369 model = translate(model, -center.x, -center.y, 0.0f); 370 model = rotateMat(model, -angle, 0.0f, 0.0f, 1.0f); 371 model = translate(model, center.x, center.y, 0.0f); 372 373 model = translate(model, position.x, position.y, 0.0f); 374 375 shader.using(); 376 377 bind(); 378 379 if (shader.getUniformLocation("projection") != -1) 380 shader.setUniform("projection", proj); 381 382 383 if (shader.getUniformLocation("model") != -1) 384 shader.setUniform("model", model); 385 386 if (shader.getUniformLocation("color") != -1) 387 shader.setUniform("color", rgba(color.r, color.g, color.b, alpha)); 388 389 vertexInfo.draw(vertexInfo.shapeinfo.type); 390 391 vertexInfo.unbindBuffer(); 392 if (vertexInfo.idElementArray != 0) vertexInfo.unbindElementBuffer(); 393 vertexInfo.unbindVertexArray(); 394 unbind(); 395 396 renderer.resetShader(); 397 renderer.resetModelMatrix(); 398 } 399 }