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 }