Merge 'remotes/trunk'
[0ad.git] / source / renderer / ShadowMap.cpp
blob2ad90279896a697b7cf549823a4e9a8c1fcab1c4
1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * Shadow mapping related texture and matrix management
22 #include "precompiled.h"
24 #include "gui/GUIutil.h"
25 #include "lib/bits.h"
26 #include "lib/ogl.h"
27 #include "ps/CLogger.h"
28 #include "ps/ConfigDB.h"
29 #include "ps/Profile.h"
31 #include "graphics/LightEnv.h"
32 #include "graphics/ShaderManager.h"
34 #include "maths/BoundingBoxAligned.h"
35 #include "maths/Brush.h"
36 #include "maths/MathUtil.h"
37 #include "maths/Matrix3D.h"
39 #include "renderer/Renderer.h"
40 #include "renderer/ShadowMap.h"
43 ///////////////////////////////////////////////////////////////////////////////////////////////////
44 // ShadowMap implementation
46 /**
47 * Struct ShadowMapInternals: Internal data for the ShadowMap implementation
49 struct ShadowMapInternals
51 // bit depth for the depth texture
52 int DepthTextureBits;
53 // the EXT_framebuffer_object framebuffer
54 GLuint Framebuffer;
55 // handle of shadow map
56 GLuint Texture;
57 // width, height of shadow map
58 int Width, Height;
59 // Shadow map quality (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
60 int QualityLevel;
61 // used width, height of shadow map
62 int EffectiveWidth, EffectiveHeight;
63 // transform light space into projected light space
64 // in projected light space, the shadowbound box occupies the [-1..1] cube
65 // calculated on BeginRender, after the final shadow bounds are known
66 CMatrix3D LightProjection;
67 // Transform world space into light space; calculated on SetupFrame
68 CMatrix3D LightTransform;
69 // Transform world space into texture space of the shadow map;
70 // calculated on BeginRender, after the final shadow bounds are known
71 CMatrix3D TextureMatrix;
73 // transform light space into world space
74 CMatrix3D InvLightTransform;
75 // bounding box of shadowed objects in light space
76 CBoundingBoxAligned ShadowCasterBound;
77 CBoundingBoxAligned ShadowReceiverBound;
79 CBoundingBoxAligned ShadowRenderBound;
81 // Camera transformed into light space
82 CCamera LightspaceCamera;
84 // Some drivers (at least some Intel Mesa ones) appear to handle alpha testing
85 // incorrectly when the FBO has only a depth attachment.
86 // When m_ShadowAlphaFix is true, we use DummyTexture to store a useless
87 // alpha texture which is attached to the FBO as a workaround.
88 GLuint DummyTexture;
90 // Copy of renderer's standard view camera, saved between
91 // BeginRender and EndRender while we replace it with the shadow camera
92 CCamera SavedViewCamera;
94 // Save the caller's FBO so it can be restored
95 GLint SavedViewFBO;
97 // Helper functions
98 void CalcShadowMatrices();
99 void CreateTexture();
103 ///////////////////////////////////////////////////////////////////////////////////////////////////
104 // Construction/Destruction
105 ShadowMap::ShadowMap()
107 m = new ShadowMapInternals;
108 m->Framebuffer = 0;
109 m->Texture = 0;
110 m->DummyTexture = 0;
111 m->Width = 0;
112 m->Height = 0;
113 m->QualityLevel = 0;
114 m->EffectiveWidth = 0;
115 m->EffectiveHeight = 0;
116 m->DepthTextureBits = 0;
117 // DepthTextureBits: 24/32 are very much faster than 16, on GeForce 4 and FX;
118 // but they're very much slower on Radeon 9800.
119 // In both cases, the default (no specified depth) is fast, so we just use
120 // that by default and hope it's alright. (Otherwise, we'd probably need to
121 // do some kind of hardware detection to work out what to use.)
123 // Avoid using uninitialised values in AddShadowedBound if SetupFrame wasn't called first
124 m->LightTransform.SetIdentity();
128 ShadowMap::~ShadowMap()
130 if (m->Texture)
131 glDeleteTextures(1, &m->Texture);
132 if (m->DummyTexture)
133 glDeleteTextures(1, &m->DummyTexture);
134 if (m->Framebuffer)
135 pglDeleteFramebuffersEXT(1, &m->Framebuffer);
137 delete m;
140 ///////////////////////////////////////////////////////////////////////////////////////////////////
141 // Force the texture/buffer/etc to be recreated, particularly when the renderer's
142 // size has changed
143 void ShadowMap::RecreateTexture()
145 if (m->Texture)
146 glDeleteTextures(1, &m->Texture);
147 if (m->DummyTexture)
148 glDeleteTextures(1, &m->DummyTexture);
149 if (m->Framebuffer)
150 pglDeleteFramebuffersEXT(1, &m->Framebuffer);
152 m->Texture = 0;
153 m->DummyTexture = 0;
154 m->Framebuffer = 0;
156 // (Texture will be constructed in next SetupFrame)
159 //////////////////////////////////////////////////////////////////////////////
160 // SetupFrame: camera and light direction for this frame
161 void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
163 if (!m->Texture)
164 m->CreateTexture();
166 CVector3D z = lightdir;
167 CVector3D y;
168 CVector3D x = camera.m_Orientation.GetIn();
169 CVector3D eyepos = camera.m_Orientation.GetTranslation();
171 z.Normalize();
172 x -= z * z.Dot(x);
173 if (x.Length() < 0.001)
175 // this is invoked if the camera and light directions almost coincide
176 // assumption: light direction has a significant Z component
177 x = CVector3D(1.0, 0.0, 0.0);
178 x -= z * z.Dot(x);
180 x.Normalize();
181 y = z.Cross(x);
183 // X axis perpendicular to light direction, flowing along with view direction
184 m->LightTransform._11 = x.X;
185 m->LightTransform._12 = x.Y;
186 m->LightTransform._13 = x.Z;
188 // Y axis perpendicular to light and view direction
189 m->LightTransform._21 = y.X;
190 m->LightTransform._22 = y.Y;
191 m->LightTransform._23 = y.Z;
193 // Z axis is in direction of light
194 m->LightTransform._31 = z.X;
195 m->LightTransform._32 = z.Y;
196 m->LightTransform._33 = z.Z;
198 // eye is at the origin of the coordinate system
199 m->LightTransform._14 = -x.Dot(eyepos);
200 m->LightTransform._24 = -y.Dot(eyepos);
201 m->LightTransform._34 = -z.Dot(eyepos);
203 m->LightTransform._41 = 0.0;
204 m->LightTransform._42 = 0.0;
205 m->LightTransform._43 = 0.0;
206 m->LightTransform._44 = 1.0;
208 m->LightTransform.GetInverse(m->InvLightTransform);
209 m->ShadowCasterBound.SetEmpty();
210 m->ShadowReceiverBound.SetEmpty();
213 m->LightspaceCamera = camera;
214 m->LightspaceCamera.m_Orientation = m->LightTransform * camera.m_Orientation;
215 m->LightspaceCamera.UpdateFrustum();
219 //////////////////////////////////////////////////////////////////////////////
220 // AddShadowedBound: add a world-space bounding box to the bounds of shadowed
221 // objects
222 void ShadowMap::AddShadowCasterBound(const CBoundingBoxAligned& bounds)
224 CBoundingBoxAligned lightspacebounds;
226 bounds.Transform(m->LightTransform, lightspacebounds);
227 m->ShadowCasterBound += lightspacebounds;
230 void ShadowMap::AddShadowReceiverBound(const CBoundingBoxAligned& bounds)
232 CBoundingBoxAligned lightspacebounds;
234 bounds.Transform(m->LightTransform, lightspacebounds);
235 m->ShadowReceiverBound += lightspacebounds;
238 CFrustum ShadowMap::GetShadowCasterCullFrustum()
240 // Get the bounds of all objects that can receive shadows
241 CBoundingBoxAligned bound = m->ShadowReceiverBound;
243 // Intersect with the camera frustum, so the shadow map doesn't have to get
244 // stretched to cover the off-screen parts of large models
245 bound.IntersectFrustumConservative(m->LightspaceCamera.GetFrustum());
247 // ShadowBound might have been empty to begin with, producing an empty result
248 if (bound.IsEmpty())
250 // CFrustum can't easily represent nothingness, so approximate it with
251 // a single point which won't match many objects
252 bound += CVector3D(0.0f, 0.0f, 0.0f);
253 return bound.ToFrustum();
256 // Extend the bounds a long way towards the light source, to encompass
257 // all objects that might cast visible shadows.
258 // (The exact constant was picked entirely arbitrarily.)
259 bound[0].Z -= 1000.f;
261 CFrustum frustum = bound.ToFrustum();
262 frustum.Transform(m->InvLightTransform);
263 return frustum;
266 ///////////////////////////////////////////////////////////////////////////////////////////////////
267 // CalcShadowMatrices: calculate required matrices for shadow map generation - the light's
268 // projection and transformation matrices
269 void ShadowMapInternals::CalcShadowMatrices()
271 // Start building the shadow map to cover all objects that will receive shadows
272 CBoundingBoxAligned receiverBound = ShadowReceiverBound;
274 // Intersect with the camera frustum, so the shadow map doesn't have to get
275 // stretched to cover the off-screen parts of large models
276 receiverBound.IntersectFrustumConservative(LightspaceCamera.GetFrustum());
278 // Intersect with the shadow caster bounds, because there's no point
279 // wasting space around the edges of the shadow map that we're not going
280 // to draw into
281 ShadowRenderBound[0].X = std::max(receiverBound[0].X, ShadowCasterBound[0].X);
282 ShadowRenderBound[0].Y = std::max(receiverBound[0].Y, ShadowCasterBound[0].Y);
283 ShadowRenderBound[1].X = std::min(receiverBound[1].X, ShadowCasterBound[1].X);
284 ShadowRenderBound[1].Y = std::min(receiverBound[1].Y, ShadowCasterBound[1].Y);
286 // Set the near and far planes to include just the shadow casters,
287 // so we make full use of the depth texture's range. Add a bit of a
288 // delta so we don't accidentally clip objects that are directly on
289 // the planes.
290 ShadowRenderBound[0].Z = ShadowCasterBound[0].Z - 2.f;
291 ShadowRenderBound[1].Z = ShadowCasterBound[1].Z + 2.f;
293 // ShadowBound might have been empty to begin with, producing an empty result
294 if (ShadowRenderBound.IsEmpty())
296 // no-op
297 LightProjection.SetIdentity();
298 TextureMatrix = LightTransform;
299 return;
302 // round off the shadow boundaries to sane increments to help reduce swim effect
303 float boundInc = 16.0f;
304 ShadowRenderBound[0].X = floor(ShadowRenderBound[0].X / boundInc) * boundInc;
305 ShadowRenderBound[0].Y = floor(ShadowRenderBound[0].Y / boundInc) * boundInc;
306 ShadowRenderBound[1].X = ceil(ShadowRenderBound[1].X / boundInc) * boundInc;
307 ShadowRenderBound[1].Y = ceil(ShadowRenderBound[1].Y / boundInc) * boundInc;
309 // Setup orthogonal projection (lightspace -> clip space) for shadowmap rendering
310 CVector3D scale = ShadowRenderBound[1] - ShadowRenderBound[0];
311 CVector3D shift = (ShadowRenderBound[1] + ShadowRenderBound[0]) * -0.5;
313 if (scale.X < 1.0)
314 scale.X = 1.0;
315 if (scale.Y < 1.0)
316 scale.Y = 1.0;
317 if (scale.Z < 1.0)
318 scale.Z = 1.0;
320 scale.X = 2.0 / scale.X;
321 scale.Y = 2.0 / scale.Y;
322 scale.Z = 2.0 / scale.Z;
324 // make sure a given world position falls on a consistent shadowmap texel fractional offset
325 float offsetX = fmod(ShadowRenderBound[0].X - LightTransform._14, 2.0f/(scale.X*EffectiveWidth));
326 float offsetY = fmod(ShadowRenderBound[0].Y - LightTransform._24, 2.0f/(scale.Y*EffectiveHeight));
328 LightProjection.SetZero();
329 LightProjection._11 = scale.X;
330 LightProjection._14 = (shift.X + offsetX) * scale.X;
331 LightProjection._22 = scale.Y;
332 LightProjection._24 = (shift.Y + offsetY) * scale.Y;
333 LightProjection._33 = scale.Z;
334 LightProjection._34 = shift.Z * scale.Z;
335 LightProjection._44 = 1.0;
337 // Calculate texture matrix by creating the clip space to texture coordinate matrix
338 // and then concatenating all matrices that have been calculated so far
340 float texscalex = scale.X * 0.5f * (float)EffectiveWidth / (float)Width;
341 float texscaley = scale.Y * 0.5f * (float)EffectiveHeight / (float)Height;
342 float texscalez = scale.Z * 0.5f;
344 CMatrix3D lightToTex;
345 lightToTex.SetZero();
346 lightToTex._11 = texscalex;
347 lightToTex._14 = (offsetX - ShadowRenderBound[0].X) * texscalex;
348 lightToTex._22 = texscaley;
349 lightToTex._24 = (offsetY - ShadowRenderBound[0].Y) * texscaley;
350 lightToTex._33 = texscalez;
351 lightToTex._34 = -ShadowRenderBound[0].Z * texscalez;
352 lightToTex._44 = 1.0;
354 TextureMatrix = lightToTex * LightTransform;
359 //////////////////////////////////////////////////////////////////////////
360 // Create the shadow map
361 void ShadowMapInternals::CreateTexture()
363 // Cleanup
364 if (Texture)
366 glDeleteTextures(1, &Texture);
367 Texture = 0;
369 if (DummyTexture)
371 glDeleteTextures(1, &DummyTexture);
372 DummyTexture = 0;
374 if (Framebuffer)
376 pglDeleteFramebuffersEXT(1, &Framebuffer);
377 Framebuffer = 0;
380 // save the caller's FBO
381 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &SavedViewFBO);
383 pglGenFramebuffersEXT(1, &Framebuffer);
385 if (g_Renderer.m_ShadowMapSize != 0)
387 // non-default option to override the size
388 Width = Height = g_Renderer.m_ShadowMapSize;
390 else
392 CFG_GET_VAL("shadowquality", QualityLevel);
394 // get shadow map size as next power of two up from view width/height
395 int shadow_map_size = (int)round_up_to_pow2((unsigned)std::max(g_Renderer.GetWidth(), g_Renderer.GetHeight()));
396 switch (QualityLevel)
398 // Very Low
399 case -2:
400 shadow_map_size /= 4;
401 break;
402 // Low
403 case -1:
404 shadow_map_size /= 2;
405 break;
406 // High
407 case 1:
408 shadow_map_size *= 2;
409 break;
410 // Ultra
411 case 2:
412 shadow_map_size *= 4;
413 break;
414 // Medium as is
415 default:
416 break;
418 Width = Height = shadow_map_size;
420 // Clamp to the maximum texture size
421 Width = std::min(Width, (int)ogl_max_tex_size);
422 Height = std::min(Height, (int)ogl_max_tex_size);
424 // Since we're using a framebuffer object, the whole texture is available
425 EffectiveWidth = Width;
426 EffectiveHeight = Height;
428 const char* formatname;
430 switch(DepthTextureBits)
432 case 16: formatname = "DEPTH_COMPONENT16"; break;
433 case 24: formatname = "DEPTH_COMPONENT24"; break;
434 case 32: formatname = "DEPTH_COMPONENT32"; break;
435 default: formatname = "DEPTH_COMPONENT"; break;
438 LOGMESSAGE("Creating shadow texture (size %dx%d) (format = %s)",
439 Width, Height, formatname);
442 if (g_Renderer.m_Options.m_ShadowAlphaFix)
444 glGenTextures(1, &DummyTexture);
445 g_Renderer.BindTexture(0, DummyTexture);
446 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
447 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
448 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
449 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
450 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
453 glGenTextures(1, &Texture);
454 g_Renderer.BindTexture(0, Texture);
456 GLenum format;
458 #if CONFIG2_GLES
459 format = GL_DEPTH_COMPONENT;
460 #else
461 switch (DepthTextureBits)
463 case 16: format = GL_DEPTH_COMPONENT16; break;
464 case 24: format = GL_DEPTH_COMPONENT24; break;
465 case 32: format = GL_DEPTH_COMPONENT32; break;
466 default: format = GL_DEPTH_COMPONENT; break;
468 #endif
470 glTexImage2D(GL_TEXTURE_2D, 0, format, Width, Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NULL);
471 // GLES requires type == UNSIGNED_SHORT or UNSIGNED_INT
473 // set texture parameters
474 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
475 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
477 #if CONFIG2_GLES
478 // GLES doesn't do depth comparisons, so treat it as a
479 // basic unfiltered depth texture
480 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
481 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
482 #else
483 // Enable automatic depth comparisons
484 glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
485 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
486 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
488 // Use GL_LINEAR to trigger automatic PCF on some devices
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
490 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
491 #endif
493 ogl_WarnIfError();
495 // bind to framebuffer object
496 glBindTexture(GL_TEXTURE_2D, 0);
497 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
499 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, Texture, 0);
501 if (g_Renderer.m_Options.m_ShadowAlphaFix)
503 pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, DummyTexture, 0);
505 else
507 #if CONFIG2_GLES
508 #warning TODO: figure out whether the glDrawBuffer/glReadBuffer stuff is needed, since it is not supported by GLES
509 #else
510 glDrawBuffer(GL_NONE);
511 #endif
514 #if !CONFIG2_GLES
515 glReadBuffer(GL_NONE);
516 #endif
518 ogl_WarnIfError();
520 GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
522 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, SavedViewFBO);
524 if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
526 LOGWARNING("Framebuffer object incomplete: 0x%04X", status);
528 // Disable shadow rendering (but let the user try again if they want)
529 g_Renderer.m_Options.m_Shadows = false;
534 ///////////////////////////////////////////////////////////////////////////////////////////////////
535 // Set up to render into shadow map texture
536 void ShadowMap::BeginRender()
538 // HACK HACK: this depends in non-obvious ways on the behaviour of the caller
540 // save caller's FBO
541 glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &m->SavedViewFBO);
543 // Calc remaining shadow matrices
544 m->CalcShadowMatrices();
547 PROFILE("bind framebuffer");
548 glBindTexture(GL_TEXTURE_2D, 0);
549 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
552 // clear buffers
554 PROFILE("clear depth texture");
555 // In case we used m_ShadowAlphaFix, we ought to clear the unused
556 // color buffer too, else Mali 400 drivers get confused.
557 // Might as well clear stencil too for completeness.
558 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
559 glColorMask(0,0,0,0);
562 // setup viewport
563 const SViewPort vp = { 0, 0, m->EffectiveWidth, m->EffectiveHeight };
564 g_Renderer.SetViewport(vp);
566 m->SavedViewCamera = g_Renderer.GetViewCamera();
568 CCamera c = m->SavedViewCamera;
569 c.SetProjection(m->LightProjection);
570 c.GetOrientation() = m->InvLightTransform;
571 g_Renderer.SetViewCamera(c);
573 #if !CONFIG2_GLES
574 glMatrixMode(GL_PROJECTION);
575 glLoadMatrixf(&m->LightProjection._11);
576 glMatrixMode(GL_MODELVIEW);
577 glLoadMatrixf(&m->LightTransform._11);
578 #endif
580 glEnable(GL_SCISSOR_TEST);
581 glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
585 ///////////////////////////////////////////////////////////////////////////////////////////////////
586 // Finish rendering into shadow map texture
587 void ShadowMap::EndRender()
589 glDisable(GL_SCISSOR_TEST);
591 g_Renderer.SetViewCamera(m->SavedViewCamera);
594 PROFILE("unbind framebuffer");
595 pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->SavedViewFBO);
598 const SViewPort vp = { 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight() };
599 g_Renderer.SetViewport(vp);
601 glColorMask(1,1,1,1);
605 ///////////////////////////////////////////////////////////////////////////////////////////////////
606 // Retrieve the texture handle and texture matrix for shadowing
607 GLuint ShadowMap::GetTexture() const
609 return m->Texture;
612 const CMatrix3D& ShadowMap::GetTextureMatrix() const
614 return m->TextureMatrix;
618 ///////////////////////////////////////////////////////////////////////////////////////////////////
619 // Depth texture bits
620 int ShadowMap::GetDepthTextureBits() const
622 return m->DepthTextureBits;
625 void ShadowMap::SetDepthTextureBits(int bits)
627 if (bits != m->DepthTextureBits)
629 if (m->Texture)
631 glDeleteTextures(1, &m->Texture);
632 m->Texture = 0;
634 m->Width = m->Height = 0;
636 m->DepthTextureBits = bits;
640 ///////////////////////////////////////////////////////////////////////////////////////////////////
641 // Depth texture size
642 int ShadowMap::GetWidth() const
644 return m->Width;
647 int ShadowMap::GetHeight() const
649 return m->Height;
652 //////////////////////////////////////////////////////////////////////////////
654 void ShadowMap::RenderDebugBounds()
656 CShaderTechniquePtr shaderTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
657 shaderTech->BeginPass();
658 CShaderProgramPtr shader = shaderTech->GetShader();
660 glDepthMask(0);
661 glDisable(GL_CULL_FACE);
663 // Render various shadow bounds:
664 // Yellow = bounds of objects in view frustum that receive shadows
665 // Red = culling frustum used to find potential shadow casters
666 // Green = bounds of objects in culling frustum that cast shadows
667 // Blue = frustum used for rendering the shadow map
669 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection() * m->InvLightTransform);
671 shader->Uniform(str_color, 1.0f, 1.0f, 0.0f, 1.0f);
672 m->ShadowReceiverBound.RenderOutline(shader);
674 shader->Uniform(str_color, 0.0f, 1.0f, 0.0f, 1.0f);
675 m->ShadowCasterBound.RenderOutline(shader);
677 glEnable(GL_BLEND);
678 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 0.25f);
680 m->ShadowRenderBound.Render(shader);
681 glDisable(GL_BLEND);
683 shader->Uniform(str_color, 0.0f, 0.0f, 1.0f, 1.0f);
684 m->ShadowRenderBound.RenderOutline(shader);
686 // Render light frustum
688 shader->Uniform(str_transform, g_Renderer.GetViewCamera().GetViewProjection());
690 CFrustum frustum = GetShadowCasterCullFrustum();
691 // We don't have a function to create a brush directly from a frustum, so use
692 // the ugly approach of creating a large cube and then intersecting with the frustum
693 CBoundingBoxAligned dummy(CVector3D(-1e4, -1e4, -1e4), CVector3D(1e4, 1e4, 1e4));
694 CBrush brush(dummy);
695 CBrush frustumBrush;
696 brush.Intersect(frustum, frustumBrush);
698 glEnable(GL_BLEND);
699 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
700 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 0.25f);
701 frustumBrush.Render(shader);
702 glDisable(GL_BLEND);
704 shader->Uniform(str_color, 1.0f, 0.0f, 0.0f, 1.0f);
705 frustumBrush.RenderOutline(shader);
708 shaderTech->EndPass();
710 #if 0
711 CMatrix3D InvTexTransform;
713 m->TextureMatrix.GetInverse(InvTexTransform);
715 // Render representative texture rectangle
716 glPushMatrix();
717 glMultMatrixf(&InvTexTransform._11);
719 glEnable(GL_BLEND);
720 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
721 glColor4ub(255,0,0,64);
722 glBegin(GL_QUADS);
723 glVertex3f(0.0, 0.0, 0.0);
724 glVertex3f(1.0, 0.0, 0.0);
725 glVertex3f(1.0, 1.0, 0.0);
726 glVertex3f(0.0, 1.0, 0.0);
727 glEnd();
728 glDisable(GL_BLEND);
730 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
731 glColor3ub(255,0,0);
732 glBegin(GL_QUADS);
733 glVertex3f(0.0, 0.0, 0.0);
734 glVertex3f(1.0, 0.0, 0.0);
735 glVertex3f(1.0, 1.0, 0.0);
736 glVertex3f(0.0, 1.0, 0.0);
737 glEnd();
738 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
739 glPopMatrix();
740 #endif
742 glEnable(GL_CULL_FACE);
743 glDepthMask(1);
746 void ShadowMap::RenderDebugTexture()
748 glDepthMask(0);
750 glDisable(GL_DEPTH_TEST);
752 #if !CONFIG2_GLES
753 g_Renderer.BindTexture(0, m->Texture);
754 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
755 #endif
757 CShaderTechniquePtr texTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_basic);
758 texTech->BeginPass();
759 CShaderProgramPtr texShader = texTech->GetShader();
761 texShader->Uniform(str_transform, GetDefaultGuiMatrix());
762 texShader->BindTexture(str_tex, m->Texture);
764 float s = 256.f;
765 float boxVerts[] = {
766 0,0, 0,s, s,0,
767 s,0, 0,s, s,s
769 float boxUV[] = {
770 0,0, 0,1, 1,0,
771 1,0, 0,1, 1,1
774 texShader->VertexPointer(2, GL_FLOAT, 0, boxVerts);
775 texShader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, boxUV);
776 texShader->AssertPointersBound();
777 glDrawArrays(GL_TRIANGLES, 0, 6);
779 texTech->EndPass();
781 #if !CONFIG2_GLES
782 g_Renderer.BindTexture(0, m->Texture);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
784 #endif
786 glEnable(GL_DEPTH_TEST);
787 glDepthMask(1);