1 module D2DGame.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 /// Creates a width x height texture containing the pixel data using ubytes. 113 public void create(uint width, uint height, int mode, void[] pixels) 114 { 115 glGenTextures(1, &_id); 116 glBindTexture(GL_TEXTURE_2D, _id); 117 118 glTexImage2D(GL_TEXTURE_2D, 0, mode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr); 119 120 applyParameters(); 121 122 this.inMode = mode; 123 this.mode = mode; 124 _width = width; 125 _height = height; 126 127 if (!valid) 128 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 129 } 130 131 /// Creates a width x height texture containing the pixel data in `inMode` format using `type` as array type and internally convertes to `mode`. 132 public void create(uint width, uint height, int inMode, int mode, void[] pixels, int type = GL_UNSIGNED_BYTE) 133 { 134 glGenTextures(1, &_id); 135 glBindTexture(GL_TEXTURE_2D, _id); 136 137 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, type, pixels.ptr); 138 139 applyParameters(); 140 141 this.inMode = inMode; 142 this.mode = mode; 143 _width = width; 144 _height = height; 145 146 if (!valid) 147 throw new Exception("OpenGL ErrorCode " ~ to!string(glGetError())); 148 } 149 150 /// Use this when changing filter or wrap after creating the texture. 151 /// Calls automatically after `create`. 152 public void applyParameters() 153 { 154 bind(0); 155 156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter); 157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter); 158 159 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapX); 160 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapY); 161 162 if (enableMipMaps) 163 { 164 glGenerateMipmap(GL_TEXTURE_2D); 165 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16); 166 } 167 } 168 169 /// Binds the current texture to the given unit. 170 public void bind(uint unit) 171 { 172 glActiveTexture(GL_TEXTURE0 + unit); 173 glBindTexture(GL_TEXTURE_2D, _id); 174 } 175 176 /// Creates the texture from a bitmap. 177 public void fromBitmap(Bitmap bitmap, string name = "Bitmap") 178 { 179 if (!bitmap.valid) 180 throw new Exception(name ~ " is invalid!"); 181 182 int mode = GL_RGB; 183 184 if (bitmap.surface.format.BytesPerPixel == 4) 185 { 186 mode = GL_RGBA; 187 } 188 189 create(bitmap.width, bitmap.height, mode, bitmap.surface.pixels[0 .. bitmap.width * bitmap.height * bitmap.surface.format.BytesPerPixel]); 190 } 191 192 /// Resizes the texture containing the new data. 193 public void resize(uint width, uint height, void[] pixels = null) 194 { 195 bind(0); 196 glTexImage2D(GL_TEXTURE_2D, 0, inMode, width, height, 0, mode, GL_UNSIGNED_BYTE, pixels.ptr); 197 _width = width; 198 _height = height; 199 } 200 201 /// Deletes the texture and invalidates `this`. 202 public void dispose() 203 { 204 if (valid) 205 { 206 glDeleteTextures(1, &_id); 207 _id = 0; 208 } 209 } 210 211 /// Checks if Texture.id is more than 0. 212 public @property bool valid() 213 { 214 return _id > 0; 215 } 216 }