Browse Source

Allow defining depth bias for materials.
Center decal frustum on the decal world position.
Use material depth bias for decals instead of applying the bias to decal geometry.

Lasse Öörni 13 years ago
parent
commit
478a5af9d1

+ 1 - 0
Bin/Data/Materials/UrhoDecal.xml

@@ -2,4 +2,5 @@
     <technique name="Techniques/DiffAdd.xml" />
     <texture unit="diffuse" name="Textures/UrhoDecal.dds" />
     <parameter name="MatDiffColor" value="1 1 0 1" />
+    <depthbias constant="-0.00001" slopescaled="0" />
 </material>

+ 2 - 2
Bin/Data/Scripts/TestScene.as

@@ -429,8 +429,8 @@ void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
                         decal.maxVertices = 2048;
                         decal.maxIndices = 4096;
                     }
-                    decal.AddDecal(result.drawable, rayHitPos - cameraNode.worldRotation * Vector3(0, 0, 0.5),
-                        cameraNode.worldRotation, 0.5, 1.0, 1.0, Vector2(0, 0), Vector2(1, 1));
+                    decal.AddDecal(result.drawable, rayHitPos, cameraNode.worldRotation, 0.5, 1.0, 1.0, Vector2(0, 0),
+                        Vector2(1, 1));
                 }
             }
         }

+ 2 - 2
Bin/Data/Scripts/TestSceneOld.as

@@ -542,8 +542,8 @@ void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
                         decal.maxVertices = 2048;
                         decal.maxIndices = 4096;
                     }
-                    decal.AddDecal(result.drawable, rayHitPos - cameraNode.worldRotation * Vector3(0, 0, 0.5),
-                        cameraNode.worldRotation, 0.5, 1.0, 1.0, Vector2(0, 0), Vector2(1, 1));
+                    decal.AddDecal(result.drawable, rayHitPos, cameraNode.worldRotation, 0.5, 1.0, 1.0, Vector2(0, 0),
+                        Vector2(1, 1));
                 }
             }
         }

+ 2 - 1
Docs/Reference.dox

@@ -569,6 +569,7 @@ A material definition looks like this:
     <parameter ... />
     <cull value="cw|ccw|none" />
     <shadowcull value="cw|ccw|none" />
+    <depthbias constant="x" slopescaled="y" />
 </material>
 \endcode
 
@@ -584,7 +585,7 @@ When a material defines several techniques for different LOD levels and quality
 
 %Material shader parameters can be floats or vectors up to 4 components. Matrix parameters are not supported.
 
-Default culling mode is counterclockwise. The shadowcull element specifies the culling mode to use in the shadow pass.
+Default culling mode is counterclockwise. The shadowcull element specifies the culling mode to use in the shadow pass. Note that material's depth bias settings do not apply in the shadow pass; during shadow rendering the light's depth bias is used instead.
 
 \section Materials_Textures Material textures
 

+ 25 - 23
Engine/Engine/GraphicsAPI.cpp

@@ -285,8 +285,30 @@ static Material* MaterialClone(const String& cloneName, Material* ptr)
     return clone.Get();
 }
 
