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