[colobot] 350/390: Added experimental support for dynamic shadows (shadow mapping)

Didier Raboud odyx at moszumanska.debian.org
Fri Jun 12 14:22:03 UTC 2015


This is an automated email from the git hooks/post-receive script.

odyx pushed a commit to branch upstream/latest
in repository colobot.

commit 0b2f00530bc05cf918665223c4dde07dc506cf09
Author: Tomasz Kapuściński <tomaszkax86 at gmail.com>
Date:   Mon May 11 15:21:17 2015 +0200

    Added experimental support for dynamic shadows (shadow mapping)
---
 src/app/app.h                    |   1 +
 src/graphics/core/device.h       |  25 +++-
 src/graphics/core/nulldevice.cpp |  29 ++++-
 src/graphics/core/nulldevice.h   |  11 +-
 src/graphics/core/texture.h      |  49 ++++++++
 src/graphics/engine/engine.cpp   | 239 ++++++++++++++++++++++++++++++++++++++-
 src/graphics/engine/engine.h     |  23 ++++
 src/graphics/opengl/gldevice.cpp | 228 +++++++++++++++++++++++++++++++++----
 src/graphics/opengl/gldevice.h   |  27 ++++-
 9 files changed, 594 insertions(+), 38 deletions(-)

diff --git a/src/app/app.h b/src/app/app.h
index d8a1e1d..651e3be 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -123,6 +123,7 @@ enum PerformanceCounter
     PCNT_RENDER_TERRAIN,        //! < rendering the terrain
     PCNT_RENDER_OBJECTS,        //! < rendering the 3D objects
     PCNT_RENDER_INTERFACE,      //! < rendering 2D interface
+    PCNT_RENDER_SHADOW_MAP,     //! < rendering shadow map
 
     PCNT_ALL,                   //! < all counters together
 
