Forráskód Böngészése

Updated gameplay-encoder to now compute both bounding boxes and bounding spheres for skinned meshes.
Updated spaceship sample to use mesh bounding box for collisions with floor.
Character sample code changes (code cleanup, replaced spot light with directional light).
Other minor cleanup.

Steve Grenier 14 éve
szülő
commit
ed302ac4f5

+ 2 - 0
gameplay-encoder/gameplay-encoder.vcxproj

@@ -15,6 +15,7 @@
     <ClCompile Include="src\Animation.cpp" />
     <ClCompile Include="src\AnimationChannel.cpp" />
     <ClCompile Include="src\Base.cpp" />
+    <ClCompile Include="src\BoundingVolume.cpp" />
     <ClCompile Include="src\Camera.cpp" />
     <ClCompile Include="src\CameraInstance.cpp" />
     <ClCompile Include="src\EncoderArguments.cpp" />
@@ -59,6 +60,7 @@
     <ClInclude Include="src\Animation.h" />
     <ClInclude Include="src\AnimationChannel.h" />
     <ClInclude Include="src\Base.h" />
+    <ClInclude Include="src\BoundingVolume.h" />
     <ClInclude Include="src\Camera.h" />
     <ClInclude Include="src\CameraInstance.h" />
     <ClInclude Include="src\EncoderArguments.h" />

+ 6 - 0
gameplay-encoder/gameplay-encoder.vcxproj.filters

@@ -97,6 +97,9 @@
       <Filter>Objects\Animation</Filter>
     </ClCompile>
     <ClCompile Include="..\gameplay\src\Curve.cpp" />
+    <ClCompile Include="src\BoundingVolume.cpp">
+      <Filter>Math</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="src\DAESceneEncoder.h" />
@@ -195,6 +198,9 @@
     </ClInclude>
     <ClInclude Include="..\gameplay\src\Curve.h" />
     <ClInclude Include="src\Miniball.h" />
+    <ClInclude Include="src\BoundingVolume.h">
+      <Filter>Math</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <Filter Include="Objects">

+ 2 - 1
gameplay-encoder/gameplay-encoder.vcxproj.user

@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <LocalDebuggerCommandArguments>-groupAnimations Bip01 dragon_animations C:\git\GamePlay\gameplay-demos\demo00-dragon\res\models\demo00-dragon.dae</LocalDebuggerCommandArguments>
+    <LocalDebuggerCommandArguments>
+    </LocalDebuggerCommandArguments>
     <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
   </PropertyGroup>
 </Project>

+ 136 - 0
gameplay-encoder/src/BoundingVolume.cpp