+static void ConstructBiasParameters(BiasParameters* ptr)
+{
+    new(ptr) BiasParameters(0.0f, 0.0f);
+}
+
+static void ConstructBiasParametersCopy(BiasParameters& parameters, BiasParameters* ptr)
+{
+    new(ptr) BiasParameters(parameters);
+}
+
+static void ConstructBiasParametersInit(float constantBias, float slopeScaledBias, BiasParameters* ptr)
+{
+    new(ptr) BiasParameters(constantBias, slopeScaledBias);
+}
+
 static void RegisterMaterial(asIScriptEngine* engine)
 {
+    engine->RegisterObjectType("BiasParameters", sizeof(BiasParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
+    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructBiasParameters), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f(const BiasParameters&in)", asFUNCTION(ConstructBiasParametersCopy), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f(float, float)", asFUNCTION(ConstructBiasParametersInit), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectProperty("BiasParameters", "float constantBias", offsetof(BiasParameters, constantBias_));
+    engine->RegisterObjectProperty("BiasParameters", "float slopeScaledBias", offsetof(BiasParameters, slopeScaledBias_));
+    
     engine->RegisterEnum("TextureUnit");
     engine->RegisterEnumValue("TextureUnit", "TU_DIFFUSE", TU_DIFFUSE);
     engine->RegisterEnumValue("TextureUnit", "TU_NORMAL", TU_NORMAL);
@@ -367,6 +389,8 @@ static void RegisterMaterial(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Material", "CullMode get_cullMode() const", asMETHOD(Material, GetCullMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "void set_shadowCullMode(CullMode)", asMETHOD(Material, SetShadowCullMode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Material", "CullMode get_shadowCullMode() const", asMETHOD(Material, GetShadowCullMode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Material", "void set_depthBias(const BiasParameters&in)", asMETHOD(Material, SetDepthBias), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Material", "const BiasParameters& get_depthBias() const", asMETHOD(Material, GetDepthBias), asCALL_THISCALL);
 }
 
 static PostProcess* PostProcessClone(PostProcess* ptr)
@@ -440,21 +464,6 @@ static void RegisterDrawable(asIScriptEngine* engine)
     RegisterDrawable<Drawable>(engine, "Drawable");
 }
 
-static void ConstructBiasParameters(BiasParameters* ptr)
-{
-    new(ptr) BiasParameters(0.0f, 0.0f);
-}
-
-static void ConstructBiasParametersCopy(BiasParameters& parameters, BiasParameters* ptr)
-{
-    new(ptr) BiasParameters(parameters);
-}
-
-static void ConstructBiasParametersInit(float constantBias, float slopeScaledBias, BiasParameters* ptr)
-{
-    new(ptr) BiasParameters(constantBias, slopeScaledBias);
-}
-
 static void ConstructCascadeParameters(CascadeParameters* ptr)
 {
     new(ptr) CascadeParameters(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
@@ -492,13 +501,6 @@ static void RegisterLight(asIScriptEngine* engine)
     engine->RegisterEnumValue("LightType", "LIGHT_SPOT", LIGHT_SPOT);
     engine->RegisterEnumValue("LightType", "LIGHT_POINT", LIGHT_POINT);
     
-    engine->RegisterObjectType("BiasParameters", sizeof(BiasParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
-    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructBiasParameters), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f(const BiasParameters&in)", asFUNCTION(ConstructBiasParametersCopy), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectBehaviour("BiasParameters", asBEHAVE_CONSTRUCT, "void f(float, float)", asFUNCTION(ConstructBiasParametersInit), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectProperty("BiasParameters", "float constantBias", offsetof(BiasParameters, constantBias_));
-    engine->RegisterObjectProperty("BiasParameters", "float slopeScaledBias", offsetof(BiasParameters, slopeScaledBias_));
-    
     engine->RegisterObjectType("CascadeParameters", sizeof(CascadeParameters), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_C);
     engine->RegisterObjectBehaviour("CascadeParameters", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ConstructCascadeParameters), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectBehaviour("CascadeParameters", asBEHAVE_CONSTRUCT, "void f(const CascadeParameters&in)", asFUNCTION(ConstructCascadeParametersCopy), asCALL_CDECL_OBJLAST);
@@ -756,7 +758,7 @@ static void RegisterParticleEmitter(asIScriptEngine* engine)
 static void RegisterDecalSet(asIScriptEngine* engine)
 {
     RegisterDrawable<DecalSet>(engine, "DecalSet");
-    engine->RegisterObjectMethod("DecalSet", "bool AddDecal(Drawable@+, const Vector3&in, const Quaternion&in, float, float, float, const Vector2&in, const Vector2&in, float timeToLive = 0.0, float normalCutoff = 0.1, float depthBias = 0.001, uint subGeometry = 0xffffffff)", asMETHOD(DecalSet, AddDecal), asCALL_THISCALL);
+    engine->RegisterObjectMethod("DecalSet", "bool AddDecal(Drawable@+, const Vector3&in, const Quaternion&in, float, float, float, const Vector2&in, const Vector2&in, float timeToLive = 0.0, float normalCutoff = 0.1, uint subGeometry = 0xffffffff)", asMETHOD(DecalSet, AddDecal), asCALL_THISCALL);
     engine->RegisterObjectMethod("DecalSet", "void RemoveDecals(uint)", asMETHOD(DecalSet, RemoveDecals), asCALL_THISCALL);
     engine->RegisterObjectMethod("DecalSet", "void RemoveAllDecals()", asMETHOD(DecalSet, RemoveAllDecals), asCALL_THISCALL);
     engine->RegisterObjectMethod("DecalSet", "void set_material(Material@+)", asMETHOD(DecalSet, SetMaterial), asCALL_THISCALL);

+ 10 - 4
Engine/Graphics/Batch.cpp

@@ -26,7 +26,7 @@
 #include "Geometry.h"
 #include "Graphics.h"
 #include "GraphicsImpl.h"
-#include "Light.h"
+#include "Material.h"
 #include "Node.h"
 #include "Renderer.h"
 #include "Profiler.h"
@@ -67,7 +67,7 @@ static void SortFrontToBack2Pass(PODVector<Batch*>& batches)
     Sort(batches.Begin(), batches.End(), CompareBatchesFrontToBack);
     
     // Then rewrite distances so that different states will be ordered front to back, and sort again.
-    // Do not do this on mobile devices as they likely use a tiled deferred approach, with which 
+    // Do not do this on mobile devices as they likely use a tiled deferred approach, with which
     // front-to-back sorting is irrelevant
     #ifndef GL_ES_VERSION_2_0
     float lastDistance;
@@ -205,9 +205,15 @@ void Batch::Prepare(Graphics* graphics, Renderer* renderer, bool setModelTransfo
     // Set pass / material-specific renderstates
     if (pass_ && material_)
     {
+        bool isShadowPass = pass_->GetType() == PASS_SHADOW;
+        
         graphics->SetBlendMode(pass_->GetBlendMode());
-        renderer->SetCullMode(pass_->GetType() != PASS_SHADOW ? material_->GetCullMode() : material_->GetShadowCullMode(),
-            camera_);
+        renderer->SetCullMode(isShadowPass ? material_->GetShadowCullMode() : material_->GetCullMode(), camera_);
+        if (!isShadowPass)
+        {
+            const BiasParameters& depthBias = material_->GetDepthBias();
+            graphics->SetDepthBias(depthBias.constantBias_, depthBias.slopeScaledBias_);
+        }
         graphics->SetDepthTest(pass_->GetDepthTestMode());
         graphics->SetDepthWrite(pass_->GetDepthWrite());
     }

+ 1 - 0
Engine/Graphics/BillboardSet.cpp

@@ -29,6 +29,7 @@
 #include "Geometry.h"
 #include "Graphics.h"
 #include "IndexBuffer.h"
+#include "Material.h"
 #include "MemoryBuffer.h"
 #include "Node.h"
 #include "Profiler.h"

+ 38 - 30
Engine/Graphics/DecalSet.cpp

@@ -31,6 +31,7 @@
 #include "Graphics.h"
 #include "IndexBuffer.h"
 #include "Log.h"
+#include "Material.h"
 #include "MemoryBuffer.h"
 #include "Node.h"
 #include "Profiler.h"
@@ -276,7 +277,7 @@ void DecalSet::SetMaxIndices(unsigned num)
 
 bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size,
     float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff,
-    float depthBias, unsigned subGeometry)
+    unsigned subGeometry)
 {
     PROFILE(AddDecal);
     
@@ -298,6 +299,8 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
         bufferSizeDirty_ = true;
     }
     
+    // Center the decal frustum on the world position
+    Vector3 adjustedWorldPosition = worldPosition - 0.5f * depth * (worldRotation * Vector3::FORWARD);
     Matrix3x4 targetTransform = target->GetNode()->GetWorldTransform().Inverse();
     
     // For an animated model, adjust the decal position back to the bind pose
@@ -316,9 +319,9 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
                 continue;
             
             // Represent the decal as a sphere, try to find the biggest colliding bone
-            Sphere decalSphere(bone->node_->GetWorldTransform().Inverse() * (worldPosition + worldRotation * (0.5f * depth *
-                Vector3::FORWARD)), 0.5f * size / bone->node_->GetWorldScale().Length());
-            float distance = (worldPosition - bone->node_->GetWorldPosition()).Length();
+            Sphere decalSphere(bone->node_->GetWorldTransform().Inverse() * adjustedWorldPosition, 0.5f * size /
+                bone->node_->GetWorldScale().Length());
+            float distance = (adjustedWorldPosition - bone->node_->GetWorldPosition()).Length();
             
             if (bone->collisionMask_ & BONECOLLISION_BOX)
             {
@@ -350,10 +353,9 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     
     // Build the decal frustum
     Frustum decalFrustum;
-    Matrix3x4 frustumTransform = targetTransform * Matrix3x4(worldPosition, worldRotation, 1.0f);
+    Matrix3x4 frustumTransform = targetTransform * Matrix3x4(adjustedWorldPosition, worldRotation, 1.0f);
     decalFrustum.DefineOrtho(size, aspectRatio, 1.0, 0.0f, depth, frustumTransform);
     
-    Vector3 biasVector = targetTransform * Vector4(worldRotation * (Vector3::BACK * depthBias), 0.0f);
     Vector3 decalNormal = (targetTransform * Vector4(worldRotation * Vector3::BACK, 0.0f)).Normalized();
     
     decals_.Resize(decals_.Size() + 1);
@@ -425,10 +427,18 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
         return false;
     }
     
-    // Finally transform vertices to this node's local space
+    // Calculate UVs
+    Matrix4 projection(Matrix4::ZERO);
+    projection.m11_ = (1.0f / (size * 0.5f));
+    projection.m00_ = projection.m11_ / aspectRatio;
+    projection.m22_ = 1.0f / depth;
+    projection.m33_ = 1.0f;
+    
+    CalculateUVs(newDecal, frustumTransform.Inverse(), projection, topLeftUV, bottomRightUV);
+    
+    // Transform vertices to this node's local space and generate tangents
     Matrix3x4 decalTransform = node_->GetWorldTransform().Inverse() * target->GetNode()->GetWorldTransform();
-    CalculateUVs(newDecal, frustumTransform.Inverse(), size, aspectRatio, depth, topLeftUV, bottomRightUV);
-    TransformVertices(newDecal, skinned_ ? Matrix3x4::IDENTITY : decalTransform, biasVector);
+    TransformVertices(newDecal, skinned_ ? Matrix3x4::IDENTITY : decalTransform);
     GenerateTangents(&newDecal.vertices_[0], sizeof(DecalVertex), &newDecal.indices_[0], sizeof(unsigned short), 0,
         newDecal.indices_.Size(), offsetof(DecalVertex, normal_), offsetof(DecalVertex, texCoord_), offsetof(DecalVertex,
         tangent_));
@@ -782,15 +792,26 @@ void DecalSet::GetFace(Vector<PODVector<DecalVertex> >& faces, Drawable* target,
     const Vector3& v0 = *((const Vector3*)(&positionData[i0 * positionStride]));
     const Vector3& v1 = *((const Vector3*)(&positionData[i1 * positionStride]));
     const Vector3& v2 = *((const Vector3*)(&positionData[i2 * positionStride]));
-    const Vector3& n0 = hasNormals ? *((const Vector3*)(&normalData[i0 * normalStride])) : Vector3::ZERO;
-    const Vector3& n1 = hasNormals ? *((const Vector3*)(&normalData[i1 * normalStride])) : Vector3::ZERO;
-    const Vector3& n2 = hasNormals ? *((const Vector3*)(&normalData[i2 * normalStride])) : Vector3::ZERO;
+    
+    // Calculate unsmoothed face normals if no normal data
+    Vector3 faceNormal = Vector3::ZERO;
+    if (!hasNormals)
+    {
+        Vector3 dist1 = v1 - v0;
+        Vector3 dist2 = v2 - v0;
+        faceNormal = (dist1.CrossProduct(dist2)).Normalized();
+    }
+    
+    const Vector3& n0 = hasNormals ? *((const Vector3*)(&normalData[i0 * normalStride])) : faceNormal;
+    const Vector3& n1 = hasNormals ? *((const Vector3*)(&normalData[i1 * normalStride])) : faceNormal;
+    const Vector3& n2 = hasNormals ? *((const Vector3*)(&normalData[i2 * normalStride])) : faceNormal;
+    
     const unsigned char* s0 = hasSkinning ? &skinningData[i0 * skinningStride] : (const unsigned char*)0;
     const unsigned char* s1 = hasSkinning ? &skinningData[i1 * skinningStride] : (const unsigned char*)0;
     const unsigned char* s2 = hasSkinning ? &skinningData[i2 * skinningStride] : (const unsigned char*)0;
     
     // Check if face is too much away from the decal normal
-    if (hasNormals && decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) < normalCutoff)
+    if (decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) < normalCutoff)
         return;
     
     // Check if face is culled completely by any of the planes
@@ -915,22 +936,9 @@ bool DecalSet::GetBones(Drawable* target, unsigned batchIndex, const float* blen
     return true;
 }
 
-void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, float size, float aspectRatio, float depth,
-    const Vector2& topLeftUV, const Vector2& bottomRightUV)
+void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, const Matrix4& projection, const Vector2& topLeftUV,
+    const Vector2& bottomRightUV)
 {
-    Matrix4 projection(Matrix4::ZERO);
-    
-    float h = (1.0f / (size * 0.5f));
-    float w = h / aspectRatio;
-    float q = 1.0f / depth;
-    float r = 0.0f;
-    
-    projection.m00_ = w;
-    projection.m11_ = h;
-    projection.m22_ = q;
-    projection.m23_ = r;
-    projection.m33_ = 1.0f;
-    
     Matrix4 viewProj = projection * view;
     
     for (PODVector<DecalVertex>::Iterator i = decal.vertices_.Begin(); i != decal.vertices_.End(); ++i)
@@ -943,11 +951,11 @@ void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, float size, flo
     }
 }
 
