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 }