Преглед изворни кода

Calculate tangents for decals.

Lasse Öörni пре 13 година
родитељ
комит
296e36a75b

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

@@ -343,7 +343,7 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
                 drawDebug = 0;
         }
 
-        if (key == 'C')
+        if (key == 'O')
             camera.orthographic = !camera.orthographic;
 
         if (key == 'B')
@@ -465,13 +465,13 @@ void HandlePostRenderUpdate()
             testScene.debugRenderer.AddBoundingBox(BoundingBox(rayHitPos + Vector3(-0.01, -0.01, -0.01), rayHitPos +
                 Vector3(0.01, 0.01, 0.01)), Color(1.0, 1.0, 1.0), true);
 
-            if (input.keyPress['H'])
+            if (input.keyPress['P'])
             {
                 DecalSet@ decal = result.drawable.node.GetComponent("DecalSet");
                 if (decal is null)
                 {
                     decal = result.drawable.node.CreateComponent("DecalSet");
-                    decal.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+                    decal.material = cache.GetResource("Material", "Materials/Test.xml");
                 }
                 decal.AddDecal(result.drawable, rayHitPos - cameraNode.worldRotation * Vector3(0, 0, 0.1), cameraNode.worldRotation, 0.1, 1.0, 0.2, Vector2(0, 0), Vector2(1, 1));
             }

+ 13 - 9
Bin/Data/Scripts/TestSceneOld.as

@@ -441,11 +441,8 @@ void HandleKeyDown(StringHash eventType, VariantMap& eventData)
             else
                 cameraLightNode.parent = testScene;
         }
-        
-        if (key == 'V')
-            cameraLight.perVertex = !cameraLight.perVertex;
 
-        if (key == 'C')
+        if (key == 'O')
             camera.orthographic = !camera.orthographic;
 
         if (key == 'B')
@@ -557,11 +554,18 @@ void HandlePostRenderUpdate()
             Vector3 rayHitPos = cameraRay.origin + cameraRay.direction * result.distance;
             testScene.debugRenderer.AddBoundingBox(BoundingBox(rayHitPos + Vector3(-0.01, -0.01, -0.01), rayHitPos +
                 Vector3(0.01, 0.01, 0.01)), Color(1.0, 1.0, 1.0), true);
-                
-            // Test creating a ragdoll
-            if (input.keyPress['R'] && result.drawable.typeName == "AnimatedModel")
-                CreateRagdoll(result.drawable);
         }
+        
+        if (input.keyPress['P'])
+        {
+            DecalSet@ decal = result.drawable.node.GetComponent("DecalSet");
+            if (decal is null)
+            {
+                decal = result.drawable.node.CreateComponent("DecalSet");
+                decal.material = cache.GetResource("Material", "Materials/Test.xml");
+            }
+            decal.AddDecal(result.drawable, rayHitPos - cameraNode.worldRotation * Vector3(0, 0, 0.1), cameraNode.worldRotation, 0.1, 1.0, 0.2, Vector2(0, 0), Vector2(1, 1));
+        }        
     }
 }
 