-void DecalSet::TransformVertices(Decal& decal, const Matrix3x4& transform, const Vector3& biasVector)
+void DecalSet::TransformVertices(Decal& decal, const Matrix3x4& transform)
 {
     for (PODVector<DecalVertex>::Iterator i = decal.vertices_.Begin(); i != decal.vertices_.End(); ++i)
     {
-        i->position_ = transform * (i->position_ + biasVector);
+        i->position_ = transform * i->position_;
         i->normal_ = (transform * i->normal_).Normalized();
     }
 }

+ 3 - 3
Engine/Graphics/DecalSet.h

@@ -126,7 +126,7 @@ class DecalSet : public Drawable
     /// %Set maximum number of decal vertex indices.
     void SetMaxIndices(unsigned num);
     /// Add a decal at world coordinates, using an existing drawable's geometry for reference. Return true if successful.
-    bool AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive = 0.0f, float normalCutoff = 0.1f, float depthBias = 0.001f, unsigned subGeometry = M_MAX_UNSIGNED);
+    bool AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive = 0.0f, float normalCutoff = 0.1f, unsigned subGeometry = M_MAX_UNSIGNED);
     /// Remove n oldest decals.
     void RemoveDecals(unsigned num);
     /// Remove all decals.
@@ -168,9 +168,9 @@ private:
     /// Get bones referenced by skinning data and remap the skinning indices. Return true if successful.
     bool GetBones(Drawable* target, unsigned batchIndex, const float* blendWeights, const unsigned char* blendIndices, unsigned char* newBlendIndices);
     /// Calculate UV coordinates for the decal.
