Parcourir la source

Started work on skinned decals.

Lasse Öörni il y a 13 ans
Parent
commit
6643bf7717

+ 1 - 1
Engine/Engine/MathAPI.cpp

@@ -78,7 +78,7 @@ static void RegisterMathFunctions(asIScriptEngine* engine)
     engine->RegisterGlobalFunction("float Acos(float)", asFUNCTION(Acos), asCALL_CDECL);
     engine->RegisterGlobalFunction("float Atan(float)", asFUNCTION(Atan), asCALL_CDECL);
     engine->RegisterGlobalFunction("float Atan2(float, float)", asFUNCTION(Atan2), asCALL_CDECL);
-    engine->RegisterGlobalFunction("float Abs(float)", asFUNCTION(fabsf), asCALL_CDECL);
+    engine->RegisterGlobalFunction("float Abs(float)", asFUNCTIONPR(Abs, (float), float), asCALL_CDECL);
     engine->RegisterGlobalFunction("float Sqrt(float)", asFUNCTION(sqrtf), asCALL_CDECL);
     engine->RegisterGlobalFunction("float Pow(float)", asFUNCTION(powf), asCALL_CDECL);
     engine->RegisterGlobalFunction("float Min(float, float)", asFUNCTIONPR(Min, (float, float), float), asCALL_CDECL);

+ 4 - 0
Engine/Graphics/AnimatedModel.cpp

@@ -384,7 +384,11 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
             }
         }
         else
+        {
             batches_[i].geometryType_ = GEOM_STATIC;
+            batches_[i].shaderData_ = 0;
+            batches_[i].shaderDataSize_ = 0;
+        }
     }
     
     MarkNetworkUpdate();

+ 1 - 1
Engine/Graphics/Camera.cpp

@@ -433,7 +433,7 @@ float Camera::GetDistance(const Vector3& worldPos) const
         return (worldPos - cameraPos).Length();
     }
     else
-        return fabsf((GetInverseWorldTransform() * worldPos).z_);
+        return Abs((GetInverseWorldTransform() * worldPos).z_);
 }
 
 float Camera::GetDistanceSquared(const Vector3& worldPos) const

+ 187 - 122
Engine/Graphics/DecalSet.cpp

@@ -22,6 +22,7 @@
 //
 
 #include "Precompiled.h"
+#include "AnimatedModel.h"
 #include "Batch.h"
 #include "Camera.h"
 #include "Context.h"
@@ -35,13 +36,14 @@
 #include "ResourceCache.h"
 #include "Scene.h"
 #include "SceneEvents.h"
-#include "StaticModel.h"
 #include "VertexBuffer.h"
 
 #include "DebugNew.h"
 
 static const Vector3 DOT_SCALE(1 / 3.0f, 1 / 3.0f, 1 / 3.0f);
-static const unsigned DEFAULT_MAX_VERTICES = 1024;
+static const unsigned DEFAULT_MAX_VERTICES = 512;
+static const unsigned UNSKINNED_ELEMENT_MASK = MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT;
+static const unsigned SKINNED_ELEMENT_MASK = MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT | MASK_BLENDWEIGHTS | MASK_BLENDINDICES;
 
 void Decal::CalculateBoundingBox()
 {
@@ -58,14 +60,14 @@ DecalSet::DecalSet(Context* context) :
     vertexBuffer_(new VertexBuffer(context_)),
     numVertices_(0),
     maxVertices_(DEFAULT_MAX_VERTICES),
+    skinned_(false),
     bufferSizeDirty_(true),
     bufferDirty_(true),
-    boundingBoxDirty_(true)
+    boundingBoxDirty_(true),
+    skinningDirty_(false)
 {
     drawableFlags_ = DRAWABLE_GEOMETRY;
     
-    geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
-    
     batches_.Resize(1);
     batches_[0].geometry_ = geometry_;
 }
@@ -102,6 +104,19 @@ void DecalSet::UpdateBatches(const FrameInfo& frame)
     
     batches_[0].distance_ = distance_;
     batches_[0].worldTransform_ = &worldTransform;
+    
+    if (skinMatrices_.Size())
+    {
+        batches_[0].geometryType_ = GEOM_SKINNED;
+        batches_[0].shaderData_ = skinMatrices_[0].Data();
+        batches_[0].shaderDataSize_ = skinMatrices_.Size() * 12;
+    }
+    else
+    {
+        batches_[0].geometryType_ = GEOM_STATIC;
+        batches_[0].shaderData_ = 0;
+        batches_[0].shaderDataSize_ = 0;
+    }
 }
 
 void DecalSet::UpdateGeometry(const FrameInfo& frame)
@@ -111,6 +126,9 @@ void DecalSet::UpdateGeometry(const FrameInfo& frame)
     
     if (bufferDirty_ || vertexBuffer_->IsDataLost())
         UpdateVertexBuffer();
+    
+    if (skinningDirty_)
+        UpdateSkinning();
 }
 
 UpdateGeometryType DecalSet::GetUpdateGeometryType()
@@ -154,6 +172,15 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
         return false;
     }
     
+    // Check for animated target and switch into skinned mode if necessary
+    AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target);
+    if ((animatedModel && !skinned_) || (!animatedModel && skinned_))
+    {
+        RemoveAllDecals();
+        skinned_ = animatedModel != 0;
+        bufferSizeDirty_ = true;
+    }
+    
     Matrix3x4 targetTransform = target->GetNode()->GetWorldTransform().Inverse();
     
     // Build the decal frustum
@@ -171,7 +198,7 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     Vector<PODVector<DecalVertex> > faces;
     PODVector<DecalVertex> tempFace;
     