@@ -0,0 +1,136 @@
+#include "BoundingVolume.h"
+
+namespace gameplay
+{
+
+BoundingVolume::BoundingVolume()
+    : radius(0.0f)
+{
+}
+
+void updateMinMax(Vector3* point, Vector3* min, Vector3* max)
+{
+    // Leftmost point.
+    if (point->x < min->x)
+    {
+        min->x = point->x;
+    }
+
+    // Rightmost point.
+    if (point->x > max->x)
+    {
+        max->x = point->x;
+    }
+
+    // Lowest point.
+    if (point->y < min->y)
+    {
+        min->y = point->y;
+    }
+
+    // Highest point.
+    if (point->y > max->y)
+    {
+        max->y = point->y;
+    }
+
+    // Farthest point.
+    if (point->z < min->z)
+    {
+        min->z = point->z;
+    }
+
+    // Nearest point.
+    if (point->z > max->z)
+    {
+        max->z = point->z;
+    }
+}
+
+void BoundingVolume::transform(const Matrix& m)
+{
+    // Transform the bounding sphere
+    m.transformPoint(center, &center);
+    Vector3 translate;
+    m.decompose(&translate, NULL, NULL);
+    float r = radius * translate.x;
+    r = std::max(radius, radius * translate.y);
+    r = std::max(radius, radius * translate.z);
+    radius = r;
+
+    // Transform the bounding box
+    Vector3 corners[8];
+    corners[0].set(min.x, max.y, max.z);
+    // Left-bottom-front.
+    corners[1].set(min.x, min.y, max.z);
+    // Right-bottom-front.
+    corners[2].set(max.x, min.y, max.z);
+    // Right-top-front.
+    corners[3].set(max.x, max.y, max.z);
+    // Right-top-back.
+    corners[4].set(max.x, max.y, min.z);
+    // Right-bottom-back.
+    corners[5].set(max.x, min.y, min.z);
+    // Left-bottom-back.
+    corners[6].set(min.x, min.y, min.z);
+    // Left-top-back.
+    corners[7].set(min.x, max.y, min.z);
+
+    // Transform the corners, recalculating the min and max points along the way.
+    m.transformPoint(corners[0], &corners[0]);
+    Vector3 newMin = corners[0];
+    Vector3 newMax = corners[0];
+    for (int i = 1; i < 8; i++)
+    {
+        m.transformPoint(corners[i], &corners[i]);
+        updateMinMax(&corners[i], &newMin, &newMax);
+    }
+    min = newMin;
+    max = newMax;
+}
+
+void BoundingVolume::merge(const BoundingVolume& v)
+{
+    // Calculate the distance between the two centers.
+    float vx = center.x - v.center.x;
+    float vy = center.y - v.center.y;
+    float vz = center.z - v.center.z;
+    float d = sqrtf(vx * vx + vy * vy + vz * vz);
+
+    // If one sphere is contained inside the other, set to the larger sphere.
+    if (d <= (v.radius - radius))
+    {
+        // Use targert volume
+        radius = v.radius;
+        center = v.center;
+    }
+    else if (d <= (radius - v.radius))
+    {
+        // No change
+    }
+    else
+    {
+        // Calculate the unit vector between the two centers.
+        float dI = 1.0f / d;
+        vx *= dI;
+        vy *= dI;
+        vz *= dI;
+
+        // Calculate the new radius.
+        float r = (radius + v.radius + d) * 0.5f;
+
+        // Calculate the new center.
+        float scaleFactor = (r - v.radius);
+        vx = vx * scaleFactor + v.center.x;
+        vy = vy * scaleFactor + v.center.y;
+        vz = vz * scaleFactor + v.center.z;
+
+        // Set the new center and radius.
+        center.x = vx;
+        center.y = vy;
+        center.z = vz;
+        radius = r;
+    }
+}
+
+}

+ 57 - 0
gameplay-encoder/src/BoundingVolume.h

@@ -0,0 +1,57 @@
+#ifndef BOUNDINGVOLUME_H_
+#define BOUNDINGVOLUME_H_
+
+#include "Vector3.h"
+#include "Matrix.h"
+
+namespace gameplay
+{
+
+/**
+ * Represents a 3D bounding volumes, which defines both a
+ * bounding sphere and an axis-aligned bounding box (AABB).
+ */
+class BoundingVolume
+{
+public:
+
+    /**
+     * Radius of the bounding sphere.
+     */
+    float radius;
+
+    /**
+     * Center point of the bounding sphere.
+     */
+    Vector3 center;
+
+    /**
+     * Minimum point of the AABB.
+     */
+    Vector3 min;
+
+    /**
+     * Maximum point of the AABB.
+     */
+    Vector3 max;
+
+    /**
+     * Constructor.
+     */
+    BoundingVolume();
+
+    /**
+     * Transforms this bounding volume by the specified matrix.
+     */
+    void transform(const Matrix& m);
+
+    /**
+     * Merges this bounding volume with the specified one and
+     * stores the result in this BoundingVolume.
+     */
+    void merge(const BoundingVolume& v);
+};
+
+}
+
+#endif

+ 1 - 1
gameplay-encoder/src/Matrix.cpp

@@ -396,7 +396,7 @@ float Matrix::determinant() const
     return (a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0);
 }
 
