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 public static bool supportsAnisotropy; 82 83 public static void load() 84 { 85 _white = new Texture(); 86 _white.create(1, 1, cast(ubyte[])[255, 255, 255, 255]); 87 } 88 89 /// Only allocates memory for the instance but does not create anything. 90 public this() {} 91 92 /// Creates and loads the texture with the given filters. 93 version (BindSDL_Image) public this(string file, TextureFilterMode min = TextureFilterMode.Linear, TextureFilterMode mag = TextureFilterMode.Linear, TextureClampMode wrapX = TextureClampMode.Repeat, TextureClampMode wrapY = TextureClampMode.Repeat) 94 { 95 if (min == TextureFilterMode.LinearMipmapLinear || min == TextureFilterMode.LinearMipmapNearest) 96 enableMipMaps = true; 97 minFilter = min; 98 magFilter = mag; 99 this.wrapX = wrapX; 100 this.wrapY = wrapY; 101 fromBitmap(Bitmap.load(file)); 102 } 103 104 public ~this() 105 { 106 dispose(); 107 } 108 109 /// Creates a width x height texture containing the pixel data in RGBA ubyte format. 110 public void create(uint width, uint height, void[] pixels) 111 { 112 create(width, height, GL_RGBA, pixels); 113 } 114 115 /// Recreates a width x height texture containing the pixel data in RGBA ubyte format without disposing the old one. 116 public void recreate(uint width, uint height, void[] pixels) 117 { 118 recreate(width, height, GL_RGBA, pixels); 119 } 120 121 /// Creates a width x height texture containing the pixel data using ubytes. 122 public void create(uint width, uint height, int mode, void[] pixels) 123 { 124 glGenTextures(1, &_id); 125 glBindTexture(GL_TEXTURE_2D, _id); 126 127 glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr); 128 129 applyParameters(); 130 131 this.inMode = mode; 132 this.mode = mode; 133 _width = width; 134 _height = height; 135 136 if (!valid) 137 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 138 } 139 140 /// Recreates a width x height texture containing the pixel data using ubytes without disposing the old one. 141 public void recreate(uint width, uint height, int mode, void[] pixels) 142 { 143 bind(0); 144 glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr); 145 146 applyParameters(); 147 148 this.inMode = mode; 149 this.mode = mode; 150 _width = width; 151 _height = height; 152 153 if (!valid) 154 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 155 } 156 157 /// Creates a width x height texture containing the pixel data in `inMode` format using `type` as array type and internally convertes to `mode`. 158 public void create(uint width, uint height, int inMode, int mode, void[] pixels, int type = GL_UNSIGNED_BYTE) 159 { 160 glGenTextures(1, &_id); 161 glBindTexture(GL_TEXTURE_2D, _id); 162 163 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, type, pixels.ptr); 164 165 applyParameters(); 166 167 this.inMode = inMode; 168 this.mode = mode; 169 _width = width; 170 _height = height; 171 172 if (!valid) 173 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 174 } 175 176 /// 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. 177 public void recreate(uint width, uint height, int inMode, int mode, void[] pixels, int type = GL_UNSIGNED_BYTE) 178 { 179 bind(0); 180 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, type, pixels.ptr); 181 182 applyParameters(); 183 184 this.inMode = inMode; 185 this.mode = mode; 186 _width = width; 187 _height = height; 188 189 if (!valid) 190 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 191 } 192 193 /// Use this when changing filter or wrap after creating the texture. 194 /// Calls automatically after `create`. 195 public void applyParameters() 196 { 197 bind(0); 198 199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); 200 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); 201 202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapX); 203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapY); 204 205 if (enableMipMaps) 206 { 207 glGenerateMipmap(GL_TEXTURE_2D); 208 if (supportsAnisotropy) 209 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY, 16); 210 } 211 } 212 213 /// Binds the current texture to the given unit. 214 public void bind(uint unit = 0) 215 { 216 glActiveTexture(GL_TEXTURE0 + unit); 217 glBindTexture(GL_TEXTURE_2D, _id); 218 } 219 220 /// Creates the texture from a bitmap. 221 public void fromBitmap(Bitmap bitmap, string name = "Bitmap") 222 { 223 if (!bitmap.valid) 224 throw new Exception(name ~ " is invalid!"); 225 226 int mode = GL_RGB; 227 228 if (bitmap.surface.format.BytesPerPixel == 4) 229 { 230 mode = GL_RGBA; 231 } 232 233 create(bitmap.width, bitmap.height, mode, bitmap.surface.pixels[0 .. bitmap.width * bitmap.height * bitmap.surface.format.BytesPerPixel]); 234 } 235 236 /// Creates the texture from a bitmap without disposing the old one. 237 public void recreateFromBitmap(Bitmap bitmap, string name = "Bitmap") 238 { 239 if (!bitmap.valid) 240 throw new Exception(name ~ " is invalid!"); 241 242 int mode = GL_RGB; 243 244 if (bitmap.surface.format.BytesPerPixel == 4) 245 { 246 mode = GL_RGBA; 247 } 248 249 recreate(bitmap.width, bitmap.height, mode, bitmap.surface.pixels[0 .. bitmap.width * bitmap.height * bitmap.surface.format.BytesPerPixel]); 250 } 251 252 /// Resizes the texture containing the new data. 253 public void resize(uint width, uint height, void[] pixels = null) 254 { 255 bind(0); 256 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr); 257 _width = width; 258 _height = height; 259 } 260 261 /// Deletes the texture and invalidates `this`. 262 public void dispose() 263 { 264 if (valid) 265 { 266 glDeleteTextures(1, &_id); 267 _id = 0; 268 } 269 } 270 271 /// Checks if Texture.id is more than 0. 272 public @property bool valid() 273 { 274 return _id > 0; 275 } 276 }