1 /*
2 Copyright (c) 2017-2020 Timur Gafarov, tg
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.csm;
29 
30 import std.math;
31 
32 import dlib.core.memory;
33 import dlib.core.ownership;
34 import dlib.math.vector;
35 import dlib.math.matrix;
36 import dlib.math.transformation;
37 import dlib.image.color;
38 
39 import dagon.core.bindings;
40 import dagon.core.time;
41 import dagon.graphics.state;
42 import dagon.graphics.shadowmap;
43 import dagon.graphics.light;
44 import dagon.graphics.camera;
45 import dagon.graphics.shader;
46 import dagon.graphics.entity;
47 
48 class ShadowArea: Owner
49 {
50     Matrix4x4f biasMatrix;
51     Matrix4x4f projectionMatrix;
52     Matrix4x4f shadowMatrix;
53     Matrix4x4f viewMatrix;
54     Matrix4x4f invViewMatrix;
55     Vector3f position;
56     float projectionSize;
57     float zStart;
58     float zEnd;
59     
60     this(float projectionSize, float zStart, float zEnd, Owner owner)
61     {
62         super(owner);
63         
64         this.biasMatrix = matrixf(
65             0.5f, 0.0f, 0.0f, 0.5f,
66             0.0f, 0.5f, 0.0f, 0.5f,
67             0.0f, 0.0f, 0.5f, 0.5f,
68             0.0f, 0.0f, 0.0f, 1.0f,
69         );
70         
71         resize(projectionSize, zStart, zEnd);
72         
73         this.shadowMatrix = Matrix4x4f.identity;
74         this.viewMatrix = Matrix4x4f.identity;
75         this.invViewMatrix = Matrix4x4f.identity;
76         this.position = Vector3f(0.0f, 0.0f, 0.0f);
77     }
78     
79     void resize(float projectionSize, float zStart, float zEnd)
80     {
81         this.projectionSize = projectionSize;
82         this.zStart = zStart;
83         this.zEnd = zEnd;
84         float hSize = projectionSize * 0.5f;
85         this.projectionMatrix = orthoMatrix(-hSize, hSize, -hSize, hSize, zStart, zEnd);
86     }
87     
88     void update(Light light, Camera camera)
89     {
90         invViewMatrix = translationMatrix(position) * light.rotationAbsolute.toMatrix4x4;
91         viewMatrix = invViewMatrix.inverse;
92         shadowMatrix = biasMatrix * projectionMatrix * viewMatrix * camera.invViewMatrix;
93     }
94 }
95 
96 class CascadedShadowMap: ShadowMap
97 {
98     Camera camera;
99     ShadowArea[3] area;
100     
101     GLuint depthTexture;
102     GLuint framebuffer1;
103     GLuint framebuffer2;
104     GLuint framebuffer3;
105     
106     uint shadowMapResolution = 2048;
107     float[3] projectionSize = [20, 60, 400];
108     float zStart = -10000.0f;
109     float zEnd = 10000.0f;
110     
111     this(Light light, Owner owner)
112     {
113         super(owner);
114         this.light = light;
115         resize(shadowMapResolution);
116         area[0] = New!ShadowArea(projectionSize[0], zStart, zEnd, this);
117         area[1] = New!ShadowArea(projectionSize[1], zStart, zEnd, this);
118         area[2] = New!ShadowArea(projectionSize[2], zStart, zEnd, this);
119     }
120     
121     ~this()
122     {
123         releaseBuffer();
124     }
125     
126     override void resize(uint res)
127     {
128         this.resolution = res;
129         
130         releaseBuffer();
131         
132         glGenTextures(1, &depthTexture);
133         glActiveTexture(GL_TEXTURE0);
134         glBindTexture(GL_TEXTURE_2D_ARRAY, depthTexture);
135         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
136         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
137         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
138         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
139         
140         Color4f borderColor = Color4f(1, 1, 1, 1);
141 
142         glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, borderColor.arrayof.ptr);
143         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
144 	    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
145         
146         glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, resolution, resolution, 3, 0, GL_DEPTH_COMPONENT, GL_FLOAT, null);
147         
148         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0);
149         glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
150 
151         glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
152         
153         glGenFramebuffers(1, &framebuffer1);
154 	    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
155         glDrawBuffer(GL_NONE);
156 	    glReadBuffer(GL_NONE);
157         glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 0);
158         glBindFramebuffer(GL_FRAMEBUFFER, 0);
159         
160         glGenFramebuffers(1, &framebuffer2);
161 	    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
162         glDrawBuffer(GL_NONE);
163 	    glReadBuffer(GL_NONE);
164         glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 1);
165         glBindFramebuffer(GL_FRAMEBUFFER, 0);
166         
167         glGenFramebuffers(1, &framebuffer3);
168 	    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer3);
169         glDrawBuffer(GL_NONE);
170 	    glReadBuffer(GL_NONE);
171         glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0, 2);
172         glBindFramebuffer(GL_FRAMEBUFFER, 0);
173     }
174     
175     void releaseBuffer()
176     {
177         if (glIsFramebuffer(framebuffer1))
178             glDeleteFramebuffers(1, &framebuffer1);
179             
180         if (glIsFramebuffer(framebuffer2))
181             glDeleteFramebuffers(1, &framebuffer2);
182             
183         if (glIsFramebuffer(framebuffer3))
184             glDeleteFramebuffers(1, &framebuffer3);
185         
186         if (glIsTexture(depthTexture))
187             glDeleteTextures(1, &depthTexture);
188     }
189 
190     override void update(Time t)
191     {
192         if (camera)
193         {
194             Vector3f cameraDirection = -camera.directionAbsolute;
195             Vector3f round(Vector3f a, float resolution)
196             {
197                 return Vector3f(a.x - fmod(a.x, resolution), a.y - fmod(a.y, resolution), a.z - fmod(a.z, resolution));
198             }
199             
200             float res1 = projectionSize[0] / shadowMapResolution * 5;
201             area[0].position = round(camera.positionAbsolute + cameraDirection * (projectionSize[0]  * 0.48f - 1.0f), res1);
202             area[0].update(light, camera);
203             
204             foreach(i; 1..projectionSize.length)
205             {
206                 auto res = projectionSize[i] / shadowMapResolution * (i == 1? 10 : 100);
207                 area[i].position = round(camera.positionAbsolute + cameraDirection * projectionSize[i] * 0.5f, res);
208                 area[i].update(light, camera);
209             }
210         }
211     }
212 }