-void Matrix::transformPoint(const Vector3& p, Vector3* dst)
+void Matrix::transformPoint(const Vector3& p, Vector3* dst) const
 {
     dst->set(
         p.x * m[0] + p.y * m[4] + p.z * m[8] +  m[12],

+ 6 - 2
gameplay-encoder/src/Matrix.h

@@ -31,6 +31,11 @@ class Matrix
 {
 public:
 
+    /**
+     * Matrix colums.
+     */
+    float m[16];
+
     /**
      * Constructor.
      */
@@ -156,9 +161,8 @@ public:
      *
      * Note that the input vector is treated as a point and NOT a vector.
      */
-    void transformPoint(const Vector3& p, Vector3* dst);
+    void transformPoint(const Vector3& p, Vector3* dst) const;
 
-    float m[16];
 };
 
 }

+ 3 - 10
gameplay-encoder/src/Mesh.cpp

@@ -183,9 +183,7 @@ void Mesh::computeBounds()
     bounds.max.x = bounds.max.y = bounds.max.z = FLT_MIN;
     bounds.center.x = bounds.center.y = bounds.center.z = 0.0f;
     bounds.radius = 0.0f;
-    
-    // for each vertex
-    Vector3 avgPos;
+
     for (std::vector<Vertex>::const_iterator i = vertices.begin(); i != vertices.end(); i++)
     {
         // Update min/max for this vertex
@@ -201,16 +199,11 @@ void Mesh::computeBounds()
             bounds.max.y = i->position.y;
         if (i->position.z > bounds.max.z)
             bounds.max.z = i->position.z;
-
-        avgPos.x += i->position.x;
-        avgPos.y += i->position.y;
-        avgPos.z += i->position.z;
     }
 
     // Compute center point
-    bounds.center.x = avgPos.x / (float)vertices.size();
-    bounds.center.y = avgPos.y / (float)vertices.size();
-    bounds.center.z = avgPos.z / (float)vertices.size();
+    Vector3::add(bounds.min, bounds.max, &bounds.center);
+    bounds.center.scale(0.5f);
 
     // Compute radius by looping through all points again and finding the max
     // distance between the center point and each vertex position

+ 2 - 7
gameplay-encoder/src/Mesh.h

@@ -5,6 +5,7 @@
 #include "Object.h"
 #include "MeshPart.h"
 #include "VertexElement.h"
+#include "BoundingVolume.h"
 
 namespace gameplay
 {
@@ -62,13 +63,7 @@ public:
     Model* model;
     std::vector<Vertex> vertices;
     std::vector<MeshPart*> parts;
-    struct
-    {
-        Vector3 min;
-        Vector3 max;
-        Vector3 center;
-        float radius;
-    } bounds;
+    BoundingVolume bounds;
     std::map<Vertex, unsigned int> vertexLookupTable;
 
 private:

+ 42 - 90
gameplay-encoder/src/MeshSkin.cpp

@@ -46,15 +46,15 @@ void MeshSkin::writeBinary(FILE* file)
     }
 
     /*
-    // Write joint bounds
+    // Write joint bounding spheres
     write((unsigned int)_jointBounds.size(), file);
     for (unsigned int i = 0; i < _jointBounds.size(); ++i)
     {
-        BoundingSphere& s = _jointBounds[i];
-        write(s.center.x, file);
-        write(s.center.y, file);
-        write(s.center.z, file);
-        write(s.radius, file);
+        BoundingVolume& v = _jointBounds[i];
+        write(v.center.x, file);
+        write(v.center.y, file);
+        write(v.center.z, file);
+        write(v.radius, file);
     }
     */
 }
@@ -132,70 +132,6 @@ bool MeshSkin::hasJoint(const char* id)
     return false;
 }
 