@@ -588,7 +592,7 @@ void HandlePhysicsCollision(StringHash eventType, VariantMap& eventData)
 void CreateRagdoll(AnimatedModel@ model)
 {
     Node@ root = model.node;
-    
+
     CreateRagdollBone(root, "Bip01_Pelvis", SHAPE_CAPSULE, Vector3(0.3, 0.3, 0.3), Vector3(0.0, 0, 0), Quaternion(0, 0, 0));
     CreateRagdollBone(root, "Bip01_Spine1", SHAPE_CAPSULE, Vector3(0.3, 0.4, 0.3), Vector3(0.15, 0, 0), Quaternion(0, 0, 90));
     CreateRagdollBone(root, "Bip01_L_Thigh", SHAPE_CAPSULE, Vector3(0.175, 0.45, 0.175), Vector3(0.25, 0, 0), Quaternion(0, 0, 90));

+ 2 - 1
Docs/GettingStarted.dox

@@ -101,9 +101,10 @@ F5          Save scene
 F7          Load scene
 1 to 9      Toggle rendering options
 T           Toggle profiling display
-C           Toggle orthographic camera
+O           Toggle orthographic camera
 F           Toggle FXAA edge filter
 B           Toggle bloom post-process
+P           Paint a decal into the mouse cursor hit location
 \endverbatim
 
 TestScene also includes a network replication test, where clients can connect, move around as invisible cameras, and create new physics objects. For this, a server needs to be started with the command TestScene.bat server (-headless switch can optionally given so that the server will not open a graphics window) and clients can connect by specifying the server address on the command line, for example TestScene.bat 127.0.0.1

+ 60 - 5
Engine/Graphics/DecalSet.cpp

@@ -63,7 +63,7 @@ DecalSet::DecalSet(Context* context) :
 {
     drawableFlags_ = DRAWABLE_GEOMETRY;
     
-    geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1);
+    geometry_->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
     
     batches_.Resize(1);
     batches_[0].geometry_ = geometry_;
@@ -227,10 +227,11 @@ bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Qu
     newDecal.vertices_.Compact();
     numVertices_ += newDecal.vertices_.Size();
     
-    // Finally transform vertices to the DecalSet's local space
+    // Finally transform vertices to this node's local space
     Matrix3x4 decalTransform = node_->GetWorldTransform().Inverse() * target->GetNode()->GetWorldTransform();
     CalculateUVs(newDecal, frustumTransform.Inverse(), size, aspectRatio, depth, topLeftUV, bottomRightUV);
     TransformVertices(newDecal, decalTransform, biasVector);
+    CalculateTangents(newDecal);
     
     newDecal.CalculateBoundingBox();
     
@@ -297,8 +298,9 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geomet
     unsigned vertexSize;
     const unsigned char* indexData;
     unsigned indexSize;
+    unsigned elementMask;
     
-    geometry->GetRawData(vertexData, vertexSize, indexData, indexSize);
+    geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
     if (!vertexData || !indexData)
     {
         LOGWARNING("Can not add decal, object does not have CPU-side geometry data");
@@ -307,7 +309,7 @@ void DecalSet::GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geomet
     
     unsigned indexStart = geometry->GetIndexStart();
     unsigned indexCount = geometry->GetIndexCount();
-    bool hasNormals = vertexSize >= 6 * sizeof(float);
+    bool hasNormals = (elementMask & MASK_NORMAL) != 0;
     
     const unsigned char* srcData = (const unsigned char*)vertexData;
     
@@ -429,6 +431,55 @@ void DecalSet::CalculateUVs(Decal& decal, const Matrix3x4& view, float size, flo
     }
 }
 
+void DecalSet::CalculateTangents(Decal& decal)
+{
+    for (unsigned i = 0; i < decal.vertices_.Size(); i += 3)
+    {
+        // 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);
+        
+        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;
+            
+            decal.vertices_[j].tangent_ = Vector4(xyz, w);
+        }
+    }
+}
+
 void DecalSet::TransformVertices(Decal& decal, const Matrix3x4& transform, const Vector3& biasVector)
 {
     for (PODVector<DecalVertex>::Iterator i = decal.vertices_.Begin(); i != decal.vertices_.End(); ++i)
@@ -468,7 +519,7 @@ void DecalSet::UpdateBufferSize()
 {
     if (vertexBuffer_->GetVertexCount() != maxVertices_)
     {
-        vertexBuffer_->SetSize(maxVertices_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1);
+        vertexBuffer_->SetSize(maxVertices_, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT);
         bufferDirty_ = true;
     }
     
@@ -494,6 +545,10 @@ void DecalSet::UpdateVertexBuffer()
                 *dest++ = vertex.normal_.z_;
                 *dest++ = vertex.texCoord_.x_;
                 *dest++ = vertex.texCoord_.y_;
+                *dest++ = vertex.tangent_.x_;
+                *dest++ = vertex.tangent_.y_;
+                *dest++ = vertex.tangent_.z_;
+                *dest++ = vertex.tangent_.w_;
             }
         }
         

+ 4 - 0
Engine/Graphics/DecalSet.h

@@ -49,6 +49,8 @@ struct DecalVertex
     Vector3 normal_;
     /// Texture coordinates coordinates.
     Vector2 texCoord_;
+    /// Tangent.
+    Vector4 tangent_;
 };
 
 /// One decal in a decal set.
@@ -126,6 +128,8 @@ private:
     void GetFaces(Vector<PODVector<DecalVertex> >& faces, Geometry* geometry, 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.
+    void CalculateTangents(Decal& decal);
     /// Transform decal's vertices from the target geometry to the decal set local space.
     void TransformVertices(Decal& decal, const Matrix3x4& transform, const Vector3& biasVector);
     /// Remove a decal by iterator and return iterator to the next decal.

+ 14 - 6
Engine/Graphics/Geometry.cpp

@@ -206,20 +206,27 @@ unsigned short Geometry::GetBufferHash() const
 }
 
 