-    // Special handling for static models, as they may use LOD: use the fixed software LOD level for decals
+    // Special handling for static/animated models, as they may use LOD: use the fixed software LOD level for decals
     StaticModel* staticModel = dynamic_cast<StaticModel*>(target);
     if (staticModel)
     {
@@ -196,7 +223,8 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
                 continue;
             
             tempFace.Resize(face.Size() + 2);
-            unsigned outVertices = ClipPolygon((float*)&face[0], (float*)&tempFace[0], face.Size(), sizeof(DecalVertex), decalFrustum.planes_[i]);
+            unsigned outVertices = ClipPolygon((float*)&face[0], (float*)&tempFace[0], face.Size(), sizeof(DecalVertex),
+                decalFrustum.planes_[i], skinned_ ? (sizeof(DecalVertex) - 4 * sizeof(float) - 4 * sizeof(unsigned char)) : 0);
             tempFace.Resize(outVertices);
             
             face = tempFace;
@@ -274,6 +302,15 @@ void DecalSet::RemoveAllDecals()
         numVertices_ = 0;
         MarkDecalsDirty();
     }
+    
+    // Remove all bones and skinning matrices
+    for (Vector<Bone>::Iterator i = bones_.Begin(); i != bones_.End(); ++i)
+    {
+        if (i->node_)
+            i->node_->RemoveListener(this);
+    }
+    bones_.Clear();
+    skinMatrices_.Clear();
 }
 
 Material* DecalSet::GetMaterial() const
@@ -352,12 +389,44 @@ VariantVector DecalSet::GetDecalsAttr() const
     return ret;
 }
 
-void DecalSet::OnWorldBoundingBoxUpdate()
+void DecalSet::OnMarkedDirty(Node* node)
 {
-    if (boundingBoxDirty_)
-        CalculateBoundingBox();
+    Drawable::OnMarkedDirty(node);
     
-    worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
+    if (skinned_)
+    {
+        // If the scene node or any of the bone nodes move, mark skinning dirty
+        skinningDirty_ = true;
+    }
+}
+
+void DecalSet::OnWorldBoundingBoxUpdate()
+{
+    if (!skinned_)
+    {
+        if (boundingBoxDirty_)
+            CalculateBoundingBox();
+        
+        worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform());
+    }
+    else
+    {
+        // If uses skinning, update world bounding box based on them
+        worldBoundingBox_.defined_ = false;
+        
+        for (Vector<Bone>::ConstIterator i = bones_.Begin(); i != bones_.End(); ++i)
+        {
+            Node* boneNode = i->node_;
+            if (!boneNode)
+                continue;
+            
+            // Use hitbox if available. If not, use only half of the sphere radius
+            if (i->collisionMask_ & BONECOLLISION_BOX)
+                worldBoundingBox_.Merge(i->boundingBox_.Transformed(boneNode->GetWorldTransform()));
+            else if (i->collisionMask_ & BONECOLLISION_SPHERE)
+                worldBoundingBox_.Merge(Sphere(boneNode->GetWorldPosition(), i->radius_ * 0.5f));
+        }
+    }
 }
 
 void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geometry, const Frustum& frustum, const Vector3& decalNormal, float normalCutoff)
@@ -380,8 +449,6 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geomet
     
     unsigned indexStart = geometry->GetIndexStart();
     unsigned indexCount = geometry->GetIndexCount();
-    bool hasNormals = (elementMask & MASK_NORMAL) != 0;
-    
     const unsigned char* srcData = (const unsigned char*)vertexData;
     
     // 16-bit indices