-    void CalculateUVs(Decal& decal, const Matrix3x4& view, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV);
+    void CalculateUVs(Decal& decal, const Matrix3x4& view, const Matrix4& projection, const Vector2& topLeftUV, const Vector2& bottomRightUV);
     /// Transform decal's vertices from the target geometry to the decal set local space.
-    void TransformVertices(Decal& decal, const Matrix3x4& transform, const Vector3& biasVector);
+    void TransformVertices(Decal& decal, const Matrix3x4& transform);
     /// Remove a decal by iterator and return iterator to the next decal.
     List<Decal>::Iterator RemoveDecal(List<Decal>::Iterator i);
     /// Mark decals and the bounding box dirty.

+ 1 - 1
Engine/Graphics/Direct3D9/D3D9Graphics.cpp

@@ -33,8 +33,8 @@
 #include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "IndexBuffer.h"
-#include "Light.h"
 #include "Log.h"
+#include "Material.h"
 #include "Octree.h"
 #include "ParticleEmitter.h"
 #include "ProcessUtils.h"

+ 16 - 1
Engine/Graphics/Drawable.cpp

@@ -25,7 +25,7 @@
 #include "Camera.h"
 #include "Context.h"
 #include "DebugRenderer.h"
-#include "Light.h"
+#include "Material.h"
 #include "Octree.h"
 #include "Scene.h"
 #include "Sort.h"