diff --git a/src/graphics/core/device.h b/src/graphics/core/device.h
index f790c24..16b32b3 100644
--- a/src/graphics/core/device.h
+++ b/src/graphics/core/device.h
@@ -91,8 +91,7 @@ enum TransformType
 {
     TRANSFORM_WORLD,
     TRANSFORM_VIEW,
-    TRANSFORM_PROJECTION,
-    TRANSFORM_TEXTURE
+    TRANSFORM_PROJECTION
 };
 
 /**
@@ -107,7 +106,8 @@ enum RenderState
     RENDER_STATE_DEPTH_TEST,
     RENDER_STATE_DEPTH_WRITE,
     RENDER_STATE_ALPHA_TEST,
-    RENDER_STATE_CULLING
+    RENDER_STATE_CULLING,
+    RENDER_STATE_DEPTH_BIAS
 };
 
 /**
@@ -276,6 +276,8 @@ public:
     virtual Texture CreateTexture(CImage *image, const TextureCreateParams &params) = 0;
     //! Creates a texture from raw image data; image data can be freed after that
     virtual Texture CreateTexture(ImageData *data, const TextureCreateParams &params) = 0;
+    //! Creates a depth texture with specific dimensions and depth
+    virtual Texture CreateDepthTexture(int width, int height, int depth) = 0;
     //! Deletes a given texture, freeing it from video memory
     virtual void DestroyTexture(const Texture &texture) = 0;
     //! Deletes all textures created so far
@@ -296,6 +298,12 @@ public:
     //! Sets only the texture wrap modes (for faster than thru stage params)
     virtual void SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wrapT) = 0;
 
+    //! Sets the texture coordinate generation mode for given texture unit
+    virtual void SetTextureCoordGeneration(int index, TextureGenerationParams &params) = 0;
+
+    //! Sets texture coordinate transform matrix
+    virtual void SetTextureMatrix(int index, Math::Matrix& matrix) = 0;
+
     //! Renders primitive composed of vertices with single texture
     virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices    , int vertexCount,
                                Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) = 0;
@@ -333,14 +341,20 @@ public:
     //! Returns a mask of frustum planes for which the test is positive
     virtual int ComputeSphereVisibility(const Math::Vector &center, float radius) = 0;
 
+    //! Changes rendering viewport
+    virtual void SetViewport(int x, int y, int width, int height) = 0;
+
     //! Enables/disables the given render state
     virtual void SetRenderState(RenderState state, bool enabled) = 0;
 
+    //! Sets the color mask
+    virtual void SetColorMask(bool red, bool green, bool blue, bool alpha) = 0;
+
     //! Sets the function of depth test
     virtual void SetDepthTestFunc(CompFunc func) = 0;
 
     //! Sets the depth bias (constant value added to Z-coords)
-    virtual void SetDepthBias(float factor) = 0;
+    virtual void SetDepthBias(float factor, float units) = 0;
 
     //! Sets the alpha test function and reference value
     virtual void SetAlphaTestFunc(CompFunc func, float refValue) = 0;
@@ -366,6 +380,9 @@ public:
     //! Sets the current fill mode
     virtual void SetFillMode(FillMode mode) = 0;
 
+    //! Copies content of framebuffer to texture
+    virtual void CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height) = 0;
+
     //! Returns the pixels of the entire screen
     virtual void* GetFrameBufferPixels() const = 0;
 };
diff --git a/src/graphics/core/nulldevice.cpp b/src/graphics/core/nulldevice.cpp
index f5e426d..869bb89 100644
--- a/src/graphics/core/nulldevice.cpp
+++ b/src/graphics/core/nulldevice.cpp
@@ -128,6 +128,13 @@ Texture CNullDevice::CreateTexture(ImageData *data, const TextureCreateParams &p
     return tex;
 }
 
+Texture CNullDevice::CreateDepthTexture(int width, int height, int depth)
+{
+    Texture tex;
+    tex.id = 1; // tex.id = 0 => invalid texture
+    return tex;
+}
+
 void CNullDevice::DestroyTexture(const Texture &texture)
 {
 }
@@ -171,6 +178,14 @@ void CNullDevice::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode
 {
 }
 
+void CNullDevice::SetTextureCoordGeneration(int index, TextureGenerationParams &params)
+{
+}
+
+void CNullDevice::SetTextureMatrix(int index, Math::Matrix& matrix)
+{
+}
+
 TextureStageParams CNullDevice::GetTextureStageParams(int index)
 {
     return TextureStageParams();
@@ -230,6 +245,10 @@ int CNullDevice::ComputeSphereVisibility(const Math::Vector &center, float radiu
     return 0;
 }
 
+void CNullDevice::SetViewport(int x, int y, int width, int height)
+{
+}
+
 void CNullDevice::SetRenderState(RenderState state, bool enabled)
 {
 }
@@ -239,6 +258,10 @@ bool CNullDevice::GetRenderState(RenderState state)
     return false;
 }
 
+void CNullDevice::SetColorMask(bool red, bool green, bool blue, bool alpha)
+{
+}
+
 void CNullDevice::SetDepthTestFunc(CompFunc func)
 {
 }
@@ -248,7 +271,7 @@ CompFunc CNullDevice::GetDepthTestFunc()
     return COMP_FUNC_NEVER;
 }
 
-void CNullDevice::SetDepthBias(float factor)
+void CNullDevice::SetDepthBias(float factor, float units)
 {
 }
 
@@ -332,6 +355,10 @@ FillMode CNullDevice::GetFillMode()
     return FILL_POINT;
 }
 
+void CNullDevice::CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height)
+{
+}
+
 void* CNullDevice::GetFrameBufferPixels() const
 {
     return nullptr;
diff --git a/src/graphics/core/nulldevice.h b/src/graphics/core/nulldevice.h
index c6065a6..a80c5fa 100644
--- a/src/graphics/core/nulldevice.h
+++ b/src/graphics/core/nulldevice.h
@@ -67,6 +67,7 @@ public:
     
     virtual Texture CreateTexture(CImage *image, const TextureCreateParams &params);
     virtual Texture CreateTexture(ImageData *data, const TextureCreateParams &params);
+    virtual Texture CreateDepthTexture(int width, int height, int depth);
     virtual void DestroyTexture(const Texture &texture);
     virtual void DestroyAllTextures();
     
@@ -81,6 +82,8 @@ public:
     virtual TextureStageParams GetTextureStageParams(int index);
     
     virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT);
+    virtual void SetTextureCoordGeneration(int index, TextureGenerationParams &params);
+    virtual void SetTextureMatrix(int index, Math::Matrix& matrix);
     
     virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices    , int vertexCount,
                                Color color = Color(1.0f, 1.0f, 1.0f, 1.0f));
@@ -98,14 +101,18 @@ public:
     virtual void DestroyStaticBuffer(unsigned int bufferId);
     
     virtual int ComputeSphereVisibility(const Math::Vector &center, float radius);
+
+    virtual void SetViewport(int x, int y, int width, int height);
     
     virtual void SetRenderState(RenderState state, bool enabled);
     virtual bool GetRenderState(RenderState state);
+
+    virtual void SetColorMask(bool red, bool green, bool blue, bool alpha);
     
     virtual void SetDepthTestFunc(CompFunc func);
     virtual CompFunc GetDepthTestFunc();
     
-    virtual void SetDepthBias(float factor);
+    virtual void SetDepthBias(float factor, float units);
     virtual float GetDepthBias();
     
     virtual void SetAlphaTestFunc(CompFunc func, float refValue);
@@ -131,6 +138,8 @@ public:
     
     virtual void SetFillMode(FillMode mode) ;
     virtual FillMode GetFillMode();
+
+    virtual void CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height);
     
     virtual void* GetFrameBufferPixels() const;
     
diff --git a/src/graphics/core/texture.h b/src/graphics/core/texture.h
index 038bbbb..70fc845 100644
--- a/src/graphics/core/texture.h
+++ b/src/graphics/core/texture.h
@@ -98,6 +98,7 @@ enum TexMagFilter
 enum TexWrapMode
 {
     TEX_WRAP_CLAMP,
+    TEX_WRAP_CLAMP_TO_BORDER,
     TEX_WRAP_REPEAT
 };
 
@@ -216,6 +217,54 @@ struct TextureStageParams
 };
 
 /**
+* \struct TexGenMode
+* \brief Texture generation mode
+*/
+enum TexGenMode
+{
+    //! No texture generation
+    TEX_GEN_NONE,
+    //! Object linear mode
+    TEX_GEN_OBJECT_LINEAR,
+    //! Eye linear mode
+    TEX_GEN_EYE_LINEAR,
+    //! Spherical mapping mode
+    TEX_GEN_SPHERE_MAP,
+    //! Normal mapping mode
+    TEX_GEN_NORMAL_MAP,
+    //! Reflection mapping mode
+    TEX_GEN_REFLECTION_MAP
+};
+
+/**
+* \struct TextureGenerationParams
+* \brief Parameters for texture coordinate generation
+*
+* These params define the generation of texture coordinate for given texture unit.
+*/
+struct TextureGenerationParams
+{
+    struct
+    {
+        TexGenMode mode;
+        float plane[4];
+    } coords[4];
+
+    TextureGenerationParams()
+    {
+        LoadDefault();
+    }
+
+    inline void LoadDefault()
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            coords[i].mode = TEX_GEN_NONE;
+        }
+    }
+};
+
+/**
  * \struct Texture
  * \brief Info about a texture
  *
diff --git a/src/graphics/engine/engine.cpp b/src/graphics/engine/engine.cpp
index d26304d..849b585 100644
--- a/src/graphics/engine/engine.cpp
+++ b/src/graphics/engine/engine.cpp
@@ -116,6 +116,8 @@ CEngine::CEngine(CApplication *app)
     m_textureQuality = 1;
     m_textureMipmapLevel = 1;
     m_textureAnisotropy = 1;
+    m_shadowMapping = false;
+    m_npotShadowMap = false;
     m_totoMode = true;
     m_lensMode = true;
     m_waterMode = true;
@@ -181,6 +183,12 @@ CEngine::CEngine(CApplication *app)
         m_textureAnisotropy = value;
     }
 
+    if (CProfile::GetInstance().GetIntProperty("Setup", "ShadowMapping", value))
+    {
+        m_shadowMapping = (value > 0);
+        m_npotShadowMap = (value > 1);
+    }
+
     m_defaultTexParams.format = TEX_IMG_AUTO;
     m_defaultTexParams.mipmap = mipmaps;
     m_defaultTexParams.filter = filter;
@@ -188,6 +196,13 @@ CEngine::CEngine(CApplication *app)
     m_terrainTexParams.format = TEX_IMG_AUTO;
     m_terrainTexParams.mipmap = mipmaps;
     m_terrainTexParams.filter = filter;
+
+    // Compute bias matrix for shadow mapping
+    Math::Matrix temp1, temp2;
+    Math::LoadScaleMatrix(temp1, Math::Vector(0.5f, 0.5f, 0.5f));
+    Math::LoadTranslationMatrix(temp2, Math::Vector(1.0f, 1.0f, 1.0f));
+    //m_shadowBias = Math::MultiplyMatrices(m_shadowBias, temporary);
+    m_shadowBias = Math::MultiplyMatrices(temp1, temp2);
 }
 
 CEngine::~CEngine()
@@ -2965,6 +2980,16 @@ int CEngine::GetTextureAnisotropyLevel()
     return m_textureAnisotropy;
 }
 
+void CEngine::SetShadowMapping(bool value)
+{
+    m_shadowMapping = value;
+}
+
+bool CEngine::GetShadowMapping()
+{
+    return m_shadowMapping;
+}
+
 void CEngine::SetTotoMode(bool present)
 {
     m_totoMode = present;
@@ -3165,6 +3190,9 @@ void CEngine::Render()
     // Begin the scene
     m_device->BeginScene();
 
+    if (m_shadowMapping)
+        RenderShadowMap();
+
     if (m_drawWorld)
         Draw3DScene();
 
@@ -3207,6 +3235,38 @@ void CEngine::Draw3DScene()
     
     m_lightMan->UpdateDeviceLights(ENG_OBJTYPE_TERRAIN);
 
+    // Enable shadow mapping
+    if (m_shadowMapping)
+    {
+        m_device->SetTextureEnabled(2, true);
+        m_device->SetTexture(2, m_shadowMap);
+        m_device->SetTextureMatrix(2, m_shadowTextureMat);
+
+        Math::Matrix identity;
+        identity.LoadIdentity();
+        m_device->SetTransform(TRANSFORM_WORLD, identity);
+        
+        TextureStageParams params;
+        params.colorOperation = TEX_MIX_OPER_MODULATE;
+        params.wrapS = TEX_WRAP_CLAMP_TO_BORDER;
+        params.wrapT = TEX_WRAP_CLAMP_TO_BORDER;
+        m_device->SetTextureStageParams(2, params);
+
+        TextureGenerationParams genParams;
+
+        for (int i = 0; i < 4; i++)
+        {
+            genParams.coords[i].mode = TEX_GEN_EYE_LINEAR;
+
+            for (int j = 0; j < 4; j++)
+            {
+                genParams.coords[i].plane[j] = (i == j ? 1.0f : 0.0f);
+            }
+        }
+
+        m_device->SetTextureCoordGeneration(2, genParams);
+    }
+
     for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
     {
         if (! m_objects[objRank].used)
@@ -3260,6 +3320,24 @@ void CEngine::Draw3DScene()
         }
     }
 
+    // Disable shadow mapping
+    if (m_shadowMapping)
+    {
+        Math::Matrix identity;
+        identity.LoadIdentity();
+
+        m_device->SetTexture(2, 0);
+        m_device->SetTextureEnabled(2, false);
+        m_device->SetTextureMatrix(2, identity);
+
+        TextureGenerationParams params;
+
+        for (int i = 0; i < 4; i++)
+            params.coords[i].mode = TEX_GEN_NONE;
+
+        m_device->SetTextureCoordGeneration(2, params);
+    }
+
         // Draws the shadows , if shadows enabled
 	if (m_shadowVisible)
         DrawShadow();
@@ -3431,6 +3509,154 @@ void CEngine::Draw3DScene()
     if (! m_overFront) DrawOverColor();      // draws the foreground color
 }
 
+void CEngine::RenderShadowMap()
+{
+    if (!m_shadowMapping) return;
+
+    m_app->StartPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
+
+    m_device->Clear();
+
+    // If no shadow map texture exists, create it
+    if (m_shadowMap.id == 0)
+    {
+        int width, height;
+
+        int depth = m_app->GetInstance().GetVideoConfig().depthSize;
+
+        if (m_npotShadowMap)
+        {
+            width = height = Math::Min(m_size.x, m_size.y);
+        }
+        else
+        {
+            int min = Math::Min(m_size.x, m_size.y);
+
+            for (int i = 0; i < 16; i++)
+            {
+                if (min < (1 << i)) break;
+
+                width = height = 1 << i;
+            }
+        }
+
+        m_shadowMap = m_device->CreateDepthTexture(width, height, depth);
+
+        GetLogger()->Info("Created shadow map texture: %dx%d, depth %d\n", width, height, depth);
+    }
+
+    // change state to rendering shadow maps
+    m_device->SetColorMask(false, false, false, false);
+    //m_device->SetDepthTestFunc(COMP_FUNC_LEQUAL);
+    m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, true);
+    m_device->SetRenderState(RENDER_STATE_DEPTH_WRITE, true);
+    m_device->SetRenderState(RENDER_STATE_BLENDING, false);
+    m_device->SetRenderState(RENDER_STATE_LIGHTING, false);
+    m_device->SetRenderState(RENDER_STATE_FOG, false);
+    m_device->SetRenderState(RENDER_STATE_CULLING, false);
+    m_device->SetRenderState(RENDER_STATE_ALPHA_TEST, true);
+
+    m_device->SetViewport(0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
+
+    // recompute matrices
+    Math::Vector worldUp(1.0f, 0.0f, 0.0f);
+    Math::Vector dir = m_lookatPt - m_eyePt;
+    float change = Math::Max(1.0f, dir.Length() / 25.0f);
+    dir.Normalize();
+    Math::Vector pos = m_lookatPt + 40.0f * dir;
+
+    Math::Vector lightPos = pos + Math::Vector(3.0f, 30.0f, 3.0f);
+    Math::Vector lookAt = pos + Math::Vector(0.0, 100.0f, 0.0f);
+
+    float dist = 75.0f; // *change;
+
+    Math::LoadOrthoProjectionMatrix(m_shadowProjMat, -dist, dist, -dist, dist, -50.0f, 50.0f);
+    Math::LoadViewMatrix(m_shadowViewMat, lightPos, lookAt, worldUp);
+
+    Math::Matrix temporary = Math::MultiplyMatrices(m_shadowProjMat, m_shadowViewMat);
+    m_shadowTextureMat = Math::MultiplyMatrices(m_shadowBias, temporary);
+
+    m_device->SetTransform(TRANSFORM_PROJECTION, m_shadowProjMat);
+    m_device->SetTransform(TRANSFORM_VIEW, m_shadowViewMat);
+
+    m_device->SetTexture(0, 0);
+    m_device->SetTexture(1, 0);
+
+    //m_device->SetCullMode(CULL_CW);
+    //m_device->SetRenderState(RENDER_STATE_DEPTH_BIAS, true);
+    //m_device->SetDepthBias(2.0f, 4.0f);
+
+    // render objects into shadow map
+    for (int objRank = 0; objRank < static_cast<int>(m_objects.size()); objRank++)
+    {
+        if (!m_objects[objRank].used)
+            continue;
+
+        if (m_objects[objRank].type == ENG_OBJTYPE_TERRAIN)
+           continue;
+
+        //if (!m_objects[objRank].drawWorld)
+        //    continue;
+
+        m_device->SetTransform(TRANSFORM_WORLD, m_objects[objRank].transform);
+
+        // TODO: check proper object filtering
+        if (!IsVisible(objRank))
+            continue;
+
+        int baseObjRank = m_objects[objRank].baseObjRank;
+        if (baseObjRank == -1)
+            continue;
+
+        assert(baseObjRank >= 0 && baseObjRank < static_cast<int>(m_baseObjects.size()));
+
+        EngineBaseObject& p1 = m_baseObjects[baseObjRank];
+        if (!p1.used)
+            continue;
+
+        //m_lightMan->UpdateDeviceLights(m_objects[objRank].type);
+
+        for (int l2 = 0; l2 < static_cast<int>(p1.next.size()); l2++)
+        {
+            EngineBaseObjTexTier& p2 = p1.next[l2];
+
+            for (int l3 = 0; l3 < static_cast<int>(p2.next.size()); l3++)
+            {
+                EngineBaseObjLODTier& p3 = p2.next[l3];
+
+                if (!IsWithinLODLimit(m_objects[objRank].distance, p3.lodLevel))
+                    continue;
+
+                for (int l4 = 0; l4 < static_cast<int>(p3.next.size()); l4++)
+                {
+                    EngineBaseObjDataTier& p4 = p3.next[l4];
+
+                    //if (m_objects[objRank].transparency != 0.0f)  // transparent ?
+                    //    continue;
+
+                    DrawObject(p4);
+                }
+            }
+        }
+    }
+
+    m_device->SetCullMode(CULL_CCW);
+    m_device->SetRenderState(RENDER_STATE_DEPTH_BIAS, false);
+
+    // copy depth buffer to shadow map
+    m_device->CopyFramebufferToTexture(m_shadowMap, 0, 0, 0, 0, m_shadowMap.size.x, m_shadowMap.size.y);
+
+    // restore default state
+    m_device->SetViewport(0, 0, m_size.x, m_size.y);
+
+    m_device->SetColorMask(true, true, true, true);
+    m_device->Clear();
+
+    m_app->StopPerformanceCounter(PCNT_RENDER_SHADOW_MAP);
+
+    m_device->SetRenderState(RENDER_STATE_DEPTH_TEST, false);
+}
+
 void CEngine::DrawObject(const EngineBaseObjDataTier& p4)
 {
     if (p4.staticBufferId != 0)
@@ -4406,8 +4632,8 @@ void CEngine::DrawStats()
     if (!m_showStats)
         return;
 
-    float height = m_text->GetAscent(FONT_COLOBOT, 12.0f);
-    float width = 0.2f;
+    float height = m_text->GetAscent(FONT_COLOBOT, 13.0f);
+    float width = 0.25f;
 
     Math::Point pos(0.04f, 0.04f + 20 * height);
 
@@ -4510,12 +4736,19 @@ void CEngine::DrawStats()
 
     pos.y -= height;
 
+    str.str("");
+    str << "Shadow map render: " << std::fixed << std::setprecision(2) << m_app->GetPerformanceCounterData(PCNT_RENDER_SHADOW_MAP);
+    m_text->DrawText(str.str(), FONT_COLOBOT, 12.0f, pos, 1.0f, TEXT_ALIGN_LEFT, 0, Color(1.0f, 1.0f, 1.0f, 1.0f));
+
+    pos.y -= height;
+
     float otherRender = m_app->GetPerformanceCounterData(PCNT_RENDER_ALL) -
                         m_app->GetPerformanceCounterData(PCNT_RENDER_PARTICLE) -
                         m_app->GetPerformanceCounterData(PCNT_RENDER_WATER) -
                         m_app->GetPerformanceCounterData(PCNT_RENDER_TERRAIN) -
                         m_app->GetPerformanceCounterData(PCNT_RENDER_OBJECTS) -
-                        m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE);
+                        m_app->GetPerformanceCounterData(PCNT_RENDER_INTERFACE) -
+                        m_app->GetPerformanceCounterData(PCNT_RENDER_SHADOW_MAP);
 
     str.str("");
     str << "Other render: " << std::fixed << std::setprecision(2) << otherRender;
diff --git a/src/graphics/engine/engine.h b/src/graphics/engine/engine.h
index 903c22d..5a5533b 100644
--- a/src/graphics/engine/engine.h
+++ b/src/graphics/engine/engine.h
@@ -1135,6 +1135,12 @@ public:
     //@}
 
     //@{
+    //! Management of shadow mapping
+    void            SetShadowMapping(bool value);
+    bool            GetShadowMapping();
+    //@}
+
+    //@{
     //! Management mode of toto
     void            SetTotoMode(bool present);
     bool            GetTotoMode();
@@ -1227,6 +1233,8 @@ public:
 protected:
     //! Prepares the interface for 3D scene
     void        Draw3DScene();
+    //! Renders shadow map
+    void        RenderShadowMap();
     //! Draw 3D object
     void        DrawObject(const EngineBaseObjDataTier& p4);
     //! Draws the user interface over the scene
@@ -1339,6 +1347,15 @@ protected:
     //! Camera angle for 3D scene
     float           m_focus;
 
+    //! Projection matrix for rendering shadow maps
+    Math::Matrix    m_shadowProjMat;
+    //! View matrix for rendering shadow maps
+    Math::Matrix    m_shadowViewMat;
+    //! Texture matrix for rendering shadow maps
+    Math::Matrix    m_shadowTextureMat;
+    //! Texture bias for sampling shadow maps
+    Math::Matrix    m_shadowBias;
+
     //! World matrix for 2D interface
     Math::Matrix    m_matWorldInterface;
     //! Projection matrix for 2D interface
@@ -1416,6 +1433,8 @@ protected:
     int             m_editIndentValue;
     float           m_tracePrecision;
 
+    Texture         m_shadowMap;
+
     //! Ranks of highlighted objects
     int             m_highlightRank[100];
     //! Highlight visible?
@@ -1436,6 +1455,10 @@ protected:
     int m_textureMipmapLevel;
     //! Requested texture anisotropy level
     int m_textureAnisotropy;
+    //! true if shadow mapping enabled
+    bool m_shadowMapping;
+    //! Override for NPOT shadow map texture
+    bool m_npotShadowMap;
 
     //! Map of loaded textures (by name)
     std::map<std::string, Texture> m_texNameMap;
diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp
index 8809fd2..8380088 100644
--- a/src/graphics/opengl/gldevice.cpp
+++ b/src/graphics/opengl/gldevice.cpp
@@ -59,8 +59,8 @@ void GLDeviceConfig::LoadDefault()
     vboMode = VBO_MODE_AUTO;
 }
 
-
-
+GLuint textureCoordinates[] = { GL_S, GL_T, GL_R, GL_Q };
+GLuint textureCoordGen[] = { GL_TEXTURE_GEN_S, GL_TEXTURE_GEN_T, GL_TEXTURE_GEN_R, GL_TEXTURE_GEN_Q };
 
 CGLDevice::CGLDevice(const GLDeviceConfig &config)
 {
@@ -72,6 +72,9 @@ CGLDevice::CGLDevice(const GLDeviceConfig &config)
     m_vertexBufferType = VBT_DISPLAY_LIST;
     m_anisotropyAvailable = false;
     m_maxAnisotropy = 1;
+    m_glMajor = 1;
+    m_glMinor = 1;
+    m_shadowMappingSupport = SMS_NONE;
 }
 
 
@@ -204,10 +207,33 @@ bool CGLDevice::Create()
             return false;
         }
 
+        // Extract OpenGL version
+        const char *version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
+        sscanf(version, "%d.%d", &m_glMajor, &m_glMinor);
+        GetLogger()->Info("OpenGL %d.%d\n", m_glMajor, m_glMinor);
+
+        // Detect multitexture support
         m_multitextureAvailable = glewIsSupported("GL_ARB_multitexture GL_ARB_texture_env_combine");
         if (!m_multitextureAvailable)
             GetLogger()->Warn("GLEW reports multitexturing not supported - graphics quality will be degraded!\n");
 
+        // Detect Shadow mapping support
+        if (m_glMajor >= 2 || m_glMinor >= 4)     // Core depth texture+shadow, OpenGL 1.4+
+        {
+            m_shadowMappingSupport = SMS_CORE;
+            GetLogger()->Info("Shadow mapping available (core)\n");
+        }
+        else if (glewIsSupported("GL_ARB_depth_texture GL_ARB_shadow"))  // ARB depth texture + shadow
+        {
+            m_shadowMappingSupport = SMS_ARB;
+            GetLogger()->Info("Shadow mapping available (ARB)\n");
+        }
+        else       // No Shadow mapping
+        {
+            m_shadowMappingSupport = SMS_NONE;
+            GetLogger()->Info("Shadow mapping not available\n");
+        }
+
         // Detect support of anisotropic filtering
         m_anisotropyAvailable = glewIsSupported("GL_EXT_texture_filter_anisotropic");
         if(m_anisotropyAvailable)
@@ -239,30 +265,24 @@ bool CGLDevice::Create()
         {
             GetLogger()->Info("Auto-detecting VBO support\n");
             
-            // extracting OpenGL version
-            const char *version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
-            int major = 0, minor = 0;
-
-            sscanf(version, "%d.%d", &major, &minor);
-            
             // detecting VBO ARB extension
             bool vboARB = glewIsSupported("GL_ARB_vertex_buffer_object");
 
             // VBO is core OpenGL feature since 1.5
             // everything below 1.5 means no VBO support
-            if(major > 1 || minor > 4)
+            if (m_glMajor > 1 || m_glMinor > 4)
             {
-                GetLogger()->Info("OpenGL %d.%d, VBO supported\n", major, minor);
+                GetLogger()->Info("Core VBO supported\n", m_glMajor, m_glMinor);
                 SetVertexBufferType(VBT_VBO_CORE);
             }
             else if(vboARB)     // VBO ARB extension available
             {
-                GetLogger()->Info("OpenGL %d.%d with GL_ARB_vertex_buffer_object, VBO supported\n", major, minor);
+                GetLogger()->Info("ARB VBO supported\n");
                 SetVertexBufferType(VBT_VBO_ARB);
             }
             else                // no VBO support
             {
-                GetLogger()->Info("OpenGL %d.%d  without GL_ARB_vertex_buffer_object, VBO not supported\n", major, minor);
+                GetLogger()->Info("VBO not supported\n");
                 SetVertexBufferType(VBT_DISPLAY_LIST);
             }
         }
@@ -275,9 +295,10 @@ bool CGLDevice::Create()
 
     // To avoid problems with scaling & lighting
     glEnable(GL_RESCALE_NORMAL);
+    //glEnable(GL_NORMALIZE);        // this needs some testing
 
     // Minimal depth bias to avoid Z-fighting
-    SetDepthBias(0.001f);
+    //SetDepthBias(0.001f);
 
     // Set just to be sure
     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -380,12 +401,6 @@ void CGLDevice::SetTransform(TransformType type, const Math::Matrix &matrix)
         glMatrixMode(GL_PROJECTION);
         glLoadMatrixf(m_projectionMat.Array());
     }
-    else if (type == TRANSFORM_TEXTURE)
-    {
-        m_textureMat = matrix;
-        glMatrixMode(GL_TEXTURE);
-        glLoadMatrixf(m_textureMat.Array());
-    }
     else
     {
         assert(false);
@@ -412,9 +427,9 @@ void CGLDevice::SetMaterial(const Material &material)
 {
     m_material = material;
 
-    glMaterialfv(GL_FRONT, GL_AMBIENT,  m_material.ambient.Array());
-    glMaterialfv(GL_FRONT, GL_DIFFUSE,  m_material.diffuse.Array());
-    glMaterialfv(GL_FRONT, GL_SPECULAR, m_material.specular.Array());
+    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,  m_material.ambient.Array());
+    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, m_material.diffuse.Array());
+    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, m_material.specular.Array());
 }
 
 int CGLDevice::GetMaxLightCount()
@@ -715,6 +730,81 @@ Texture CGLDevice::CreateTexture(ImageData *data, const TextureCreateParams &par
     return result;
 }
 
+Texture CGLDevice::CreateDepthTexture(int width, int height, int depth)
+{
+    Texture result;
+
+    if (m_shadowMappingSupport == SMS_NONE)
+    {
+        result.id = 0;
+        return result;
+    }
+    
+    result.alpha = false;
+    result.size.x = width;
+    result.size.y = height;
+
+    // Use & enable 1st texture stage
+    if (m_multitextureAvailable)
+        glActiveTexture(GL_TEXTURE0);
+
+    glGenTextures(1, &result.id);
+    glBindTexture(GL_TEXTURE_2D, result.id);
+
+    GLuint format = GL_DEPTH_COMPONENT;
+
+    if (m_shadowMappingSupport == SMS_CORE)
+    {
+        switch (depth)
+        {
+        case 16:
+            format = GL_DEPTH_COMPONENT16;
+            break;
+        case 24:
+            format = GL_DEPTH_COMPONENT24;
+            break;
+        case 32:
+            format = GL_DEPTH_COMPONENT32;
+            break;
+        }
+
+        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+    }
+    else
+    {
+        switch (depth)
+        {
+        case 16:
+            format = GL_DEPTH_COMPONENT16_ARB;
+            break;
+        case 24:
+            format = GL_DEPTH_COMPONENT24_ARB;
+            break;
+        case 32:
+            format = GL_DEPTH_COMPONENT32_ARB;
+            break;
+        }
+
+        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
+    }
+
+    float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
+
+    glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id);
+
+    return result;
+}
+
 void CGLDevice::DestroyTexture(const Texture &texture)
 {
     // Unbind the texture if in use anywhere
@@ -833,6 +923,60 @@ void CGLDevice::SetTextureStageParams(int index, const TextureStageParams &param
     UpdateTextureParams(index);
 }
 
+void CGLDevice::SetTextureCoordGeneration(int index, TextureGenerationParams &params)
+{
+    if (!m_multitextureAvailable && index != 0)
+        return;
+
+    if (m_multitextureAvailable)
+        glActiveTexture(GL_TEXTURE0 + index);
+
+    for (int i = 0; i < 4; i++)
+    {
+        GLuint texCoordGen = textureCoordGen[i];
+        GLuint texCoord = textureCoordinates[i];
+
+        if (params.coords[i].mode == TEX_GEN_NONE)
+        {
+            glDisable(texCoordGen);
+        }
+        else
+        {
+            glEnable(texCoordGen);
+
+            switch (params.coords[i].mode)
+            {
+            case TEX_GEN_OBJECT_LINEAR:
+                glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+                glTexGenfv(texCoord, GL_OBJECT_PLANE, params.coords[i].plane);
+                break;
+            case TEX_GEN_EYE_LINEAR:
+                glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
+                glTexGenfv(texCoord, GL_EYE_PLANE, params.coords[i].plane);
+                break;
+            case TEX_GEN_SPHERE_MAP:
+                glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
+                break;
+            case TEX_GEN_NORMAL_MAP:
+                glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
+                break;
+            case TEX_GEN_REFLECTION_MAP:
+                glTexGeni(texCoord, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
+                break;
+            }
+        }
+    }
+}
+
+void CGLDevice::SetTextureMatrix(int index, Math::Matrix& matrix)
+{
+    if (!m_multitextureAvailable && index != 0)
+        return;
+
+    glMatrixMode(GL_TEXTURE);
+    glLoadMatrixf(matrix.Array());
+}
+
 void CGLDevice::UpdateTextureParams(int index)
 {
     assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
@@ -851,12 +995,16 @@ void CGLDevice::UpdateTextureParams(int index)
 
     if      (params.wrapS == TEX_WRAP_CLAMP)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    else if (params.wrapS == TEX_WRAP_CLAMP_TO_BORDER)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
     else if (params.wrapS == TEX_WRAP_REPEAT)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     else  assert(false);
 
     if      (params.wrapT == TEX_WRAP_CLAMP)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    else if (params.wrapT == TEX_WRAP_CLAMP_TO_BORDER)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
     else if (params.wrapT == TEX_WRAP_REPEAT)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     else  assert(false);
@@ -987,15 +1135,19 @@ void CGLDevice::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wr
 
     if (m_multitextureAvailable)
         glActiveTexture(GL_TEXTURE0 + index);
-
+    
     if      (wrapS == TEX_WRAP_CLAMP)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    else if (wrapS == TEX_WRAP_CLAMP_TO_BORDER)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
     else if (wrapS == TEX_WRAP_REPEAT)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
     else  assert(false);
 
     if      (wrapT == TEX_WRAP_CLAMP)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    else if (wrapT == TEX_WRAP_CLAMP_TO_BORDER)
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
     else if (wrapT == TEX_WRAP_REPEAT)
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
     else  assert(false);
@@ -1546,6 +1698,11 @@ int CGLDevice::ComputeSphereVisibility(const Math::Vector &center, float radius)
     return result;
 }
 
+void CGLDevice::SetViewport(int x, int y, int width, int height)
+{
+    glViewport(x, y, width, height);
+}
+
 void CGLDevice::SetRenderState(RenderState state, bool enabled)
 {
     if (state == RENDER_STATE_DEPTH_WRITE)
@@ -1580,6 +1737,7 @@ void CGLDevice::SetRenderState(RenderState state, bool enabled)
         case RENDER_STATE_DEPTH_TEST:  flag = GL_DEPTH_TEST; break;
         case RENDER_STATE_ALPHA_TEST:  flag = GL_ALPHA_TEST; break;
         case RENDER_STATE_CULLING:     flag = GL_CULL_FACE; break;
+        case RENDER_STATE_DEPTH_BIAS:  flag = GL_POLYGON_OFFSET_FILL; break;
         default: assert(false); break;
     }
 
@@ -1623,14 +1781,19 @@ GLenum TranslateGfxCompFunc(CompFunc func)
     return 0;
 }
 
+void CGLDevice::SetColorMask(bool red, bool green, bool blue, bool alpha)
+{
+    glColorMask(red, green, blue, alpha);
+}
+
 void CGLDevice::SetDepthTestFunc(CompFunc func)
 {
     glDepthFunc(TranslateGfxCompFunc(func));
 }
 
-void CGLDevice::SetDepthBias(float factor)
+void CGLDevice::SetDepthBias(float factor, float units)
 {
-    glPolygonOffset(factor, 0.0f);
+    glPolygonOffset(factor, units);
 }
 
 void CGLDevice::SetAlphaTestFunc(CompFunc func, float refValue)
@@ -1731,6 +1894,21 @@ void CGLDevice::SetFillMode(FillMode mode)
     else assert(false);
 }
 
+void CGLDevice::CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height)
+{
+    if (texture.id == 0) return;
+
+    // Use & enable 1st texture stage
+    if (m_multitextureAvailable)
+        glActiveTexture(GL_TEXTURE0);
+
+    glBindTexture(GL_TEXTURE_2D, texture.id);
+    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height);
+
+    // Restore previous texture
+    glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id);
+}
+
 void* CGLDevice::GetFrameBufferPixels()const{
 
     GLubyte* pixels = new GLubyte[4 * m_config.size.x * m_config.size.y];
diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h
index e543f13..4ba1410 100644
--- a/src/graphics/opengl/gldevice.h
+++ b/src/graphics/opengl/gldevice.h
@@ -58,6 +58,13 @@ enum VertexBufferType
    VBT_VBO_ARB          //! use ARB extension VBOs
 };
 
+enum ShadowMappingSupport
+{
+    SMS_NONE,           //! No support for depth textures
+    SMS_ARB,            //! ARB extension
+    SMS_CORE            //! Core support
+};
+
 /**
  \struct GLDeviceConfig
  \brief Additional config with OpenGL-specific settings */
