1 module d2d.rendering.texture;
2
3 import d2d;
4
5 import std.conv : to;
6
7 /// Texture filter mode for min and mag filters.
8 enum TextureFilterMode : int
9 {
10 Linear = GL_LINEAR, /// `GL_LINEAR` sampling. Smooth looking textures.
11 Nearest = GL_NEAREST, /// `GL_NEAREST` sampling. No smoothing.
12 NearestMipmapNearest = GL_NEAREST_MIPMAP_NEAREST, /// `GL_NEAREST_MIPMAP_NEAREST` sampling. Not usable in mag filter.
13 LinearMipmapNearest = GL_LINEAR_MIPMAP_NEAREST, /// `GL_LINEAR_MIPMAP_NEAREST` sampling. Not usable in mag filter.
14 NearestMipmapLinear = GL_NEAREST_MIPMAP_LINEAR, /// `GL_NEAREST_MIPMAP_LINEAR` sampling. Not usable in mag filter.
15 LinearMipmapLinear = GL_LINEAR_MIPMAP_LINEAR, /// `GL_LINEAR_MIPMAP_LINEAR` sampling. Not usable in mag filter.
16 }
17
18 /// Texture clamp mode for wrap x, wrap y, wrap z.
19 enum TextureClampMode : int
20 {
21 ClampToBorder = GL_CLAMP_TO_BORDER, /// Clamps the texture coordinate at the border. Will include a border.
22 ClampToEdge = GL_CLAMP_TO_EDGE, /// Clamps the texture coordinate at the edge of the texture.
23 Repeat = GL_REPEAT, /// Repeats the texture coordinate when being larger than 1.
24 Mirror = GL_MIRRORED_REPEAT /// Repeats the texture coordinate and mirrors every time for better tiling.
25 }
26
27 /// Texture for drawing using OpenGL.
28 class Texture : IDisposable, IVerifiable
29 {
30 /// Enable mipmaps. Disabled by default.
31 public bool enableMipMaps = false;
32
33 /// Min Filter for this texture.
34 /// Needs to call Texture.applyParameters when called after creation.
35 public TextureFilterMode minFilter = TextureFilterMode.Linear;
36
37 /// Mag Filter for this texture.
38 /// Needs to call Texture.applyParameters when called after creation.
39 public TextureFilterMode magFilter = TextureFilterMode.Linear;
40
41 /// Wrap x for this texture.
42 /// Needs to call Texture.applyParameters when called after creation.
43 public TextureClampMode wrapX = TextureClampMode.Repeat;
44
45 /// Wrap y Filter for this texture.
46 /// Needs to call Texture.applyParameters when called after creation.
47 public TextureClampMode wrapY = TextureClampMode.Repeat;
48
49 private int inMode, mode;
50 private uint _id;
51 private uint _width, _height;
52
53 /// OpenGL id of this texture.
54 /// id == 0 when not created.
55 public @property uint id()
56 {
57 return _id;
58 }
59
60 /// Width of this texture.
61 /// width == 0 when not created.
62 public @property uint width()
63 {
64 return _width;
65 }
66
67 /// Height of this texture.
68 /// height == 0 when not created.
69 public @property uint height()
70 {
71 return _height;
72 }
73
74 /// 1x1 texture containing a white pixel for solid shapes.
75 public static @property Texture white()
76 {
77 return _white;
78 }
79
80 private static Texture _white;
81
82 public static void load()
83 {
84 _white = new Texture();
85 _white.create(1, 1, cast(ubyte[])[255, 255, 255, 255]);
86 }
87
88 /// Only allocates memory for the instance but does not create anything.
89 public this() {}
90
91 /// Creates and loads the texture with the given filters.
92 public this(string file, TextureFilterMode min = TextureFilterMode.Linear, TextureFilterMode mag = TextureFilterMode.Linear, TextureClampMode wrapX = TextureClampMode.Repeat, TextureClampMode wrapY = TextureClampMode.Repeat)
93 {
94 if (min == TextureFilterMode.LinearMipmapLinear || min == TextureFilterMode.LinearMipmapNearest)
95 enableMipMaps = true;
96 minFilter = min;
97 magFilter = mag;
98 this.wrapX = wrapX;
99 this.wrapY = wrapY;
100 fromBitmap(Bitmap.load(file));
101 }
102
103 public ~this()
104 {
105 dispose();
106 }
107
108 /// Creates a width x height texture containing the pixel data in RGBA ubyte format.
109 public void create(uint width, uint height, void[] pixels)
110 {
111 create(width, height, GL_RGBA, pixels);
112 }
113
114 /// Recreates a width x height texture containing the pixel data in RGBA ubyte format without disposing the old one.
115 public void recreate(uint width, uint height, void[] pixels)
116 {
117 recreate(width, height, GL_RGBA, pixels);
118 }
119
120 /// Creates a width x height texture containing the pixel data using ubytes.
121 public void create(uint width, uint height, int mode, void[] pixels)
122 {
123 glGenTextures(1, &_id);
124 glBindTexture(GL_TEXTURE_2D, _id);
125
126 glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr);
127
128 applyParameters();
129
130 this.inMode = mode;
131 this.mode = mode;
132 _width = width;
133 _height = height;
134
135 if (!valid)
136 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError()));
137 }
138
139 /// Recreates a width x height texture containing the pixel data using ubytes without disposing the old one.
140 public void recreate(uint width, uint height, int mode, void[] pixels)
141 {
142 bind(0);
143 glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr);
144
145 applyParameters();
146
147 this.inMode = mode;
148 this.mode = mode;
149 _width = width;
150 _height = height;
151
152 if (!valid)
153 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError()));
154 }
155
156 /// Creates a width x height texture containing the pixel data in `inMode` format using `type` as array type and internally convertes to `mode`.
157 public void create(uint width, uint height, int inMode, int mode, void[] pixels, int type = GL_UNSIGNED_BYTE)
158 {
159 glGenTextures(1, &_id);
160 glBindTexture(GL_TEXTURE_2D, _id);
161
162 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, type, pixels.ptr);
163
164 applyParameters();
165
166 this.inMode = inMode;
167 this.mode = mode;
168 _width = width;
169 _height = height;
170
171 if (!valid)
172 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError()));
173 }
174
175 /// Recreates a width x height texture containing the pixel data in `inMode` format using `type` as array type and internally convertes to `mode` without disposing the old one.
176 public void recreate(uint width, uint height, int inMode, int mode, void[] pixels, int type = GL_UNSIGNED_BYTE)
177 {
178 bind(0);
179 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, type, pixels.ptr);
180
181 applyParameters();
182
183 this.inMode = inMode;
184 this.mode = mode;
185 _width = width;
186 _height = height;
187
188 if (!valid)
189 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError()));
190 }
191
192 /// Use this when changing filter or wrap after creating the texture.
193 /// Calls automatically after `create`.
194 public void applyParameters()
195 {
196 bind(0);
197
198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
200
201 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapX);
202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapY);
203
204 if (enableMipMaps)
205 {
206 glGenerateMipmap(GL_TEXTURE_2D);
207 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
208 }
209 }
210
211 /// Binds the current texture to the given unit.
212 public void bind(uint unit)
213 {
214 glActiveTexture(GL_TEXTURE0 + unit);
215 glBindTexture(GL_TEXTURE_2D, _id);
216 }
217
218 /// Creates the texture from a bitmap.
219 public void fromBitmap(Bitmap bitmap, string name = "Bitmap")
220 {
221 if (!bitmap.valid)
222 throw new Exception(name ~ " is invalid!");
223
224 int mode = GL_RGB;
225
226 if (bitmap.surface.format.BytesPerPixel == 4)
227 {
228 mode = GL_RGBA;
229 }
230
231 create(bitmap.width, bitmap.height, mode, bitmap.surface.pixels[0 .. bitmap.width * bitmap.height * bitmap.surface.format.BytesPerPixel]);
232 }
233
234 /// Creates the texture from a bitmap without disposing the old one.
235 public void recreateFromBitmap(Bitmap bitmap, string name = "Bitmap")
236 {
237 if (!bitmap.valid)
238 throw new Exception(name ~ " is invalid!");
239
240 int mode = GL_RGB;
241
242 if (bitmap.surface.format.BytesPerPixel == 4)
243 {
244 mode = GL_RGBA;
245 }
246
247 recreate(bitmap.width, bitmap.height, mode, bitmap.surface.pixels[0 .. bitmap.width * bitmap.height * bitmap.surface.format.BytesPerPixel]);
248 }
249
250 /// Resizes the texture containing the new data.
251 public void resize(uint width, uint height, void[] pixels = null)
252 {
253 bind(0);
254 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr);
255 _width = width;
256 _height = height;
257 }
258
259 /// Deletes the texture and invalidates `this`.
260 public void dispose()
261 {
262 if (valid)
263 {
264 glDeleteTextures(1, &_id);
265 _id = 0;
266 }
267 }
268
269 /// Checks if Texture.id is more than 0.
270 public @property bool valid()
271 {
272 return _id > 0;
273 }
274 }