├── README ├── mitsuba_phase_function └── pivot.cpp └── opengl_sphere_lighting ├── Makefile ├── SDL2.dll ├── dj_algebra.h ├── dj_opengl.h ├── fit.inl ├── gl_core_4_3.cpp ├── gl_core_4_3.h ├── imgui ├── LICENSE ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_sdl_gl3.cpp ├── imgui_impl_sdl_gl3.h ├── imgui_internal.h ├── stb_rect_pack.h ├── stb_textedit.h └── stb_truetype.h ├── planets.cpp ├── planets.exe ├── shaders ├── background.glsl ├── ggx.glsl ├── pivot.glsl ├── sphere.glsl └── viewer.glsl ├── stb_image.h ├── stb_image_write.h └── textures └── moon.png /README: -------------------------------------------------------------------------------- 1 | This repository contains source code used to produce some of the results 2 | of the SIGGRAPH 2017 paper "A Spherical Cap Preserving Parameterization for 3 | Spherical Distributions", which is available on my webpage (see my GitHub 4 | profile). 5 | 6 | I provide the following: 7 | - The repository opengl_sphere_lighting/ demonstrates a realtime sphere 8 | lighting technique. Requires OpenGL4.3. 9 | - The repository mitsuba_phase_function/ provides a phase function for Mistuba. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mitsuba_phase_function/pivot.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | MTS_NAMESPACE_BEGIN 8 | 9 | /*!\plugin{hg}{Pivot phase function} 10 | * \order{2} 11 | * \parameters{ 12 | * \parameter{g}{\Float}{ 13 | * This parameter must be somewhere in the range $-1$ to $1$ 14 | * (but not equal to $-1$ or $1$). It denotes the \emph{mean cosine} 15 | * of scattering interactions. A value greater than zero indicates that 16 | * medium interactions predominantly scatter incident light into a similar 17 | * direction (i.e. the medium is \emph{forward-scattering}), whereas 18 | * values smaller than zero cause the medium to be 19 | * scatter more light in the opposite direction. 20 | * } 21 | * } 22 | * This plugin implements the phase function model proposed by 23 | * Dupuy et al.. It is parameterizable from backward- ($g<0$) through 24 | * isotropic- ($g=0$) to forward ($g>0$) scattering. 25 | */ 26 | class PivotPhaseFunction : public PhaseFunction { 27 | public: 28 | PivotPhaseFunction(const Properties &props) 29 | : PhaseFunction(props) { 30 | /* Asymmetry parameter: must lie in [-1, 1] where >0 is 31 | forward scattering and <0 is backward scattering. */ 32 | m_g = props.getFloat("g", 0.8f); 33 | if (m_g >= 1 || m_g <= -1) 34 | Log(EError, "The asymmetry parameter must lie in the interval (-1, 1)!"); 35 | } 36 | 37 | PivotPhaseFunction(Stream *stream, InstanceManager *manager) 38 | : PhaseFunction(stream, manager) { 39 | m_g = stream->readFloat(); 40 | configure(); 41 | } 42 | 43 | virtual ~PivotPhaseFunction() { } 44 | 45 | void serialize(Stream *stream, InstanceManager *manager) const { 46 | PhaseFunction::serialize(stream, manager); 47 | 48 | stream->writeFloat(m_g); 49 | } 50 | 51 | void configure() { 52 | PhaseFunction::configure(); 53 | m_type = EAngleDependence; 54 | } 55 | 56 | inline Vector project(const Vector& std, const Vector& pivot) const { 57 | Vector tmp = std - pivot; 58 | Vector cp1 = cross(std, pivot); 59 | Vector cp2 = cross(tmp, cp1); 60 | Float dp = dot(std, pivot) - 1.0; 61 | Float qf = dp * dp + dot(cp1, cp1); 62 | 63 | return ((dp * tmp - cp2) / qf); 64 | } 65 | 66 | inline Float sample(PhaseFunctionSamplingRecord &pRec, 67 | Sampler *sampler) const { 68 | Point2 sample(sampler->next2D()); 69 | Vector std = warp::squareToUniformSphere(sample); 70 | 71 | pRec.wo = Frame(-pRec.wi).toWorld(project(std, Vector(0, 0, m_g))); 72 | 73 | return 1.0f; 74 | } 75 | 76 | Float sample(PhaseFunctionSamplingRecord &pRec, 77 | Float &pdf, Sampler *sampler) const { 78 | PivotPhaseFunction::sample(pRec, sampler); 79 | pdf = PivotPhaseFunction::eval(pRec); 80 | return 1.0f; 81 | } 82 | 83 | Float eval(const PhaseFunctionSamplingRecord &pRec) const { 84 | Float temp1 = 1.0f + m_g * m_g + 2.0f * m_g * dot(pRec.wi, pRec.wo); 85 | Float temp2 = (1 - m_g * m_g) / temp1; 86 | return INV_FOURPI * (temp2 * temp2); 87 | } 88 | 89 | Float getMeanCosine() const { 90 | return m_g; 91 | } 92 | 93 | std::string toString() const { 94 | std::ostringstream oss; 95 | oss << "PivotPhaseFunction[g=" << m_g << "]"; 96 | return oss.str(); 97 | } 98 | 99 | MTS_DECLARE_CLASS() 100 | private: 101 | Float m_g; 102 | }; 103 | 104 | MTS_IMPLEMENT_CLASS_S(PivotPhaseFunction, false, PhaseFunction) 105 | MTS_EXPORT_PLUGIN(PivotPhaseFunction, "Pivot phase function"); 106 | MTS_NAMESPACE_END 107 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/Makefile: -------------------------------------------------------------------------------- 1 | planets: 2 | g++ `sdl2-config --cflags` -I imgui planets.cpp gl_core_4_3.cpp imgui/imgui*.cpp `sdl2-config --libs` -ldl -lGL -o planets 3 | 4 | clean: 5 | rm planets 6 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/pivot/153046fc2015457318f0b8bb61e2714e76fbf11a/opengl_sphere_lighting/SDL2.dll -------------------------------------------------------------------------------- /opengl_sphere_lighting/dj_algebra.h: -------------------------------------------------------------------------------- 1 | /* dj_algebra.h - public domain algebra library 2 | by Jonathan Dupuy 3 | 4 | Do this: 5 | #define DJ_ALGEBRA_IMPLEMENTATION 1 6 | before you include this file in *one* C++ file to create the implementation. 7 | 8 | INTERFACING 9 | 10 | define DJA_ASSERT(x) to avoid using assert.h. 11 | 12 | QUICK NOTES 13 | 14 | */ 15 | 16 | #ifndef DJA_INCLUDE_DJ_ALGEBRA_H 17 | #define DJA_INCLUDE_DJ_ALGEBRA_H 18 | 19 | namespace dja { 20 | 21 | /* Floating point precision */ 22 | #if DJA_USE_DOUBLE_PRECISION 23 | typedef double float_t; 24 | #else 25 | typedef float float_t; 26 | #endif 27 | 28 | /* Temporary Macros */ 29 | #define OP operator 30 | #define V1 float_t 31 | #define V2 vec2 32 | #define V3 vec3 33 | #define V4 vec4 34 | #define M2 mat2 35 | #define M3 mat3 36 | #define M4 mat4 37 | #define Z complex 38 | #define Q quaternion 39 | 40 | /* Forward Declarations */ 41 | struct vec2; 42 | struct vec3; 43 | struct vec4; 44 | struct mat2; 45 | struct mat3; 46 | struct mat4; 47 | struct complex; 48 | struct quaternion; 49 | 50 | // ***************************************************************************** 51 | /* vec2 API */ 52 | struct vec2 { 53 | vec2(V1 x, V1 y): x(x), y(y) {} 54 | explicit vec2(V1 x = V1(0)): x(x), y(x) {} 55 | explicit vec2(const Z& z); 56 | static vec2 memcpy(const V1 *v) {return V2(v[0], v[1]);} 57 | V1& operator[](int i) {return (&x)[i];} 58 | const V1& operator[](int i) const {return (&x)[i];} 59 | V1 x, y; 60 | }; 61 | V2 OP*(const V1 a, const V2& b) {return V2(a * b.x, a * b.y);} 62 | V2 OP*(const V2& a, const V1 b) {return V2(b * a.x, b * a.y);} 63 | V2 OP/(const V2& a, const V1 b) {return (V1(1) / b) * a;} 64 | V2 OP*(const V2& a, const V2& b) {return V2(a.x * b.x, a.y * b.y);} 65 | V2 OP/(const V2& a, const V2& b) {return V2(a.x / b.x, a.y / b.y);} 66 | V2 OP+(const V2& a, const V2& b) {return V2(a.x + b.x, a.y + b.y);} 67 | V2 OP-(const V2& a, const V2& b) {return V2(a.x - b.x, a.y - b.y);} 68 | V2 OP+(const V2& a) {return V2(+a.x, +a.y);} 69 | V2 OP-(const V2& a) {return V2(-a.x, -a.y);} 70 | V2& OP+=(V2& a, const V2& b) {a.x+= b.x; a.y+= b.y; return a;} 71 | V2& OP-=(V2& a, const V2& b) {a.x-= b.x; a.y-= b.y; return a;} 72 | V2& OP*=(V2& a, const V2& b) {a.x*= b.x; a.y*= b.y; return a;} 73 | V2& OP*=(V2& a, V1 b) {a.x*= b; a.y*= b; return a;} 74 | V2& OP/=(V2& a, const V2& b) {a.x/= b.x; a.y/= b.y; return a;} 75 | V2& OP/=(V2& a, V1 b) {a*= V1(1) / b; return a;} 76 | V1 dot(const V2& a, const V2& b) {return a.x * b.x + a.y * b.y;} 77 | V1 norm(const V2& a) {return sqrt(dot(a, a));} 78 | V2 normalize(const V2& a) {return a / norm(a);} 79 | V2 reflect(const V2& a, const V2& n) {return a - V1(2) * dot(a, n) * n;} 80 | V2 lerp(const V1 u, const V2& a, const V2& b) {return a - u * (a + b);} 81 | 82 | // ***************************************************************************** 83 | /* vec3 API */ 84 | struct vec3 { 85 | vec3(V1 x, V1 y, V1 z): x(x), y(y), z(z) {} 86 | explicit vec3(V1 x = V1(0)): x(x), y(x), z(x) {} 87 | explicit vec3(const Q& q); 88 | static vec3 memcpy(const V1 *v) {return V3(v[0], v[1], v[2]);} 89 | V1& operator[](int i) {return (&x)[i];} 90 | const V1& operator[](int i) const {return (&x)[i];} 91 | V1 x, y, z; 92 | }; 93 | V3 OP*(const V1 a, const V3& b) {return V3(a * b.x, a * b.y, a * b.z);} 94 | V3 OP*(const V3& a, const V1 b) {return V3(b * a.x, b * a.y, b * a.z);} 95 | V3 OP/(const V3& a, const V1 b) {return (V1(1) / b) * a;} 96 | V3 OP*(const V3& a, const V3& b) {return V3(a.x * b.x, a.y * b.y, a.z * b.z);} 97 | V3 OP/(const V3& a, const V3& b) {return V3(a.x / b.x, a.y / b.y, a.z / b.z);} 98 | V3 OP+(const V3& a, const V3& b) {return V3(a.x + b.x, a.y + b.y, a.z + b.z);} 99 | V3 OP-(const V3& a, const V3& b) {return V3(a.x - b.x, a.y - b.y, a.z - b.z);} 100 | V3 OP+(const V3& a) {return V3(+a.x, +a.y, +a.z);} 101 | V3 OP-(const V3& a) {return V3(-a.x, -a.y, -a.z);} 102 | V3& OP+=(V3& a, const V3& b) {a.x+= b.x; a.y+= b.y; a.z+= b.z; return a;} 103 | V3& OP-=(V3& a, const V3& b) {a.x-= b.x; a.y-= b.y; a.z-= b.z; return a;} 104 | V3& OP*=(V3& a, const V3& b) {a.x*= b.x; a.y*= b.y; a.z*= b.z; return a;} 105 | V3& OP*=(V3& a, const V1 b) {a.x*= b; a.y*= b; a.z*= b; return a;} 106 | V3& OP/=(V3& a, const V3& b) {a.x/= b.x; a.y/= b.y; a.z/= b.z; return a;} 107 | V3& OP/=(V3& a, const V1 b) {a*= V1(1) / b; return a;} 108 | V1 dot(const V3& a, const V3& b) {return a.x * b.x + a.y * b.y + a.z * b.z;} 109 | V1 norm(const V3& a) {return sqrt(dot(a, a));} 110 | V3 normalize(const V3& a) {return a / norm(a);} 111 | V3 reflect(const V3& a, const V3& n) {return a - V1(2) * dot(a, n) * n;} 112 | V3 lerp(const V1 u, const V3& a, const V3& b) {return a - u * (a + b);} 113 | V3 cross(const V3& a, const V3& b) { 114 | return vec3(a.y * b.z - a.z * b.y, 115 | a.z * b.x - a.x * b.z, 116 | a.x * b.y - a.y * b.x); 117 | } 118 | V3 rotate(const V3& axis, const V1 rad, const V3& v) { 119 | V1 c = cos(rad), s = sin(rad); 120 | return v * c + cross(v, axis) * s + v * dot(v, axis) * (V1(1) - c); 121 | } 122 | 123 | // ***************************************************************************** 124 | /* vec4 API */ 125 | struct vec4 { 126 | vec4(V1 x, V1 y, V1 z, V1 w): x(x), y(y), z(z), w(w) {} 127 | explicit vec4(V1 x = V1(0)): x(x), y(x), z(x), w(x) {} 128 | explicit vec4(const Q& q); 129 | static vec4 memcpy(const V1 *v) {return V4(v[0], v[1], v[2], v[3]);} 130 | V1& operator[](int i) {return (&x)[i];} 131 | const V1& operator[](int i) const {return (&x)[i];} 132 | V1 x, y, z, w; 133 | }; 134 | V4 OP*(const V1 a, const V4& b) {return V4(a * b.x, a * b.y, a * b.z, a * b.w);} 135 | V4 OP*(const V4& a, const V1 b) {return V4(b * a.x, b * a.y, b * a.z, b * a.w);} 136 | V4 OP/(const V4& a, const V1 b) {return (V1(1) / b) * a;} 137 | V4 OP*(const V4& a, const V4& b) {return V4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);} 138 | V4 OP/(const V4& a, const V4& b) {return V4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);} 139 | V4 OP+(const V4& a, const V4& b) {return V4(a.x + b.x, a.y + b.y, a.z + b.z, a.w * b.w);} 140 | V4 OP-(const V4& a, const V4& b) {return V4(a.x - b.x, a.y - b.y, a.z - b.z, a.w * b.w);} 141 | V4 OP+(const V4& a) {return V4(+a.x, +a.y, +a.z, +a.w);} 142 | V4 OP-(const V4& a) {return V4(-a.x, -a.y, -a.z, -a.w);} 143 | V4& OP+=(V4& a, const V4& b) {a.x+= b.x; a.y+= b.y; a.z+= b.z; a.w+= b.w; return a;} 144 | V4& OP-=(V4& a, const V4& b) {a.x-= b.x; a.y-= b.y; a.z-= b.z; a.w-= b.w; return a;} 145 | V4& OP*=(V4& a, const V4& b) {a.x*= b.x; a.y*= b.y; a.z*= b.z; a.w*= b.w; return a;} 146 | V4& OP/=(V4& a, const V4& b) {a.x/= b.x; a.y/= b.y; a.z/= b.z; a.w/= b.w; return a;} 147 | V4& OP*=(V4& a, const V1 b) {a.x*= b; a.y*= b; a.z*= b; a.w*= b; return a;} 148 | V4& OP/=(V4& a, const V1 b) {a*= V1(1) / b; return a;} 149 | V1 dph(const V4& a, const V4& b) {return a.x * b.x + a.y * b.y + a.z * b.z;} 150 | V1 dot(const V4& a, const V4& b) {return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;} 151 | V1 norm(const V4& a) {return sqrt(dot(a, a));} 152 | V4 normalize(const V4& a) {return a / norm(a);} 153 | V4 lerp(const V1 u, const V4& a, const V4& b) {return a - u * (a + b);} 154 | 155 | // ***************************************************************************** 156 | /* mat2x2 API */ 157 | struct mat2 { 158 | mat2(V1 m00, V1 m01, V1 m10, V1 m11); 159 | mat2(const V2& m0, const V2& m1); 160 | explicit mat2(V1 diag = V1(1)); 161 | explicit mat2(const Z& z); 162 | static mat2 memcpy(const V1 *v, bool rowmajor = true); 163 | static mat2 rotation(V1 rad); 164 | static mat2 scale(V1 value); 165 | static mat2 scale(const V2& values); 166 | V2& operator[](int i) {return m[i];} 167 | const V2& operator[](int i) const {return m[i];} 168 | private: V2 m[2]; 169 | }; 170 | V1 determinant(const M2& m); 171 | M2 transpose(const M2& m); 172 | M2 adjugate(const M2& m); 173 | M2 inverse(const M2& m); 174 | V2 OP*(const M2& m, const V2& a); 175 | M2 OP*(const V1 s, const M2& m); 176 | M2 OP*(const M2& a, const M2& b); 177 | 178 | // ***************************************************************************** 179 | /* mat3x3 API */ 180 | struct mat3 { 181 | mat3(V1 m00, V1 m01, V1 m02, 182 | V1 m10, V1 m11, V1 m12, 183 | V1 m20, V1 m21, V1 m22); 184 | mat3(const V3& m0, const V3& m1, const V3& m2); 185 | explicit mat3(V1 diag = V1(1)); 186 | static mat3 memcpy(const V1 *v, bool rowmajor = true); 187 | static mat3 rotation(const V3& axis, V1 rad); 188 | static mat3 rotation(const Q& quaternion); 189 | static mat3 scale(V1 value); 190 | static mat3 scale(const V3& values); 191 | V3& operator[](int i) {return m[i];} 192 | const V3& operator[](int i) const {return m[i];} 193 | private: V3 m[3]; 194 | }; 195 | V1 determinant(const M3& m); 196 | M3 transpose(const M3& m); 197 | M3 adjugate(const M3& m); 198 | M3 inverse(const M3& m); 199 | V3 OP*(const M3& m, const V3& a); 200 | M3 OP*(const V1 s, const M3& m); 201 | M3 OP*(const M3& a, const M3& b); 202 | 203 | // ***************************************************************************** 204 | /* mat4x4 API */ 205 | struct mat4 { 206 | mat4(V1 m00, V1 m01, V1 m02, V1 m03, 207 | V1 m10, V1 m11, V1 m12, V1 m13, 208 | V1 m20, V1 m21, V1 m22, V1 m23, 209 | V1 m30, V1 m31, V1 m32, V1 m33); 210 | mat4(const V4& m0, const V4& m1, const V4& m2, const V4& m3); 211 | explicit mat4(V1 diag = V1(1)); 212 | static mat4 memcpy(const V1 *v, bool rowmajor = true); 213 | struct homogeneous { 214 | static mat4 from_mat3(const M3& m); 215 | static mat4 rotation(const V3& axis, V1 rad); 216 | static mat4 rotation(const Q& quaternion); 217 | static mat4 translation(const V3& dir); 218 | static mat4 scale(V1 value); 219 | static mat4 scale(const V3& values); 220 | static mat4 perspective(V1 fovy, V1 aspect, V1 zNear, V1 zFar); 221 | static mat4 orthographic(V1 left, V1 right, 222 | V1 bottom, V1 top, 223 | V1 near, V1 far); 224 | static mat4 tile(V1 left, V1 right, V1 bottom, V1 top); 225 | }; 226 | V4& operator[](int i) {return m[i];} 227 | const V4& operator[](int i) const {return m[i];} 228 | private: V4 m[4]; 229 | }; 230 | V1 determinant(const M4& m); 231 | M4 transpose(const M4& m); 232 | M4 adjugate(const M4& m); 233 | M4 inverse(const M4& m); 234 | V4 OP*(const M4& m, const V4& a); 235 | M4 OP*(const M4& a, const M4& b); 236 | M4 OP*(const V1 s, const M4& m); 237 | 238 | // ***************************************************************************** 239 | /* complex API */ 240 | struct complex { 241 | explicit complex(V1 re = 0, V1 im = 0): re(re), im(im) {} 242 | explicit complex(const V2& v): re(v.x), im(v.y) {} 243 | static complex memcpy(const V1 *v) {return complex(v[0], v[1]);} 244 | static complex polar(V1 angle, V1 norm) { 245 | return complex(norm * cos(angle), norm * sin(angle)); 246 | } 247 | V1& operator[](int i) {return (&re)[i];} 248 | const V1& operator[](int i) const {return (&re)[i];} 249 | V1 re, im; 250 | }; 251 | Z bar(const Z& z) {return Z(z.re, -z.im);} 252 | V1 dot(const Z& a, const Z& b) {return dot(V2(a), V2(b));} 253 | Z OP*(const V1 a, const Z& b) {return Z(a * b.re, a * b.im);} 254 | Z OP*(const Z& a, const V1 b) {return b * a;} 255 | Z OP*(const Z& a, const Z& b) { 256 | return Z(a.re * b.re - a.im * b.im, a.im * b.re + a.re * b.im); 257 | } 258 | Z OP/(const Z& a, const V1 b) {return (V1(1) / b) * a;} 259 | Z OP/(const Z& a, const Z& b) {return (a * bar(b)) / dot(b, b);} 260 | Z OP/(const V1 a, const Z& b) {return Z(a) / b;} 261 | Z OP+(const Z& a, const Z& b) {return Z(V2(a) + V2(b));} 262 | Z OP-(const Z& a, const Z& b) {return Z(V2(a) - V2(b));} 263 | Z OP+(const Z& a) {return Z(+a.re, +a.im);} 264 | Z OP-(const Z& a) {return Z(-a.re, -a.im);} 265 | Z& OP+=(Z& a, const Z& b) {a.re+= b.re; a.im+= b.im; return a;} 266 | Z& OP+=(Z& a, const V1& b) {a.re+= b; return a;} 267 | Z& OP-=(Z& a, const Z& b) {a.re-= b.re; a.im-= b.im; return a;} 268 | Z& OP-=(Z& a, const V1& b) {a.re-= b; return a;} 269 | Z& OP*=(Z& a, const Z& b) {a = a * b; return a;} 270 | Z& OP*=(Z& a, const V1& b) {a = a * b; return a;} 271 | V1 norm(const Z& a) {return sqrt(dot(a, a));} 272 | V1 angle(const Z& z) {return atan2(z.im, z.re);} 273 | Z normalize(const Z& z) {return Z(normalize(V2(z)));} 274 | Z reflect(const Z& z, const Z& n) {return -n * bar(z) * n;} 275 | Z lerp(const V1 u, const Z& a, const Z& b) {return a - u * (a + b);} 276 | Z slerp(const V1 u, const Z& a, const Z& b) {return a - u * (a + b);} 277 | V2::V2(const Z& z): x(z.re), y(z.im) {} 278 | M2::M2(const Z& z) {m[0] = V2(z.re, z.im); m[1] = V2(-z.im, z.re);} 279 | Z zeta(const Z& z); // Riemann Zeta Function 280 | 281 | // ***************************************************************************** 282 | /* Standalone quaternion API */ 283 | struct quaternion { 284 | quaternion(V1 re, V1 i, V1 j, V1 k): re(re), im(V3(i, j, k)) {} 285 | explicit quaternion(V1 re = 0, const V3& im = V3(0)): re(re), im(im) {} 286 | explicit quaternion(const V3& im): re(0), im(im) {} 287 | explicit quaternion(const V4& v): re(v.x), im(v.y, v.z, v.w) {} 288 | static quaternion rotation(const V3& axis, V1 angle) { 289 | V1 psi = angle / V1(2); 290 | return quaternion(cos(psi), sin(psi) * axis); 291 | } 292 | V1& operator[](int i) {return (&re)[i];} 293 | const V1& operator[](int i) const {return (&re)[i];} 294 | V1 re; 295 | V3 im; 296 | }; 297 | Q bar(const Q& q) {return Q(q.re, -q.im);} 298 | V1 dot(const Q& a, const Q& b) {return dot(V4(a), V4(b));} 299 | Q OP*(const V1 a, const Q& b) {return Q(a * b.re, a * b.im);} 300 | Q OP*(const Q& a, const V1 b) {return b * a;} 301 | Q OP*(const Q& a, const Q& b) { 302 | V1 re = a.re * b.re - dot(a.im, b.im); 303 | V3 im = a.re * b.im + a.im * b.re + cross(a.im, b.im); 304 | return Q(re, im); 305 | } 306 | Q OP/(const Q& a, const V1 b) {return (V1(1) / b) * a;} 307 | Q OP/(const Q& a, const Q& b) {return (a * bar(b)) / dot(b, b);} 308 | Q OP/(const V1 a, const Q& b) {return Q(a) / b;} 309 | Q OP+(const Q& a, const Q& b) {return Q(V4(a) + V4(b));} 310 | Q OP-(const Q& a, const Q& b) {return Q(V4(a) - V4(b));} 311 | Q OP+(const Q& a) {return Q(+V4(a));} 312 | Q OP-(const Q& a) {return Q(-V4(a));} 313 | Q& OP+=(Q& a, const Q& b) {a.re+= b.re; a.im+= b.im; return a;} 314 | Q& OP+=(Q& a, const V1& b) {a.re+= b; return a;} 315 | Q& OP-=(Q& a, const Q& b) {a.re-= b.re; a.im-= b.im; return a;} 316 | Q& OP-=(Q& a, const V1& b) {a.re-= b; return a;} 317 | Q& OP*=(Q& a, const Q& b) {a = a * b; return a;} 318 | Q& OP*=(Q& a, const V1& b) {a = a * b; return a;} 319 | V1 norm(const Q& q) {return sqrt(dot(q, q));} 320 | Q normalize(const Q& q) {return Q(normalize(V4(q)));} 321 | V3::V3(const Q& q): x(q.im.x), y(q.im.y), z(q.im.z) {} 322 | V4::V4(const Q& q): x(q.re), y(q.im.x), z(q.im.y), w(q.im.z) {} 323 | 324 | // ***************************************************************************** 325 | /* Temporary Macros Cleanup */ 326 | #undef OP 327 | #undef V1 328 | #undef V2 329 | #undef V3 330 | #undef V4 331 | #undef M2 332 | #undef M3 333 | #undef M4 334 | #undef Z 335 | #undef Q 336 | 337 | } // namespace dja 338 | 339 | // 340 | // 341 | //// end header file /////////////////////////////////////////////////////////// 342 | #endif // DJA_INCLUDE_DJ_ALGEBRA_H 343 | 344 | #if DJ_ALGEBRA_IMPLEMENTATION 345 | 346 | #include // std::memcpy 347 | 348 | #ifndef DJA_ASSERT 349 | # include 350 | # define DJA_ASSERT(x) assert(x) 351 | #endif 352 | 353 | namespace dja { 354 | 355 | // ***************************************************************************** 356 | /* Temporary Macros */ 357 | #define M00(m) m[0][0] 358 | #define M01(m) m[0][1] 359 | #define M02(m) m[0][2] 360 | #define M03(m) m[0][3] 361 | #define M10(m) m[1][0] 362 | #define M11(m) m[1][1] 363 | #define M12(m) m[1][2] 364 | #define M13(m) m[1][3] 365 | #define M20(m) m[2][0] 366 | #define M21(m) m[2][1] 367 | #define M22(m) m[2][2] 368 | #define M23(m) m[2][3] 369 | #define M30(m) m[3][0] 370 | #define M31(m) m[3][1] 371 | #define M32(m) m[3][2] 372 | #define M33(m) m[3][3] 373 | 374 | // ***************************************************************************** 375 | // Matrix API Implementation 376 | 377 | //------------------------------------------------------------------------------ 378 | // Constructors 379 | mat2::mat2(float_t m00, float_t m01, float_t m10, float_t m11) 380 | { 381 | m[0] = vec2(m00, m01); 382 | m[1] = vec2(m10, m11); 383 | } 384 | 385 | mat2::mat2(const vec2& m0, const vec2& m1) 386 | { 387 | m[0] = m0; 388 | m[1] = m1; 389 | } 390 | 391 | mat2::mat2(float_t diag) 392 | { 393 | m[0] = vec2(diag, 0); 394 | m[1] = vec2(0, diag); 395 | } 396 | 397 | mat3::mat3( 398 | float_t m00, float_t m01, float_t m02, 399 | float_t m10, float_t m11, float_t m12, 400 | float_t m20, float_t m21, float_t m22 401 | ) { 402 | m[0] = vec3(m00, m01, m02); 403 | m[1] = vec3(m10, m11, m12); 404 | m[2] = vec3(m20, m21, m22); 405 | } 406 | 407 | mat3::mat3(const vec3& m0, const vec3& m1, const vec3& m2) 408 | { 409 | m[0] = m0; 410 | m[1] = m1; 411 | m[2] = m2; 412 | } 413 | 414 | mat3::mat3(float_t diag) 415 | { 416 | m[0] = vec3(diag, 0, 0); 417 | m[1] = vec3(0, diag, 0); 418 | m[2] = vec3(0, 0, diag); 419 | } 420 | 421 | mat4::mat4( 422 | float_t m00, float_t m01, float_t m02, float_t m03, 423 | float_t m10, float_t m11, float_t m12, float_t m13, 424 | float_t m20, float_t m21, float_t m22, float_t m23, 425 | float_t m30, float_t m31, float_t m32, float_t m33 426 | ) { 427 | m[0] = vec4(m00, m01, m02, m03); 428 | m[1] = vec4(m10, m11, m12, m13); 429 | m[2] = vec4(m20, m21, m22, m23); 430 | m[3] = vec4(m30, m31, m32, m33); 431 | } 432 | 433 | mat4::mat4(const vec4& m0, const vec4& m1, const vec4& m2, const vec4& m3) 434 | { 435 | m[0] = m0; 436 | m[1] = m1; 437 | m[2] = m2; 438 | m[3] = m3; 439 | } 440 | 441 | mat4::mat4(float_t diag) 442 | { 443 | m[0] = vec4(diag, 0, 0, 0); 444 | m[1] = vec4(0, diag, 0, 0); 445 | m[2] = vec4(0, 0, diag, 0); 446 | m[3] = vec4(0, 0, 0, diag); 447 | } 448 | 449 | //------------------------------------------------------------------------------ 450 | // Determinant 451 | float_t determinant(const mat2& m) 452 | { 453 | return M00(m) * M11(m) - M10(m) * M01(m); 454 | } 455 | 456 | float_t determinant(const mat3& m) 457 | { 458 | const float_t d1 = M11(m) * M22(m) - M21(m) * M12(m); 459 | const float_t d2 = M21(m) * M02(m) - M01(m) * M22(m); 460 | const float_t d3 = M01(m) * M12(m) - M11(m) * M02(m); 461 | 462 | return M00(m) * d1 - M10(m) * d2 + M20(m) * d3; 463 | } 464 | 465 | float_t determinant(const mat4& m) 466 | { 467 | const float_t s0 = M00(m) * M11(m) - M10(m) * M01(m); 468 | const float_t s1 = M00(m) * M12(m) - M10(m) * M02(m); 469 | const float_t s2 = M00(m) * M13(m) - M10(m) * M03(m); 470 | const float_t s3 = M01(m) * M12(m) - M11(m) * M02(m); 471 | const float_t s4 = M01(m) * M13(m) - M11(m) * M03(m); 472 | const float_t s5 = M02(m) * M13(m) - M12(m) * M03(m); 473 | const float_t c5 = M22(m) * M33(m) - M32(m) * M23(m); 474 | const float_t c4 = M21(m) * M33(m) - M31(m) * M23(m); 475 | const float_t c3 = M21(m) * M32(m) - M31(m) * M22(m); 476 | const float_t c2 = M20(m) * M33(m) - M30(m) * M23(m); 477 | const float_t c1 = M20(m) * M32(m) - M30(m) * M22(m); 478 | const float_t c0 = M20(m) * M31(m) - M30(m) * M21(m); 479 | const float_t det = s0 * c5 - s1 * c4 + s2 * c3 480 | + s3 * c2 - s4 * c1 + s5 * c0; 481 | 482 | return det; 483 | } 484 | 485 | //------------------------------------------------------------------------------ 486 | // Transpose 487 | mat2 transpose(const mat2& m) 488 | { 489 | return mat2(M00(m), M10(m), M01(m), M11(m)); 490 | } 491 | 492 | mat3 transpose(const mat3& m) 493 | { 494 | return mat3(M00(m), M10(m), M20(m), 495 | M01(m), M11(m), M21(m), 496 | M02(m), M12(m), M22(m)); 497 | } 498 | 499 | mat4 transpose(const mat4& m) 500 | { 501 | return mat4(M00(m), M10(m), M20(m), M30(m), 502 | M01(m), M11(m), M21(m), M31(m), 503 | M02(m), M12(m), M22(m), M32(m), 504 | M03(m), M13(m), M23(m), M33(m)); 505 | } 506 | 507 | //------------------------------------------------------------------------------ 508 | // Adjugate 509 | mat2 adjugate(const mat2& m) 510 | { 511 | return mat2( M11(m), -M01(m), 512 | -M10(m), M00(m)); 513 | } 514 | 515 | mat3 adjugate(const mat3& m) 516 | { 517 | mat3 a; 518 | 519 | M00(a) = M11(m) * M22(m) - M12(m) * M21(m); 520 | M01(a) =-M01(m) * M22(m) + M02(m) * M21(m); 521 | M02(a) = M01(m) * M12(m) - M02(m) * M11(m); 522 | 523 | M10(a) =-M10(m) * M22(m) + M12(m) * M20(m); 524 | M11(a) = M00(m) * M22(m) - M02(m) * M20(m); 525 | M12(a) =-M00(m) * M12(m) + M02(m) * M10(m); 526 | 527 | M20(a) = M10(m) * M21(m) - M11(m) * M20(m); 528 | M21(a) =-M00(m) * M21(m) + M01(m) * M20(m); 529 | M22(a) = M00(m) * M11(m) - M01(m) * M10(m); 530 | 531 | return a; 532 | } 533 | 534 | mat4 adjugate(const mat4& m) 535 | { 536 | /* based on Laplace expansion theorem */ 537 | const float_t s0 = M00(m) * M11(m) - M10(m) * M01(m); 538 | const float_t s1 = M00(m) * M12(m) - M10(m) * M02(m); 539 | const float_t s2 = M00(m) * M13(m) - M10(m) * M03(m); 540 | const float_t s3 = M01(m) * M12(m) - M11(m) * M02(m); 541 | const float_t s4 = M01(m) * M13(m) - M11(m) * M03(m); 542 | const float_t s5 = M02(m) * M13(m) - M12(m) * M03(m); 543 | const float_t c5 = M22(m) * M33(m) - M32(m) * M23(m); 544 | const float_t c4 = M21(m) * M33(m) - M31(m) * M23(m); 545 | const float_t c3 = M21(m) * M32(m) - M31(m) * M22(m); 546 | const float_t c2 = M20(m) * M33(m) - M30(m) * M23(m); 547 | const float_t c1 = M20(m) * M32(m) - M30(m) * M22(m); 548 | const float_t c0 = M20(m) * M31(m) - M30(m) * M21(m); 549 | mat4 a; 550 | 551 | M00(a) = M11(m)*c5 - M12(m)*c4 + M13(m)*c3; 552 | M01(a) =-M01(m)*c5 + M02(m)*c4 - M03(m)*c3; 553 | M02(a) = M31(m)*s5 - M32(m)*s4 + M33(m)*s3; 554 | M03(a) =-M21(m)*s5 + M22(m)*s4 - M23(m)*s3; 555 | 556 | M10(a) =-M10(m)*c5 + M12(m)*c2 - M13(m)*c1; 557 | M11(a) = M00(m)*c5 - M02(m)*c2 + M03(m)*c1; 558 | M12(a) =-M30(m)*s5 + M32(m)*s2 - M33(m)*s1; 559 | M13(a) = M20(m)*s5 - M22(m)*s2 + M23(m)*s1; 560 | 561 | M20(a) = M10(m)*c4 - M11(m)*c2 + M13(m)*c0; 562 | M21(a) =-M00(m)*c4 + M01(m)*c2 - M03(m)*c0; 563 | M22(a) = M30(m)*s4 - M31(m)*s2 + M33(m)*s0; 564 | M23(a) =-M20(m)*s4 + M21(m)*s2 - M23(m)*s0; 565 | 566 | M30(a) =-M10(m)*c3 + M11(m)*c1 - M12(m)*c0; 567 | M31(a) = M00(m)*c3 - M01(m)*c1 + M02(m)*c0; 568 | M32(a) =-M30(m)*s3 + M31(m)*s1 - M32(m)*s0; 569 | M33(a) = M20(m)*s3 - M21(m)*s1 + M22(m)*s0; 570 | 571 | return a; 572 | } 573 | 574 | //------------------------------------------------------------------------------ 575 | // Inverse 576 | mat2 inverse(const mat2& m) 577 | { 578 | float_t det = determinant(m); 579 | DJA_ASSERT(det != float_t(0)); 580 | 581 | return (float_t(1) / det) * adjugate(m); 582 | } 583 | 584 | mat3 inverse(const mat3& m) 585 | { 586 | float_t det = determinant(m); 587 | DJA_ASSERT(det != float_t(0)); 588 | 589 | return (float_t(1) / det) * adjugate(m); 590 | } 591 | 592 | mat4 inverse(const mat4& m) 593 | { 594 | /* based on Laplace expansion theorem */ 595 | const float_t s0 = M00(m) * M11(m) - M10(m) * M01(m); 596 | const float_t s1 = M00(m) * M12(m) - M10(m) * M02(m); 597 | const float_t s2 = M00(m) * M13(m) - M10(m) * M03(m); 598 | const float_t s3 = M01(m) * M12(m) - M11(m) * M02(m); 599 | const float_t s4 = M01(m) * M13(m) - M11(m) * M03(m); 600 | const float_t s5 = M02(m) * M13(m) - M12(m) * M03(m); 601 | const float_t c5 = M22(m) * M33(m) - M32(m) * M23(m); 602 | const float_t c4 = M21(m) * M33(m) - M31(m) * M23(m); 603 | const float_t c3 = M21(m) * M32(m) - M31(m) * M22(m); 604 | const float_t c2 = M20(m) * M33(m) - M30(m) * M23(m); 605 | const float_t c1 = M20(m) * M32(m) - M30(m) * M22(m); 606 | const float_t c0 = M20(m) * M31(m) - M30(m) * M21(m); 607 | const float_t det = s0 * c5 - s1 * c4 + s2 * c3 608 | + s3 * c2 - s4 * c1 + s5 * c0; 609 | DJA_ASSERT(det != float_t(0)); 610 | mat4 a; 611 | 612 | M00(a) = M11(m)*c5 - M12(m)*c4 + M13(m)*c3; 613 | M01(a) =-M01(m)*c5 + M02(m)*c4 - M03(m)*c3; 614 | M02(a) = M31(m)*s5 - M32(m)*s4 + M33(m)*s3; 615 | M03(a) =-M21(m)*s5 + M22(m)*s4 - M23(m)*s3; 616 | 617 | M10(a) =-M10(m)*c5 + M12(m)*c2 - M13(m)*c1; 618 | M11(a) = M00(m)*c5 - M02(m)*c2 + M03(m)*c1; 619 | M12(a) =-M30(m)*s5 + M32(m)*s2 - M33(m)*s1; 620 | M13(a) = M20(m)*s5 - M22(m)*s2 + M23(m)*s1; 621 | 622 | M20(a) = M10(m)*c4 - M11(m)*c2 + M13(m)*c0; 623 | M21(a) =-M00(m)*c4 + M01(m)*c2 - M03(m)*c0; 624 | M22(a) = M30(m)*s4 - M31(m)*s2 + M33(m)*s0; 625 | M23(a) =-M20(m)*s4 + M21(m)*s2 - M23(m)*s0; 626 | 627 | M30(a) =-M10(m)*c3 + M11(m)*c1 - M12(m)*c0; 628 | M31(a) = M00(m)*c3 - M01(m)*c1 + M02(m)*c0; 629 | M32(a) =-M30(m)*s3 + M31(m)*s1 - M32(m)*s0; 630 | M33(a) = M20(m)*s3 - M21(m)*s1 + M22(m)*s0; 631 | 632 | return (float_t(1) / det) * a; 633 | } 634 | 635 | //------------------------------------------------------------------------------ 636 | // Matrix-Scalar Muliplication 637 | mat2 operator*(const float_t s, const mat2& m) 638 | { 639 | return mat2(s * m[0], s * m[1]); 640 | } 641 | 642 | mat3 operator*(const float_t s, const mat3& m) 643 | { 644 | return mat3(s * m[0], s * m[1], s * m[2]); 645 | } 646 | 647 | mat4 operator*(const float_t s, const mat4& m) 648 | { 649 | return mat4(s * m[0], s * m[1], s * m[2], s * m[3]); 650 | } 651 | 652 | //------------------------------------------------------------------------------ 653 | // Matrix-Vector Muliplication 654 | vec2 operator*(const mat2& m, const vec2& a) 655 | { 656 | return vec2(dot(m[0], a), dot(m[1], a)); 657 | } 658 | 659 | vec3 operator*(const mat3& m, const vec3& a) 660 | { 661 | return vec3(dot(m[0], a), dot(m[1], a), dot(m[2], a)); 662 | } 663 | 664 | vec4 operator*(const mat4& m, const vec4& a) 665 | { 666 | return vec4(dot(m[0], a), dot(m[1], a), dot(m[2], a), dot(m[3], a)); 667 | } 668 | 669 | 670 | //------------------------------------------------------------------------------ 671 | // Matrix-Matrix Muliplication 672 | mat2 operator*(const mat2& a, const mat2& b) 673 | { 674 | mat2 t = transpose(b), r; 675 | 676 | for (int j = 0; j < 2; ++j) 677 | for (int i = 0; i < 2; ++i) 678 | r[j][i] = dot(a[j], t[i]); 679 | 680 | return r; 681 | } 682 | 683 | mat3 operator*(const mat3& a, const mat3& b) 684 | { 685 | mat3 t = transpose(b), r; 686 | 687 | for (int j = 0; j < 3; ++j) 688 | for (int i = 0; i < 3; ++i) 689 | r[j][i] = dot(a[j], t[i]); 690 | 691 | return r; 692 | } 693 | 694 | mat4 operator*(const mat4& a, const mat4& b) 695 | { 696 | mat4 t = transpose(b), r; 697 | 698 | for (int j = 0; j < 4; ++j) 699 | for (int i = 0; i < 4; ++i) 700 | r[j][i] = dot(a[j], t[i]); 701 | 702 | return r; 703 | } 704 | 705 | //------------------------------------------------------------------------------ 706 | // Matrix Factories 707 | mat2 mat2::memcpy(const float_t *v, bool rowmajor) 708 | { 709 | mat2 m; 710 | std::memcpy(&m[0][0], v, sizeof(mat2)); 711 | return rowmajor ? m : transpose(m); 712 | } 713 | 714 | mat3 mat3::memcpy(const float_t *v, bool rowmajor) 715 | { 716 | mat3 m; 717 | std::memcpy(&m[0][0], v, sizeof(mat3)); 718 | return rowmajor ? m : transpose(m); 719 | } 720 | 721 | mat4 mat4::memcpy(const float_t *v, bool rowmajor) 722 | { 723 | mat4 m; 724 | std::memcpy(&m[0][0], v, sizeof(mat4)); 725 | return rowmajor ? m : transpose(m); 726 | } 727 | 728 | /* Scale */ 729 | 730 | mat2 mat2::scale(float_t value) { return mat2(value); } 731 | mat2 mat2::scale(const vec2& values) 732 | { 733 | return mat2(values[0], 0, 734 | 0, values[1]); 735 | } 736 | 737 | mat3 mat3::scale(float_t value) { return mat3(value); } 738 | mat3 mat3::scale(const vec3& values) 739 | { 740 | return mat3(values[0], 0, 0, 741 | 0, values[1], 0, 742 | 0, 0, values[2]); 743 | } 744 | 745 | mat4 mat4::homogeneous::scale(float_t value) 746 | { 747 | return mat4::homogeneous::from_mat3(mat3::scale(value)); 748 | } 749 | mat4 mat4::homogeneous::scale(const vec3& values) 750 | { 751 | return mat4::homogeneous::from_mat3(mat3::scale(values)); 752 | } 753 | 754 | /* Rotation */ 755 | 756 | mat2 mat2::rotation(float_t rad) 757 | { 758 | return mat2(complex::polar(rad, 1)); 759 | } 760 | 761 | mat3 mat3::rotation(const vec3& axis, float_t rad) 762 | { 763 | return rotation(quaternion::rotation(axis, rad)); 764 | } 765 | mat3 mat3::rotation(const quaternion& q) 766 | { 767 | float_t jj2 = float_t(2) * q[2] * q[2]; 768 | float_t ij2 = float_t(2) * q[1] * q[2]; 769 | float_t ik2 = float_t(2) * q[1] * q[3]; 770 | float_t jk2 = float_t(2) * q[2] * q[3]; 771 | float_t kk2 = float_t(2) * q[3] * q[3]; 772 | float_t rk2 = float_t(2) * q[0] * q[3]; 773 | float_t rj2 = float_t(2) * q[0] * q[2]; 774 | float_t ri2 = float_t(2) * q[0] * q[1]; 775 | float_t ii2 = float_t(2) * q[1] * q[1]; 776 | 777 | return mat3(float_t(1) - jj2 - kk2, ij2 + rk2, ik2 - rj2, 778 | ij2 - rk2, float_t(1) - ii2 - kk2, jk2 + ri2, 779 | ik2 + rj2, jk2 - ri2, float_t(1) - ii2 - jj2); 780 | } 781 | 782 | mat4 mat4::homogeneous::rotation(const vec3& axis, float_t rad) 783 | { 784 | return mat4::homogeneous::from_mat3(mat3::rotation(axis, rad)); 785 | } 786 | mat4 mat4::homogeneous::rotation(const quaternion& q) 787 | { 788 | return mat4::homogeneous::from_mat3(mat3::rotation(q)); 789 | } 790 | 791 | /* Homogeneous Transformations */ 792 | mat4 mat4::homogeneous::from_mat3(const mat3& m) 793 | { 794 | return mat4(M00(m), M01(m), M02(m), 0, 795 | M10(m), M11(m), M12(m), 0, 796 | M20(m), M21(m), M22(m), 0, 797 | 0 , 0 , 0 , 1); 798 | } 799 | 800 | mat4 mat4::homogeneous::translation(const vec3& dir) 801 | { 802 | return mat4(1, 0, 0, dir.x, 803 | 0, 1, 0, dir.y, 804 | 0, 0, 1, dir.z, 805 | 0, 0, 0, 1 ); 806 | } 807 | 808 | /* Projections */ 809 | mat4 810 | mat4::homogeneous::perspective( 811 | float_t fovy, 812 | float_t aspect, 813 | float_t zNear, 814 | float_t zFar 815 | ) { 816 | DJA_ASSERT(fovy > float_t(0)); 817 | DJA_ASSERT(aspect > float_t(0)); 818 | DJA_ASSERT(zNear > float_t(0)); 819 | DJA_ASSERT(zNear < zFar); 820 | float_t f = float_t(1) / tan(fovy / float_t(2)); 821 | float_t c = float_t(1) / (zNear - zFar); 822 | float_t a = (zFar + zNear) * c; 823 | float_t b = float_t(2) * zNear * zFar * c; 824 | #if 0 // Standard OpenGL 825 | return mat4(f / aspect, 0, 0 , 0, 826 | 0 , f, 0 , 0, 827 | 0 , 0, a , b, 828 | 0 , 0, -1, 0); 829 | #else // XYZ -> OpenGL 830 | return mat4( 0, f / aspect, 0, 0, 831 | 0, 0 , f, 0, 832 | a, 0 , 0, b, 833 | -1, 0 , 0, 0); 834 | #endif 835 | } 836 | 837 | mat4 838 | mat4::homogeneous::orthographic( 839 | float_t left, float_t right, 840 | float_t bottom, float_t top, 841 | float_t near, float_t far 842 | ) { 843 | DJA_ASSERT(left != right && bottom != top && near != far); 844 | float_t c1 = float_t(1) / (right - left); 845 | float_t c2 = float_t(1) / (top - bottom); 846 | float_t c3 = float_t(1) / (far - near); 847 | float_t d1 = float_t(2) * c1; 848 | float_t d2 = float_t(2) * c2; 849 | float_t d3 = -float_t(2) * c3; 850 | float_t tx = -(right + left) * c1; 851 | float_t ty = -(top + bottom) * c2; 852 | float_t tz = -(far + near) * c3; 853 | 854 | #if 0 // Standard OpenGL 855 | return mat4(d1, 0 , 0 , tx, 856 | 0 , d2, 0 , ty, 857 | 0 , 0 , d3, tz, 858 | 0 , 0 , 0 , 1); 859 | #else // XYZ -> OpenGL 860 | return mat4(0 , d1, 0 , tx, 861 | 0 , 0 , d2, ty, 862 | d3, 0 , 0 , tz, 863 | 0 , 0 , 0 , 1); 864 | #endif 865 | } 866 | 867 | mat4 868 | mat4::homogeneous::tile( 869 | float_t left, float_t right, 870 | float_t bottom, float_t top 871 | ) { 872 | DJA_ASSERT(left != right && bottom != top); 873 | float_t c1 = float_t(1) / (right - left); 874 | float_t c2 = float_t(1) / (top - bottom); 875 | float_t d1 = float_t(2) * c1; 876 | float_t d2 = float_t(2) * c2; 877 | float_t tx = -(right + left) * c1; 878 | float_t ty = -(top + bottom) * c2; 879 | 880 | return mat4(d1, 0 , 0, tx, 881 | 0 , d2, 0, ty, 882 | 0 , 0 , 1, 0, 883 | 0 , 0 , 0, 1); 884 | } 885 | 886 | 887 | // ***************************************************************************** 888 | /* Temporary Macros Cleanup */ 889 | #undef M00 890 | #undef M01 891 | #undef M02 892 | #undef M03 893 | #undef M10 894 | #undef M11 895 | #undef M12 896 | #undef M13 897 | #undef M20 898 | #undef M21 899 | #undef M22 900 | #undef M23 901 | #undef M30 902 | #undef M31 903 | #undef M32 904 | #undef M33 905 | 906 | } // namespace dja 907 | 908 | #endif // DJ_ALGEBRA_IMPLEMENTATION 909 | 910 | 911 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2015 Omar Cornut and ImGui contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // USER IMPLEMENTATION 3 | // This file contains compile-time options for ImGui. 4 | // Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). 5 | //----------------------------------------------------------------------------- 6 | 7 | #pragma once 8 | 9 | //---- Define assertion handler. Defaults to calling assert(). 10 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 11 | 12 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. 13 | //#define IMGUI_API __declspec( dllexport ) 14 | //#define IMGUI_API __declspec( dllimport ) 15 | 16 | //---- Include imgui_user.h at the end of imgui.h 17 | //#define IMGUI_INCLUDE_IMGUI_USER_H 18 | 19 | //---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) 20 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS 21 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS 22 | 23 | //---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty) 24 | //#define IMGUI_DISABLE_TEST_WINDOWS 25 | 26 | //---- Don't define obsolete functions names 27 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 28 | 29 | //---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) 30 | //#define IMGUI_USE_BGRA_PACKED_COLOR 31 | 32 | //---- Implement STB libraries in a namespace to avoid conflicts 33 | //#define IMGUI_STB_NAMESPACE ImGuiStb 34 | 35 | //---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. 36 | /* 37 | #define IM_VEC2_CLASS_EXTRA \ 38 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 39 | operator MyVec2() const { return MyVec2(x,y); } 40 | 41 | #define IM_VEC4_CLASS_EXTRA \ 42 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 43 | operator MyVec4() const { return MyVec4(x,y,z,w); } 44 | */ 45 | 46 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 47 | //---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. 48 | /* 49 | namespace ImGui 50 | { 51 | void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); 52 | } 53 | */ 54 | 55 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/imgui_impl_sdl_gl3.cpp: -------------------------------------------------------------------------------- 1 | // ImGui SDL2 binding with OpenGL3 2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | 4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7 | // https://github.com/ocornut/imgui 8 | 9 | 10 | #include "imgui.h" 11 | #include "imgui_impl_sdl_gl3.h" 12 | 13 | // SDL,GL3W 14 | #include 15 | #include 16 | #include "../gl_core_4_3.h" 17 | #define GL_FUNC_ADD 0x8006 18 | 19 | // Data 20 | static double g_Time = 0.0f; 21 | static bool g_MousePressed[3] = { false, false, false }; 22 | static float g_MouseWheel = 0.0f; 23 | static GLuint g_FontTexture = 0; 24 | static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; 25 | static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; 26 | static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; 27 | static unsigned int g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0; 28 | 29 | // This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) 30 | // If text or lines are blurry when integrating ImGui in your engine: 31 | // - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) 32 | void ImGui_ImplSdlGL3_RenderDrawLists(ImDrawData* draw_data) 33 | { 34 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 35 | ImGuiIO& io = ImGui::GetIO(); 36 | int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); 37 | int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); 38 | if (fb_width == 0 || fb_height == 0) 39 | return; 40 | draw_data->ScaleClipRects(io.DisplayFramebufferScale); 41 | 42 | // Backup GL state 43 | GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); 44 | GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 45 | GLint last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, &last_active_texture); 46 | GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 47 | GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer); 48 | GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 49 | GLint last_blend_src; glGetIntegerv(GL_BLEND_SRC, &last_blend_src); 50 | GLint last_blend_dst; glGetIntegerv(GL_BLEND_DST, &last_blend_dst); 51 | GLint last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, &last_blend_equation_rgb); 52 | GLint last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &last_blend_equation_alpha); 53 | GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); 54 | GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); 55 | GLboolean last_enable_blend = glIsEnabled(GL_BLEND); 56 | GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); 57 | GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); 58 | GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); 59 | 60 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled 61 | glEnable(GL_BLEND); 62 | glBlendEquation(GL_FUNC_ADD); 63 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 64 | glDisable(GL_CULL_FACE); 65 | glDisable(GL_DEPTH_TEST); 66 | glEnable(GL_SCISSOR_TEST); 67 | glActiveTexture(GL_TEXTURE0); 68 | 69 | // Setup orthographic projection matrix 70 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); 71 | const float ortho_projection[4][4] = 72 | { 73 | { 2.0f/io.DisplaySize.x, 0.0f, 0.0f, 0.0f }, 74 | { 0.0f, 2.0f/-io.DisplaySize.y, 0.0f, 0.0f }, 75 | { 0.0f, 0.0f, -1.0f, 0.0f }, 76 | {-1.0f, 1.0f, 0.0f, 1.0f }, 77 | }; 78 | glUseProgram(g_ShaderHandle); 79 | glUniform1i(g_AttribLocationTex, 0); 80 | glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); 81 | glBindVertexArray(g_VaoHandle); 82 | 83 | for (int n = 0; n < draw_data->CmdListsCount; n++) 84 | { 85 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 86 | const ImDrawIdx* idx_buffer_offset = 0; 87 | 88 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); 89 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); 90 | 91 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); 92 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); 93 | 94 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 95 | { 96 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 97 | if (pcmd->UserCallback) 98 | { 99 | pcmd->UserCallback(cmd_list, pcmd); 100 | } 101 | else 102 | { 103 | glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); 104 | glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); 105 | glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); 106 | } 107 | idx_buffer_offset += pcmd->ElemCount; 108 | } 109 | } 110 | 111 | // Restore modified GL state 112 | glUseProgram(last_program); 113 | glActiveTexture(last_active_texture); 114 | glBindTexture(GL_TEXTURE_2D, last_texture); 115 | glBindVertexArray(last_vertex_array); 116 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 117 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer); 118 | glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); 119 | glBlendFunc(last_blend_src, last_blend_dst); 120 | if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); 121 | if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); 122 | if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); 123 | if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); 124 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); 125 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); 126 | } 127 | 128 | static const char* ImGui_ImplSdlGL3_GetClipboardText(void *dummy) 129 | { 130 | return SDL_GetClipboardText(); 131 | } 132 | 133 | static void ImGui_ImplSdlGL3_SetClipboardText(void *dummy, const char* text) 134 | { 135 | SDL_SetClipboardText(text); 136 | } 137 | 138 | bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event) 139 | { 140 | ImGuiIO& io = ImGui::GetIO(); 141 | switch (event->type) 142 | { 143 | case SDL_MOUSEWHEEL: 144 | { 145 | if (event->wheel.y > 0) 146 | g_MouseWheel = 1; 147 | if (event->wheel.y < 0) 148 | g_MouseWheel = -1; 149 | return true; 150 | } 151 | case SDL_MOUSEBUTTONDOWN: 152 | { 153 | if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; 154 | if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; 155 | if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; 156 | return true; 157 | } 158 | case SDL_TEXTINPUT: 159 | { 160 | io.AddInputCharactersUTF8(event->text.text); 161 | return true; 162 | } 163 | case SDL_KEYDOWN: 164 | case SDL_KEYUP: 165 | { 166 | int key = event->key.keysym.sym & ~SDLK_SCANCODE_MASK; 167 | io.KeysDown[key] = (event->type == SDL_KEYDOWN); 168 | io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); 169 | io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); 170 | io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); 171 | io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); 172 | return true; 173 | } 174 | } 175 | return false; 176 | } 177 | 178 | void ImGui_ImplSdlGL3_CreateFontsTexture() 179 | { 180 | // Build texture atlas 181 | ImGuiIO& io = ImGui::GetIO(); 182 | unsigned char* pixels; 183 | int width, height; 184 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits for OpenGL3 demo because it is more likely to be compatible with user's existing shader. 185 | 186 | // Upload texture to graphics system 187 | GLint last_texture; 188 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 189 | glGenTextures(1, &g_FontTexture); 190 | glBindTexture(GL_TEXTURE_2D, g_FontTexture); 191 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 192 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 193 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 194 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 195 | 196 | // Store our identifier 197 | io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; 198 | 199 | // Restore state 200 | glBindTexture(GL_TEXTURE_2D, last_texture); 201 | } 202 | 203 | bool ImGui_ImplSdlGL3_CreateDeviceObjects() 204 | { 205 | // Backup GL state 206 | GLint last_texture, last_array_buffer, last_vertex_array; 207 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 208 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 209 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 210 | 211 | const GLchar *vertex_shader = 212 | "#version 330\n" 213 | "uniform mat4 ProjMtx;\n" 214 | "in vec2 Position;\n" 215 | "in vec2 UV;\n" 216 | "in vec4 Color;\n" 217 | "out vec2 Frag_UV;\n" 218 | "out vec4 Frag_Color;\n" 219 | "void main()\n" 220 | "{\n" 221 | " Frag_UV = UV;\n" 222 | " Frag_Color = Color;\n" 223 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 224 | "}\n"; 225 | 226 | const GLchar* fragment_shader = 227 | "#version 330\n" 228 | "uniform sampler2D Texture;\n" 229 | "in vec2 Frag_UV;\n" 230 | "in vec4 Frag_Color;\n" 231 | "out vec4 Out_Color;\n" 232 | "void main()\n" 233 | "{\n" 234 | " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" 235 | "}\n"; 236 | 237 | g_ShaderHandle = glCreateProgram(); 238 | g_VertHandle = glCreateShader(GL_VERTEX_SHADER); 239 | g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); 240 | glShaderSource(g_VertHandle, 1, &vertex_shader, 0); 241 | glShaderSource(g_FragHandle, 1, &fragment_shader, 0); 242 | glCompileShader(g_VertHandle); 243 | glCompileShader(g_FragHandle); 244 | glAttachShader(g_ShaderHandle, g_VertHandle); 245 | glAttachShader(g_ShaderHandle, g_FragHandle); 246 | glLinkProgram(g_ShaderHandle); 247 | 248 | g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); 249 | g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); 250 | g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); 251 | g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); 252 | g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); 253 | 254 | glGenBuffers(1, &g_VboHandle); 255 | glGenBuffers(1, &g_ElementsHandle); 256 | 257 | glGenVertexArrays(1, &g_VaoHandle); 258 | glBindVertexArray(g_VaoHandle); 259 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); 260 | glEnableVertexAttribArray(g_AttribLocationPosition); 261 | glEnableVertexAttribArray(g_AttribLocationUV); 262 | glEnableVertexAttribArray(g_AttribLocationColor); 263 | 264 | #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) 265 | glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, pos)); 266 | glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, uv)); 267 | glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)OFFSETOF(ImDrawVert, col)); 268 | #undef OFFSETOF 269 | 270 | ImGui_ImplSdlGL3_CreateFontsTexture(); 271 | 272 | // Restore modified GL state 273 | glBindTexture(GL_TEXTURE_2D, last_texture); 274 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 275 | glBindVertexArray(last_vertex_array); 276 | 277 | return true; 278 | } 279 | 280 | void ImGui_ImplSdlGL3_InvalidateDeviceObjects() 281 | { 282 | if (g_VaoHandle) glDeleteVertexArrays(1, &g_VaoHandle); 283 | if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); 284 | if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); 285 | g_VaoHandle = g_VboHandle = g_ElementsHandle = 0; 286 | 287 | if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); 288 | if (g_VertHandle) glDeleteShader(g_VertHandle); 289 | g_VertHandle = 0; 290 | 291 | if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); 292 | if (g_FragHandle) glDeleteShader(g_FragHandle); 293 | g_FragHandle = 0; 294 | 295 | if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); 296 | g_ShaderHandle = 0; 297 | 298 | if (g_FontTexture) 299 | { 300 | glDeleteTextures(1, &g_FontTexture); 301 | ImGui::GetIO().Fonts->TexID = 0; 302 | g_FontTexture = 0; 303 | } 304 | } 305 | 306 | bool ImGui_ImplSdlGL3_Init(SDL_Window* window) 307 | { 308 | ImGuiIO& io = ImGui::GetIO(); 309 | io.KeyMap[ImGuiKey_Tab] = SDLK_TAB; // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. 310 | io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; 311 | io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; 312 | io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; 313 | io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; 314 | io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; 315 | io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; 316 | io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; 317 | io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; 318 | io.KeyMap[ImGuiKey_Delete] = SDLK_DELETE; 319 | io.KeyMap[ImGuiKey_Backspace] = SDLK_BACKSPACE; 320 | io.KeyMap[ImGuiKey_Enter] = SDLK_RETURN; 321 | io.KeyMap[ImGuiKey_Escape] = SDLK_ESCAPE; 322 | io.KeyMap[ImGuiKey_A] = SDLK_a; 323 | io.KeyMap[ImGuiKey_C] = SDLK_c; 324 | io.KeyMap[ImGuiKey_V] = SDLK_v; 325 | io.KeyMap[ImGuiKey_X] = SDLK_x; 326 | io.KeyMap[ImGuiKey_Y] = SDLK_y; 327 | io.KeyMap[ImGuiKey_Z] = SDLK_z; 328 | 329 | io.RenderDrawListsFn = ImGui_ImplSdlGL3_RenderDrawLists; // Alternatively you can set this to NULL and call ImGui::GetDrawData() after ImGui::Render() to get the same ImDrawData pointer. 330 | io.SetClipboardTextFn = ImGui_ImplSdlGL3_SetClipboardText; 331 | io.GetClipboardTextFn = ImGui_ImplSdlGL3_GetClipboardText; 332 | 333 | #ifdef _WIN32 334 | SDL_SysWMinfo wmInfo; 335 | SDL_VERSION(&wmInfo.version); 336 | SDL_GetWindowWMInfo(window, &wmInfo); 337 | io.ImeWindowHandle = wmInfo.info.win.window; 338 | #else 339 | (void)window; 340 | #endif 341 | 342 | return true; 343 | } 344 | 345 | void ImGui_ImplSdlGL3_Shutdown() 346 | { 347 | ImGui_ImplSdlGL3_InvalidateDeviceObjects(); 348 | ImGui::Shutdown(); 349 | } 350 | 351 | void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window) 352 | { 353 | if (!g_FontTexture) 354 | ImGui_ImplSdlGL3_CreateDeviceObjects(); 355 | 356 | ImGuiIO& io = ImGui::GetIO(); 357 | 358 | // Setup display size (every frame to accommodate for window resizing) 359 | int w, h; 360 | int display_w, display_h; 361 | SDL_GetWindowSize(window, &w, &h); 362 | SDL_GL_GetDrawableSize(window, &display_w, &display_h); 363 | io.DisplaySize = ImVec2((float)w, (float)h); 364 | io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); 365 | 366 | // Setup time step 367 | Uint32 time = SDL_GetTicks(); 368 | double current_time = time / 1000.0; 369 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); 370 | g_Time = current_time; 371 | 372 | // Setup inputs 373 | // (we already got mouse wheel, keyboard keys & characters from SDL_PollEvent()) 374 | int mx, my; 375 | Uint32 mouseMask = SDL_GetMouseState(&mx, &my); 376 | if (SDL_GetWindowFlags(window) & SDL_WINDOW_MOUSE_FOCUS) 377 | io.MousePos = ImVec2((float)mx, (float)my); // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) 378 | else 379 | io.MousePos = ImVec2(-1, -1); 380 | 381 | io.MouseDown[0] = g_MousePressed[0] || (mouseMask & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. 382 | io.MouseDown[1] = g_MousePressed[1] || (mouseMask & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; 383 | io.MouseDown[2] = g_MousePressed[2] || (mouseMask & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; 384 | g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; 385 | 386 | io.MouseWheel = g_MouseWheel; 387 | g_MouseWheel = 0.0f; 388 | 389 | // Hide OS mouse cursor if ImGui is drawing it 390 | SDL_ShowCursor(io.MouseDrawCursor ? 0 : 1); 391 | 392 | // Start the frame 393 | ImGui::NewFrame(); 394 | } 395 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/imgui_impl_sdl_gl3.h: -------------------------------------------------------------------------------- 1 | // ImGui SDL2 binding with OpenGL3 2 | // In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. 3 | 4 | // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. 5 | // If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). 6 | // If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. 7 | // https://github.com/ocornut/imgui 8 | 9 | 10 | struct SDL_Window; 11 | typedef union SDL_Event SDL_Event; 12 | 13 | IMGUI_API bool ImGui_ImplSdlGL3_Init(SDL_Window* window); 14 | IMGUI_API void ImGui_ImplSdlGL3_Shutdown(); 15 | IMGUI_API void ImGui_ImplSdlGL3_NewFrame(SDL_Window* window); 16 | IMGUI_API bool ImGui_ImplSdlGL3_ProcessEvent(SDL_Event* event); 17 | 18 | // Use if you want to reset your rendering device without losing ImGui state. 19 | IMGUI_API void ImGui_ImplSdlGL3_InvalidateDeviceObjects(); 20 | IMGUI_API bool ImGui_ImplSdlGL3_CreateDeviceObjects(); 21 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/imgui_internal.h: -------------------------------------------------------------------------------- 1 | // dear imgui, v1.50 WIP 2 | // (internals) 3 | 4 | // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! 5 | // Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) 6 | // #define IMGUI_DEFINE_MATH_OPERATORS 7 | 8 | #pragma once 9 | 10 | #ifndef IMGUI_VERSION 11 | #error Must include imgui.h before imgui_internal.h 12 | #endif 13 | 14 | #include // FILE* 15 | #include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf 16 | 17 | #ifdef _MSC_VER 18 | #pragma warning (push) 19 | #pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) 20 | #endif 21 | 22 | #ifdef __clang__ 23 | #pragma clang diagnostic push 24 | #pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h 25 | #pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h 26 | #pragma clang diagnostic ignored "-Wold-style-cast" 27 | #endif 28 | 29 | //----------------------------------------------------------------------------- 30 | // Forward Declarations 31 | //----------------------------------------------------------------------------- 32 | 33 | struct ImRect; 34 | struct ImGuiColMod; 35 | struct ImGuiStyleMod; 36 | struct ImGuiGroupData; 37 | struct ImGuiSimpleColumns; 38 | struct ImGuiDrawContext; 39 | struct ImGuiTextEditState; 40 | struct ImGuiIniData; 41 | struct ImGuiMouseCursorData; 42 | struct ImGuiPopupRef; 43 | struct ImGuiWindow; 44 | 45 | typedef int ImGuiLayoutType; // enum ImGuiLayoutType_ 46 | typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ 47 | typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ 48 | typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ 49 | 50 | //------------------------------------------------------------------------- 51 | // STB libraries 52 | //------------------------------------------------------------------------- 53 | 54 | namespace ImGuiStb 55 | { 56 | 57 | #undef STB_TEXTEDIT_STRING 58 | #undef STB_TEXTEDIT_CHARTYPE 59 | #define STB_TEXTEDIT_STRING ImGuiTextEditState 60 | #define STB_TEXTEDIT_CHARTYPE ImWchar 61 | #define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f 62 | #include "stb_textedit.h" 63 | 64 | } // namespace ImGuiStb 65 | 66 | //----------------------------------------------------------------------------- 67 | // Context 68 | //----------------------------------------------------------------------------- 69 | 70 | #ifndef GImGui 71 | extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer 72 | #endif 73 | 74 | //----------------------------------------------------------------------------- 75 | // Helpers 76 | //----------------------------------------------------------------------------- 77 | 78 | #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) 79 | #define IM_PI 3.14159265358979323846f 80 | #define IM_OFFSETOF(_TYPE,_ELM) ((size_t)&(((_TYPE*)0)->_ELM)) 81 | 82 | // Helpers: UTF-8 <> wchar 83 | IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count 84 | IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count 85 | IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count 86 | IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) 87 | IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points 88 | 89 | // Helpers: Misc 90 | IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings 91 | IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); 92 | IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); 93 | IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c); 94 | static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } 95 | static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } 96 | 97 | // Helpers: String 98 | IMGUI_API int ImStricmp(const char* str1, const char* str2); 99 | IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); 100 | IMGUI_API char* ImStrdup(const char* str); 101 | IMGUI_API int ImStrlenW(const ImWchar* str); 102 | IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line 103 | IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); 104 | IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3); 105 | IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args); 106 | 107 | // Helpers: Math 108 | // We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined) 109 | #ifdef IMGUI_DEFINE_MATH_OPERATORS 110 | static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } 111 | static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } 112 | static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); } 113 | static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); } 114 | static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); } 115 | static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); } 116 | static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } 117 | static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } 118 | static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } 119 | static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } 120 | static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); } 121 | #endif 122 | 123 | static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } 124 | static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } 125 | static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; } 126 | static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } 127 | static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); } 128 | static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); } 129 | static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } 130 | static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } 131 | static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } 132 | static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } 133 | static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } 134 | static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } 135 | static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } 136 | static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } 137 | static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } 138 | static inline float ImFloor(float f) { return (float)(int)f; } 139 | static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } 140 | 141 | // We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. 142 | // Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. 143 | #ifdef IMGUI_DEFINE_PLACEMENT_NEW 144 | struct ImPlacementNewDummy {}; 145 | inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } 146 | inline void operator delete(void*, ImPlacementNewDummy, void*) {} 147 | #define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) 148 | #endif 149 | 150 | //----------------------------------------------------------------------------- 151 | // Types 152 | //----------------------------------------------------------------------------- 153 | 154 | enum ImGuiButtonFlags_ 155 | { 156 | ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat 157 | ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set) 158 | ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release) 159 | ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release) 160 | ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release) 161 | ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping 162 | ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press 163 | ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction 164 | ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only 165 | ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held 166 | ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable 167 | }; 168 | 169 | enum ImGuiSliderFlags_ 170 | { 171 | ImGuiSliderFlags_Vertical = 1 << 0 172 | }; 173 | 174 | enum ImGuiSelectableFlagsPrivate_ 175 | { 176 | // NB: need to be in sync with last value of ImGuiSelectableFlags_ 177 | ImGuiSelectableFlags_Menu = 1 << 3, 178 | ImGuiSelectableFlags_MenuItem = 1 << 4, 179 | ImGuiSelectableFlags_Disabled = 1 << 5, 180 | ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 181 | }; 182 | 183 | // FIXME: this is in development, not exposed/functional as a generic feature yet. 184 | enum ImGuiLayoutType_ 185 | { 186 | ImGuiLayoutType_Vertical, 187 | ImGuiLayoutType_Horizontal 188 | }; 189 | 190 | enum ImGuiPlotType 191 | { 192 | ImGuiPlotType_Lines, 193 | ImGuiPlotType_Histogram 194 | }; 195 | 196 | enum ImGuiDataType 197 | { 198 | ImGuiDataType_Int, 199 | ImGuiDataType_Float, 200 | ImGuiDataType_Float2, 201 | }; 202 | 203 | enum ImGuiCorner 204 | { 205 | ImGuiCorner_TopLeft = 1 << 0, // 1 206 | ImGuiCorner_TopRight = 1 << 1, // 2 207 | ImGuiCorner_BottomRight = 1 << 2, // 4 208 | ImGuiCorner_BottomLeft = 1 << 3, // 8 209 | ImGuiCorner_All = 0x0F 210 | }; 211 | 212 | // 2D axis aligned bounding-box 213 | // NB: we can't rely on ImVec2 math operators being available here 214 | struct IMGUI_API ImRect 215 | { 216 | ImVec2 Min; // Upper-left 217 | ImVec2 Max; // Lower-right 218 | 219 | ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} 220 | ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} 221 | ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} 222 | ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} 223 | 224 | ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); } 225 | ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); } 226 | float GetWidth() const { return Max.x-Min.x; } 227 | float GetHeight() const { return Max.y-Min.y; } 228 | ImVec2 GetTL() const { return Min; } // Top-left 229 | ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right 230 | ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left 231 | ImVec2 GetBR() const { return Max; } // Bottom-right 232 | bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } 233 | bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } 234 | bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } 235 | void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; } 236 | void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; } 237 | void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } 238 | void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } 239 | void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } 240 | void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } 241 | void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } 242 | ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const 243 | { 244 | if (!on_edge && Contains(p)) 245 | return p; 246 | if (p.x > Max.x) p.x = Max.x; 247 | else if (p.x < Min.x) p.x = Min.x; 248 | if (p.y > Max.y) p.y = Max.y; 249 | else if (p.y < Min.y) p.y = Min.y; 250 | return p; 251 | } 252 | }; 253 | 254 | // Stacked color modifier, backup of modified data so we can restore it 255 | struct ImGuiColMod 256 | { 257 | ImGuiCol Col; 258 | ImVec4 BackupValue; 259 | }; 260 | 261 | // Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. 262 | struct ImGuiStyleMod 263 | { 264 | ImGuiStyleVar VarIdx; 265 | union { int BackupInt[2]; float BackupFloat[2]; }; 266 | ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } 267 | ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } 268 | ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } 269 | }; 270 | 271 | // Stacked data for BeginGroup()/EndGroup() 272 | struct ImGuiGroupData 273 | { 274 | ImVec2 BackupCursorPos; 275 | ImVec2 BackupCursorMaxPos; 276 | float BackupIndentX; 277 | float BackupGroupOffsetX; 278 | float BackupCurrentLineHeight; 279 | float BackupCurrentLineTextBaseOffset; 280 | float BackupLogLinePosY; 281 | bool BackupActiveIdIsAlive; 282 | bool AdvanceCursor; 283 | }; 284 | 285 | // Per column data for Columns() 286 | struct ImGuiColumnData 287 | { 288 | float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) 289 | //float IndentX; 290 | }; 291 | 292 | // Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper. 293 | struct IMGUI_API ImGuiSimpleColumns 294 | { 295 | int Count; 296 | float Spacing; 297 | float Width, NextWidth; 298 | float Pos[8], NextWidths[8]; 299 | 300 | ImGuiSimpleColumns(); 301 | void Update(int count, float spacing, bool clear); 302 | float DeclColumns(float w0, float w1, float w2); 303 | float CalcExtraSpace(float avail_w); 304 | }; 305 | 306 | // Internal state of the currently focused/edited text input box 307 | struct IMGUI_API ImGuiTextEditState 308 | { 309 | ImGuiID Id; // widget id owning the text state 310 | ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. 311 | ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) 312 | ImVector TempTextBuffer; 313 | int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. 314 | int BufSizeA; // end-user buffer size 315 | float ScrollX; 316 | ImGuiStb::STB_TexteditState StbState; 317 | float CursorAnim; 318 | bool CursorFollow; 319 | bool SelectedAllMouseLock; 320 | 321 | ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } 322 | void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking 323 | void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } 324 | bool HasSelection() const { return StbState.select_start != StbState.select_end; } 325 | void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } 326 | void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; } 327 | void OnKeyPressed(int key); 328 | }; 329 | 330 | // Data saved in imgui.ini file 331 | struct ImGuiIniData 332 | { 333 | char* Name; 334 | ImGuiID Id; 335 | ImVec2 Pos; 336 | ImVec2 Size; 337 | bool Collapsed; 338 | }; 339 | 340 | // Mouse cursor data (used when io.MouseDrawCursor is set) 341 | struct ImGuiMouseCursorData 342 | { 343 | ImGuiMouseCursor Type; 344 | ImVec2 HotOffset; 345 | ImVec2 Size; 346 | ImVec2 TexUvMin[2]; 347 | ImVec2 TexUvMax[2]; 348 | }; 349 | 350 | // Storage for current popup stack 351 | struct ImGuiPopupRef 352 | { 353 | ImGuiID PopupId; // Set on OpenPopup() 354 | ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() 355 | ImGuiWindow* ParentWindow; // Set on OpenPopup() 356 | ImGuiID ParentMenuSet; // Set on OpenPopup() 357 | ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup 358 | 359 | ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; } 360 | }; 361 | 362 | // Main state for ImGui 363 | struct ImGuiContext 364 | { 365 | bool Initialized; 366 | ImGuiIO IO; 367 | ImGuiStyle Style; 368 | ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() 369 | float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() 370 | float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. 371 | ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel 372 | 373 | float Time; 374 | int FrameCount; 375 | int FrameCountEnded; 376 | int FrameCountRendered; 377 | ImVector Windows; 378 | ImVector WindowsSortBuffer; 379 | ImGuiWindow* CurrentWindow; // Being drawn into 380 | ImVector CurrentWindowStack; 381 | ImGuiWindow* FocusedWindow; // Will catch keyboard inputs 382 | ImGuiWindow* HoveredWindow; // Will catch mouse inputs 383 | ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) 384 | ImGuiID HoveredId; // Hovered widget 385 | bool HoveredIdAllowOverlap; 386 | ImGuiID HoveredIdPreviousFrame; 387 | ImGuiID ActiveId; // Active widget 388 | ImGuiID ActiveIdPreviousFrame; 389 | bool ActiveIdIsAlive; 390 | bool ActiveIdIsJustActivated; // Set at the time of activation for one frame 391 | bool ActiveIdAllowOverlap; // Set only by active widget 392 | ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) 393 | ImGuiWindow* ActiveIdWindow; 394 | ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. 395 | ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId 396 | ImVector Settings; // .ini Settings 397 | float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero 398 | ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() 399 | ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() 400 | ImVector FontStack; // Stack for PushFont()/PopFont() 401 | ImVector OpenPopupStack; // Which popups are open (persistent) 402 | ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) 403 | 404 | // Storage for SetNexWindow** and SetNextTreeNode*** functions 405 | ImVec2 SetNextWindowPosVal; 406 | ImVec2 SetNextWindowSizeVal; 407 | ImVec2 SetNextWindowContentSizeVal; 408 | bool SetNextWindowCollapsedVal; 409 | ImGuiSetCond SetNextWindowPosCond; 410 | ImGuiSetCond SetNextWindowSizeCond; 411 | ImGuiSetCond SetNextWindowContentSizeCond; 412 | ImGuiSetCond SetNextWindowCollapsedCond; 413 | ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true 414 | ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback; 415 | void* SetNextWindowSizeConstraintCallbackUserData; 416 | bool SetNextWindowSizeConstraint; 417 | bool SetNextWindowFocus; 418 | bool SetNextTreeNodeOpenVal; 419 | ImGuiSetCond SetNextTreeNodeOpenCond; 420 | 421 | // Render 422 | ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user 423 | ImVector RenderDrawLists[3]; 424 | float ModalWindowDarkeningRatio; 425 | ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays 426 | ImGuiMouseCursor MouseCursor; 427 | ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_]; 428 | 429 | // Widget state 430 | ImGuiTextEditState InputTextState; 431 | ImFont InputTextPasswordFont; 432 | ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. 433 | ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode 434 | float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings 435 | ImVec2 DragLastMouseDelta; 436 | float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio 437 | float DragSpeedScaleSlow; 438 | float DragSpeedScaleFast; 439 | ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? 440 | char Tooltip[1024]; 441 | char* PrivateClipboard; // If no custom clipboard handler is defined 442 | ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor 443 | 444 | // Logging 445 | bool LogEnabled; 446 | FILE* LogFile; // If != NULL log to stdout/ file 447 | ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. 448 | int LogStartDepth; 449 | int LogAutoExpandMaxDepth; 450 | 451 | // Misc 452 | float FramerateSecPerFrame[120]; // calculate estimate of framerate for user 453 | int FramerateSecPerFrameIdx; 454 | float FramerateSecPerFrameAccum; 455 | int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags 456 | int CaptureKeyboardNextFrame; 457 | char TempBuffer[1024*3+1]; // temporary text buffer 458 | 459 | ImGuiContext() 460 | { 461 | Initialized = false; 462 | Font = NULL; 463 | FontSize = FontBaseSize = 0.0f; 464 | FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); 465 | 466 | Time = 0.0f; 467 | FrameCount = 0; 468 | FrameCountEnded = FrameCountRendered = -1; 469 | CurrentWindow = NULL; 470 | FocusedWindow = NULL; 471 | HoveredWindow = NULL; 472 | HoveredRootWindow = NULL; 473 | HoveredId = 0; 474 | HoveredIdAllowOverlap = false; 475 | HoveredIdPreviousFrame = 0; 476 | ActiveId = 0; 477 | ActiveIdPreviousFrame = 0; 478 | ActiveIdIsAlive = false; 479 | ActiveIdIsJustActivated = false; 480 | ActiveIdAllowOverlap = false; 481 | ActiveIdClickOffset = ImVec2(-1,-1); 482 | ActiveIdWindow = NULL; 483 | MovedWindow = NULL; 484 | MovedWindowMoveId = 0; 485 | SettingsDirtyTimer = 0.0f; 486 | 487 | SetNextWindowPosVal = ImVec2(0.0f, 0.0f); 488 | SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); 489 | SetNextWindowCollapsedVal = false; 490 | SetNextWindowPosCond = 0; 491 | SetNextWindowSizeCond = 0; 492 | SetNextWindowContentSizeCond = 0; 493 | SetNextWindowCollapsedCond = 0; 494 | SetNextWindowSizeConstraintRect = ImRect(); 495 | SetNextWindowSizeConstraintCallback = NULL; 496 | SetNextWindowSizeConstraintCallbackUserData = NULL; 497 | SetNextWindowSizeConstraint = false; 498 | SetNextWindowFocus = false; 499 | SetNextTreeNodeOpenVal = false; 500 | SetNextTreeNodeOpenCond = 0; 501 | 502 | ScalarAsInputTextId = 0; 503 | DragCurrentValue = 0.0f; 504 | DragLastMouseDelta = ImVec2(0.0f, 0.0f); 505 | DragSpeedDefaultRatio = 1.0f / 100.0f; 506 | DragSpeedScaleSlow = 0.01f; 507 | DragSpeedScaleFast = 10.0f; 508 | ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); 509 | memset(Tooltip, 0, sizeof(Tooltip)); 510 | PrivateClipboard = NULL; 511 | OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); 512 | 513 | ModalWindowDarkeningRatio = 0.0f; 514 | OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging 515 | MouseCursor = ImGuiMouseCursor_Arrow; 516 | memset(MouseCursorData, 0, sizeof(MouseCursorData)); 517 | 518 | LogEnabled = false; 519 | LogFile = NULL; 520 | LogClipboard = NULL; 521 | LogStartDepth = 0; 522 | LogAutoExpandMaxDepth = 2; 523 | 524 | memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); 525 | FramerateSecPerFrameIdx = 0; 526 | FramerateSecPerFrameAccum = 0.0f; 527 | CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1; 528 | memset(TempBuffer, 0, sizeof(TempBuffer)); 529 | } 530 | }; 531 | 532 | // Transient per-window data, reset at the beginning of the frame 533 | // FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. 534 | struct IMGUI_API ImGuiDrawContext 535 | { 536 | ImVec2 CursorPos; 537 | ImVec2 CursorPosPrevLine; 538 | ImVec2 CursorStartPos; 539 | ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame 540 | float CurrentLineHeight; 541 | float CurrentLineTextBaseOffset; 542 | float PrevLineHeight; 543 | float PrevLineTextBaseOffset; 544 | float LogLinePosY; 545 | int TreeDepth; 546 | ImGuiID LastItemId; 547 | ImRect LastItemRect; 548 | bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) 549 | bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) 550 | bool MenuBarAppending; 551 | float MenuBarOffsetX; 552 | ImVector ChildWindows; 553 | ImGuiStorage* StateStorage; 554 | ImGuiLayoutType LayoutType; 555 | 556 | // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. 557 | float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window 558 | float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] 559 | bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] 560 | bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] 561 | ImVector ItemWidthStack; 562 | ImVector TextWrapPosStack; 563 | ImVector AllowKeyboardFocusStack; 564 | ImVector ButtonRepeatStack; 565 | ImVectorGroupStack; 566 | ImGuiColorEditMode ColorEditMode; 567 | int StackSizesBackup[6]; // Store size of various stacks for asserting 568 | 569 | float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) 570 | float GroupOffsetX; 571 | float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. 572 | int ColumnsCurrent; 573 | int ColumnsCount; 574 | float ColumnsMinX; 575 | float ColumnsMaxX; 576 | float ColumnsStartPosY; 577 | float ColumnsCellMinY; 578 | float ColumnsCellMaxY; 579 | bool ColumnsShowBorders; 580 | ImGuiID ColumnsSetId; 581 | ImVector ColumnsData; 582 | 583 | ImGuiDrawContext() 584 | { 585 | CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); 586 | CurrentLineHeight = PrevLineHeight = 0.0f; 587 | CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; 588 | LogLinePosY = -1.0f; 589 | TreeDepth = 0; 590 | LastItemId = 0; 591 | LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); 592 | LastItemHoveredAndUsable = LastItemHoveredRect = false; 593 | MenuBarAppending = false; 594 | MenuBarOffsetX = 0.0f; 595 | StateStorage = NULL; 596 | LayoutType = ImGuiLayoutType_Vertical; 597 | ItemWidth = 0.0f; 598 | ButtonRepeat = false; 599 | AllowKeyboardFocus = true; 600 | TextWrapPos = -1.0f; 601 | ColorEditMode = ImGuiColorEditMode_RGB; 602 | memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); 603 | 604 | IndentX = 0.0f; 605 | GroupOffsetX = 0.0f; 606 | ColumnsOffsetX = 0.0f; 607 | ColumnsCurrent = 0; 608 | ColumnsCount = 1; 609 | ColumnsMinX = ColumnsMaxX = 0.0f; 610 | ColumnsStartPosY = 0.0f; 611 | ColumnsCellMinY = ColumnsCellMaxY = 0.0f; 612 | ColumnsShowBorders = true; 613 | ColumnsSetId = 0; 614 | } 615 | }; 616 | 617 | // Windows data 618 | struct IMGUI_API ImGuiWindow 619 | { 620 | char* Name; 621 | ImGuiID ID; // == ImHash(Name) 622 | ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ 623 | int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. 624 | ImVec2 PosFloat; 625 | ImVec2 Pos; // Position rounded-up to nearest pixel 626 | ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) 627 | ImVec2 SizeFull; // Size when non collapsed 628 | ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame 629 | ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() 630 | ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis 631 | ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect 632 | ImGuiID MoveId; // == window->GetID("#MOVE") 633 | ImVec2 Scroll; 634 | ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) 635 | ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered 636 | bool ScrollbarX, ScrollbarY; 637 | ImVec2 ScrollbarSizes; 638 | float BorderSize; 639 | bool Active; // Set to true on Begin() 640 | bool WasActive; 641 | bool Accessed; // Set to true when any widget access the current window 642 | bool Collapsed; // Set when collapsing window to become only title-bar 643 | bool SkipItems; // == Visible && !Collapsed 644 | int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) 645 | ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) 646 | int AutoFitFramesX, AutoFitFramesY; 647 | bool AutoFitOnlyGrows; 648 | int AutoPosLastDirection; 649 | int HiddenFrames; 650 | int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. 651 | int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag. 652 | int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag. 653 | bool SetWindowPosCenterWanted; 654 | 655 | ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame 656 | ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack 657 | ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. 658 | ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. 659 | int LastFrameActive; 660 | float ItemWidthDefault; 661 | ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items 662 | ImGuiStorage StateStorage; 663 | float FontWindowScale; // Scale multiplier per-window 664 | ImDrawList* DrawList; 665 | ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. 666 | ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. 667 | ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL. 668 | 669 | // Navigation / Focus 670 | int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() 671 | int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) 672 | int FocusIdxAllRequestCurrent; // Item being requested for focus 673 | int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus 674 | int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) 675 | int FocusIdxTabRequestNext; // " 676 | 677 | public: 678 | ImGuiWindow(const char* name); 679 | ~ImGuiWindow(); 680 | 681 | ImGuiID GetID(const char* str, const char* str_end = NULL); 682 | ImGuiID GetID(const void* ptr); 683 | ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); 684 | 685 | ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } 686 | float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } 687 | float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } 688 | ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } 689 | float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } 690 | ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } 691 | }; 692 | 693 | //----------------------------------------------------------------------------- 694 | // Internal API 695 | // No guarantee of forward compatibility here. 696 | //----------------------------------------------------------------------------- 697 | 698 | namespace ImGui 699 | { 700 | // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) 701 | // If this ever crash because g.CurrentWindow is NULL it means that either 702 | // - ImGui::NewFrame() has never been called, which is illegal. 703 | // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. 704 | inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } 705 | inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; } 706 | IMGUI_API ImGuiWindow* GetParentWindow(); 707 | IMGUI_API ImGuiWindow* FindWindowByName(const char* name); 708 | IMGUI_API void FocusWindow(ImGuiWindow* window); 709 | 710 | IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! 711 | 712 | IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); 713 | IMGUI_API void ClearActiveID(); 714 | IMGUI_API void SetHoveredID(ImGuiID id); 715 | IMGUI_API void KeepAliveID(ImGuiID id); 716 | 717 | IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); 718 | IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); 719 | IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id); 720 | IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); 721 | IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false); 722 | IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested 723 | IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); 724 | IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); 725 | IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); 726 | 727 | IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); 728 | 729 | // NB: All position are in absolute pixels coordinates (not window coordinates) 730 | // FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. 731 | // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers. 732 | IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); 733 | IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); 734 | IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); 735 | IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); 736 | IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f); 737 | IMGUI_API void RenderBullet(ImVec2 pos); 738 | IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); 739 | IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. 740 | 741 | IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); 742 | IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); 743 | IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); 744 | 745 | IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); 746 | IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power); 747 | IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format); 748 | 749 | IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); 750 | IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power); 751 | IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format); 752 | 753 | IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); 754 | IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags); 755 | IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags); 756 | IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); 757 | IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); 758 | 759 | IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); 760 | IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging 761 | IMGUI_API void TreePushRawID(ImGuiID id); 762 | 763 | IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); 764 | 765 | IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value); 766 | IMGUI_API float RoundScalar(float value, int decimal_precision); 767 | 768 | } // namespace ImGui 769 | 770 | #ifdef __clang__ 771 | #pragma clang diagnostic pop 772 | #endif 773 | 774 | #ifdef _MSC_VER 775 | #pragma warning (pop) 776 | #endif 777 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/imgui/stb_rect_pack.h: -------------------------------------------------------------------------------- 1 | // stb_rect_pack.h - v0.10 - public domain - rectangle packing 2 | // Sean Barrett 2014 3 | // 4 | // Useful for e.g. packing rectangular textures into an atlas. 5 | // Does not do rotation. 6 | // 7 | // Not necessarily the awesomest packing method, but better than 8 | // the totally naive one in stb_truetype (which is primarily what 9 | // this is meant to replace). 10 | // 11 | // Has only had a few tests run, may have issues. 12 | // 13 | // More docs to come. 14 | // 15 | // No memory allocations; uses qsort() and assert() from stdlib. 16 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 17 | // 18 | // This library currently uses the Skyline Bottom-Left algorithm. 19 | // 20 | // Please note: better rectangle packers are welcome! Please 21 | // implement them to the same API, but with a different init 22 | // function. 23 | // 24 | // Credits 25 | // 26 | // Library 27 | // Sean Barrett 28 | // Minor features 29 | // Martins Mozeiko 30 | // Bugfixes / warning fixes 31 | // Jeremy Jaussaud 32 | // 33 | // Version history: 34 | // 35 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 36 | // 0.09 (2016-08-27) fix compiler warnings 37 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 38 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 39 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 40 | // 0.05: added STBRP_ASSERT to allow replacing assert 41 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 42 | // 0.01: initial release 43 | // 44 | // LICENSE 45 | // 46 | // This software is dual-licensed to the public domain and under the following 47 | // license: you are granted a perpetual, irrevocable license to copy, modify, 48 | // publish, and distribute this file as you see fit. 49 | 50 | ////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // INCLUDE SECTION 53 | // 54 | 55 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 56 | #define STB_INCLUDE_STB_RECT_PACK_H 57 | 58 | #define STB_RECT_PACK_VERSION 1 59 | 60 | #ifdef STBRP_STATIC 61 | #define STBRP_DEF static 62 | #else 63 | #define STBRP_DEF extern 64 | #endif 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | typedef struct stbrp_context stbrp_context; 71 | typedef struct stbrp_node stbrp_node; 72 | typedef struct stbrp_rect stbrp_rect; 73 | 74 | #ifdef STBRP_LARGE_RECTS 75 | typedef int stbrp_coord; 76 | #else 77 | typedef unsigned short stbrp_coord; 78 | #endif 79 | 80 | STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 81 | // Assign packed locations to rectangles. The rectangles are of type 82 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 83 | // are 'num_rects' many of them. 84 | // 85 | // Rectangles which are successfully packed have the 'was_packed' flag 86 | // set to a non-zero value and 'x' and 'y' store the minimum location 87 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 88 | // if you imagine y increasing downwards). Rectangles which do not fit 89 | // have the 'was_packed' flag set to 0. 90 | // 91 | // You should not try to access the 'rects' array from another thread 92 | // while this function is running, as the function temporarily reorders 93 | // the array while it executes. 94 | // 95 | // To pack into another rectangle, you need to call stbrp_init_target 96 | // again. To continue packing into the same rectangle, you can call 97 | // this function again. Calling this multiple times with multiple rect 98 | // arrays will probably produce worse packing results than calling it 99 | // a single time with the full rectangle array, but the option is 100 | // available. 101 | 102 | struct stbrp_rect 103 | { 104 | // reserved for your use: 105 | int id; 106 | 107 | // input: 108 | stbrp_coord w, h; 109 | 110 | // output: 111 | stbrp_coord x, y; 112 | int was_packed; // non-zero if valid packing 113 | 114 | }; // 16 bytes, nominally 115 | 116 | 117 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 118 | // Initialize a rectangle packer to: 119 | // pack a rectangle that is 'width' by 'height' in dimensions 120 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 121 | // 122 | // You must call this function every time you start packing into a new target. 123 | // 124 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 125 | // the following stbrp_pack_rects() call (or calls), but can be freed after 126 | // the call (or calls) finish. 127 | // 128 | // Note: to guarantee best results, either: 129 | // 1. make sure 'num_nodes' >= 'width' 130 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 131 | // 132 | // If you don't do either of the above things, widths will be quantized to multiples 133 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 134 | // 135 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 136 | // may run out of temporary storage and be unable to pack some rectangles. 137 | 138 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 139 | // Optionally call this function after init but before doing any packing to 140 | // change the handling of the out-of-temp-memory scenario, described above. 141 | // If you call init again, this will be reset to the default (false). 142 | 143 | 144 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 145 | // Optionally select which packing heuristic the library should use. Different 146 | // heuristics will produce better/worse results for different data sets. 147 | // If you call init again, this will be reset to the default. 148 | 149 | enum 150 | { 151 | STBRP_HEURISTIC_Skyline_default=0, 152 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 153 | STBRP_HEURISTIC_Skyline_BF_sortHeight 154 | }; 155 | 156 | 157 | ////////////////////////////////////////////////////////////////////////////// 158 | // 159 | // the details of the following structures don't matter to you, but they must 160 | // be visible so you can handle the memory allocations for them 161 | 162 | struct stbrp_node 163 | { 164 | stbrp_coord x,y; 165 | stbrp_node *next; 166 | }; 167 | 168 | struct stbrp_context 169 | { 170 | int width; 171 | int height; 172 | int align; 173 | int init_mode; 174 | int heuristic; 175 | int num_nodes; 176 | stbrp_node *active_head; 177 | stbrp_node *free_head; 178 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 179 | }; 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | #endif 186 | 187 | ////////////////////////////////////////////////////////////////////////////// 188 | // 189 | // IMPLEMENTATION SECTION 190 | // 191 | 192 | #ifdef STB_RECT_PACK_IMPLEMENTATION 193 | #ifndef STBRP_SORT 194 | #include 195 | #define STBRP_SORT qsort 196 | #endif 197 | 198 | #ifndef STBRP_ASSERT 199 | #include 200 | #define STBRP_ASSERT assert 201 | #endif 202 | 203 | #ifdef _MSC_VER 204 | #define STBRP__NOTUSED(v) (void)(v) 205 | #else 206 | #define STBRP__NOTUSED(v) (void)sizeof(v) 207 | #endif 208 | 209 | enum 210 | { 211 | STBRP__INIT_skyline = 1 212 | }; 213 | 214 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 215 | { 216 | switch (context->init_mode) { 217 | case STBRP__INIT_skyline: 218 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 219 | context->heuristic = heuristic; 220 | break; 221 | default: 222 | STBRP_ASSERT(0); 223 | } 224 | } 225 | 226 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 227 | { 228 | if (allow_out_of_mem) 229 | // if it's ok to run out of memory, then don't bother aligning them; 230 | // this gives better packing, but may fail due to OOM (even though 231 | // the rectangles easily fit). @TODO a smarter approach would be to only 232 | // quantize once we've hit OOM, then we could get rid of this parameter. 233 | context->align = 1; 234 | else { 235 | // if it's not ok to run out of memory, then quantize the widths 236 | // so that num_nodes is always enough nodes. 237 | // 238 | // I.e. num_nodes * align >= width 239 | // align >= width / num_nodes 240 | // align = ceil(width/num_nodes) 241 | 242 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 243 | } 244 | } 245 | 246 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 247 | { 248 | int i; 249 | #ifndef STBRP_LARGE_RECTS 250 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 251 | #endif 252 | 253 | for (i=0; i < num_nodes-1; ++i) 254 | nodes[i].next = &nodes[i+1]; 255 | nodes[i].next = NULL; 256 | context->init_mode = STBRP__INIT_skyline; 257 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 258 | context->free_head = &nodes[0]; 259 | context->active_head = &context->extra[0]; 260 | context->width = width; 261 | context->height = height; 262 | context->num_nodes = num_nodes; 263 | stbrp_setup_allow_out_of_mem(context, 0); 264 | 265 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 266 | context->extra[0].x = 0; 267 | context->extra[0].y = 0; 268 | context->extra[0].next = &context->extra[1]; 269 | context->extra[1].x = (stbrp_coord) width; 270 | #ifdef STBRP_LARGE_RECTS 271 | context->extra[1].y = (1<<30); 272 | #else 273 | context->extra[1].y = 65535; 274 | #endif 275 | context->extra[1].next = NULL; 276 | } 277 | 278 | // find minimum y position if it starts at x1 279 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 280 | { 281 | stbrp_node *node = first; 282 | int x1 = x0 + width; 283 | int min_y, visited_width, waste_area; 284 | 285 | STBRP__NOTUSED(c); 286 | 287 | STBRP_ASSERT(first->x <= x0); 288 | 289 | #if 0 290 | // skip in case we're past the node 291 | while (node->next->x <= x0) 292 | ++node; 293 | #else 294 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 295 | #endif 296 | 297 | STBRP_ASSERT(node->x <= x0); 298 | 299 | min_y = 0; 300 | waste_area = 0; 301 | visited_width = 0; 302 | while (node->x < x1) { 303 | if (node->y > min_y) { 304 | // raise min_y higher. 305 | // we've accounted for all waste up to min_y, 306 | // but we'll now add more waste for everything we've visted 307 | waste_area += visited_width * (node->y - min_y); 308 | min_y = node->y; 309 | // the first time through, visited_width might be reduced 310 | if (node->x < x0) 311 | visited_width += node->next->x - x0; 312 | else 313 | visited_width += node->next->x - node->x; 314 | } else { 315 | // add waste area 316 | int under_width = node->next->x - node->x; 317 | if (under_width + visited_width > width) 318 | under_width = width - visited_width; 319 | waste_area += under_width * (min_y - node->y); 320 | visited_width += under_width; 321 | } 322 | node = node->next; 323 | } 324 | 325 | *pwaste = waste_area; 326 | return min_y; 327 | } 328 | 329 | typedef struct 330 | { 331 | int x,y; 332 | stbrp_node **prev_link; 333 | } stbrp__findresult; 334 | 335 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 336 | { 337 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 338 | stbrp__findresult fr; 339 | stbrp_node **prev, *node, *tail, **best = NULL; 340 | 341 | // align to multiple of c->align 342 | width = (width + c->align - 1); 343 | width -= width % c->align; 344 | STBRP_ASSERT(width % c->align == 0); 345 | 346 | node = c->active_head; 347 | prev = &c->active_head; 348 | while (node->x + width <= c->width) { 349 | int y,waste; 350 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 351 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 352 | // bottom left 353 | if (y < best_y) { 354 | best_y = y; 355 | best = prev; 356 | } 357 | } else { 358 | // best-fit 359 | if (y + height <= c->height) { 360 | // can only use it if it first vertically 361 | if (y < best_y || (y == best_y && waste < best_waste)) { 362 | best_y = y; 363 | best_waste = waste; 364 | best = prev; 365 | } 366 | } 367 | } 368 | prev = &node->next; 369 | node = node->next; 370 | } 371 | 372 | best_x = (best == NULL) ? 0 : (*best)->x; 373 | 374 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 375 | // 376 | // e.g, if fitting 377 | // 378 | // ____________________ 379 | // |____________________| 380 | // 381 | // into 382 | // 383 | // | | 384 | // | ____________| 385 | // |____________| 386 | // 387 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 388 | // 389 | // This makes BF take about 2x the time 390 | 391 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 392 | tail = c->active_head; 393 | node = c->active_head; 394 | prev = &c->active_head; 395 | // find first node that's admissible 396 | while (tail->x < width) 397 | tail = tail->next; 398 | while (tail) { 399 | int xpos = tail->x - width; 400 | int y,waste; 401 | STBRP_ASSERT(xpos >= 0); 402 | // find the left position that matches this 403 | while (node->next->x <= xpos) { 404 | prev = &node->next; 405 | node = node->next; 406 | } 407 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 408 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 409 | if (y + height < c->height) { 410 | if (y <= best_y) { 411 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 412 | best_x = xpos; 413 | STBRP_ASSERT(y <= best_y); 414 | best_y = y; 415 | best_waste = waste; 416 | best = prev; 417 | } 418 | } 419 | } 420 | tail = tail->next; 421 | } 422 | } 423 | 424 | fr.prev_link = best; 425 | fr.x = best_x; 426 | fr.y = best_y; 427 | return fr; 428 | } 429 | 430 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 431 | { 432 | // find best position according to heuristic 433 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 434 | stbrp_node *node, *cur; 435 | 436 | // bail if: 437 | // 1. it failed 438 | // 2. the best node doesn't fit (we don't always check this) 439 | // 3. we're out of memory 440 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 441 | res.prev_link = NULL; 442 | return res; 443 | } 444 | 445 | // on success, create new node 446 | node = context->free_head; 447 | node->x = (stbrp_coord) res.x; 448 | node->y = (stbrp_coord) (res.y + height); 449 | 450 | context->free_head = node->next; 451 | 452 | // insert the new node into the right starting point, and 453 | // let 'cur' point to the remaining nodes needing to be 454 | // stiched back in 455 | 456 | cur = *res.prev_link; 457 | if (cur->x < res.x) { 458 | // preserve the existing one, so start testing with the next one 459 | stbrp_node *next = cur->next; 460 | cur->next = node; 461 | cur = next; 462 | } else { 463 | *res.prev_link = node; 464 | } 465 | 466 | // from here, traverse cur and free the nodes, until we get to one 467 | // that shouldn't be freed 468 | while (cur->next && cur->next->x <= res.x + width) { 469 | stbrp_node *next = cur->next; 470 | // move the current node to the free list 471 | cur->next = context->free_head; 472 | context->free_head = cur; 473 | cur = next; 474 | } 475 | 476 | // stitch the list back in 477 | node->next = cur; 478 | 479 | if (cur->x < res.x + width) 480 | cur->x = (stbrp_coord) (res.x + width); 481 | 482 | #ifdef _DEBUG 483 | cur = context->active_head; 484 | while (cur->x < context->width) { 485 | STBRP_ASSERT(cur->x < cur->next->x); 486 | cur = cur->next; 487 | } 488 | STBRP_ASSERT(cur->next == NULL); 489 | 490 | { 491 | stbrp_node *L1 = NULL, *L2 = NULL; 492 | int count=0; 493 | cur = context->active_head; 494 | while (cur) { 495 | L1 = cur; 496 | cur = cur->next; 497 | ++count; 498 | } 499 | cur = context->free_head; 500 | while (cur) { 501 | L2 = cur; 502 | cur = cur->next; 503 | ++count; 504 | } 505 | STBRP_ASSERT(count == context->num_nodes+2); 506 | } 507 | #endif 508 | 509 | return res; 510 | } 511 | 512 | static int rect_height_compare(const void *a, const void *b) 513 | { 514 | const stbrp_rect *p = (const stbrp_rect *) a; 515 | const stbrp_rect *q = (const stbrp_rect *) b; 516 | if (p->h > q->h) 517 | return -1; 518 | if (p->h < q->h) 519 | return 1; 520 | return (p->w > q->w) ? -1 : (p->w < q->w); 521 | } 522 | 523 | static int rect_width_compare(const void *a, const void *b) 524 | { 525 | const stbrp_rect *p = (const stbrp_rect *) a; 526 | const stbrp_rect *q = (const stbrp_rect *) b; 527 | if (p->w > q->w) 528 | return -1; 529 | if (p->w < q->w) 530 | return 1; 531 | return (p->h > q->h) ? -1 : (p->h < q->h); 532 | } 533 | 534 | static int rect_original_order(const void *a, const void *b) 535 | { 536 | const stbrp_rect *p = (const stbrp_rect *) a; 537 | const stbrp_rect *q = (const stbrp_rect *) b; 538 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 539 | } 540 | 541 | #ifdef STBRP_LARGE_RECTS 542 | #define STBRP__MAXVAL 0xffffffff 543 | #else 544 | #define STBRP__MAXVAL 0xffff 545 | #endif 546 | 547 | STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 548 | { 549 | int i; 550 | 551 | // we use the 'was_packed' field internally to allow sorting/unsorting 552 | for (i=0; i < num_rects; ++i) { 553 | rects[i].was_packed = i; 554 | #ifndef STBRP_LARGE_RECTS 555 | STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); 556 | #endif 557 | } 558 | 559 | // sort according to heuristic 560 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 561 | 562 | for (i=0; i < num_rects; ++i) { 563 | if (rects[i].w == 0 || rects[i].h == 0) { 564 | rects[i].x = rects[i].y = 0; // empty rect needs no space 565 | } else { 566 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 567 | if (fr.prev_link) { 568 | rects[i].x = (stbrp_coord) fr.x; 569 | rects[i].y = (stbrp_coord) fr.y; 570 | } else { 571 | rects[i].x = rects[i].y = STBRP__MAXVAL; 572 | } 573 | } 574 | } 575 | 576 | // unsort 577 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 578 | 579 | // set was_packed flags 580 | for (i=0; i < num_rects; ++i) 581 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 582 | } 583 | #endif 584 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/planets.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/pivot/153046fc2015457318f0b8bb61e2714e76fbf11a/opengl_sphere_lighting/planets.exe -------------------------------------------------------------------------------- /opengl_sphere_lighting/shaders/background.glsl: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- 2 | // Uniforms 3 | // -------------------------------------------------- 4 | uniform vec3 u_ClearColor; 5 | 6 | // -------------------------------------------------- 7 | // Vertex shader 8 | // -------------------------------------------------- 9 | #ifdef VERTEX_SHADER 10 | void main() { 11 | // draw a full screen quad 12 | vec2 p = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1) * 2.0 - 1.0; 13 | gl_Position = vec4(p, 0.999, 1); // make sure the cubemap is visible 14 | } 15 | #endif 16 | 17 | // -------------------------------------------------- 18 | // Fragment shader 19 | // -------------------------------------------------- 20 | #ifdef FRAGMENT_SHADER 21 | layout(location = 0) out vec4 o_FragColor; 22 | 23 | void main() { 24 | o_FragColor = vec4(u_ClearColor, 1); 25 | } 26 | #endif 27 | 28 | 29 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/shaders/ggx.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | /* ggx.glsl - public domain GLSL library 3 | by Jonathan Dupuy 4 | 5 | This file provides utility functions for GGX BRDFs, which are used 6 | in the sphere light shading technique described in my paper 7 | "A Spherical Cap Preserving Parameterization for Spherical Distributions". 8 | */ 9 | 10 | // Evaluate GGX BRDF (brdf times cosine) 11 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf); 12 | 13 | // Importance sample a visible microfacet normal from direction wi 14 | // Note: the algorithm I use is an improvement over Eric Heit'z 15 | // algorithm. It relies on Shirley's concentric mapping and produces less 16 | // distortion. 17 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha); 18 | 19 | // 20 | // 21 | //// end header file /////////////////////////////////////////////////////////// 22 | 23 | // ***************************************************************************** 24 | /** 25 | * GGX Functions 26 | * 27 | */ 28 | 29 | #define PI 3.141592654 30 | 31 | // ----------------------------------------------------------------------------- 32 | // Evaluation 33 | float ggx_evalp(vec3 wi, vec3 wo, float alpha, out float pdf) 34 | { 35 | if (wo.z > 0.0 && wi.z > 0.0) { 36 | vec3 wh = normalize(wi + wo); 37 | vec3 wh_xform = vec3(wh.xy / alpha, wh.z); 38 | vec3 wi_xform = vec3(wi.xy * alpha, wi.z); 39 | vec3 wo_xform = vec3(wo.xy * alpha, wo.z); 40 | float wh_xform_mag = length(wh_xform); 41 | float wi_xform_mag = length(wi_xform); 42 | float wo_xform_mag = length(wo_xform); 43 | wh_xform/= wh_xform_mag; // normalize 44 | wi_xform/= wi_xform_mag; // normalize 45 | wo_xform/= wo_xform_mag; // normalize 46 | float sigma_i = 0.5 + 0.5 * wi_xform.z; 47 | float sigma_o = 0.5 + 0.5 * wo_xform.z; 48 | float Gi = clamp(wi.z, 0.0, 1.0) / (sigma_i * wi_xform_mag); 49 | float Go = clamp(wo.z, 0.0, 1.0) / (sigma_o * wo_xform_mag); 50 | float J = alpha * alpha * wh_xform_mag * wh_xform_mag * wh_xform_mag; 51 | float Dvis = clamp(dot(wo_xform, wh_xform), 0.0, 1.0) / (sigma_o * PI * J); 52 | float Gcond = Gi / (Gi + Go - Gi * Go); 53 | float cos_theta_d = dot(wh, wo); 54 | 55 | pdf = (Dvis / (cos_theta_d * 4.0)); 56 | return pdf * Gcond; 57 | } 58 | pdf = 0.0; 59 | return 0.0; 60 | } 61 | 62 | // ----------------------------------------------------------------------------- 63 | // uniform to concentric disk 64 | vec2 ggx__u2_to_d2(vec2 u) 65 | { 66 | /* Concentric map code with less branching (by Dave Cline), see 67 | http://psgraphics.blogspot.ch/2011/01/improved-code-for-concentric-map.html */ 68 | float r1 = 2 * u.x - 1; 69 | float r2 = 2 * u.y - 1; 70 | float phi, r; 71 | 72 | if (r1 == 0 && r2 == 0) { 73 | r = phi = 0; 74 | } else if (r1 * r1 > r2 * r2) { 75 | r = r1; 76 | phi = (PI / 4) * (r2 / r1); 77 | } else { 78 | r = r2; 79 | phi = (PI / 2) - (r1 / r2) * (PI / 4); 80 | } 81 | 82 | return r * vec2(cos(phi), sin(phi)); 83 | } 84 | 85 | // ----------------------------------------------------------------------------- 86 | // uniform to half a concentric disk 87 | vec2 ggx__u2_to_hd2(vec2 u) 88 | { 89 | vec2 v = vec2((1 + u.x) / 2, u.y); 90 | return ggx__u2_to_d2(v); 91 | } 92 | 93 | // ----------------------------------------------------------------------------- 94 | // uniform to microfacet normal projected onto concentric disk 95 | vec2 ggx__u2_to_md2(vec2 u, float zi) 96 | { 97 | float a = 1.0f / (1.0f + zi); 98 | 99 | if (u.x > a) { 100 | float xu = (u.x - a) / (1.0f - a); // remap to [0, 1] 101 | 102 | return vec2(zi, 1) * ggx__u2_to_hd2(vec2(xu, u.y)); 103 | } else { 104 | float xu = (u.x - a) / a; // remap to [-1, 0] 105 | 106 | return ggx__u2_to_hd2(vec2(xu, u.y)); 107 | } 108 | } 109 | 110 | // ----------------------------------------------------------------------------- 111 | // concentric disk to microfacet normal 112 | vec3 ggx__d2_to_h2(vec2 d, float zi, float z_i) 113 | { 114 | vec3 z = vec3(z_i, 0, zi); 115 | vec3 y = vec3(0, 1, 0); 116 | vec3 x = vec3(zi, 0, -z_i); // cross(z, y) 117 | float tmp = clamp(1 - dot(d, d), 0.0, 1.0); 118 | vec3 wm = x * d.x + y * d.y + z * sqrt(tmp); 119 | 120 | return vec3(wm.x, wm.y, clamp(wm.z, 0.0, 1.0)); 121 | } 122 | 123 | // ----------------------------------------------------------------------------- 124 | vec3 ggx__u2_to_h2_std_radial(vec2 u, float zi, float z_i) 125 | { 126 | return ggx__d2_to_h2(ggx__u2_to_md2(u, zi), zi, z_i); 127 | } 128 | 129 | // ----------------------------------------------------------------------------- 130 | // standard GGX variate exploiting rotational symmetry 131 | vec3 ggx__u2_to_h2_std(vec2 u, vec3 wi) 132 | { 133 | float zi = wi.z; 134 | float z_i = sqrt(wi.x * wi.x + wi.y * wi.y); 135 | vec3 wm = ggx__u2_to_h2_std_radial(u, zi, z_i); 136 | 137 | // rotate for non-normal incidence 138 | if (z_i > 0) { 139 | float nrm = 1 / z_i; 140 | float c = wi.x * nrm; 141 | float s = wi.y * nrm; 142 | float x = c * wm.x - s * wm.y; 143 | float y = s * wm.x + c * wm.y; 144 | 145 | wm = vec3(x, y, wm.z); 146 | } 147 | 148 | return wm; 149 | } 150 | 151 | // ----------------------------------------------------------------------------- 152 | // warp the domain to match the standard GGX distribution 153 | // (note: works with anisotropic roughness) 154 | vec3 ggx__u2_to_h2(vec2 u, vec3 wi, float r1, float r2) 155 | { 156 | vec3 wi_std = normalize(vec3(r1, r2, 1) * wi); 157 | vec3 wm_std = ggx__u2_to_h2_std(u, wi_std); 158 | vec3 wm = normalize(vec3(r1, r2, 1) * wm_std); 159 | 160 | return wm; 161 | } 162 | 163 | // ----------------------------------------------------------------------------- 164 | // importance sample: map the unit square to the hemisphere 165 | vec3 ggx_sample(vec2 u, vec3 wi, float alpha) 166 | { 167 | return ggx__u2_to_h2(u, wi, alpha, alpha); 168 | } 169 | 170 | #undef PI 171 | 172 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/shaders/pivot.glsl: -------------------------------------------------------------------------------- 1 | #line 1 2 | /* pivot.glsl - public domain GLSL library 3 | by Jonathan Dupuy 4 | 5 | This file provides utility functions for the sphere light 6 | shading technique described in my paper "A Spherical Cap Preserving 7 | Parameterization for Spherical Distributions".. 8 | */ 9 | 10 | // Spherical Cap 11 | struct cap { 12 | vec3 dir; // direction 13 | float z; // cos of the aperture angle 14 | }; 15 | 16 | // Sphere 17 | struct sphere { 18 | vec3 pos; // center 19 | float r; // radius 20 | }; 21 | 22 | // Mappings 23 | vec3 u2_to_cap(vec2 u, cap c); 24 | vec3 u2_to_cos(vec2 u); 25 | vec3 u2_to_s2(vec2 u); 26 | vec3 u2_to_h2(vec2 u); 27 | vec3 u2_to_ps2(vec2 u, vec3 r_p); 28 | vec3 u2_to_ph2(vec2 u, vec3 r_p); 29 | vec3 u2_to_pcap(vec2 u, cap c, vec3 r_p); 30 | vec3 r3_to_pr3(vec3 r, vec3 r_p); 31 | vec3 s2_to_ps2(vec3 r, vec3 r_p); 32 | cap cap_to_pcap(cap c, vec3 r_p); 33 | 34 | // PDFs 35 | float pdf_cap(vec3 wk, cap c); 36 | float pdf_cos(vec3 wk); 37 | float pdf_s2(vec3 wk); 38 | float pdf_h2(vec3 wk); 39 | float pdf_ps2(vec3 wk, vec3 r_p); 40 | float pdf_pcap(vec3 wk, cap c, vec3 r_p); 41 | float pdf_pcap_fast(vec3 wk, cap c_std, vec3 r_p); 42 | 43 | // solid angles 44 | float cap_solidangle(cap c); 45 | float cap_solidangle(cap c1, cap c2); 46 | 47 | // Approximate BRDF shading 48 | float GGXSphereLightingPivotApprox(sphere s, vec3 wo, vec3 pivot); 49 | 50 | // 51 | // 52 | //// end header file /////////////////////////////////////////////////////////// 53 | 54 | 55 | // Frisvad's method to build an orthonomal basis around a direction w 56 | void basis(vec3 w, out vec3 t1, out vec3 t2) 57 | { 58 | if (w.z < -0.9999999) { 59 | t1 = vec3( 0, -1, 0); 60 | t2 = vec3(-1, 0, 0); 61 | } else { 62 | const float a = 1.0 / (1.0 + w.z); 63 | const float b = -w.x * w.y * a; 64 | t1 = vec3(1.0 - w.x * w.x * a, b, -w.x); 65 | t2 = vec3(b, 1.0 - w.y * w.y * a, -w.y); 66 | } 67 | } 68 | 69 | #define TWOPI 6.283185307 70 | 71 | float cap_solidangle(cap c) 72 | { 73 | return TWOPI - TWOPI * c.z; 74 | } 75 | 76 | // Based on Oat and Sander's 2008 technique 77 | float cap_solidangle(cap c1, cap c2) 78 | { 79 | float r1 = acos(c1.z); 80 | float r2 = acos(c2.z); 81 | float rd = acos(dot(c1.dir, c2.dir)); 82 | float fArea = 0.0; 83 | 84 | if (rd <= max(r1, r2) - min(r1, r2)) { 85 | // One cap in completely inside the other 86 | fArea = TWOPI - TWOPI * max(c1.z, c2.z); 87 | } else if (rd >= r1 + r2) { 88 | // No intersection exists 89 | fArea = 0; 90 | } else { 91 | float fDiff = abs(r1 - r2); 92 | float den = r1 + r2 - fDiff; 93 | float x = 1.0 - clamp((rd - fDiff) / den, 0.0, 1.0); 94 | fArea = smoothstep(0.0, 1.0, x); 95 | fArea*= TWOPI - TWOPI * max(c1.z, c2.z); 96 | } 97 | 98 | return fArea; 99 | } 100 | 101 | 102 | float GGXSphereLightingPivotApprox(sphere s, vec3 wo, vec3 pivot) 103 | { 104 | // compute the spherical cap produced by the sphere 105 | float tmp = clamp(s.r * s.r / dot(s.pos, s.pos), 0.0, 1.0); 106 | cap c = cap(normalize(s.pos), sqrt(1.0 - tmp)); 107 | 108 | // integrate 109 | cap c1 = cap_to_pcap(c, pivot); 110 | cap c2 = cap_to_pcap(cap(vec3(0, 0, 1), 0.0), pivot); 111 | float res = cap_solidangle(c1, c2) * /*1/4pi*/0.079577472; 112 | return clamp(res, 0.0, 1.0); 113 | } 114 | 115 | // ----------------------------------------------------------------------------- 116 | // sample warps 117 | 118 | /* Sphere */ 119 | vec3 u2_to_s2(vec2 u) 120 | { 121 | float z = 2.0 * u.x - 1.0; // in [-1, 1) 122 | float sin_theta = sqrt(1.0 - z * z); 123 | float phi = TWOPI * u.y; // in [0, 2pi) 124 | float x = sin_theta * cos(phi); 125 | float y = sin_theta * sin(phi); 126 | 127 | return vec3(x, y, z); 128 | } 129 | 130 | /* Hemisphere */ 131 | vec3 u2_to_h2(vec2 u) 132 | { 133 | float z = u.x; // in [0, 1) 134 | float sin_theta = sqrt(1.0 - z * z); 135 | float phi = TWOPI * u.y; // in [0, 2pi) 136 | float x = sin_theta * cos(phi); 137 | float y = sin_theta * sin(phi); 138 | 139 | return vec3(x, y, z); 140 | } 141 | 142 | /* Spherical Cap */ 143 | vec3 u2_to_cap(vec2 u, cap c) 144 | { 145 | // generate the sample in the basis aligned with the cap 146 | float z = (1.0 - c.z) * u.x + c.z; // in [cap_cos, 1) 147 | float sin_theta = sqrt(1.0 - z * z); 148 | float phi = TWOPI * u.y; // in [0, 2pi) 149 | float x = sin_theta * cos(phi); 150 | float y = sin_theta * sin(phi); 151 | 152 | // compute basis vectors 153 | vec3 t1, t2; 154 | basis(c.dir, t1, t2); 155 | mat3 xf = mat3(t1, t2, c.dir); 156 | 157 | // warp the sample in the proper basis 158 | return normalize(xf * vec3(x, y, z)); 159 | } 160 | 161 | /* Disk */ 162 | vec2 u2_to_disk(vec2 u) 163 | { 164 | float r = sqrt(u.x); // in [0, 1) 165 | float phi = TWOPI * u.y; // in [0, 2pi) 166 | return r * vec2(cos(phi), sin(phi)); 167 | } 168 | 169 | /* Clamped Cosine */ 170 | vec3 u2_to_cos(vec2 u) 171 | { 172 | // project a disk sample back to the hemisphere 173 | vec2 d = u2_to_disk(u); 174 | float z = sqrt(1.0 - dot(d, d)); 175 | return vec3(d, z); 176 | } 177 | 178 | /* Pivot 3D Transformation */ 179 | vec3 r3_to_pr3(vec3 r, vec3 r_p) 180 | { 181 | vec3 tmp = r - r_p; 182 | vec3 cp1 = cross(r, r_p); 183 | vec3 cp2 = cross(tmp, cp1); 184 | float dp = dot(r, r_p) - 1.f; 185 | float qf = dp * dp + dot(cp1, cp1); 186 | 187 | return ((dp * tmp - cp2) / qf); 188 | } 189 | vec3 s2_to_ps2(vec3 wk, vec3 r_p) 190 | { 191 | return r3_to_pr3(wk, r_p); 192 | } 193 | 194 | /* Pivot Transformed Sphere Sample */ 195 | vec3 u2_to_ps2(vec2 u, vec3 r_p) 196 | { 197 | vec3 std = u2_to_s2(u); 198 | return s2_to_ps2(std, r_p); 199 | } 200 | 201 | /* Pivot Transformed Hemisphere Sample */ 202 | vec3 u2_to_ph2(vec2 u, vec3 r_p) 203 | { 204 | vec3 std = u2_to_h2(u); 205 | return s2_to_ps2(std, r_p); 206 | } 207 | 208 | /* Pivot Transformed Cap Sample */ 209 | vec3 u2_to_pcap(vec2 u, cap c, vec3 r_p) 210 | { 211 | vec3 std = u2_to_cap(u, c); 212 | return s2_to_ps2(std, r_p); 213 | } 214 | 215 | /* Pivot 2D Transformation */ 216 | vec2 r2_to_pr2(vec2 r, float r_p) 217 | { 218 | vec2 tmp1 = vec2(r.x - r_p, r.y); 219 | vec2 tmp2 = r_p * r - vec2(1, 0); 220 | float x = dot(tmp1, tmp2); 221 | float y = tmp1.y * tmp2.x - tmp1.x * tmp2.y; 222 | float qf = dot(tmp2, tmp2); 223 | 224 | return (vec2(x, y) / qf); 225 | } 226 | 227 | /* Pivot Transformed Cap */ 228 | cap cap_to_pcap(cap c, vec3 r_p) 229 | { 230 | // extract pivot length and direction 231 | float pivot_mag = length(r_p); 232 | // special case: the pivot is at the origin 233 | if (pivot_mag < 0.001) 234 | return cap(-c.dir, c.z); 235 | vec3 pivot_dir = r_p / pivot_mag; 236 | 237 | // 2D cap dir 238 | float cos_phi = dot(c.dir, pivot_dir); 239 | float sin_phi = sqrt(1.0 - cos_phi * cos_phi); 240 | 241 | // 2D basis = (pivotDir, PivotOrthogonalDirection) 242 | vec3 pivot_ortho_dir; 243 | if (abs(cos_phi) < 0.9999) { 244 | pivot_ortho_dir = (c.dir - cos_phi * pivot_dir) / sin_phi; 245 | } else { 246 | pivot_ortho_dir = vec3(0, 0, 0); 247 | } 248 | 249 | // compute cap 2D end points 250 | float cap_sin = sqrt(1.0 - c.z * c.z); 251 | float a1 = cos_phi * c.z; 252 | float a2 = sin_phi * cap_sin; 253 | float a3 = sin_phi * c.z; 254 | float a4 = cos_phi * cap_sin; 255 | vec2 dir1 = vec2(a1 + a2, a3 - a4); 256 | vec2 dir2 = vec2(a1 - a2, a3 + a4); 257 | 258 | // project in 2D 259 | vec2 dir1_xf = r2_to_pr2(dir1, pivot_mag); 260 | vec2 dir2_xf = r2_to_pr2(dir2, pivot_mag); 261 | 262 | // compute the cap 2D direction 263 | float area = dir1_xf.x * dir2_xf.y - dir1_xf.y * dir2_xf.x; 264 | float s = area > 0.0 ? 1.0 : -1.0; 265 | vec2 dir_xf = s * normalize(dir1_xf + dir2_xf); 266 | 267 | // compute the 3D cap parameters 268 | vec3 cap_dir = dir_xf.x * pivot_dir + dir_xf.y * pivot_ortho_dir; 269 | float cap_cos = dot(dir_xf, dir1_xf); 270 | 271 | return cap(cap_dir, cap_cos); 272 | } 273 | 274 | 275 | // ----------------------------------------------------------------------------- 276 | // PDFs 277 | float pdf_cap(vec3 wk, cap c) 278 | { 279 | // make sure the sample lies in the the cap 280 | if (dot(wk, c.dir) >= c.z) { 281 | return 1.0 / cap_solidangle(c); 282 | } 283 | return 0.0; 284 | } 285 | 286 | float pdf_cos(vec3 wk) 287 | { 288 | return clamp(wk.z, 0.0, 1.0) * /* 1/pi */0.318309886; 289 | } 290 | 291 | float pdf_s2(vec3 wk) 292 | { 293 | return /* 1/4pi */ 0.079577472; 294 | } 295 | 296 | float pdf_h2(vec3 wk) 297 | { 298 | return wk.z > 0.0 ?/* 1/2pi */ 0.159154943 : 0.0; 299 | } 300 | 301 | float pivot_jacobian(vec3 wk, vec3 r_p) 302 | { 303 | float num = 1.0 - dot(r_p, r_p); 304 | vec3 tmp = wk - r_p; 305 | float den = dot(tmp, tmp); 306 | 307 | return (num * num) / (den * den); 308 | } 309 | 310 | float pdf_ps2(vec3 wk, vec3 r_p) 311 | { 312 | float std = pdf_s2(s2_to_ps2(wk, r_p)); 313 | float J = pivot_jacobian(wk, r_p); 314 | return std * J; 315 | } 316 | 317 | float pdf_pcap_fast(vec3 wk, cap c_std, vec3 r_p) 318 | { 319 | float std = pdf_cap(s2_to_ps2(wk, r_p), c_std); 320 | float J = pivot_jacobian(wk, r_p); 321 | return std * J; 322 | } 323 | 324 | float pdf_pcap(vec3 wk, cap c, vec3 r_p) 325 | { 326 | return pdf_pcap_fast(wk, cap_to_pcap(c, r_p), r_p); 327 | } 328 | 329 | #undef TWOPI 330 | 331 | 332 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/shaders/sphere.glsl: -------------------------------------------------------------------------------- 1 | // ***************************************************************************** 2 | /** 3 | * Uniforms 4 | * 5 | */ 6 | uniform int u_SamplesPerPass; 7 | 8 | uniform sampler2D u_PivotSampler; 9 | uniform sampler2D u_RoughnessSampler; 10 | 11 | struct Sphere { 12 | vec4 geometry; // xyz: pos; w: radius 13 | vec4 light; // rgb: color; a: isLight 14 | vec4 brdf; // r: roughness; yzw: reserved 15 | vec4 reserved; 16 | }; 17 | 18 | layout(std140, binding = BUFFER_BINDING_SPHERES) 19 | uniform Spheres { 20 | Sphere u_Spheres[SPHERE_COUNT]; 21 | }; 22 | 23 | struct Transform { 24 | mat4 model; 25 | mat4 modelView; 26 | mat4 modelViewProjection; 27 | mat4 viewInv; 28 | }; 29 | 30 | layout(std140, row_major, binding = BUFFER_BINDING_TRANSFORMS) 31 | uniform Transforms { 32 | Transform u_Transforms[SPHERE_COUNT]; 33 | }; 34 | 35 | layout(std140, binding = BUFFER_BINDING_RANDOM) 36 | uniform Random { 37 | vec4 value[64]; 38 | } u_Random; 39 | 40 | vec4 rand(int idx) { return u_Random.value[idx]; } 41 | float hash(vec2 p) 42 | { 43 | float h = dot(p, vec2(127.1, 311.7)); 44 | return fract(sin(h) * 43758.5453123); 45 | } 46 | 47 | 48 | // ***************************************************************************** 49 | /** 50 | * Vertex Shader 51 | * 52 | * The shader outputs attributes relevant for shading in view space. 53 | */ 54 | #ifdef VERTEX_SHADER 55 | layout(location = 0) in vec4 i_Position; 56 | layout(location = 1) in vec4 i_TexCoord; 57 | layout(location = 2) in vec4 i_Tangent1; 58 | layout(location = 3) in vec4 i_Tangent2; 59 | layout(location = 0) out vec4 o_Position; 60 | layout(location = 1) out vec4 o_TexCoord; 61 | layout(location = 2) out vec4 o_Tangent1; 62 | layout(location = 3) out vec4 o_Tangent2; 63 | layout(location = 4) flat out int o_SphereId; 64 | 65 | void main(void) 66 | { 67 | o_Position = u_Transforms[gl_InstanceID].modelView * i_Position; 68 | o_TexCoord = i_TexCoord; 69 | o_Tangent1 = u_Transforms[gl_InstanceID].modelView * i_Tangent1; 70 | o_Tangent2 = u_Transforms[gl_InstanceID].modelView * i_Tangent2; 71 | o_SphereId = gl_InstanceID; 72 | 73 | gl_Position = u_Transforms[gl_InstanceID].modelViewProjection * i_Position; 74 | } 75 | #endif // VERTEX_SHADER 76 | 77 | // ***************************************************************************** 78 | /** 79 | * Fragment Shader 80 | * 81 | */ 82 | #ifdef FRAGMENT_SHADER 83 | layout(location = 0) in vec4 i_Position; 84 | layout(location = 1) in vec4 i_TexCoord; 85 | layout(location = 2) in vec4 i_Tangent1; 86 | layout(location = 3) in vec4 i_Tangent2; 87 | layout(location = 4) flat in int i_SphereId; 88 | layout(location = 0) out vec4 o_FragColor; 89 | 90 | // helper function to extract the pivot parameters 91 | // wo is assumed to be expressed in tangent space 92 | vec3 extractPivot(vec3 wo, float alpha, out float brdfScale) 93 | { 94 | // fetch pivot fit params 95 | float theta = acos(wo.z); 96 | vec2 fitLookup = vec2(sqrt(alpha), 2.0 * theta / 3.14159); 97 | fitLookup = fma(fitLookup, vec2(63.0 / 64.0), vec2(0.5 / 64.0)); 98 | vec4 pivotParams = texture(u_PivotSampler, fitLookup); 99 | float pivotNorm = pivotParams.r; 100 | float pivotElev = pivotParams.g; 101 | vec3 pivot = pivotNorm * vec3(sin(pivotElev), 0, cos(pivotElev)); 102 | 103 | // express the pivot in tangent space 104 | mat3 basis; 105 | basis[0] = wo.z < 0.999 ? normalize(wo - vec3(0, 0, wo.z)) : vec3(1, 0, 0); 106 | basis[1] = cross(vec3(0, 0, 1), basis[0]); 107 | basis[2] = vec3(0, 0, 1); 108 | pivot = basis * pivot; 109 | 110 | // return 111 | brdfScale = pivotParams.a; 112 | return pivot; 113 | } 114 | 115 | void main(void) 116 | { 117 | // extract attributes 118 | vec3 wx = normalize(i_Tangent1.xyz); 119 | vec3 wy = normalize(i_Tangent2.xyz); 120 | vec3 wn = normalize(cross(wx, wy)); 121 | vec3 wo = normalize(-i_Position.xyz); 122 | mat3 tg = transpose(mat3(wx, wy, wn)); 123 | float alpha = max(5e-3, texture(u_RoughnessSampler, i_TexCoord.xy).r); 124 | 125 | // express data in tangent space 126 | wo = tg * wo; 127 | wn = vec3(0, 0, 1); 128 | 129 | // initialize emitted and outgoing radiance 130 | vec3 Le = u_Spheres[i_SphereId].light.rgb; 131 | vec3 Lo = vec3(0); 132 | 133 | // ----------------------------------------------------------------------------- 134 | /** 135 | * Area Light Shading 136 | * 137 | * The surface is shaded with a spherical light. The shading computes 138 | * the integral of a GGX BRDF against a spherical cap. The computation is 139 | * not exact: the GGX BRDF is approximated with a pivot distribution, which 140 | * can be integrated in closed form against spherical caps. For more details, 141 | * see my paper "A Spherical Cap Preserving Parameterization for Spherical 142 | * Distributions". 143 | */ 144 | #if SHADE_PIVOT 145 | // fetch pivot fit params 146 | float brdfScale; 147 | vec3 pivot = extractPivot(wo, alpha, brdfScale); 148 | 149 | for (int i = 0; i < SPHERE_COUNT; ++i) { 150 | if (i_SphereId == i) continue; 151 | if (u_Spheres[i].light.a == 0.0) continue; 152 | vec3 spherePos = tg * (u_Spheres[i].geometry.xyz - i_Position.xyz); 153 | float sphereRadius = (u_Spheres[i].geometry.w); 154 | sphere s = sphere(spherePos, sphereRadius); 155 | 156 | Lo+= GGXSphereLightingPivotApprox(s, wo, pivot) 157 | * u_Spheres[i].light.rgb; 158 | } 159 | Lo*= brdfScale; 160 | Lo+= Le; 161 | 162 | o_FragColor = vec4(Lo, 1); 163 | 164 | // ----------------------------------------------------------------------------- 165 | /** 166 | * Area Light Shading with Spherical Light Importance Sampling 167 | * 168 | * The surface is shaded with a spherical light. The shading computes 169 | * the integral of a GGX BRDF against a spherical cap. The computation is 170 | * exact and performed numerically with Monte Carlo importance sampling. 171 | * Note that most of these technique converge very slowly; they are provided 172 | * for pedagogical and debugging purposes. 173 | */ 174 | #elif (SHADE_MC_GGX || SHADE_MC_CAP || SHADE_MC_COS || SHADE_MC_H2 || SHADE_MC_S2) 175 | // iterate over all spheres 176 | for (int i = 0; i < SPHERE_COUNT; ++i) { 177 | if (i_SphereId == i) continue; 178 | if (u_Spheres[i].light.a == 0.0) continue; 179 | vec3 spherePos = tg * (u_Spheres[i].geometry.xyz - i_Position.xyz); 180 | float sphereRadius = u_Spheres[i].geometry.w; 181 | sphere s = sphere(spherePos, sphereRadius); 182 | vec3 Li = u_Spheres[i].light.rgb; 183 | float invSphereMagSqr = 1.0 / dot(s.pos, s.pos); 184 | vec3 capDir = s.pos * sqrt(invSphereMagSqr); 185 | float capCos = sqrt(1.0 - s.r * s.r * invSphereMagSqr); 186 | cap c = cap(capDir, capCos); 187 | 188 | // loop over all samples 189 | for (int j = 0; j < u_SamplesPerPass; ++j) { 190 | // compute a uniform sample 191 | float h1 = hash(gl_FragCoord.xy); 192 | float h2 = hash(gl_FragCoord.yx); 193 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 194 | #if SHADE_MC_CAP 195 | vec3 wi = u2_to_cap(u2, c); 196 | float pdf = pdf_cap(wi, c); 197 | #elif SHADE_MC_COS 198 | vec3 wi = u2_to_cos(u2); 199 | float pdf = pdf_cos(wi); 200 | #elif SHADE_MC_H2 201 | vec3 wi = u2_to_h2(u2); 202 | float pdf = pdf_h2(wi); 203 | #elif SHADE_MC_S2 204 | vec3 wi = u2_to_s2(u2); 205 | float pdf = pdf_s2(wi); 206 | #elif SHADE_MC_GGX 207 | vec3 wm = ggx_sample(u2, wo, alpha); 208 | vec3 wi = 2.0 * wm * dot(wo, wm) - wo; 209 | float pdf = 0.0; // initialized below 210 | #endif 211 | float pdf_dummy; 212 | float frp = ggx_evalp(wi, wo, alpha, pdf_dummy); 213 | float raySphereIntersection = pdf_cap(wi, c); 214 | #if SHADE_MC_GGX 215 | pdf = pdf_dummy; 216 | #endif 217 | 218 | if (pdf > 0.0 && raySphereIntersection > 0.0) 219 | Lo+= Li * frp / pdf; 220 | } 221 | } 222 | Lo+= Le * u_SamplesPerPass; 223 | o_FragColor = vec4(Lo, u_SamplesPerPass); 224 | 225 | // ----------------------------------------------------------------------------- 226 | /** 227 | * Area Light Shading with MIS 228 | * 229 | * The surface is shaded with a spherical light. The shading computes 230 | * the integral of a GGX BRDF against a spherical cap. The computation is 231 | * exact and performed numerically with Monte Carlo and MIS. Two strategies 232 | * are combined, namely a GGX VNDF strategy and a spherical cap strategy. 233 | * Such combinations are found in state of the art Monte Carlo offline 234 | * renderers. 235 | */ 236 | #elif SHADE_MC_MIS 237 | // iterate over all spheres 238 | for (int i = 0; i < SPHERE_COUNT; ++i) { 239 | if (i_SphereId == i) continue; 240 | if (u_Spheres[i].light.a == 0.0) continue; 241 | vec3 spherePos = tg * (u_Spheres[i].geometry.xyz - i_Position.xyz); 242 | float sphereRadius = u_Spheres[i].geometry.w; 243 | sphere s = sphere(spherePos, sphereRadius); 244 | vec3 Li = u_Spheres[i].light.rgb; 245 | float invSphereMagSqr = 1.0 / dot(s.pos, s.pos); 246 | vec3 capDir = s.pos * sqrt(invSphereMagSqr); 247 | float capCos = sqrt(1.0 - s.r * s.r * invSphereMagSqr); 248 | cap c = cap(capDir, capCos); 249 | 250 | // loop over all samples 251 | for (int j = 0; j < u_SamplesPerPass; ++j) { 252 | // compute a uniform sample 253 | float h1 = hash(gl_FragCoord.xy); 254 | float h2 = hash(gl_FragCoord.yx); 255 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 256 | 257 | // importance sample the BRDF 258 | if (true) { 259 | vec3 wm = ggx_sample(u2, wo, alpha); 260 | vec3 wi = 2.0 * wm * dot(wo, wm) - wo; 261 | float pdf1; 262 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 263 | float raySphereIntersection = pdf_cap(wi, c); 264 | 265 | // raytrace the sphere light 266 | if (pdf1 > 0.0 && raySphereIntersection > 0.0) { 267 | float pdf2 = raySphereIntersection; 268 | float misWeight = pdf1 * pdf1; 269 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 270 | 271 | Lo+= Li * frp / pdf1 * misWeight / misNrm; 272 | } 273 | } 274 | 275 | // importance sample the spherical cap 276 | if (true) { 277 | vec3 wi = u2_to_cap(u2, c); 278 | float pdf1; 279 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 280 | float pdf2 = pdf_cap(wi, c); 281 | 282 | if (pdf2 > 0.0) { 283 | float misWeight = pdf2 * pdf2; 284 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 285 | 286 | Lo+= Li * frp / pdf2 * misWeight / misNrm; 287 | } 288 | } 289 | } 290 | } 291 | Lo+= Le * u_SamplesPerPass; 292 | o_FragColor = vec4(Lo, u_SamplesPerPass); 293 | 294 | // ----------------------------------------------------------------------------- 295 | /** 296 | * Area Light Shading with Joint MIS 297 | * 298 | * The surface is shaded with a spherical light. The shading computes 299 | * the integral of a GGX BRDF against a spherical cap. The computation is 300 | * exact and performed numerically with Monte Carlo and MIS. Two strategies 301 | * are combined, namely a GGX VNDF strategy and a pivot transformed spherical 302 | * cap strategy. The pivot is chosen so as to produce a density close to that 303 | * of the GGX microfacet BRDF. This combination is faster than state of the art 304 | * MIS techniques. 305 | */ 306 | #elif SHADE_MC_MIS_JOINT 307 | // fetch pivot fit params 308 | float brdfScale; // this won't be used here 309 | vec3 pivot = extractPivot(wo, alpha, brdfScale); 310 | 311 | // iterate over all spheres 312 | for (int i = 0; i < SPHERE_COUNT; ++i) { 313 | if (i_SphereId == i) continue; 314 | if (u_Spheres[i].light.a == 0.0) continue; 315 | vec3 spherePos = tg * (u_Spheres[i].geometry.xyz - i_Position.xyz); 316 | float sphereRadius = u_Spheres[i].geometry.w; 317 | sphere s = sphere(spherePos, sphereRadius); 318 | vec3 Li = u_Spheres[i].light.rgb; 319 | float invSphereMagSqr = 1.0 / dot(s.pos, s.pos); 320 | vec3 capDir = s.pos * sqrt(invSphereMagSqr); 321 | float capCos = sqrt(1.0 - s.r * s.r * invSphereMagSqr); 322 | cap c = cap(capDir, capCos); 323 | cap c_std = cap_to_pcap(c, pivot); 324 | 325 | if (c.z < 0.99) { 326 | // Joint MIS: loop over all samples 327 | for (int j = 0; j < u_SamplesPerPass; ++j) { 328 | // compute a uniform sample 329 | float h1 = hash(gl_FragCoord.xy); 330 | float h2 = hash(gl_FragCoord.yx); 331 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 332 | 333 | // importance sample the BRDF 334 | if (true) { 335 | vec3 wm = ggx_sample(u2, wo, alpha); 336 | vec3 wi = 2.0 * wm * dot(wo, wm) - wo; 337 | float pdf1; 338 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 339 | float raySphereIntersection = pdf_cap(wi, c); 340 | 341 | // raytrace the sphere light 342 | if (pdf1 > 0.0 && raySphereIntersection > 0.0) { 343 | float pdf2 = pdf_pcap_fast(wi, c_std, pivot); 344 | float misWeight = pdf1 * pdf1; 345 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 346 | 347 | Lo+= Li * frp / pdf1 * misWeight / misNrm; 348 | } 349 | } 350 | 351 | // importance sample the pivot transformed spherical cap 352 | if (true) { 353 | vec3 wi = u2_to_pcap(u2, c_std, pivot); 354 | float pdf1; 355 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 356 | float pdf2 = pdf_pcap_fast(wi, c_std, pivot); 357 | 358 | if (pdf2 > 0.0) { 359 | float misWeight = pdf2 * pdf2; 360 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 361 | 362 | Lo+= Li * frp / pdf2 * misWeight / misNrm; 363 | } 364 | } 365 | } 366 | } else { 367 | // classic MIS: loop over all samples 368 | for (int j = 0; j < u_SamplesPerPass; ++j) { 369 | // compute a uniform sample 370 | float h1 = hash(gl_FragCoord.xy); 371 | float h2 = hash(gl_FragCoord.yx); 372 | vec2 u2 = mod(vec2(h1, h2) + rand(j).xy, vec2(1.0)); 373 | 374 | // importance sample the BRDF 375 | if (true) { 376 | vec3 wm = ggx_sample(u2, wo, alpha); 377 | vec3 wi = 2.0 * wm * dot(wo, wm) - wo; 378 | float pdf1; 379 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 380 | float raySphereIntersection = pdf_cap(wi, c); 381 | 382 | // raytrace the sphere light 383 | if (pdf1 > 0.0 && raySphereIntersection > 0.0) { 384 | float pdf2 = pdf_cap(wi, c); 385 | float misWeight = pdf1 * pdf1; 386 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 387 | 388 | Lo+= Li * frp / pdf1 * misWeight / misNrm; 389 | } 390 | } 391 | 392 | // importance sample the spherical cap 393 | if (true) { 394 | vec3 wi = u2_to_cap(u2, c); 395 | float pdf1; 396 | float frp = ggx_evalp(wi, wo, alpha, pdf1); 397 | float pdf2 = pdf_cap(wi, c); 398 | 399 | if (pdf2 > 0.0) { 400 | float misWeight = pdf2 * pdf2; 401 | float misNrm = pdf1 * pdf1 + pdf2 * pdf2; 402 | 403 | Lo+= Li * frp / pdf2 * misWeight / misNrm; 404 | } 405 | } 406 | } 407 | } 408 | } 409 | Lo+= Le * u_SamplesPerPass; 410 | o_FragColor = vec4(Lo, u_SamplesPerPass); 411 | 412 | // ----------------------------------------------------------------------------- 413 | /** 414 | * Debug Shading 415 | * 416 | * Do whatever you like in here. 417 | */ 418 | #elif SHADE_DEBUG 419 | o_FragColor = vec4(0, 1, 0, 1); 420 | // ----------------------------------------------------------------------------- 421 | #endif // SHADE 422 | 423 | } 424 | #endif // FRAGMENT_SHADER 425 | 426 | 427 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/shaders/viewer.glsl: -------------------------------------------------------------------------------- 1 | uniform float u_Exposure; 2 | uniform float u_Gamma; 3 | uniform vec3 u_Viewport; 4 | 5 | #if MSAA_FACTOR 6 | uniform sampler2DMS u_FramebufferSampler; 7 | #else 8 | uniform sampler2D u_FramebufferSampler; 9 | #endif 10 | 11 | // ------------------------------------------------------------------------------------------------- 12 | /** 13 | * Vertex Shader 14 | * 15 | * This vertex shader draws a fullscreen quad 16 | */ 17 | #ifdef VERTEX_SHADER 18 | layout(location = 0) out vec2 o_TexCoord; 19 | 20 | void main(void) 21 | { 22 | o_TexCoord = vec2(gl_VertexID & 1, gl_VertexID >> 1 & 1); 23 | gl_Position = vec4(2.0 * o_TexCoord - 1.0, 0.0, 1.0); 24 | } 25 | #endif 26 | 27 | // ------------------------------------------------------------------------------------------------- 28 | /** 29 | * Fragment Shader 30 | * 31 | * This fragment shader post-processes the scene framebuffer by applying 32 | * tone mapping, gamma correction and image scaling. 33 | */ 34 | #ifdef FRAGMENT_SHADER 35 | layout(location = 0) in vec2 i_TexCoord; 36 | layout(location = 0) out vec4 o_FragColor; 37 | 38 | void main(void) 39 | { 40 | vec4 color = vec4(0); 41 | ivec2 P = ivec2(gl_FragCoord.xy); 42 | 43 | // get framebuffer data 44 | #if MSAA_FACTOR 45 | for (int i = 0; i < MSAA_FACTOR; ++i) { 46 | vec4 c = texelFetch(u_FramebufferSampler, P, i); 47 | if (c.a > 0.0) color+= c / c.a; // normalize by number of samples 48 | } 49 | color/= vec4(MSAA_FACTOR); 50 | #else 51 | color = texelFetch(u_FramebufferSampler, P, 0); 52 | if (color.a > 0.0) color.rgb/= color.a; 53 | #endif 54 | 55 | // make fragments store positive values 56 | if (any(lessThan(color.rgb, vec3(0)))) { 57 | o_FragColor = vec4(1, 0, 0, 1); 58 | return; 59 | } 60 | 61 | // exposure 62 | color.rgb*= exp2(u_Exposure); 63 | 64 | // gamma 65 | color.rgb = pow(color.rgb, vec3(1.0 / u_Gamma)); 66 | 67 | // final color 68 | o_FragColor = vec4(color.rgb, 1.0); 69 | 70 | // make sure the fragments store real values 71 | if (any(isnan(color.rgb))) 72 | o_FragColor = vec4(1, 0, 0, 1); 73 | 74 | // o_FragColor = vec4(i_TexCoord, 0, 1); 75 | } 76 | #endif 77 | 78 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/stb_image_write.h: -------------------------------------------------------------------------------- 1 | /* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h 2 | writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 3 | no warranty implied; use at your own risk 4 | 5 | Before #including, 6 | 7 | #define STB_IMAGE_WRITE_IMPLEMENTATION 8 | 9 | in the file that you want to have the implementation. 10 | 11 | Will probably not work correctly with strict-aliasing optimizations. 12 | 13 | ABOUT: 14 | 15 | This header file is a library for writing images to C stdio. It could be 16 | adapted to write to memory or a general streaming interface; let me know. 17 | 18 | The PNG output is not optimal; it is 20-50% larger than the file 19 | written by a decent optimizing implementation. This library is designed 20 | for source code compactness and simplicity, not optimal image file size 21 | or run-time performance. 22 | 23 | BUILDING: 24 | 25 | You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. 26 | You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace 27 | malloc,realloc,free. 28 | You can define STBIW_MEMMOVE() to replace memmove() 29 | 30 | USAGE: 31 | 32 | There are four functions, one for each image file format: 33 | 34 | int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 35 | int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 36 | int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 37 | int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 38 | 39 | There are also four equivalent functions that use an arbitrary write function. You are 40 | expected to open/close your file-equivalent before and after calling these: 41 | 42 | int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 43 | int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 44 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 45 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 46 | 47 | where the callback is: 48 | void stbi_write_func(void *context, void *data, int size); 49 | 50 | You can define STBI_WRITE_NO_STDIO to disable the file variant of these 51 | functions, so the library will not use stdio.h at all. However, this will 52 | also disable HDR writing, because it requires stdio for formatted output. 53 | 54 | Each function returns 0 on failure and non-0 on success. 55 | 56 | The functions create an image file defined by the parameters. The image 57 | is a rectangle of pixels stored from left-to-right, top-to-bottom. 58 | Each pixel contains 'comp' channels of data stored interleaved with 8-bits 59 | per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is 60 | monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. 61 | The *data pointer points to the first byte of the top-left-most pixel. 62 | For PNG, "stride_in_bytes" is the distance in bytes from the first byte of 63 | a row of pixels to the first byte of the next row of pixels. 64 | 65 | PNG creates output files with the same number of components as the input. 66 | The BMP format expands Y to RGB in the file format and does not 67 | output alpha. 68 | 69 | PNG supports writing rectangles of data even when the bytes storing rows of 70 | data are not consecutive in memory (e.g. sub-rectangles of a larger image), 71 | by supplying the stride between the beginning of adjacent rows. The other 72 | formats do not. (Thus you cannot write a native-format BMP through the BMP 73 | writer, both because it is in BGR order and because it may have padding 74 | at the end of the line.) 75 | 76 | HDR expects linear float data. Since the format is always 32-bit rgb(e) 77 | data, alpha (if provided) is discarded, and for monochrome data it is 78 | replicated across all three channels. 79 | 80 | TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed 81 | data, set the global variable 'stbi_write_tga_with_rle' to 0. 82 | 83 | CREDITS: 84 | 85 | PNG/BMP/TGA 86 | Sean Barrett 87 | HDR 88 | Baldur Karlsson 89 | TGA monochrome: 90 | Jean-Sebastien Guay 91 | misc enhancements: 92 | Tim Kelsey 93 | TGA RLE 94 | Alan Hickman 95 | initial file IO callback implementation 96 | Emmanuel Julien 97 | bugfixes: 98 | github:Chribba 99 | Guillaume Chereau 100 | github:jry2 101 | github:romigrou 102 | Sergio Gonzalez 103 | Jonas Karlsson 104 | Filip Wasil 105 | Thatcher Ulrich 106 | 107 | LICENSE 108 | 109 | This software is dual-licensed to the public domain and under the following 110 | license: you are granted a perpetual, irrevocable license to copy, modify, 111 | publish, and distribute this file as you see fit. 112 | 113 | */ 114 | 115 | #ifndef INCLUDE_STB_IMAGE_WRITE_H 116 | #define INCLUDE_STB_IMAGE_WRITE_H 117 | 118 | #ifdef __cplusplus 119 | extern "C" { 120 | #endif 121 | 122 | #ifdef STB_IMAGE_WRITE_STATIC 123 | #define STBIWDEF static 124 | #else 125 | #define STBIWDEF extern 126 | extern int stbi_write_tga_with_rle; 127 | #endif 128 | 129 | #ifndef STBI_WRITE_NO_STDIO 130 | STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); 131 | STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); 132 | STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); 133 | STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); 134 | #endif 135 | 136 | typedef void stbi_write_func(void *context, void *data, int size); 137 | 138 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); 139 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 140 | STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); 141 | STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); 142 | 143 | #ifdef __cplusplus 144 | } 145 | #endif 146 | 147 | #endif//INCLUDE_STB_IMAGE_WRITE_H 148 | 149 | #ifdef STB_IMAGE_WRITE_IMPLEMENTATION 150 | 151 | #ifdef _WIN32 152 | #ifndef _CRT_SECURE_NO_WARNINGS 153 | #define _CRT_SECURE_NO_WARNINGS 154 | #endif 155 | #ifndef _CRT_NONSTDC_NO_DEPRECATE 156 | #define _CRT_NONSTDC_NO_DEPRECATE 157 | #endif 158 | #endif 159 | 160 | #ifndef STBI_WRITE_NO_STDIO 161 | #include 162 | #endif // STBI_WRITE_NO_STDIO 163 | 164 | #include 165 | #include 166 | #include 167 | #include 168 | 169 | #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) 170 | // ok 171 | #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) 172 | // ok 173 | #else 174 | #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." 175 | #endif 176 | 177 | #ifndef STBIW_MALLOC 178 | #define STBIW_MALLOC(sz) malloc(sz) 179 | #define STBIW_REALLOC(p,newsz) realloc(p,newsz) 180 | #define STBIW_FREE(p) free(p) 181 | #endif 182 | 183 | #ifndef STBIW_REALLOC_SIZED 184 | #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) 185 | #endif 186 | 187 | 188 | #ifndef STBIW_MEMMOVE 189 | #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) 190 | #endif 191 | 192 | 193 | #ifndef STBIW_ASSERT 194 | #include 195 | #define STBIW_ASSERT(x) assert(x) 196 | #endif 197 | 198 | #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) 199 | 200 | typedef struct 201 | { 202 | stbi_write_func *func; 203 | void *context; 204 | } stbi__write_context; 205 | 206 | // initialize a callback-based context 207 | static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) 208 | { 209 | s->func = c; 210 | s->context = context; 211 | } 212 | 213 | #ifndef STBI_WRITE_NO_STDIO 214 | 215 | static void stbi__stdio_write(void *context, void *data, int size) 216 | { 217 | fwrite(data,1,size,(FILE*) context); 218 | } 219 | 220 | static int stbi__start_write_file(stbi__write_context *s, const char *filename) 221 | { 222 | FILE *f = fopen(filename, "wb"); 223 | stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); 224 | return f != NULL; 225 | } 226 | 227 | static void stbi__end_write_file(stbi__write_context *s) 228 | { 229 | fclose((FILE *)s->context); 230 | } 231 | 232 | #endif // !STBI_WRITE_NO_STDIO 233 | 234 | typedef unsigned int stbiw_uint32; 235 | typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; 236 | 237 | #ifdef STB_IMAGE_WRITE_STATIC 238 | static int stbi_write_tga_with_rle = 1; 239 | #else 240 | int stbi_write_tga_with_rle = 1; 241 | #endif 242 | 243 | static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) 244 | { 245 | while (*fmt) { 246 | switch (*fmt++) { 247 | case ' ': break; 248 | case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); 249 | s->func(s->context,&x,1); 250 | break; } 251 | case '2': { int x = va_arg(v,int); 252 | unsigned char b[2]; 253 | b[0] = STBIW_UCHAR(x); 254 | b[1] = STBIW_UCHAR(x>>8); 255 | s->func(s->context,b,2); 256 | break; } 257 | case '4': { stbiw_uint32 x = va_arg(v,int); 258 | unsigned char b[4]; 259 | b[0]=STBIW_UCHAR(x); 260 | b[1]=STBIW_UCHAR(x>>8); 261 | b[2]=STBIW_UCHAR(x>>16); 262 | b[3]=STBIW_UCHAR(x>>24); 263 | s->func(s->context,b,4); 264 | break; } 265 | default: 266 | STBIW_ASSERT(0); 267 | return; 268 | } 269 | } 270 | } 271 | 272 | static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) 273 | { 274 | va_list v; 275 | va_start(v, fmt); 276 | stbiw__writefv(s, fmt, v); 277 | va_end(v); 278 | } 279 | 280 | static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) 281 | { 282 | unsigned char arr[3]; 283 | arr[0] = a, arr[1] = b, arr[2] = c; 284 | s->func(s->context, arr, 3); 285 | } 286 | 287 | static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) 288 | { 289 | unsigned char bg[3] = { 255, 0, 255}, px[3]; 290 | int k; 291 | 292 | if (write_alpha < 0) 293 | s->func(s->context, &d[comp - 1], 1); 294 | 295 | switch (comp) { 296 | case 1: 297 | s->func(s->context,d,1); 298 | break; 299 | case 2: 300 | if (expand_mono) 301 | stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp 302 | else 303 | s->func(s->context, d, 1); // monochrome TGA 304 | break; 305 | case 4: 306 | if (!write_alpha) { 307 | // composite against pink background 308 | for (k = 0; k < 3; ++k) 309 | px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; 310 | stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); 311 | break; 312 | } 313 | /* FALLTHROUGH */ 314 | case 3: 315 | stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); 316 | break; 317 | } 318 | if (write_alpha > 0) 319 | s->func(s->context, &d[comp - 1], 1); 320 | } 321 | 322 | static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) 323 | { 324 | stbiw_uint32 zero = 0; 325 | int i,j, j_end; 326 | 327 | if (y <= 0) 328 | return; 329 | 330 | if (vdir < 0) 331 | j_end = -1, j = y-1; 332 | else 333 | j_end = y, j = 0; 334 | 335 | for (; j != j_end; j += vdir) { 336 | for (i=0; i < x; ++i) { 337 | unsigned char *d = (unsigned char *) data + (j*x+i)*comp; 338 | stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); 339 | } 340 | s->func(s->context, &zero, scanline_pad); 341 | } 342 | } 343 | 344 | static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) 345 | { 346 | if (y < 0 || x < 0) { 347 | return 0; 348 | } else { 349 | va_list v; 350 | va_start(v, fmt); 351 | stbiw__writefv(s, fmt, v); 352 | va_end(v); 353 | stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); 354 | return 1; 355 | } 356 | } 357 | 358 | static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) 359 | { 360 | int pad = (-x*3) & 3; 361 | return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, 362 | "11 4 22 4" "4 44 22 444444", 363 | 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 364 | 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header 365 | } 366 | 367 | STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 368 | { 369 | stbi__write_context s; 370 | stbi__start_write_callbacks(&s, func, context); 371 | return stbi_write_bmp_core(&s, x, y, comp, data); 372 | } 373 | 374 | #ifndef STBI_WRITE_NO_STDIO 375 | STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) 376 | { 377 | stbi__write_context s; 378 | if (stbi__start_write_file(&s,filename)) { 379 | int r = stbi_write_bmp_core(&s, x, y, comp, data); 380 | stbi__end_write_file(&s); 381 | return r; 382 | } else 383 | return 0; 384 | } 385 | #endif //!STBI_WRITE_NO_STDIO 386 | 387 | static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) 388 | { 389 | int has_alpha = (comp == 2 || comp == 4); 390 | int colorbytes = has_alpha ? comp-1 : comp; 391 | int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 392 | 393 | if (y < 0 || x < 0) 394 | return 0; 395 | 396 | if (!stbi_write_tga_with_rle) { 397 | return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, 398 | "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); 399 | } else { 400 | int i,j,k; 401 | 402 | stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); 403 | 404 | for (j = y - 1; j >= 0; --j) { 405 | unsigned char *row = (unsigned char *) data + j * x * comp; 406 | int len; 407 | 408 | for (i = 0; i < x; i += len) { 409 | unsigned char *begin = row + i * comp; 410 | int diff = 1; 411 | len = 1; 412 | 413 | if (i < x - 1) { 414 | ++len; 415 | diff = memcmp(begin, row + (i + 1) * comp, comp); 416 | if (diff) { 417 | const unsigned char *prev = begin; 418 | for (k = i + 2; k < x && len < 128; ++k) { 419 | if (memcmp(prev, row + k * comp, comp)) { 420 | prev += comp; 421 | ++len; 422 | } else { 423 | --len; 424 | break; 425 | } 426 | } 427 | } else { 428 | for (k = i + 2; k < x && len < 128; ++k) { 429 | if (!memcmp(begin, row + k * comp, comp)) { 430 | ++len; 431 | } else { 432 | break; 433 | } 434 | } 435 | } 436 | } 437 | 438 | if (diff) { 439 | unsigned char header = STBIW_UCHAR(len - 1); 440 | s->func(s->context, &header, 1); 441 | for (k = 0; k < len; ++k) { 442 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); 443 | } 444 | } else { 445 | unsigned char header = STBIW_UCHAR(len - 129); 446 | s->func(s->context, &header, 1); 447 | stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); 448 | } 449 | } 450 | } 451 | } 452 | return 1; 453 | } 454 | 455 | int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) 456 | { 457 | stbi__write_context s; 458 | stbi__start_write_callbacks(&s, func, context); 459 | return stbi_write_tga_core(&s, x, y, comp, (void *) data); 460 | } 461 | 462 | #ifndef STBI_WRITE_NO_STDIO 463 | int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) 464 | { 465 | stbi__write_context s; 466 | if (stbi__start_write_file(&s,filename)) { 467 | int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); 468 | stbi__end_write_file(&s); 469 | return r; 470 | } else 471 | return 0; 472 | } 473 | #endif 474 | 475 | // ************************************************************************************************* 476 | // Radiance RGBE HDR writer 477 | // by Baldur Karlsson 478 | #ifndef STBI_WRITE_NO_STDIO 479 | 480 | #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) 481 | 482 | void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) 483 | { 484 | int exponent; 485 | float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); 486 | 487 | if (maxcomp < 1e-32f) { 488 | rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; 489 | } else { 490 | float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; 491 | 492 | rgbe[0] = (unsigned char)(linear[0] * normalize); 493 | rgbe[1] = (unsigned char)(linear[1] * normalize); 494 | rgbe[2] = (unsigned char)(linear[2] * normalize); 495 | rgbe[3] = (unsigned char)(exponent + 128); 496 | } 497 | } 498 | 499 | void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) 500 | { 501 | unsigned char lengthbyte = STBIW_UCHAR(length+128); 502 | STBIW_ASSERT(length+128 <= 255); 503 | s->func(s->context, &lengthbyte, 1); 504 | s->func(s->context, &databyte, 1); 505 | } 506 | 507 | void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) 508 | { 509 | unsigned char lengthbyte = STBIW_UCHAR(length); 510 | STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code 511 | s->func(s->context, &lengthbyte, 1); 512 | s->func(s->context, data, length); 513 | } 514 | 515 | void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) 516 | { 517 | unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; 518 | unsigned char rgbe[4]; 519 | float linear[3]; 520 | int x; 521 | 522 | scanlineheader[2] = (width&0xff00)>>8; 523 | scanlineheader[3] = (width&0x00ff); 524 | 525 | /* skip RLE for images too small or large */ 526 | if (width < 8 || width >= 32768) { 527 | for (x=0; x < width; x++) { 528 | switch (ncomp) { 529 | case 4: /* fallthrough */ 530 | case 3: linear[2] = scanline[x*ncomp + 2]; 531 | linear[1] = scanline[x*ncomp + 1]; 532 | linear[0] = scanline[x*ncomp + 0]; 533 | break; 534 | default: 535 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 536 | break; 537 | } 538 | stbiw__linear_to_rgbe(rgbe, linear); 539 | s->func(s->context, rgbe, 4); 540 | } 541 | } else { 542 | int c,r; 543 | /* encode into scratch buffer */ 544 | for (x=0; x < width; x++) { 545 | switch(ncomp) { 546 | case 4: /* fallthrough */ 547 | case 3: linear[2] = scanline[x*ncomp + 2]; 548 | linear[1] = scanline[x*ncomp + 1]; 549 | linear[0] = scanline[x*ncomp + 0]; 550 | break; 551 | default: 552 | linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; 553 | break; 554 | } 555 | stbiw__linear_to_rgbe(rgbe, linear); 556 | scratch[x + width*0] = rgbe[0]; 557 | scratch[x + width*1] = rgbe[1]; 558 | scratch[x + width*2] = rgbe[2]; 559 | scratch[x + width*3] = rgbe[3]; 560 | } 561 | 562 | s->func(s->context, scanlineheader, 4); 563 | 564 | /* RLE each component separately */ 565 | for (c=0; c < 4; c++) { 566 | unsigned char *comp = &scratch[width*c]; 567 | 568 | x = 0; 569 | while (x < width) { 570 | // find first run 571 | r = x; 572 | while (r+2 < width) { 573 | if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) 574 | break; 575 | ++r; 576 | } 577 | if (r+2 >= width) 578 | r = width; 579 | // dump up to first run 580 | while (x < r) { 581 | int len = r-x; 582 | if (len > 128) len = 128; 583 | stbiw__write_dump_data(s, len, &comp[x]); 584 | x += len; 585 | } 586 | // if there's a run, output it 587 | if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd 588 | // find next byte after run 589 | while (r < width && comp[r] == comp[x]) 590 | ++r; 591 | // output run up to r 592 | while (x < r) { 593 | int len = r-x; 594 | if (len > 127) len = 127; 595 | stbiw__write_run_data(s, len, comp[x]); 596 | x += len; 597 | } 598 | } 599 | } 600 | } 601 | } 602 | } 603 | 604 | static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) 605 | { 606 | if (y <= 0 || x <= 0 || data == NULL) 607 | return 0; 608 | else { 609 | // Each component is stored separately. Allocate scratch space for full output scanline. 610 | unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); 611 | int i, len; 612 | char buffer[128]; 613 | char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; 614 | s->func(s->context, header, sizeof(header)-1); 615 | 616 | len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); 617 | s->func(s->context, buffer, len); 618 | 619 | for(i=0; i < y; i++) 620 | stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); 621 | STBIW_FREE(scratch); 622 | return 1; 623 | } 624 | } 625 | 626 | int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) 627 | { 628 | stbi__write_context s; 629 | stbi__start_write_callbacks(&s, func, context); 630 | return stbi_write_hdr_core(&s, x, y, comp, (float *) data); 631 | } 632 | 633 | int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) 634 | { 635 | stbi__write_context s; 636 | if (stbi__start_write_file(&s,filename)) { 637 | int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); 638 | stbi__end_write_file(&s); 639 | return r; 640 | } else 641 | return 0; 642 | } 643 | #endif // STBI_WRITE_NO_STDIO 644 | 645 | 646 | ////////////////////////////////////////////////////////////////////////////// 647 | // 648 | // PNG writer 649 | // 650 | 651 | // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() 652 | #define stbiw__sbraw(a) ((int *) (a) - 2) 653 | #define stbiw__sbm(a) stbiw__sbraw(a)[0] 654 | #define stbiw__sbn(a) stbiw__sbraw(a)[1] 655 | 656 | #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) 657 | #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) 658 | #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) 659 | 660 | #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) 661 | #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) 662 | #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) 663 | 664 | static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) 665 | { 666 | int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; 667 | void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); 668 | STBIW_ASSERT(p); 669 | if (p) { 670 | if (!*arr) ((int *) p)[1] = 0; 671 | *arr = (void *) ((int *) p + 2); 672 | stbiw__sbm(*arr) = m; 673 | } 674 | return *arr; 675 | } 676 | 677 | static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) 678 | { 679 | while (*bitcount >= 8) { 680 | stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); 681 | *bitbuffer >>= 8; 682 | *bitcount -= 8; 683 | } 684 | return data; 685 | } 686 | 687 | static int stbiw__zlib_bitrev(int code, int codebits) 688 | { 689 | int res=0; 690 | while (codebits--) { 691 | res = (res << 1) | (code & 1); 692 | code >>= 1; 693 | } 694 | return res; 695 | } 696 | 697 | static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) 698 | { 699 | int i; 700 | for (i=0; i < limit && i < 258; ++i) 701 | if (a[i] != b[i]) break; 702 | return i; 703 | } 704 | 705 | static unsigned int stbiw__zhash(unsigned char *data) 706 | { 707 | stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); 708 | hash ^= hash << 3; 709 | hash += hash >> 5; 710 | hash ^= hash << 4; 711 | hash += hash >> 17; 712 | hash ^= hash << 25; 713 | hash += hash >> 6; 714 | return hash; 715 | } 716 | 717 | #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) 718 | #define stbiw__zlib_add(code,codebits) \ 719 | (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) 720 | #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) 721 | // default huffman tables 722 | #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) 723 | #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) 724 | #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) 725 | #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) 726 | #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) 727 | #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) 728 | 729 | #define stbiw__ZHASH 16384 730 | 731 | unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) 732 | { 733 | static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; 734 | static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; 735 | static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; 736 | static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; 737 | unsigned int bitbuf=0; 738 | int i,j, bitcount=0; 739 | unsigned char *out = NULL; 740 | unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); 741 | if (quality < 5) quality = 5; 742 | 743 | stbiw__sbpush(out, 0x78); // DEFLATE 32K window 744 | stbiw__sbpush(out, 0x5e); // FLEVEL = 1 745 | stbiw__zlib_add(1,1); // BFINAL = 1 746 | stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman 747 | 748 | for (i=0; i < stbiw__ZHASH; ++i) 749 | hash_table[i] = NULL; 750 | 751 | i=0; 752 | while (i < data_len-3) { 753 | // hash next 3 bytes of data to be compressed 754 | int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; 755 | unsigned char *bestloc = 0; 756 | unsigned char **hlist = hash_table[h]; 757 | int n = stbiw__sbcount(hlist); 758 | for (j=0; j < n; ++j) { 759 | if (hlist[j]-data > i-32768) { // if entry lies within window 760 | int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); 761 | if (d >= best) best=d,bestloc=hlist[j]; 762 | } 763 | } 764 | // when hash table entry is too long, delete half the entries 765 | if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { 766 | STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); 767 | stbiw__sbn(hash_table[h]) = quality; 768 | } 769 | stbiw__sbpush(hash_table[h],data+i); 770 | 771 | if (bestloc) { 772 | // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal 773 | h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); 774 | hlist = hash_table[h]; 775 | n = stbiw__sbcount(hlist); 776 | for (j=0; j < n; ++j) { 777 | if (hlist[j]-data > i-32767) { 778 | int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); 779 | if (e > best) { // if next match is better, bail on current match 780 | bestloc = NULL; 781 | break; 782 | } 783 | } 784 | } 785 | } 786 | 787 | if (bestloc) { 788 | int d = (int) (data+i - bestloc); // distance back 789 | STBIW_ASSERT(d <= 32767 && best <= 258); 790 | for (j=0; best > lengthc[j+1]-1; ++j); 791 | stbiw__zlib_huff(j+257); 792 | if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); 793 | for (j=0; d > distc[j+1]-1; ++j); 794 | stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); 795 | if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); 796 | i += best; 797 | } else { 798 | stbiw__zlib_huffb(data[i]); 799 | ++i; 800 | } 801 | } 802 | // write out final bytes 803 | for (;i < data_len; ++i) 804 | stbiw__zlib_huffb(data[i]); 805 | stbiw__zlib_huff(256); // end of block 806 | // pad with 0 bits to byte boundary 807 | while (bitcount) 808 | stbiw__zlib_add(0,1); 809 | 810 | for (i=0; i < stbiw__ZHASH; ++i) 811 | (void) stbiw__sbfree(hash_table[i]); 812 | STBIW_FREE(hash_table); 813 | 814 | { 815 | // compute adler32 on input 816 | unsigned int s1=1, s2=0; 817 | int blocklen = (int) (data_len % 5552); 818 | j=0; 819 | while (j < data_len) { 820 | for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; 821 | s1 %= 65521, s2 %= 65521; 822 | j += blocklen; 823 | blocklen = 5552; 824 | } 825 | stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); 826 | stbiw__sbpush(out, STBIW_UCHAR(s2)); 827 | stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); 828 | stbiw__sbpush(out, STBIW_UCHAR(s1)); 829 | } 830 | *out_len = stbiw__sbn(out); 831 | // make returned pointer freeable 832 | STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); 833 | return (unsigned char *) stbiw__sbraw(out); 834 | } 835 | 836 | static unsigned int stbiw__crc32(unsigned char *buffer, int len) 837 | { 838 | static unsigned int crc_table[256] = 839 | { 840 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 841 | 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 842 | 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 843 | 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 844 | 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 845 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 846 | 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 847 | 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 848 | 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 849 | 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 850 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 851 | 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 852 | 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 853 | 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 854 | 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 855 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 856 | 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 857 | 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 858 | 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 859 | 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 860 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 861 | 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 862 | 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 863 | 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 864 | 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 865 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 866 | 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 867 | 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 868 | 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 869 | 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 870 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 871 | 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D 872 | }; 873 | 874 | unsigned int crc = ~0u; 875 | int i; 876 | for (i=0; i < len; ++i) 877 | crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; 878 | return ~crc; 879 | } 880 | 881 | #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) 882 | #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); 883 | #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) 884 | 885 | static void stbiw__wpcrc(unsigned char **data, int len) 886 | { 887 | unsigned int crc = stbiw__crc32(*data - len - 4, len+4); 888 | stbiw__wp32(*data, crc); 889 | } 890 | 891 | static unsigned char stbiw__paeth(int a, int b, int c) 892 | { 893 | int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); 894 | if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); 895 | if (pb <= pc) return STBIW_UCHAR(b); 896 | return STBIW_UCHAR(c); 897 | } 898 | 899 | unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) 900 | { 901 | int ctype[5] = { -1, 0, 4, 2, 6 }; 902 | unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; 903 | unsigned char *out,*o, *filt, *zlib; 904 | signed char *line_buffer; 905 | int i,j,k,p,zlen; 906 | 907 | if (stride_bytes == 0) 908 | stride_bytes = x * n; 909 | 910 | filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; 911 | line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } 912 | for (j=0; j < y; ++j) { 913 | static int mapping[] = { 0,1,2,3,4 }; 914 | static int firstmap[] = { 0,1,0,5,6 }; 915 | int *mymap = j ? mapping : firstmap; 916 | int best = 0, bestval = 0x7fffffff; 917 | for (p=0; p < 2; ++p) { 918 | for (k= p?best:0; k < 5; ++k) { 919 | int type = mymap[k],est=0; 920 | unsigned char *z = pixels + stride_bytes*j; 921 | for (i=0; i < n; ++i) 922 | switch (type) { 923 | case 0: line_buffer[i] = z[i]; break; 924 | case 1: line_buffer[i] = z[i]; break; 925 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 926 | case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; 927 | case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; 928 | case 5: line_buffer[i] = z[i]; break; 929 | case 6: line_buffer[i] = z[i]; break; 930 | } 931 | for (i=n; i < x*n; ++i) { 932 | switch (type) { 933 | case 0: line_buffer[i] = z[i]; break; 934 | case 1: line_buffer[i] = z[i] - z[i-n]; break; 935 | case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; 936 | case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; 937 | case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; 938 | case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; 939 | case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; 940 | } 941 | } 942 | if (p) break; 943 | for (i=0; i < x*n; ++i) 944 | est += abs((signed char) line_buffer[i]); 945 | if (est < bestval) { bestval = est; best = k; } 946 | } 947 | } 948 | // when we get here, best contains the filter type, and line_buffer contains the data 949 | filt[j*(x*n+1)] = (unsigned char) best; 950 | STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); 951 | } 952 | STBIW_FREE(line_buffer); 953 | zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory 954 | STBIW_FREE(filt); 955 | if (!zlib) return 0; 956 | 957 | // each tag requires 12 bytes of overhead 958 | out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); 959 | if (!out) return 0; 960 | *out_len = 8 + 12+13 + 12+zlen + 12; 961 | 962 | o=out; 963 | STBIW_MEMMOVE(o,sig,8); o+= 8; 964 | stbiw__wp32(o, 13); // header length 965 | stbiw__wptag(o, "IHDR"); 966 | stbiw__wp32(o, x); 967 | stbiw__wp32(o, y); 968 | *o++ = 8; 969 | *o++ = STBIW_UCHAR(ctype[n]); 970 | *o++ = 0; 971 | *o++ = 0; 972 | *o++ = 0; 973 | stbiw__wpcrc(&o,13); 974 | 975 | stbiw__wp32(o, zlen); 976 | stbiw__wptag(o, "IDAT"); 977 | STBIW_MEMMOVE(o, zlib, zlen); 978 | o += zlen; 979 | STBIW_FREE(zlib); 980 | stbiw__wpcrc(&o, zlen); 981 | 982 | stbiw__wp32(o,0); 983 | stbiw__wptag(o, "IEND"); 984 | stbiw__wpcrc(&o,0); 985 | 986 | STBIW_ASSERT(o == out + *out_len); 987 | 988 | return out; 989 | } 990 | 991 | #ifndef STBI_WRITE_NO_STDIO 992 | STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) 993 | { 994 | FILE *f; 995 | int len; 996 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 997 | if (png == NULL) return 0; 998 | f = fopen(filename, "wb"); 999 | if (!f) { STBIW_FREE(png); return 0; } 1000 | fwrite(png, 1, len, f); 1001 | fclose(f); 1002 | STBIW_FREE(png); 1003 | return 1; 1004 | } 1005 | #endif 1006 | 1007 | STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) 1008 | { 1009 | int len; 1010 | unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); 1011 | if (png == NULL) return 0; 1012 | func(context, png, len); 1013 | STBIW_FREE(png); 1014 | return 1; 1015 | } 1016 | 1017 | #endif // STB_IMAGE_WRITE_IMPLEMENTATION 1018 | 1019 | /* Revision history 1020 | 1.02 (2016-04-02) 1021 | avoid allocating large structures on the stack 1022 | 1.01 (2016-01-16) 1023 | STBIW_REALLOC_SIZED: support allocators with no realloc support 1024 | avoid race-condition in crc initialization 1025 | minor compile issues 1026 | 1.00 (2015-09-14) 1027 | installable file IO function 1028 | 0.99 (2015-09-13) 1029 | warning fixes; TGA rle support 1030 | 0.98 (2015-04-08) 1031 | added STBIW_MALLOC, STBIW_ASSERT etc 1032 | 0.97 (2015-01-18) 1033 | fixed HDR asserts, rewrote HDR rle logic 1034 | 0.96 (2015-01-17) 1035 | add HDR output 1036 | fix monochrome BMP 1037 | 0.95 (2014-08-17) 1038 | add monochrome TGA output 1039 | 0.94 (2014-05-31) 1040 | rename private functions to avoid conflicts with stb_image.h 1041 | 0.93 (2014-05-27) 1042 | warning fixes 1043 | 0.92 (2010-08-01) 1044 | casts to unsigned char to fix warnings 1045 | 0.91 (2010-07-17) 1046 | first public release 1047 | 0.90 first internal release 1048 | */ 1049 | -------------------------------------------------------------------------------- /opengl_sphere_lighting/textures/moon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdupuy/pivot/153046fc2015457318f0b8bb61e2714e76fbf11a/opengl_sphere_lighting/textures/moon.png --------------------------------------------------------------------------------