@@ -132,6 +139,7 @@ public:
 
     virtual Texture CreateTexture(CImage *image, const TextureCreateParams &params) OVERRIDE;
     virtual Texture CreateTexture(ImageData *data, const TextureCreateParams &params) OVERRIDE;
+    virtual Texture CreateDepthTexture(int width, int height, int depth) OVERRIDE;
     virtual void DestroyTexture(const Texture &texture) OVERRIDE;
     virtual void DestroyAllTextures() OVERRIDE;
 
@@ -143,6 +151,8 @@ public:
     virtual void SetTextureStageParams(int index, const TextureStageParams &params) OVERRIDE;
 
     virtual void SetTextureStageWrap(int index, Gfx::TexWrapMode wrapS, Gfx::TexWrapMode wrapT) OVERRIDE;
+    virtual void SetTextureCoordGeneration(int index, TextureGenerationParams &params) OVERRIDE;
+    virtual void SetTextureMatrix(int index, Math::Matrix& matrix) OVERRIDE;
 
     virtual void DrawPrimitive(PrimitiveType type, const Vertex *vertices    , int vertexCount,
                                Color color = Color(1.0f, 1.0f, 1.0f, 1.0f)) OVERRIDE;
@@ -161,11 +171,15 @@ public:
 
     virtual int ComputeSphereVisibility(const Math::Vector &center, float radius) OVERRIDE;
 
