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