1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module g4d.gl.texture;
8 import g4d.gl.lib,
9        g4d.gl.type,
10        g4d.math.rational,
11        g4d.util.bitmap;
12 import gl3n.linalg;
13 import std.conv;
14 
15 /// A baseclass of OpenGL texture.
16 abstract class Texture
17 {
18     protected static Texture _bindedTexture;
19 
20     /// Invalid texture id.
21     enum NullId = 0;
22 
23     protected GLuint _id;
24     /// OpenGL texture id.
25     const @property id () { return _id; }
26 
27     /// Size of this texture.
28     const vec2i  size;
29 
30     /// GL_TEXTURE_2D, etc...
31     const pure @property GLenum target ();
32 
33     ///
34     this ( vec2i sz )
35     {
36         size = sz;
37 
38         enforce!glGenTextures( 1, &_id );
39         bind();
40 
41         enforce!glTexParameteri( target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
42         enforce!glTexParameteri( target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
43     }
44 
45     ///
46     ~this ()
47     {
48         dispose();
49     }
50     /// Checks if the texture is disposed.
51     const @property disposed ()
52     {
53         return _id == NullId;
54     }
55     /// Deletes the texture.
56     void dispose ()
57     {
58         if ( !disposed ) {
59             enforce!glDeleteTextures( 1, &_id );
60         }
61         if ( binded ) {
62             _bindedTexture = null;
63         }
64         _id = NullId;
65     }
66 
67     /// Checks if the texture is binded.
68     const @property binded ()
69     {
70         return _bindedTexture is this;
71     }
72     /// Sets the texture binded.
73     const void bind ()
74     {
75         if ( !binded ) {
76             enforce!glBindTexture( target, id );
77             _bindedTexture = cast(Texture) this;
78         }
79     }
80 }
81 
82 /// A 2-dimensional texture.
83 /// target returns GL_TEXTURE_2D.
84 class Tex2D : Texture
85 {
86     protected static auto resizeBitmapPower2 (B) ( in B bmp )
87         if ( isBitmap!B )
88     {
89         auto sz = vec2i( bmp.width.nextPower2.to!int,
90                 bmp.rows.nextPower2.to!int );
91         return bmp.conservativeResize( sz );
92     }
93 
94     ///
95     override const pure @property GLenum target ()
96     {
97         return GL_TEXTURE_2D;
98     }
99 
100     ///
101     this (B) ( in B bmp, bool compress = false )
102         if ( isBitmap!B )
103     {
104         auto formatted = resizeBitmapPower2( bmp );
105 
106         super( formatted.size );
107         enum type = toGLType!(B.Type);
108         enum lpp  = B.LengthPerPixel;
109 
110         enum  srcFormat = lpp.toFormat;
111         const texFormat = compress?
112             lpp.toCompressedFormat: srcFormat;
113 
114         enforce!glTexImage2D( target, 0, texFormat,
115                 size.x, size.y, 0, srcFormat, type, formatted.data );
116         formatted.dispose();
117     }
118 
119     /// Creates an empty texture.
120     /// Compressing is not allowed,
121     /// because compressed texture cannot use glTexSubImage2D.
122     this ( vec2i sz, uint lpp = 4 )
123     {
124         super( vec2i( sz.x.nextPower2, sz.y.nextPower2 ) );
125 
126         enforce!glTexImage2D( target, 0, lpp.toFormat,
127                 size.x, size.y, 0, GL_RED,
128                 GL_UNSIGNED_BYTE, null );
129     }
130 
131     /// Modifies the texture.
132     void overwrite (B) ( in B bmp, vec2i offset = vec2i(0,0) )
133         if ( isBitmap!B )
134     {
135         auto formatted = resizeBitmapPower2( bmp );
136         scope(exit) formatted.dispose();
137 
138         enum  type    = toGLType!(B.bitType);
139         enum  format  = B.lengthPerPixel.toFormat;
140         const bmpSize = formatted.size;
141 
142         bind();
143         enforce!glTexSubImage2D( target, 0, offset.x, offset.y,
144                 bmpSize.x, bmpSize.y, format, type, formatted.data );
145     }
146 }