1 /* 2 Copyright (c) 2017-2019 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 module dagon.graphics.texture; 29 30 import std.stdio; 31 import std.math; 32 import std.algorithm; 33 34 import dlib.core.memory; 35 import dlib.core.ownership; 36 import dlib.image.color; 37 import dlib.image.image; 38 import dlib.image.hdri; 39 import dlib.math.vector; 40 41 import dagon.core.bindings; 42 import dagon.core.application; 43 import dagon.graphics.compressedimage; 44 45 // S3TC formats 46 enum GL_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; // DXT1/BC1_UNORM 47 enum GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; // DXT3/BC2_UNORM 48 enum GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; // DXT5/BC3_UNORM 49 50 // RGTC formats 51 enum GL_COMPRESSED_RED_RGTC1 = 0x8DBB; // BC4_UNORM 52 enum GL_COMPRESSED_SIGNED_RED_RGTC1 = 0x8DBC; // BC4_SNORM 53 enum GL_COMPRESSED_RG_RGTC2 = 0x8DBD; // BC5_UNORM 54 enum GL_COMPRESSED_SIGNED_RG_RGTC2 = 0x8DBE; // BC5_SNORM 55 56 // BPTC formats 57 enum GL_COMPRESSED_RGBA_BPTC_UNORM_ARB = 0x8E8C; // BC7_UNORM 58 enum GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB = 0x8E8D; // BC7_UNORM_SRGB 59 enum GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB = 0x8E8E; // BC6H_SF16 60 enum GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB = 0x8E8F; // BC6H_UF16 61 62 class Texture: Owner 63 { 64 SuperImage image; 65 66 GLuint tex; 67 GLenum format; 68 GLint intFormat; 69 GLenum type; 70 71 int width; 72 int height; 73 int numMipmapLevels; 74 75 Vector2f translation; 76 Vector2f scale; 77 float rotation; 78 79 bool useMipmapFiltering = true; 80 bool useLinearFiltering = true; 81 82 protected bool mipmapGenerated = false; 83 84 this(Owner owner) 85 { 86 super(owner); 87 translation = Vector2f(0.0f, 0.0f); 88 scale = Vector2f(1.0f, 1.0f); 89 rotation = 0.0f; 90 } 91 92 this(SuperImage img, Owner owner, bool genMipmaps = false) 93 { 94 super(owner); 95 translation = Vector2f(0.0f, 0.0f); 96 scale = Vector2f(1.0f, 1.0f); 97 rotation = 0.0f; 98 createFromImage(img, genMipmaps); 99 } 100 101 void createFromImage(SuperImage img, bool genMipmaps = true) 102 { 103 releaseGLTexture(); 104 105 image = img; 106 width = img.width; 107 height = img.height; 108 109 glGenTextures(1, &tex); 110 glActiveTexture(GL_TEXTURE0); 111 glBindTexture(GL_TEXTURE_2D, tex); 112 113 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 117 118 CompressedImage compressedImg = cast(CompressedImage)img; 119 if (compressedImg) 120 { 121 uint blockSize; 122 uint numMipMaps = compressedImg.mipMapLevels; 123 124 if (!compressedFormatToTextureFormat(compressedImg.compressedFormat, intFormat, blockSize)) 125 { 126 writeln("Unsupported compressed texture format ", compressedImg.compressedFormat); 127 fallback(); 128 } 129 else 130 { 131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); 132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, numMipMaps - 1); 133 134 uint w = width; 135 uint h = height; 136 uint offset = 0; 137 for (uint i = 0; i < numMipMaps; i++) 138 { 139 uint size = ((w + 3) / 4) * ((h + 3) / 4) * blockSize; 140 glCompressedTexImage2D(GL_TEXTURE_2D, i, intFormat, w, h, 0, size, cast(void*)(img.data.ptr + offset)); 141 offset += size; 142 w /= 2; 143 h /= 2; 144 } 145 146 useMipmapFiltering = genMipmaps; 147 mipmapGenerated = true; 148 } 149 } 150 else 151 { 152 if (!pixelFormatToTextureFormat(img.pixelFormat, format, intFormat, type)) 153 { 154 writeln("Unsupported pixel format ", img.pixelFormat); 155 fallback(); 156 } 157 else 158 { 159 glTexImage2D(GL_TEXTURE_2D, 0, intFormat, width, height, 0, format, type, cast(void*)img.data.ptr); 160 161 useMipmapFiltering = genMipmaps; 162 if (useMipmapFiltering) 163 { 164 glGenerateMipmap(GL_TEXTURE_2D); 165 mipmapGenerated = true; 166 } 167 } 168 } 169 170 glBindTexture(GL_TEXTURE_2D, 0); 171 } 172 173 protected void fallback() 174 { 175 // TODO: make fallback texture 176 } 177 178 void bind() 179 { 180 if (glIsTexture(tex)) 181 { 182 glBindTexture(GL_TEXTURE_2D, tex); 183 184 if (!mipmapGenerated && useMipmapFiltering) 185 { 186 glGenerateMipmap(GL_TEXTURE_2D); 187 mipmapGenerated = true; 188 } 189 190 if (useMipmapFiltering) 191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 192 else if (useLinearFiltering) 193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 194 else 195 { 196 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 197 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 198 } 199 } 200 } 201 202 void unbind() 203 { 204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 205 glBindTexture(GL_TEXTURE_2D, 0); 206 } 207 208 bool valid() 209 { 210 return cast(bool)glIsTexture(tex); 211 } 212 213 Color4f sample(float u, float v) 214 { 215 if (image) 216 { 217 int x = cast(int)floor(u * width); 218 int y = cast(int)floor(v * height); 219 return image[x, y]; 220 } 221 else 222 return Color4f(0, 0, 0, 0); 223 } 224 225 void release() 226 { 227 releaseGLTexture(); 228 if (image) 229 { 230 Delete(image); 231 image = null; 232 } 233 } 234 235 void releaseGLTexture() 236 { 237 if (glIsTexture(tex)) 238 glDeleteTextures(1, &tex); 239 } 240 241 ~this() 242 { 243 release(); 244 } 245 } 246 247 bool pixelFormatToTextureFormat(uint pixelFormat, out GLenum textureFormat, out GLint textureInternalFormat, out GLenum pixelType) 248 { 249 switch (pixelFormat) 250 { 251 case PixelFormat.L8: textureInternalFormat = GL_R8; textureFormat = GL_RED; pixelType = GL_UNSIGNED_BYTE; break; 252 case PixelFormat.LA8: textureInternalFormat = GL_RG8; textureFormat = GL_RG; pixelType = GL_UNSIGNED_BYTE; break; 253 case PixelFormat.RGB8: textureInternalFormat = GL_RGB8; textureFormat = GL_RGB; pixelType = GL_UNSIGNED_BYTE; break; 254 case PixelFormat.RGBA8: textureInternalFormat = GL_RGBA8; textureFormat = GL_RGBA; pixelType = GL_UNSIGNED_BYTE; break; 255 case FloatPixelFormat.RGBAF32: textureInternalFormat = GL_RGBA32F; textureFormat = GL_RGBA; pixelType = GL_FLOAT; break; 256 default: 257 return false; 258 } 259 260 return true; 261 } 262 263 bool compressedFormatToTextureFormat(CompressedImageFormat compFormat, out GLint textureInternalFormat, out uint blockSize) 264 { 265 switch (compFormat) 266 { 267 case CompressedImageFormat.S3TC_RGB_DXT1: textureInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; blockSize = 8; break; 268 case CompressedImageFormat.S3TC_RGBA_DXT3: textureInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; blockSize = 16; break; 269 case CompressedImageFormat.S3TC_RGBA_DXT5: textureInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; blockSize = 16; break; 270 case CompressedImageFormat.BPTC_RGBA_UNORM: textureInternalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; blockSize = 16; break; 271 case CompressedImageFormat.BPTC_SRGBA_UNORM: textureInternalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; blockSize = 16; break; 272 default: 273 return false; 274 } 275 276 return true; 277 }