@@ -392,89 +459,69 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geomet
         
         while (indices < indicesEnd)
         {
-            const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
-            const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
-            const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
-            const Vector3& n0 = hasNormals ? *((const Vector3*)(&srcData[indices[0] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            const Vector3& n1 = hasNormals ? *((const Vector3*)(&srcData[indices[1] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            const Vector3& n2 = hasNormals ? *((const Vector3*)(&srcData[indices[2] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            
-            if (!hasNormals || decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) >= normalCutoff)
-            {
-                // First check coarsely against all planes to avoid adding unnecessary faces to the clipping algorithm
-                bool outside = false;
-                for (unsigned i = PLANE_FAR; i < NUM_FRUSTUM_PLANES; --i)
-                {
-                    const Plane& plane = frustum.planes_[i];
-                    if (plane.Distance(v0) < 0.0f && plane.Distance(v1) < 0.0f && plane.Distance(v2) < 0.0f)
-                    {
-                        outside = true;
-                        break;
-                    }
-                }
-                
-                if (!outside)
-                {
-                    faces.Resize(faces.Size() + 1);
-                    PODVector<DecalVertex>& face = faces.Back();
-                    face.Push(DecalVertex(v0, n0));
-                    face.Push(DecalVertex(v1, n1));
-                    face.Push(DecalVertex(v2, n2));
-                }
-            }
-            
+            GetFace(faces, srcData, indices[0], indices[1], indices[2], vertexSize, elementMask, frustum, decalNormal, normalCutoff);
             indices += 3;
         }
     }
-    // 32-bit indices
     else
+    // 32-bit indices
     {
         const unsigned* indices = ((const unsigned*)indexData) + indexStart;
         const unsigned* indicesEnd = indices + indexCount;
         
         while (indices < indicesEnd)
         {
-            const Vector3& v0 = *((const Vector3*)(&srcData[indices[0] * vertexSize]));
-            const Vector3& v1 = *((const Vector3*)(&srcData[indices[1] * vertexSize]));
-            const Vector3& v2 = *((const Vector3*)(&srcData[indices[2] * vertexSize]));
-            const Vector3& n0 = hasNormals ? *((const Vector3*)(&srcData[indices[0] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            const Vector3& n1 = hasNormals ? *((const Vector3*)(&srcData[indices[1] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            const Vector3& n2 = hasNormals ? *((const Vector3*)(&srcData[indices[2] * vertexSize + 3 * sizeof(float)])) :
-                Vector3::ZERO;
-            
-            if (!hasNormals || decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) >= normalCutoff)
-            {
-                bool outside = false;
-                for (unsigned i = PLANE_FAR; i < NUM_FRUSTUM_PLANES; --i)
-                {
-                    const Plane& plane = frustum.planes_[i];
-                    if (plane.Distance(v0) < 0.0f && plane.Distance(v1) < 0.0f && plane.Distance(v2) < 0.0f)
-                    {
-                        outside = true;
-                        break;
-                    }
-                }
-                
-                if (!outside)
-                {
-                    faces.Resize(faces.Size() + 1);
-                    PODVector<DecalVertex>& face = faces.Back();
-                    face.Push(DecalVertex(v0, n0));
-                    face.Push(DecalVertex(v1, n1));
-                    face.Push(DecalVertex(v2, n2));
-                }
-            }
-            
+            GetFace(faces, srcData, indices[0], indices[1], indices[2], vertexSize, elementMask, frustum, decalNormal, normalCutoff);
             indices += 3;
         }
     }
 }
 
+void DecalSet::GetFace(Vector<PODVector<DecalVertex> >& faces, const unsigned char* srcData, unsigned i0, unsigned i1, unsigned i2, unsigned vertexSize, unsigned elementMask, const Frustum& frustum, const Vector3& decalNormal, float normalCutoff)
+{
+    bool hasNormals = (elementMask & MASK_NORMAL) != 0;
+    bool hasSkinning = skinned_ && (elementMask & MASK_BLENDWEIGHTS) != 0;
+    unsigned normalOffset = VertexBuffer::GetElementOffset(elementMask, ELEMENT_NORMAL);
+    unsigned skinningOffset = VertexBuffer::GetElementOffset(elementMask, ELEMENT_BLENDWEIGHTS);
+    
+    const Vector3& v0 = *((const Vector3*)(&srcData[i0 * vertexSize]));
+    const Vector3& v1 = *((const Vector3*)(&srcData[i1 * vertexSize]));
+    const Vector3& v2 = *((const Vector3*)(&srcData[i2 * vertexSize]));
+    const Vector3& n0 = hasNormals ? *((const Vector3*)(&srcData[i0 * vertexSize + normalOffset])) : Vector3::ZERO;
+    const Vector3& n1 = hasNormals ? *((const Vector3*)(&srcData[i1 * vertexSize + normalOffset])) : Vector3::ZERO;
+    const Vector3& n2 = hasNormals ? *((const Vector3*)(&srcData[i2 * vertexSize + normalOffset])) : Vector3::ZERO;
+    const void* s0 = hasSkinning ? (const void*)(&srcData[i0 * vertexSize + skinningOffset]) : (const void*)0;
+    const void* s1 = hasSkinning ? (const void*)(&srcData[i0 * vertexSize + skinningOffset]) : (const void*)0;
+    const void* s2 = hasSkinning ? (const void*)(&srcData[i0 * vertexSize + skinningOffset]) : (const void*)0;
+    
+    // Check if face is too much away from the decal normal
+    if (hasNormals && decalNormal.DotProduct((n0 + n1 + n2) / 3.0f) < normalCutoff)
+        return;
+    
+    // Check if face is culled completely by any of the planes
+    for (unsigned i = PLANE_FAR; i < NUM_FRUSTUM_PLANES; --i)
+    {
+        const Plane& plane = frustum.planes_[i];
+        if (plane.Distance(v0) < 0.0f && plane.Distance(v1) < 0.0f && plane.Distance(v2) < 0.0f)
+            return;
+    }
+    
+    faces.Resize(faces.Size() + 1);
+    PODVector<DecalVertex>& face = faces.Back();
+    if (!hasSkinning)
+    {
+        face.Push(DecalVertex(v0, n0));
+        face.Push(DecalVertex(v1, n1));
+        face.Push(DecalVertex(v2, n2));
+    }
+    else
+    {
+        face.Push(DecalVertex(v0, n0, s0));
+        face.Push(DecalVertex(v1, n1, s1));
+        face.Push(DecalVertex(v2, n2, s2));
+    }
+}
+
 void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV)
 {
     Matrix4 projection(Matrix4::ZERO);
@@ -506,46 +553,46 @@ void DecalSet::CalculateTangents(Decal& decal)
 {
     for (unsigned i = 0; i < decal.vertices_.Size(); i += 3)
     {
-        // Tangent generation from
+        // Tangent generation from
         // http://www.terathon.com/code/tangent.html
-        const Vector3& v1 = decal.vertices_[i].position_;
-        const Vector3& v2 = decal.vertices_[i + 1].position_;
-        const Vector3& v3 = decal.vertices_[i + 2].position_;
-        
-        const Vector2& w1 = decal.vertices_[i].texCoord_;
-        const Vector2& w2 = decal.vertices_[i + 1].texCoord_;
-        const Vector2& w3 = decal.vertices_[i + 2].texCoord_;
-        
-        float x1 = v2.x_ - v1.x_;
-        float x2 = v3.x_ - v1.x_;
-        float y1 = v2.y_ - v1.y_;
-        float y2 = v3.y_ - v1.y_;
-        float z1 = v2.z_ - v1.z_;
-        float z2 = v3.z_ - v1.z_;
-        
-        float s1 = w2.x_ - w1.x_;
-        float s2 = w3.x_ - w1.x_;
-        float t1 = w2.y_ - w1.y_;
-        float t2 = w3.y_ - w1.y_;
-        
-        float r = 1.0f / (s1 * t2 - s2 * t1);
-        Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
-                (t2 * z1 - t1 * z2) * r);
-        Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
-                (s1 * z2 - s2 * z1) * r);
+        const Vector3& v1 = decal.vertices_[i].position_;
+        const Vector3& v2 = decal.vertices_[i + 1].position_;
+        const Vector3& v3 = decal.vertices_[i + 2].position_;
+        
+        const Vector2& w1 = decal.vertices_[i].texCoord_;
+        const Vector2& w2 = decal.vertices_[i + 1].texCoord_;
+        const Vector2& w3 = decal.vertices_[i + 2].texCoord_;
+        
+        float x1 = v2.x_ - v1.x_;
+        float x2 = v3.x_ - v1.x_;
+        float y1 = v2.y_ - v1.y_;
+        float y2 = v3.y_ - v1.y_;
+        float z1 = v2.z_ - v1.z_;
+        float z2 = v3.z_ - v1.z_;
+        
+        float s1 = w2.x_ - w1.x_;
+        float s2 = w3.x_ - w1.x_;
+        float t1 = w2.y_ - w1.y_;
+        float t2 = w3.y_ - w1.y_;
+        
+        float r = 1.0f / (s1 * t2 - s2 * t1);
+        Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+                (t2 * z1 - t1 * z2) * r);
+        Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+                (s1 * z2 - s2 * z1) * r);
         
         for (unsigned j = i; j < i + 3; ++j)
         {
-            const Vector3& n = decal.vertices_[j].normal_;
-            Vector3 xyz;
-            float w;
-            
-            // Gram-Schmidt orthogonalize
-            xyz = (sdir - n * n.DotProduct(sdir)).Normalized();
-            
-            // Calculate handedness
-            w = n.CrossProduct(sdir).DotProduct(tdir) < 0.0f ? -1.0f : 1.0f;
-            
+            const Vector3& n = decal.vertices_[j].normal_;
+            Vector3 xyz;
+            float w;
+            
+            // Gram-Schmidt orthogonalize
+            xyz = (sdir - n * n.DotProduct(sdir)).Normalized();
+            
+            // Calculate handedness
+            w = n.CrossProduct(sdir).DotProduct(tdir) < 0.0f ? -1.0f : 1.0f;
+            
             decal.vertices_[j].tangent_ = Vector4(xyz, w);
         }
     }
@@ -588,12 +635,13 @@ void DecalSet::CalculateBoundingBox()
 
 void DecalSet::UpdateBufferSize()
 {
-    if (vertexBuffer_->GetVertexCount() != maxVertices_)
-    {
-        vertexBuffer_->SetSize(maxVertices_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
-        bufferDirty_ = true;
-    }
+    if (!skinned_)
+        vertexBuffer_->SetSize(maxVertices_, UNSKINNED_ELEMENT_MASK);
+    else
+        vertexBuffer_->SetSize(maxVertices_, SKINNED_ELEMENT_MASK);
     
+    geometry_->SetVertexBuffer(0, vertexBuffer_);
+    bufferDirty_ = true;
     bufferSizeDirty_ = false;
 }
 
@@ -631,6 +679,23 @@ void DecalSet::UpdateVertexBuffer()
     bufferDirty_ = false;
 }
 
+void DecalSet::UpdateSkinning()
+{
+    // Use model's world transform in case a bone is missing
+    const Matrix3x4& worldTransform = node_->GetWorldTransform();
+    
+    for (unsigned i = 0; i < bones_.Size(); ++i)
+    {
+        const Bone& bone = bones_[i];
+        if (bone.node_)
+            skinMatrices_[i] = bone.node_->GetWorldTransform() * bone.offsetMatrix_;
+        else
+            skinMatrices_[i] = worldTransform;
+    }
+    
+    skinningDirty_ = false;
+}
+
 void DecalSet::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
 {
     using namespace ScenePostUpdate;

+ 34 - 0
Engine/Graphics/DecalSet.h

@@ -26,6 +26,7 @@
 #include "Drawable.h"
 #include "Frustum.h"
 #include "List.h"
+#include "Skeleton.h"
 
 /// Decal vertex.
 struct DecalVertex
@@ -42,6 +43,21 @@ struct DecalVertex
     {
     }
     
+    /// Construct with position, normal and skinning information
+    DecalVertex(const Vector3& position, const Vector3& normal, const void* skinningData) :
+        position_(position),
+        normal_(normal)
+    {
+        const float* blendWeights = (const float*)skinningData;
+        const unsigned char* blendIndices = ((const unsigned char*)skinningData) + 4 * sizeof(float);
+        
+        for (unsigned i = 0; i < 4; ++i)
+        {
+            blendWeights_[i] = blendWeights[i];
+            blendIndices_[i] = blendIndices[i];
+        }
+    }
+    
     /// Position.
     Vector3 position_;
     /// Normal.
@@ -50,6 +66,10 @@ struct DecalVertex
     Vector2 texCoord_;
     /// Tangent.
     Vector4 tangent_;
+    /// Blend weights.
+    float blendWeights_[4];
+    /// Blend indices.
+    unsigned char blendIndices_[4];
 };
 
 /// One decal in a decal set.
@@ -128,10 +148,14 @@ class DecalSet : public Drawable
 protected:
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
+    /// Handle node transform being dirtied.
+    virtual void OnMarkedDirty(Node* node);
     
 private:
     /// Get triangle faces from the target geometry.
     void GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geometry, const Frustum& frustum, const Vector3& decalNormal, float normalCutoff);
+    /// Get triangle face from the target geometry.
+    void GetFace(Vector<PODVector<DecalVertex> >& faces, const unsigned char* srcData, unsigned i0, unsigned i1, unsigned i2, unsigned vertexSize, unsigned elementMask, const Frustum& frustum, const Vector3& decalNormal, float normalCutoff);
     /// 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);
     /// Calculate tangents for the decal.
@@ -148,6 +172,8 @@ private:
     void UpdateBufferSize();
     /// Rewrite decal vertex buffer.
     void UpdateVertexBuffer();
+    /// Recalculate skinning.
+    void UpdateSkinning();
     /// Handle scene post-update event.
     void HandleScenePostUpdate(StringHash eventType, VariantMap& eventData);
     
@@ -157,16 +183,24 @@ private:
     SharedPtr<VertexBuffer> vertexBuffer_;
     /// Decals.
     List<Decal> decals_;
+    /// Bones used for skinned decals.
+    Vector<Bone> bones_;
+    /// Skinning matrices.
+    PODVector<Matrix3x4> skinMatrices_;
     /// Local-space bounding box.
     BoundingBox boundingBox_;
     /// Vertices in the current decals.
     unsigned numVertices_;
     /// Maximum vertices.
     unsigned maxVertices_;
+    /// Skinned mode flag.
+    bool skinned_;
     /// Buffer needs resize flag.
     bool bufferSizeDirty_;
     /// Vertex buffer needs rewrite flag.
     bool bufferDirty_;
     /// Bounding box needs update flag.
     bool boundingBoxDirty_;
+    /// Skinning dirty flag.
+    bool skinningDirty_;
 };

+ 18 - 2
Engine/Graphics/Direct3D9/D3D9VertexBuffer.cpp

@@ -368,19 +368,35 @@ unsigned long long VertexBuffer::GetBufferHash(unsigned streamIndex, unsigned us
     return bufferHash;
 }
 
-unsigned VertexBuffer::GetVertexSize(unsigned mask)
+unsigned VertexBuffer::GetVertexSize(unsigned elementMask)
 {
     unsigned vertexSize = 0;
     
     for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
     {
-        if (mask & (1 << i))
+        if (elementMask & (1 << i))
             vertexSize += elementSize[i];
     }
     
     return vertexSize;
 }
 
+unsigned VertexBuffer::GetElementOffset(unsigned elementMask, VertexElement element)
+{
+    unsigned offset = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (i == element)
+            break;
+        
+        if (elementMask & (1 << i))
+            offset += elementSize[i];
+    }
+    
+    return offset;
+}
+
 bool VertexBuffer::Create()
 {
     Release();

+ 2 - 0
Engine/Graphics/Direct3D9/D3D9VertexBuffer.h

@@ -79,6 +79,8 @@ public:
     
     /// Return vertex size corresponding to a vertex element mask.
     static unsigned GetVertexSize(unsigned elementMask);
+    /// Return element offset from an element mask.
+    static unsigned GetElementOffset(unsigned elementMask, VertexElement element);
     
     /// Vertex element sizes.
     static const unsigned elementSize[];

+ 18 - 2
Engine/Graphics/OpenGL/OGLVertexBuffer.cpp

@@ -375,19 +375,35 @@ void VertexBuffer::UpdateOffsets()
     vertexSize_ = elementOffset;
 }
 
-unsigned VertexBuffer::GetVertexSize(unsigned mask)
+unsigned VertexBuffer::GetVertexSize(unsigned elementMask)
 {
     unsigned vertexSize = 0;
     
     for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
     {
-        if (mask & (1 << i))
+        if (elementMask & (1 << i))
             vertexSize += elementSize[i];
     }
     
     return vertexSize;
 }
 
+unsigned VertexBuffer::GetElementOffset(unsigned elementMask, VertexElement element)
+{
+    unsigned offset = 0;
+    
+    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
+    {
+        if (i == element)
+            break;
+        
+        if (elementMask & (1 << i))
+            offset += elementSize[i];
+    }
+    
+    return offset;
+}
+
 bool VertexBuffer::Create()
 {
     if (!vertexCount_ || !elementMask_)

+ 3 - 0
Engine/Graphics/OpenGL/OGLVertexBuffer.h

@@ -74,8 +74,11 @@ public:
     unsigned GetElementOffset(VertexElement element) const { return elementOffset_[element]; }
     /// Return CPU memory shadow data.
     unsigned char* GetShadowData() const { return shadowData_.Get(); }
+    
     /// Return vertex size corresponding to a vertex element mask.
     static unsigned GetVertexSize(unsigned elementMask);
+    /// Return element offset from an element mask.
+    static unsigned GetElementOffset(unsigned elementMask, VertexElement element);
     
     /// Vertex element sizes in bytes.
     static const unsigned elementSize[];

+ 3 - 3
Engine/Graphics/View.cpp

@@ -228,7 +228,7 @@ void CheckVisibilityWork(const WorkItem* item, unsigned threadIndex)
                 float viewCenterZ = viewMatrix.m20_ * center.x_ + viewMatrix.m21_ * center.y_ + viewMatrix.m22_ * center.z_ +
                     viewMatrix.m23_;
                 Vector3 edge = geomBox.Size() * 0.5f;
-                float viewEdgeZ = fabsf(viewMatrix.m20_) * edge.x_ + fabsf(viewMatrix.m21_) * edge.y_ + fabsf(viewMatrix.m22_) *
+                float viewEdgeZ = Abs(viewMatrix.m20_) * edge.x_ + Abs(viewMatrix.m21_) * edge.y_ + Abs(viewMatrix.m22_) *
                     edge.z_;
                 
                 drawable->SetMinMaxZ(viewCenterZ - viewEdgeZ, viewCenterZ + viewEdgeZ);
@@ -2123,8 +2123,8 @@ void View::FinalizeShadowCamera(Camera* shadowCamera, Light* light, const IntRec
     {
         if (parameters.focus_)
         {
-            float viewSizeX = Max(fabsf(shadowCasterBox.min_.x_), fabsf(shadowCasterBox.max_.x_));
-            float viewSizeY = Max(fabsf(shadowCasterBox.min_.y_), fabsf(shadowCasterBox.max_.y_));
+            float viewSizeX = Max(Abs(shadowCasterBox.min_.x_), Abs(shadowCasterBox.max_.x_));
+            float viewSizeY = Max(Abs(shadowCasterBox.min_.y_), Abs(shadowCasterBox.max_.y_));
             float viewSize = Max(viewSizeX, viewSizeY);
             // Scale the quantization parameters, because view size is in projection space (-1.0 - 1.0)
             float invOrthoSize = 1.0f / shadowCamera->GetOrthoSize();

+ 12 - 12
Engine/Math/BoundingBox.cpp

@@ -129,9 +129,9 @@ void BoundingBox::Transform(const Matrix3& transform)
     Vector3 newCenter = transform * Center();
     Vector3 oldEdge = Size() * 0.5f;
     Vector3 newEdge = Vector3(
-        fabsf(transform.m00_) * oldEdge.x_ + fabsf(transform.m01_) * oldEdge.y_ + fabsf(transform.m02_) * oldEdge.z_,
-        fabsf(transform.m10_) * oldEdge.x_ + fabsf(transform.m11_) * oldEdge.y_ + fabsf(transform.m12_) * oldEdge.z_,
-        fabsf(transform.m20_) * oldEdge.x_ + fabsf(transform.m21_) * oldEdge.y_ + fabsf(transform.m22_) * oldEdge.z_
+        Abs(transform.m00_) * oldEdge.x_ + Abs(transform.m01_) * oldEdge.y_ + Abs(transform.m02_) * oldEdge.z_,
+        Abs(transform.m10_) * oldEdge.x_ + Abs(transform.m11_) * oldEdge.y_ + Abs(transform.m12_) * oldEdge.z_,
+        Abs(transform.m20_) * oldEdge.x_ + Abs(transform.m21_) * oldEdge.y_ + Abs(transform.m22_) * oldEdge.z_
     );
     
     min_ = newCenter - newEdge;
@@ -143,9 +143,9 @@ void BoundingBox::Transform(const Matrix3x4& transform)
     Vector3 newCenter = transform * Center();
     Vector3 oldEdge = Size() * 0.5f;
     Vector3 newEdge = Vector3(
-        fabsf(transform.m00_) * oldEdge.x_ + fabsf(transform.m01_) * oldEdge.y_ + fabsf(transform.m02_) * oldEdge.z_,
-        fabsf(transform.m10_) * oldEdge.x_ + fabsf(transform.m11_) * oldEdge.y_ + fabsf(transform.m12_) * oldEdge.z_,
-        fabsf(transform.m20_) * oldEdge.x_ + fabsf(transform.m21_) * oldEdge.y_ + fabsf(transform.m22_) * oldEdge.z_
+        Abs(transform.m00_) * oldEdge.x_ + Abs(transform.m01_) * oldEdge.y_ + Abs(transform.m02_) * oldEdge.z_,
+        Abs(transform.m10_) * oldEdge.x_ + Abs(transform.m11_) * oldEdge.y_ + Abs(transform.m12_) * oldEdge.z_,
+        Abs(transform.m20_) * oldEdge.x_ + Abs(transform.m21_) * oldEdge.y_ + Abs(transform.m22_) * oldEdge.z_
     );
     
     min_ = newCenter - newEdge;
@@ -157,9 +157,9 @@ BoundingBox BoundingBox::Transformed(const Matrix3& transform) const
     Vector3 newCenter = transform * Center();
     Vector3 oldEdge = Size() * 0.5f;
     Vector3 newEdge = Vector3(
-        fabsf(transform.m00_) * oldEdge.x_ + fabsf(transform.m01_) * oldEdge.y_ + fabsf(transform.m02_) * oldEdge.z_,
-        fabsf(transform.m10_) * oldEdge.x_ + fabsf(transform.m11_) * oldEdge.y_ + fabsf(transform.m12_) * oldEdge.z_,
-        fabsf(transform.m20_) * oldEdge.x_ + fabsf(transform.m21_) * oldEdge.y_ + fabsf(transform.m22_) * oldEdge.z_
+        Abs(transform.m00_) * oldEdge.x_ + Abs(transform.m01_) * oldEdge.y_ + Abs(transform.m02_) * oldEdge.z_,
+        Abs(transform.m10_) * oldEdge.x_ + Abs(transform.m11_) * oldEdge.y_ + Abs(transform.m12_) * oldEdge.z_,
+        Abs(transform.m20_) * oldEdge.x_ + Abs(transform.m21_) * oldEdge.y_ + Abs(transform.m22_) * oldEdge.z_
     );
     
     return BoundingBox(newCenter - newEdge, newCenter + newEdge);
@@ -170,9 +170,9 @@ BoundingBox BoundingBox::Transformed(const Matrix3x4& transform) const
     Vector3 newCenter = transform * Center();
     Vector3 oldEdge = Size() * 0.5f;
     Vector3 newEdge = Vector3(
-        fabsf(transform.m00_) * oldEdge.x_ + fabsf(transform.m01_) * oldEdge.y_ + fabsf(transform.m02_) * oldEdge.z_,
-        fabsf(transform.m10_) * oldEdge.x_ + fabsf(transform.m11_) * oldEdge.y_ + fabsf(transform.m12_) * oldEdge.z_,
-        fabsf(transform.m20_) * oldEdge.x_ + fabsf(transform.m21_) * oldEdge.y_ + fabsf(transform.m22_) * oldEdge.z_
+        Abs(transform.m00_) * oldEdge.x_ + Abs(transform.m01_) * oldEdge.y_ + Abs(transform.m02_) * oldEdge.z_,
+        Abs(transform.m10_) * oldEdge.x_ + Abs(transform.m11_) * oldEdge.y_ + Abs(transform.m12_) * oldEdge.z_,
+        Abs(transform.m20_) * oldEdge.x_ + Abs(transform.m21_) * oldEdge.y_ + Abs(transform.m22_) * oldEdge.z_
     );
     
     return BoundingBox(newCenter - newEdge, newCenter + newEdge);

+ 28 - 7
Engine/Math/GeometryUtils.cpp

@@ -25,12 +25,32 @@
 #include "GeometryUtils.h"
 #include "Plane.h"
 
-inline void ClipEdge(float* output, float* v0, float* v1, float d0, float d1, unsigned components)
+inline void ClipEdge(float* output, float* v0, float* v1, float d0, float d1, unsigned components, unsigned blendWeightsComp)
 {
     float t = d0 / (d0 - d1);
     
-    for (unsigned i = 0; i < components; ++i)
-        output[i] = v0[i] + t * (v1[i] - v0[i]);
+    if (!blendWeightsComp)
+    {
+       for (unsigned i = 0; i < components; ++i)
+            output[i] = v0[i] + t * (v1[i] - v0[i]);
+    }
+    else
+    {
+        for (unsigned i = 0; i < blendWeightsComp; ++i)
+            output[i] = v0[i] + t * (v1[i] - v0[i]);
+        
+        // Blend weights / indices can not be easily interpolated, choose the one that is nearer to the split plane
+        if (Abs(d0) < Abs(d1))
+        {
+            for (unsigned i = blendWeightsComp; i < components; ++i)
+                output[i] = v0[i];
+        }
+        else
+        {
+            for (unsigned i = blendWeightsComp; i < components; ++i)
+                output[i] = v1[i];
+        }
+    }
 }
 
 inline void CopyVertex(float* output, float* input, unsigned components)
@@ -39,9 +59,10 @@ inline void CopyVertex(float* output, float* input, unsigned components)
         output[i] = input[i];
 }
 
-unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned vertexSize, const Plane& plane, float* clip, unsigned* clipVertexCount)
+unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned vertexSize, const Plane& plane, unsigned blendWeightsOffset, float* clip, unsigned* clipVertexCount)
 {
     unsigned components = vertexSize / sizeof(float);
+    unsigned blendWeightsComp = blendWeightsOffset / sizeof(float);
     unsigned outVertexCount = 0;
     float* lastVertex = input;
     float lastDistance = 0.0f;
@@ -58,7 +79,7 @@ unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned
         {
             if (lastDistance < 0.0f)
             {
-                ClipEdge(output, lastVertex, vertex, lastDistance, distance, components);
+                ClipEdge(output, lastVertex, vertex, lastDistance, distance, components, blendWeightsComp);
                 if (clip && clipVertexCount)
                 {
                     CopyVertex(clip, output, components);
@@ -78,7 +99,7 @@ unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned
         {
             if (lastDistance >= 0.0f && i != 0)
             {
-                ClipEdge(output, lastVertex, vertex, lastDistance, distance, components);
+                ClipEdge(output, lastVertex, vertex, lastDistance, distance, components, blendWeightsComp);
                 if (clip && clipVertexCount)
                 {
                     CopyVertex(clip, output, components);
@@ -101,7 +122,7 @@ unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned
         float distance = plane.Distance(position);
         if ((lastDistance < 0.0f && distance >= 0.0f) || (lastDistance >= 0.0f && distance < 0.0f))
         {
-            ClipEdge(output, lastVertex, vertex, lastDistance, distance, components);
+            ClipEdge(output, lastVertex, vertex, lastDistance, distance, components, blendWeightsComp);
             if (clip && clipVertexCount)
             {
                 CopyVertex(clip, output, components);

+ 2 - 2
Engine/Math/GeometryUtils.h

@@ -25,6 +25,6 @@
 
 class Plane;
 
-/// Clip a convex polygon with a plane. Output may be two vertices more than input. Vertices can have additional float values which will be interpolated. Return number of output vertices. Optionally put clipped vertices into a separate output array.
-unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned vertexSize, const Plane& plane, float* clip = 0, unsigned* clipVertexCount = 0);
+/// Clip a polygon with a plane. Output may have up to 2x vertices of input. Vertices can have additional float values which will be interpolated, and/or blend weights which will just be copied from the nearer vertex. Optionally put clipped vertices into a separate output array. Return number of output vertices.
+unsigned ClipPolygon(float* input, float* output, unsigned vertexCount, unsigned vertexSize, const Plane& plane, unsigned blendWeightsOffset = 0, float* clip = 0, unsigned* clipVertexCount = 0);
 

+ 4 - 0
Engine/Math/MathDefs.h

@@ -60,6 +60,8 @@ inline float Lerp(float lhs, float rhs, float t) { return lhs * (1.0f - t) + rhs
 inline float Min(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; }
 /// Return the larger of two floats.
 inline float Max(float lhs, float rhs) { return lhs > rhs ? lhs : rhs; }
+/// Return absolute value of a float.
+inline float Abs(float value) { return value >= 0.0f ? value : -value; }
 
 /// Clamp a float to a range.
 inline float Clamp(float value, float min, float max)
@@ -78,6 +80,8 @@ inline bool Equals(float lhs, float rhs) { return lhs + M_EPSILON >= rhs && lhs
 inline int Min(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; }
 /// Return the larger of two integers.
 inline int Max(int lhs, int rhs) { return lhs > rhs ? lhs : rhs; }
+/// Return absolute value of an integer
+inline int Abs(int value) { return value >= 0 ? value : -value; }
 
 /// Clamp an integer to a range.
 inline int Clamp(int value, int min, int max)

+ 2 - 2
Engine/Math/Plane.h

@@ -60,7 +60,7 @@ public:
         Vector3 dist2 = v2 - v0;
         
         normal_ = (dist1.CrossProduct(dist2)).Normalized();
-        absNormal_ = Vector3(fabsf(normal_.x_), fabsf(normal_.y_), fabsf(normal_.z_));
+        absNormal_ = Vector3(Abs(normal_.x_), Abs(normal_.y_), Abs(normal_.z_));
         intercept_ = normal_.DotProduct(v0);
     }
 
@@ -68,7 +68,7 @@ public:
     void Define(const Vector3& normal, const Vector3& point)
     {
         normal_ = normal;
-        absNormal_ = Vector3(fabsf(normal_.x_), fabsf(normal_.y_), fabsf(normal_.z_));
+        absNormal_ = Vector3(Abs(normal_.x_), Abs(normal_.y_), Abs(normal_.z_));
         intercept_ = normal_.DotProduct(point);
     }
     

+ 2 - 2
Engine/Math/Polyhedron.cpp

@@ -106,11 +106,11 @@ void Polyhedron::Clip(const Plane& plane)
         if (face.Empty())
             continue;
         
-        outFace_.Resize(face.Size() + 2);
+        outFace_.Resize(face.Size() * 2);
         
         unsigned outClippedVertices = 0;
         unsigned outVertices = ClipPolygon((float*)&face[0], (float*)&outFace_[0], face.Size(), sizeof(Vector3), plane,
-            (float*)&clippedVertices_[totalClippedVertices], &outClippedVertices);
+            0, (float*)&clippedVertices_[totalClippedVertices], &outClippedVertices);
         totalClippedVertices += outClippedVertices;
         
         outFace_.Resize(outVertices);

+ 2 - 2
Engine/Math/Ray.cpp

@@ -54,7 +54,7 @@ Vector3 Ray::ClosestPoint(const Ray& ray) const
     float d2121 = p21.DotProduct(p21);
     
     float d = d2121 * d4343 - d4321 * d4321;
-    if (fabsf(d) < M_EPSILON)
+    if (Abs(d) < M_EPSILON)
         return origin_;
     float n = d1343 * d4321 - d1321 * d4343;
     float a = n / d;
@@ -65,7 +65,7 @@ Vector3 Ray::ClosestPoint(const Ray& ray) const
 float Ray::HitDistance(const Plane& plane) const
 {
     float d = plane.normal_.DotProduct(direction_);
-    if (fabsf(d) >= M_EPSILON)
+    if (Abs(d) >= M_EPSILON)
     {
         float t = -(plane.normal_.DotProduct(origin_) - plane.intercept_) / d;
         if (t >= 0.0f)

+ 2 - 2
Engine/Math/Vector2.h

@@ -153,9 +153,9 @@ public:
     /// Calculate dot product.
     float DotProduct(const Vector2& rhs) const { return x_ * rhs.x_ + y_ * rhs.y_; }
     /// Calculate absolute dot product.
-    float AbsDotProduct(const Vector2& rhs) const { return fabsf(x_ * rhs.x_) + fabsf(y_ * rhs.y_); }
+    float AbsDotProduct(const Vector2& rhs) const { return ::Abs(x_ * rhs.x_) + ::Abs(y_ * rhs.y_); }
     /// Return absolute vector.
-    Vector2 Abs() const { return Vector2(fabsf(x_), fabsf(y_)); }
+    Vector2 Abs() const { return Vector2(::Abs(x_), ::Abs(y_)); }
     /// Linear interpolation with another vector.
     Vector2 Lerp(const Vector2& rhs, float t) const { return *this * (1.0f - t) + rhs * t; }
     ///// Test for equality with another vectir with epsilon.

+ 2 - 2
Engine/Math/Vector3.h

@@ -171,7 +171,7 @@ public:
     /// Calculate dot product.
     float DotProduct(const Vector3& rhs) const { return x_ * rhs.x_ + y_ * rhs.y_ + z_ * rhs.z_; }
     /// Calculate absolute dot product.
-    float AbsDotProduct(const Vector3& rhs) const { return fabsf(x_ * rhs.x_) + fabsf(y_ * rhs.y_) + fabsf(z_ * rhs.z_); }
+    float AbsDotProduct(const Vector3& rhs) const { return ::Abs(x_ * rhs.x_) + ::Abs(y_ * rhs.y_) + ::Abs(z_ * rhs.z_); }
     
     /// Calculate cross product.
     Vector3 CrossProduct(const Vector3& rhs) const
@@ -184,7 +184,7 @@ public:
     }
     
     /// Return absolute vector.
-    Vector3 Abs() const { return Vector3(fabsf(x_), fabsf(y_), fabsf(z_)); }
+    Vector3 Abs() const { return Vector3(::Abs(x_), ::Abs(y_), ::Abs(z_)); }
     /// Linear interpolation with another vector.
     Vector3 Lerp(const Vector3& rhs, float t) const { return *this * (1.0f - t) + rhs * t; }
     /// Test for equality with another vector with epsilon.

+ 2 - 2
Engine/Math/Vector4.h

@@ -163,9 +163,9 @@ public:
     /// Calculate dot product.
     float DotProduct(const Vector4& rhs) const { return x_ * rhs.x_ + y_ * rhs.y_ + z_ * rhs.z_ + w_ * rhs.w_; }
     /// Calculate absolute dot product.
-    float AbsDotProduct(const Vector4& rhs) const { return fabsf(x_ * rhs.x_) + fabsf(y_ * rhs.y_) + fabsf(z_ * rhs.z_) + fabsf(w_ * rhs.w_); }
+    float AbsDotProduct(const Vector4& rhs) const { return ::Abs(x_ * rhs.x_) + ::Abs(y_ * rhs.y_) + ::Abs(z_ * rhs.z_) + ::Abs(w_ * rhs.w_); }
     /// Return absolute vector.
-    Vector4 Abs() const { return Vector4(fabsf(x_), fabsf(y_), fabsf(z_), fabsf(w_)); }
+    Vector4 Abs() const { return Vector4(::Abs(x_), ::Abs(y_), ::Abs(z_), ::Abs(w_)); }
     /// Linear interpolation with another vector.
     Vector4 Lerp(const Vector4& rhs, float t) const { return *this * (1.0f - t) + rhs * t; }
     /// Test for equality with another vector with epsilon.