-BoundingSphere mergeSpheres(const BoundingSphere& sphere1, const BoundingSphere& sphere2)
-{
-    BoundingSphere result;
-
-    // Calculate the distance between the two centers.
-    float vx = sphere1.center.x - sphere2.center.x;
-    float vy = sphere1.center.y - sphere2.center.y;
-    float vz = sphere1.center.z - sphere2.center.z;
-    float d = sqrtf(vx * vx + vy * vy + vz * vz);
-
-    // If one sphere is contained inside the other, set to the larger sphere.
-    if (d <= (sphere2.radius - sphere1.radius))
-    {
-        result = sphere2;
-        return result;
-    }
-    else if (d <= (sphere1.radius - sphere2.radius))
-    {
-        result = sphere1;
-        return result;
-    }
-
-    // Calculate the unit vector between the two centers.
-    float dI = 1.0f / d;
-    vx *= dI;
-    vy *= dI;
-    vz *= dI;
-
-    // Calculate the new radius.
-    float r = (sphere1.radius + sphere2.radius + d) * 0.5f;
-
-    // Calculate the new center.
-    float scaleFactor = (r - sphere2.radius);
-    vx = vx * scaleFactor + sphere2.center.x;
-    vy = vy * scaleFactor + sphere2.center.y;
-    vz = vz * scaleFactor + sphere2.center.z;
-
-    // Set the new center and radius.
-    result.center.x = vx;
-    result.center.y = vy;
-    result.center.z = vz;
-    result.radius = r;
-    return result;
-}
-
-BoundingSphere transformBoundingSphere(const BoundingSphere& sphere, Matrix& matrix)
-{
-    BoundingSphere result = sphere;
-
-    // Translate the center point.
-    Vector3 translate;
-    matrix.transformPoint(sphere.center, &translate);
-    result.center = translate;
-
-    // Calculate the sphere's new radius from the radii in each direction (take the largest).
-    matrix.decompose(&translate, NULL, NULL);
-    float r = sphere.radius * translate.x;
-    r = std::max(sphere.radius, sphere.radius * translate.y);
-    r = std::max(sphere.radius, sphere.radius * translate.z);
-    result.radius = r;
-
-    return result;
-}
-
 void MeshSkin::computeBounds()
 {
     // Find the offset of the blend indices and blend weights within the mesh vertices
@@ -285,7 +221,9 @@ void MeshSkin::computeBounds()
 
         // Calculate the local bounding volume for this joint
         vertices.clear();
-        BoundingSphere jointSphere;
+        BoundingVolume jointBounds;
+        jointBounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
+        jointBounds.max.set(FLT_MIN, FLT_MIN, FLT_MIN);
         for (unsigned int j = 0; j < vertexCount; ++j)
         {
             const Vertex& v = _mesh->getVertex(j);
@@ -296,21 +234,36 @@ void MeshSkin::computeBounds()
                 (v.blendIndices.w == i && !ISZERO(v.blendWeights.w)))
             {
                 vertices.push_back(v.position);
-                jointSphere.center.add(v.position);
+                // Update box min/max
+                if (v.position.x < jointBounds.min.x)
+                    jointBounds.min.x = v.position.x;
+                if (v.position.y < jointBounds.min.y)
+                    jointBounds.min.y = v.position.y;
+                if (v.position.z < jointBounds.min.z)
+                    jointBounds.min.z = v.position.z;
+                if (v.position.x > jointBounds.max.x)
+                    jointBounds.max.x = v.position.x;
+                if (v.position.y > jointBounds.max.y)
+                    jointBounds.max.y = v.position.y;
+                if (v.position.z > jointBounds.max.z)
+                    jointBounds.max.z = v.position.z;
             }
         }
         if (vertices.size() > 0)
         {
-            jointSphere.center.scale(1.0f / (float)vertices.size());
+            // Compute center point
+            Vector3::add(jointBounds.min, jointBounds.max, &jointBounds.center);
+            jointBounds.center.scale(0.5f);
+            // Compute radius
             for (unsigned int j = 0, jointVertexCount = vertices.size(); j < jointVertexCount; ++j)
             {
-                float d = jointSphere.center.distanceSquared(vertices[j]);
-                if (d > jointSphere.radius)
-                    jointSphere.radius = d;
+                float d = jointBounds.center.distanceSquared(vertices[j]);
+                if (d > jointBounds.radius)
+                    jointBounds.radius = d;
             }
-            jointSphere.radius = sqrtf(jointSphere.radius);
+            jointBounds.radius = sqrtf(jointBounds.radius);
         }
-        _jointBounds[i] = jointSphere;
+        _jointBounds[i] = jointBounds;
 
         DEBUGPRINT("> %d%%\r", (int)((float)(i+1) / (float)jointCount * 100.0f));
     }
@@ -320,7 +273,7 @@ void MeshSkin::computeBounds()
 
     // Create a Curve for each animation channel
     float maxDuration = 0.0f;
