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 }