@@ -35,6 +35,21 @@
 
 static const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
 
+SourceBatch::SourceBatch() :
+    distance_(0.0f),
+    geometry_(0),
+    worldTransform_(&Matrix3x4::IDENTITY),
+    shaderData_(0),
+    shaderDataSize_(0),
+    geometryType_(GEOM_STATIC),
+    overrideView_(false)
+{
+}
+
+SourceBatch::~SourceBatch()
+{
+}
+
 OBJECTTYPESTATIC(Drawable);
 
 Drawable::Drawable(Context* context) :

+ 4 - 11
Engine/Graphics/Drawable.h

@@ -26,7 +26,6 @@
 #include "BoundingBox.h"
 #include "Component.h"
 #include "GraphicsDefs.h"
-#include "Material.h"
 
 static const unsigned DRAWABLE_GEOMETRY = 0x1;
 static const unsigned DRAWABLE_LIGHT = 0x2;
@@ -42,6 +41,7 @@ static const int MAX_VERTEX_LIGHTS = 4;
 class Camera;
 class Geometry;
 class Light;
+class Material;
 class OcclusionBuffer;
 class Octant;
 class RayOctreeQuery;
@@ -74,16 +74,9 @@ struct FrameInfo
 struct SourceBatch
 {
     /// Construct with defaults.
-    SourceBatch() :
-        distance_(0.0f),
-        geometry_(0),
-        worldTransform_(&Matrix3x4::IDENTITY),
-        shaderData_(0),
-        shaderDataSize_(0),
-        geometryType_(GEOM_STATIC),
-        overrideView_(false)
-    {
-    }
+    SourceBatch();
+    /// Destruct.
+    ~SourceBatch();
     
     /// Distance from camera.
     float distance_;

+ 2 - 2
Engine/Graphics/Light.cpp

@@ -56,8 +56,8 @@ static const char* typeNames[] =
 
 void BiasParameters::Validate()
 {
-    constantBias_ = Clamp(constantBias_, 0.0f, 1.0f);
-    slopeScaledBias_ = Clamp(slopeScaledBias_, 0.0f, 16.0f);
+    constantBias_ = Clamp(constantBias_, -1.0f, 1.0f);
+    slopeScaledBias_ = Clamp(slopeScaledBias_, -16.0f, 16.0f);
 }
 
 void CascadeParameters::Validate()

+ 17 - 1
Engine/Graphics/Material.cpp

@@ -95,9 +95,10 @@ OBJECTTYPESTATIC(Material);
 
 Material::Material(Context* context) :
     Resource(context),
-    auxViewFrameNumber_(0),
     cullMode_(CULL_CCW),
     shadowCullMode_(CULL_CCW),
+    depthBias_(BiasParameters(0.0f, 0.0f)),
+    auxViewFrameNumber_(0),
     occlusion_(true)
 {
     SetNumTechniques(1);
@@ -202,6 +203,10 @@ bool Material::Load(Deserializer& source)
     if (shadowCullElem)
         SetShadowCullMode((CullMode)GetStringListIndex(shadowCullElem.GetAttribute("value"), cullModeNames, CULL_CCW));
     
+    XMLElement depthBiasElem = rootElem.GetChild("depthbias");
+    if (depthBiasElem)
+        SetDepthBias(BiasParameters(depthBiasElem.GetFloat("constant"), depthBiasElem.GetFloat("slopescaled")));
+    
     // Calculate memory use
     unsigned memoryUse = sizeof(Material);
     
@@ -260,6 +265,11 @@ bool Material::Save(Serializer& dest)
     XMLElement shadowCullElem = materialElem.CreateChild("shadowcull");
     shadowCullElem.SetString("value", cullModeNames[shadowCullMode_]);
     
+    // Write depth bias
+    XMLElement depthBiasElem = materialElem.CreateChild("depthbias");
+    depthBiasElem.SetFloat("constant", depthBias_.constantBias_);
+    depthBiasElem.SetFloat("slopescaled", depthBias_.slopeScaledBias_);
+    
     return xml->Save(dest);
 }
 
@@ -340,6 +350,12 @@ void Material::SetShadowCullMode(CullMode mode)
     shadowCullMode_ = mode;
 }
 
