1 /* 2 Copyright (c) 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.cubemap; 29 30 import std.stdio; 31 import std.math; 32 import std.traits; 33 34 import dlib.image.color; 35 import dlib.image.image; 36 import dlib.math.vector; 37 import dlib.math.matrix; 38 import dlib.math.transformation; 39 import dlib.math.utils; 40 41 import dagon.core.libs; 42 import dagon.core.ownership; 43 import dagon.graphics.texture; 44 import dagon.graphics.framebuffer; 45 46 enum CubeFace 47 { 48 NegativeX = GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 49 PositiveX = GL_TEXTURE_CUBE_MAP_POSITIVE_X, 50 NegativeY = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 51 PositiveY = GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 52 PositiveZ = GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 53 NegativeZ = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 54 } 55 56 class Cubemap: Texture 57 { 58 this(Owner o) 59 { 60 super(o); 61 initialize(); 62 } 63 64 this(uint resolution, Owner o) 65 { 66 super(o); 67 initialize(resolution); 68 } 69 70 ~this() 71 { 72 release(); 73 } 74 75 void initialize() 76 { 77 releaseGLTexture(); 78 79 glActiveTexture(GL_TEXTURE0); 80 81 glGenTextures(1, &tex); 82 glBindTexture(GL_TEXTURE_CUBE_MAP, tex); 83 84 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 85 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 86 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 87 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 88 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 89 90 glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 91 } 92 93 void initialize(uint resolution) 94 { 95 releaseGLTexture(); 96 97 width = resolution; 98 height = resolution; 99 100 glActiveTexture(GL_TEXTURE0); 101 102 glGenTextures(1, &tex); 103 glBindTexture(GL_TEXTURE_CUBE_MAP, tex); 104 105 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 106 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 107 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 108 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 109 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 110 111 glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 112 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 113 glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 114 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 115 glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 116 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA16F, resolution, resolution, 0, GL_RGBA, GL_FLOAT, null); 117 118 glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 119 } 120 121 void setFaceImage(CubeFace face, SuperImage img) 122 { 123 if (img.width != img.height) 124 { 125 writefln("Cubemap face image must be square"); 126 return; 127 } 128 129 width = img.width; 130 height = img.height; 131 132 GLenum format; 133 GLint intFormat; 134 GLenum type; 135 136 if (!pixelFormatToTextureFormat(cast(PixelFormat)img.pixelFormat, format, intFormat, type)) 137 writefln("Unsupported pixel format %s", img.pixelFormat); 138 139 glBindTexture(GL_TEXTURE_CUBE_MAP, tex); 140 glTexImage2D(face, 0, intFormat, width, height, 0, format, type, cast(void*)img.data.ptr); 141 glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 142 } 143 144 void fromEquirectangularMap(Texture tex) 145 { 146 fromEquirectangularMap(tex.image); 147 } 148 149 void fromEquirectangularMap(SuperImage envmap) 150 { 151 SuperImage faceImage = envmap.createSameFormat(width, width); 152 153 foreach(i, face; EnumMembers!CubeFace) 154 { 155 Matrix4x4f dirTransform = cubeFaceMatrix(face); 156 157 foreach(x; 0..width) 158 foreach(y; 0..width) 159 { 160 float cubex = (cast(float)x / cast(float)width) * 2.0f - 1.0f; 161 float cubey = (1.0f - cast(float)y / cast(float)width) * 2.0f - 1.0f; 162 Vector3f dir = Vector3f(cubex, cubey, 1.0f).normalized * dirTransform; 163 Vector2f uv = equirectProj(dir); 164 Color4f c = bilinearPixel(envmap, uv.x * envmap.width, uv.y * envmap.height); 165 faceImage[x, y] = c; 166 } 167 168 setFaceImage(face, faceImage); 169 } 170 171 faceImage.free(); 172 } 173 174 override void bind() 175 { 176 if (glIsTexture(tex)) 177 { 178 glBindTexture(GL_TEXTURE_CUBE_MAP, tex); 179 180 if (!mipmapGenerated && useMipmapFiltering) 181 { 182 glGenerateMipmap(GL_TEXTURE_CUBE_MAP); 183 mipmapGenerated = true; 184 } 185 } 186 } 187 188 override void unbind() 189 { 190 glBindTexture(GL_TEXTURE_CUBE_MAP, 0); 191 } 192 193 void invalidateMipmap() 194 { 195 mipmapGenerated = false; 196 } 197 } 198 199 Matrix4x4f cubeFaceMatrix(CubeFace cf) 200 { 201 switch(cf) 202 { 203 case CubeFace.PositiveX: 204 return rotationMatrix(1, degtorad(-90.0f)); 205 case CubeFace.NegativeX: 206 return rotationMatrix(1, degtorad(90.0f)); 207 case CubeFace.PositiveY: 208 return rotationMatrix(0, degtorad(90.0f)); 209 case CubeFace.NegativeY: 210 return rotationMatrix(0, degtorad(-90.0f)); 211 case CubeFace.PositiveZ: 212 return rotationMatrix(1, degtorad(0.0f)); 213 case CubeFace.NegativeZ: 214 return rotationMatrix(1, degtorad(180.0f)); 215 default: 216 return Matrix4x4f.identity; 217 } 218 } 219 220 Matrix4x4f cubeFaceCameraMatrix(CubeFace cf, Vector3f pos) 221 { 222 Matrix4x4f m; 223 switch(cf) 224 { 225 case CubeFace.PositiveX: 226 m = translationMatrix(pos) * rotationMatrix(1, degtorad(90.0f)) * rotationMatrix(2, degtorad(180.0f)); 227 break; 228 case CubeFace.NegativeX: 229 m = translationMatrix(pos) * rotationMatrix(1, degtorad(-90.0f)) * rotationMatrix(2, degtorad(180.0f)); 230 break; 231 case CubeFace.PositiveY: 232 m = translationMatrix(pos) * rotationMatrix(1, degtorad(0.0f)) * rotationMatrix(0, degtorad(-90.0f)); 233 break; 234 case CubeFace.NegativeY: 235 m = translationMatrix(pos) * rotationMatrix(1, degtorad(0.0f)) * rotationMatrix(0, degtorad(90.0f)); 236 break; 237 case CubeFace.PositiveZ: 238 m = translationMatrix(pos) * rotationMatrix(1, degtorad(180.0f)) * rotationMatrix(2, degtorad(180.0f)); 239 break; 240 case CubeFace.NegativeZ: 241 m = translationMatrix(pos) * rotationMatrix(1, degtorad(0.0f)) * rotationMatrix(2, degtorad(180.0f)); 242 break; 243 default: 244 m = Matrix4x4f.identity; break; 245 } 246 return m; 247 } 248 249 Vector2f equirectProj(Vector3f dir) 250 { 251 float phi = acos(dir.y); 252 float theta = atan2(dir.x, dir.z) + PI; 253 return Vector2f(theta / (PI * 2.0f), phi / PI); 254 }