-    DEBUGPRINT("> Populating animation curve data...\n");
+    DEBUGPRINT("> Building animation curves...\n");
     DEBUGPRINT("> 0%%\r");
     for (unsigned int i = 0; i < channelCount; ++i)
     {
@@ -404,9 +357,9 @@ void MeshSkin::computeBounds()
     _mesh->bounds.radius = 0;
     Vector3 skinnedPos;
     Vector3 tempPos;
-    DEBUGPRINT("> Animating joints...\n");
+    DEBUGPRINT("> Evaluating joints...\n");
     DEBUGPRINT("> 0%%\r");
-    BoundingSphere finalSphere;
+    BoundingVolume finalBounds;
     while (time <= maxDuration)
     {
         // Evaluate joint transforms at this time interval
@@ -432,20 +385,20 @@ void MeshSkin::computeBounds()
         std::vector<Matrix>::const_iterator bindPoseItr = _bindPoses.begin();
         for (unsigned int i = 0; i < jointCount; ++i, bindPoseItr++)
         {
-            BoundingSphere sphere = _jointBounds[i];
-            if (ISZERO(sphere.radius))
+            BoundingVolume bounds = _jointBounds[i];
+            if (ISZERO(bounds.radius))
                 continue;
 
             Matrix& m = jointTransforms[i];
             Matrix::multiply(_joints[i]->getWorldMatrix().m, bindPoseItr->m, m.m);
             Matrix::multiply(m.m, _bindShape, m.m);
 
-            // Get a world-space bounding sphere for this joint
-            sphere = transformBoundingSphere(sphere, m);
-            if (ISZERO(finalSphere.radius))
-                finalSphere = sphere;
+            // Get a world-space bounding volume for this joint
+            bounds.transform(m);
+            if (ISZERO(finalBounds.radius))
+                finalBounds = bounds;
             else
-                finalSphere = mergeSpheres(finalSphere, sphere);
+                finalBounds.merge(bounds);
         }
 
         // Increment time by 1/30th of second (~ 33 ms)
@@ -459,8 +412,7 @@ void MeshSkin::computeBounds()
     DEBUGPRINT("\n");
 
     // Update the bounding sphere for the mesh
-    _mesh->bounds.center = finalSphere.center;
-    _mesh->bounds.radius = finalSphere.radius;
+    _mesh->bounds = finalBounds;
 
     // Restore original joint transforms
     for (unsigned int i = 0; i < jointCount; ++i)

+ 2 - 22
gameplay-encoder/src/MeshSkin.h

@@ -7,6 +7,7 @@
 #include "Object.h"
 #include "Matrix.h"
 #include "Animation.h"
+#include "BoundingVolume.h"
 
 namespace gameplay
 {
@@ -14,27 +15,6 @@ namespace gameplay
 class Node;
 class Mesh;
 
-struct BoundingSphere
-{
-    Vector3 center;
-    float radius;
-
-    BoundingSphere() : radius(0)
-    {
-    }
-
-    BoundingSphere(const BoundingSphere& copy)
-    {
-        set(copy);
-    }
-
-    void set(const BoundingSphere& copy)
-    {
-        center = copy.center;
-        radius = copy.radius;
-    }
-};
-
 class MeshSkin : public Object
 {
     friend class Model;
@@ -87,7 +67,7 @@ private:
     std::vector<Matrix> _bindPoses;
     std::vector<std::string> _jointNames;
     unsigned int _vertexInfluenceCount;
-    std::vector<BoundingSphere> _jointBounds;
+    std::vector<BoundingVolume> _jointBounds;
 };
 
 }

+ 1 - 0
gameplay-encoder/src/Vector3.h

@@ -5,6 +5,7 @@
 #ifndef VECTOR3_H_
 #define VECTOR3_H_
 
+#include <cstdio>
 
 namespace gameplay
 {

+ 11 - 0
gameplay.sln

@@ -32,6 +32,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample04-sandbox", "gamepla
 		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
 	EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "demo00-dragon", "gameplay-demos\demo00-dragon\demo00-dragon.vcxproj", "{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}"
+	ProjectSection(ProjectDependencies) = postProject
+		{1032BA4B-57EB-4348-9E03-29DD63E80E4A} = {1032BA4B-57EB-4348-9E03-29DD63E80E4A}
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Win32 = Debug|Win32
@@ -81,6 +86,12 @@ Global
 		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.DebugMem|Win32.Build.0 = DebugMem|Win32
 		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Release|Win32.ActiveCfg = Release|Win32
 		{FA260001-5B2E-41B7-86DD-C7F26DF3A485}.Release|Win32.Build.0 = Release|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.Debug|Win32.ActiveCfg = Debug|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.Debug|Win32.Build.0 = Debug|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.DebugMem|Win32.ActiveCfg = DebugMem|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.DebugMem|Win32.Build.0 = DebugMem|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.Release|Win32.ActiveCfg = Release|Win32
+		{D20F2DDA-9825-4B10-BF8E-5ED10BD7C047}.Release|Win32.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 9 - 2
gameplay/src/Curve.cpp

@@ -1,6 +1,13 @@
+// Purposely not including Base.h here, or any other gameplay dependencies
+// so this class can be reused between gameplay and gameplay-encoder.
 #include "Curve.h"
-#include <cassert>
-#include <memory>
+#include <assert.h>
+#include <math.h>
+#include <memory.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
 
 namespace gameplay
 {