+void Material::SetDepthBias(const BiasParameters& parameters)
+{
+    depthBias_ = parameters;
+    depthBias_.Validate();
+}
+
 void Material::RemoveShaderParameter(const String& name)
 {
     shaderParameters_.Erase(StringHash(name));

+ 9 - 2
Engine/Graphics/Material.h

@@ -25,6 +25,7 @@
 
 #include "GraphicsDefs.h"
 #include "HashMap.h"
+#include "Light.h"
 #include "Resource.h"
 #include "Vector4.h"
 
@@ -95,6 +96,8 @@ public:
     void SetCullMode(CullMode mode);
     /// %Set culling mode for shadows.
     void SetShadowCullMode(CullMode mode);
+    /// %Set depth bias.
+    void SetDepthBias(const BiasParameters& parameters);
     /// Remove shader parameter.
     void RemoveShaderParameter(const String& name);
     /// Reset all shader pointers.
@@ -126,6 +129,8 @@ public:
     CullMode GetCullMode() const { return cullMode_; }
     /// Return culling mode for shadows.
     CullMode GetShadowCullMode() const { return shadowCullMode_; }
+    /// Return depth bias.
+    const BiasParameters& GetDepthBias() const { return depthBias_; }
     /// Return last auxiliary view rendered frame number.
     unsigned GetAuxViewFrameNumber() const { return auxViewFrameNumber_; }
     /// Return whether should render occlusion.
@@ -148,12 +153,14 @@ private:
     SharedPtr<Texture> textures_[MAX_MATERIAL_TEXTURE_UNITS];
     /// %Shader parameters.
     HashMap<StringHash, MaterialShaderParameter> shaderParameters_;
-    /// Last auxiliary view rendered frame number.
-    unsigned auxViewFrameNumber_;
     /// Normal culling mode.
     CullMode cullMode_;
     /// Culling mode for shadow rendering.
     CullMode shadowCullMode_;
+    /// Depth bias parameters.
+    BiasParameters depthBias_;
+    /// Last auxiliary view rendered frame number.
+    unsigned auxViewFrameNumber_;
     /// Render occlusion flag.
     bool occlusion_;
     /// Specular lighting flag.

+ 1 - 1
Engine/Graphics/OpenGL/OGLGraphics.cpp

@@ -34,8 +34,8 @@
 #include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "IndexBuffer.h"
-#include "Light.h"
 #include "Log.h"
+#include "Material.h"
 #include "Mutex.h"
 #include "Octree.h"
 #include "ParticleEmitter.h"

+ 0 - 1
Engine/Graphics/Renderer.cpp

@@ -30,7 +30,6 @@
 #include "GraphicsEvents.h"
 #include "GraphicsImpl.h"
 #include "IndexBuffer.h"
-#include "Light.h"
 #include "Log.h"
 #include "Material.h"
 #include "OcclusionBuffer.h"

+ 1 - 0
Engine/Graphics/StaticModel.cpp

@@ -27,6 +27,7 @@
 #include "Context.h"
 #include "Geometry.h"
 #include "Log.h"
+#include "Material.h"
 #include "Model.h"
 #include "OcclusionBuffer.h"
 #include "OctreeQuery.h"

+ 1 - 20
Engine/Graphics/TerrainPatch.cpp

@@ -26,6 +26,7 @@
 #include "Context.h"
 #include "Geometry.h"
 #include "IndexBuffer.h"
+#include "Material.h"
 #include "Node.h"
 #include "OcclusionBuffer.h"
 #include "OctreeQuery.h"
@@ -278,26 +279,6 @@ Terrain* TerrainPatch::GetOwner() const
     return owner_;
 }
 