-void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize)
+void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize, unsigned& elementMask)
 {
     if (positionBufferIndex_ < vertexBuffers_.Size() && vertexBuffers_[positionBufferIndex_])
     {
         vertexData = vertexBuffers_[positionBufferIndex_]->GetShadowData();
         if (vertexData)
+        {
             vertexSize = vertexBuffers_[positionBufferIndex_]->GetVertexSize();
+            elementMask = vertexBuffers_[positionBufferIndex_]->GetElementMask();
+        }
         else
+        {
             vertexSize = 0;
+            elementMask = 0;
+        }
     }
     else
     {
         vertexData = 0;
         vertexSize = 0;
+        elementMask = 0;
     }
     
     if (indexBuffer_)
@@ -239,12 +246,13 @@ void Geometry::GetRawData(const unsigned char*& vertexData, unsigned& vertexSize
 
 float Geometry::GetDistance(const Ray& ray)
 {
-    const unsigned char* rawVertexData = 0;
-    const unsigned char* rawIndexData = 0;
-    unsigned vertexSize = 0;
-    unsigned indexSize = 0;
+    const unsigned char* rawVertexData;
+    const unsigned char* rawIndexData;
+    unsigned vertexSize;
+    unsigned indexSize;
+    unsigned elementMask;
     
-    GetRawData(rawVertexData, vertexSize, rawIndexData, indexSize);
+    GetRawData(rawVertexData, vertexSize, rawIndexData, indexSize, elementMask);
     if (!rawVertexData || !rawIndexData)
         return M_INFINITY;
     

+ 1 - 1
Engine/Graphics/Geometry.h

@@ -87,7 +87,7 @@ public:
     /// Return buffers' combined hash value for state sorting.
     unsigned short GetBufferHash() const;
     /// Return raw vertex and index data for CPU operations, or null pointers if not available.
-    void GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize);
+    void GetRawData(const unsigned char*& vertexData, unsigned& vertexSize, const unsigned char*& indexData, unsigned& indexSize, unsigned& elementMask);
     /// Return ray hit distance or infinity if no hit. Requires raw data to be set.
     float GetDistance(const Ray& ray);
     

+ 16 - 15
Engine/Graphics/StaticModel.cpp

@@ -110,10 +110,10 @@ void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQuer
                 // Then the actual test using triangle geometry
                 for (unsigned i = 0; i < geometries_.Size(); ++i)
                 {
-                    Geometry* geom = GetSoftwareGeometry(i);
-                    if (geom)
+                    Geometry* geometry = GetSoftwareGeometry(i);
+                    if (geometry)
                     {
-                        distance = geom->GetDistance(localRay);
+                        distance = geometry->GetDistance(localRay);
                         if (distance <= query.maxDistance_)
                         {
                             RayQueryResult result;
@@ -167,8 +167,8 @@ unsigned StaticModel::GetNumOccluderTriangles()
     
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
-        Geometry* geom = GetSoftwareGeometry(i);
-        if (!geom)
+        Geometry* geometry = GetSoftwareGeometry(i);
+        if (!geometry)
             continue;
         
         // Check that the material is suitable for occlusion (default material always is)
@@ -176,7 +176,7 @@ unsigned StaticModel::GetNumOccluderTriangles()
         if (mat && !mat->GetOcclusion())
             continue;
         
-        triangles += geom->GetIndexCount() / 3;
+        triangles += geometry->GetIndexCount() / 3;
     }
     
     return triangles;
@@ -188,17 +188,17 @@ bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
     
     for (unsigned i = 0; i < batches_.Size(); ++i)
     {
-        Geometry* geom = GetSoftwareGeometry(i);
-        if (!geom)
+        Geometry* geometry = GetSoftwareGeometry(i);
+        if (!geometry)
             continue;
         
         // Check that the material is suitable for occlusion (default material always is) and set culling mode
-        Material* mat = batches_[i].material_;
-        if (mat)
+        Material* material = batches_[i].material_;
+        if (material)
         {
-            if (!mat->GetOcclusion())
+            if (!material->GetOcclusion())
                 continue;
-            buffer->SetCullMode(mat->GetCullMode());
+            buffer->SetCullMode(material->GetCullMode());
         }
         else
             buffer->SetCullMode(CULL_CCW);
@@ -207,14 +207,15 @@ bool StaticModel::DrawOcclusion(OcclusionBuffer* buffer)
         unsigned vertexSize;
         const unsigned char* indexData;
         unsigned indexSize;
+        unsigned elementMask;
         
-        geom->GetRawData(vertexData, vertexSize, indexData, indexSize);
+        geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
         // Check for valid geometry data
         if (!vertexData || !indexData)
             continue;
         
-        unsigned indexStart = geom->GetIndexStart();
-        unsigned indexCount = geom->GetIndexCount();
+        unsigned indexStart = geometry->GetIndexStart();
+        unsigned indexCount = geometry->GetIndexCount();
         
         // Draw and check for running out of triangles
         if (!buffer->Draw(node_->GetWorldTransform(), vertexData, vertexSize, indexData, indexSize, indexStart, indexCount))

+ 4 - 2
Engine/Physics/CollisionShape.cpp

@@ -83,8 +83,9 @@ TriangleMeshData::TriangleMeshData(Model* model, unsigned lodLevel) :
         const unsigned char* indexData;
         unsigned vertexSize;
         unsigned indexSize;
+        unsigned elementMask;
         
-        geom->GetRawData(vertexData, vertexSize, indexData, indexSize);
+        geom->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
         if (!vertexData || !indexData)
             continue;
         
@@ -152,8 +153,9 @@ ConvexData::ConvexData(Model* model, unsigned lodLevel)
         const unsigned char* indexData;
         unsigned vertexSize;
         unsigned indexSize;
+        unsigned elementMask;
         
-        geom->GetRawData(vertexData, vertexSize, indexData, indexSize);
+        geom->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
         if (!vertexData || !indexData)
             continue;