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 }