-TerrainPatch* TerrainPatch::GetNorthPatch() const
-{
-    return north_;
-}
-
-TerrainPatch* TerrainPatch::GetSouthPatch() const
-{
-    return south_;
-}
-
-TerrainPatch* TerrainPatch::GetWestPatch() const
-{
-    return west_;
-}
-
-TerrainPatch* TerrainPatch::GetEastPatch() const
-{
-    return east_;
-}
-
 void TerrainPatch::OnWorldBoundingBoxUpdate()
 {
     worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());

+ 4 - 4
Engine/Graphics/TerrainPatch.h

@@ -77,13 +77,13 @@ public:
     /// Return owner terrain.
     Terrain* GetOwner() const;
     /// Return north neighbor patch.
-    TerrainPatch* GetNorthPatch() const;
+    TerrainPatch* GetNorthPatch() const { return north_; }
     /// Return south neighbor patch.
-    TerrainPatch* GetSouthPatch() const;
+    TerrainPatch* GetSouthPatch() const { return south_; }
     /// Return west neighbor patch.
-    TerrainPatch* GetWestPatch() const;
+    TerrainPatch* GetWestPatch() const { return west_; }
     /// Return east neighbor patch.
-    TerrainPatch* GetEastPatch() const;
+    TerrainPatch* GetEastPatch() const { return east_; }
     /// Return local-space bounding box.
     const BoundingBox& GetBoundingBox() const { return boundingBox_; }
     /// Return patch coordinates.