+    virtual void SetViewport(int x, int y, int width, int height) OVERRIDE;
+
     virtual void SetRenderState(RenderState state, bool enabled) OVERRIDE;
 
+    virtual void SetColorMask(bool red, bool green, bool blue, bool alpha) OVERRIDE;
+
     virtual void SetDepthTestFunc(CompFunc func) OVERRIDE;
 
-    virtual void SetDepthBias(float factor) OVERRIDE;
+    virtual void SetDepthBias(float factor, float units) OVERRIDE;
 
     virtual void SetAlphaTestFunc(CompFunc func, float refValue) OVERRIDE;
 
@@ -181,7 +195,9 @@ public:
 
     virtual void SetShadeModel(ShadeModel model) OVERRIDE;
 
-    virtual void SetFillMode(FillMode mode)  OVERRIDE;
+    virtual void SetFillMode(FillMode mode) OVERRIDE;
+
+    virtual void CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height) OVERRIDE;
 
     virtual void* GetFrameBufferPixels() const OVERRIDE;
 
@@ -205,8 +221,6 @@ private:
     Math::Matrix m_modelviewMat;
     //! Current projection matrix
     Math::Matrix m_projectionMat;
-    //! Current texture matrix
-    Math::Matrix m_textureMat;
 
     //! The current material
     Material m_material;
@@ -245,6 +259,11 @@ private:
         int vertexCount;
     };
 
+    //! Detected capabilities
+    //! OpenGL version
+    int m_glMajor, m_glMinor;
+    //! Depth texture support
+    ShadowMappingSupport m_shadowMappingSupport;
     //! Whether to use multitexturing
     bool m_multitextureAvailable;
     //! Whether to use VBOs or display lists

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/colobot.git



More information about the Pkg-games-commits mailing list