+ 4 - 2
Engine/Graphics/View.cpp

@@ -27,8 +27,8 @@
 #include "Geometry.h"
 #include "Graphics.h"
 #include "GraphicsImpl.h"
-#include "Light.h"
 #include "Log.h"
+#include "Material.h"
 #include "OcclusionBuffer.h"
 #include "Octree.h"
 #include "Renderer.h"
@@ -475,9 +475,10 @@ void View::Render()
     camera_->SetFlipVertical(false);
     #endif
     
-    graphics_->SetViewTexture(0);
+    graphics_->SetDepthBias(0.0f, 0.0f);
     graphics_->SetScissorTest(false);
     graphics_->SetStencilTest(false);
+    graphics_->SetViewTexture(0);
     graphics_->ResetStreamFrequencies();
     
     // Run post-processes or framebuffer blitting now
@@ -2433,6 +2434,7 @@ void View::SetupLightVolumeBatch(Batch& batch)
     
     // Use replace blend mode for the first pre-pass light volume, and additive for the rest
     graphics_->SetBlendMode(renderMode_ == RENDER_PREPASS && light == lightQueues_.Front().light_ ? BLEND_REPLACE : BLEND_ADD);
+    graphics_->SetDepthBias(0.0f, 0.0f);
     graphics_->SetDepthWrite(false);
     
     if (type != LIGHT_DIRECTIONAL)