Handle physics corner cases (literally)
This commit is contained in:
parent
a2f7f62a67
commit
e51d22cb52
62
TrueCraft.API/BoundingCylinder.cs
Normal file
62
TrueCraft.API/BoundingCylinder.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
namespace TrueCraft.API
|
||||
{
|
||||
public struct BoundingCylinder : IEquatable<BoundingCylinder>
|
||||
{
|
||||
public Vector3 Min;
|
||||
|
||||
public Vector3 Max;
|
||||
|
||||
public double Radius;
|
||||
|
||||
public BoundingCylinder(Vector3 min, Vector3 max, double radius)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
Radius = radius;
|
||||
}
|
||||
|
||||
public bool Intersects(Vector3 q)
|
||||
{
|
||||
return DistancePointLine(q, Min, Max) < Radius;
|
||||
}
|
||||
|
||||
public bool Intersects(BoundingBox q)
|
||||
{
|
||||
var corners = q.GetCorners();
|
||||
for (int i = 0; i < corners.Length; i++)
|
||||
{
|
||||
if (Intersects(corners[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// http://answers.unity3d.com/questions/62644/distance-between-a-ray-and-a-point.html
|
||||
public static double DistancePointLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
|
||||
{
|
||||
return (ProjectPointLine(point, lineStart, lineEnd) - point).Distance;
|
||||
}
|
||||
|
||||
public static Vector3 ProjectPointLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
|
||||
{
|
||||
var rhs = point - lineStart;
|
||||
var vector2 = lineEnd - lineStart;
|
||||
var magnitude = vector2.Distance;
|
||||
var lhs = vector2;
|
||||
if (magnitude > 1E-06f)
|
||||
lhs = lhs / magnitude;
|
||||
var num2 = Vector3.Dot(lhs, rhs);
|
||||
if (num2 < 0) num2 = 0;
|
||||
if (num2 > magnitude) num2 = magnitude;
|
||||
return lineStart + (lhs * num2);
|
||||
}
|
||||
|
||||
public bool Equals(BoundingCylinder other)
|
||||
{
|
||||
return other.Max == this.Max
|
||||
&& other.Min == this.Min
|
||||
&& other.Radius == this.Radius;
|
||||
}
|
||||
}
|
||||
}
|
@ -127,6 +127,7 @@
|
||||
<Compile Include="Logic\IBurnableItem.cs" />
|
||||
<Compile Include="Logic\SoundEffectClass.cs" />
|
||||
<Compile Include="Matrix.cs" />
|
||||
<Compile Include="BoundingCylinder.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup />
|
||||
|
@ -168,6 +168,42 @@ namespace TrueCraft.API
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the dot product between two vectors.
|
||||
/// </summary>
|
||||
public static double Dot(Vector3 value1, Vector3 value2)
|
||||
{
|
||||
return value1.X * value2.X + value1.Y * value2.Y + value1.Z * value2.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the cross product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="vector1">The first vector.</param>
|
||||
/// <param name="vector2">The second vector.</param>
|
||||
/// <returns>The cross product of two vectors.</returns>
|
||||
public static Vector3 Cross(Vector3 vector1, Vector3 vector2)
|
||||
{
|
||||
Cross(ref vector1, ref vector2, out vector1);
|
||||
return vector1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the cross product of two vectors.
|
||||
/// </summary>
|
||||
/// <param name="vector1">The first vector.</param>
|
||||
/// <param name="vector2">The second vector.</param>
|
||||
/// <param name="result">The cross product of two vectors as an output parameter.</param>
|
||||
public static void Cross(ref Vector3 vector1, ref Vector3 vector2, out Vector3 result)
|
||||
{
|
||||
var x = vector1.Y * vector2.Z - vector2.Y * vector1.Z;
|
||||
var y = -(vector1.X * vector2.Z - vector2.X * vector1.Z);
|
||||
var z = vector1.X * vector2.Y - vector2.X * vector1.Y;
|
||||
result.X = x;
|
||||
result.Y = y;
|
||||
result.Z = z;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
@ -216,7 +216,6 @@ namespace TrueCraft.Core.Test.Physics
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Fails until I rewrite the physics engine AGAIN")]
|
||||
public void TestCornerCollision()
|
||||
{
|
||||
var repository = GetBlockRepository();
|
||||
@ -233,8 +232,8 @@ namespace TrueCraft.Core.Test.Physics
|
||||
// Test
|
||||
physics.Update(TimeSpan.FromSeconds(1));
|
||||
|
||||
Assert.AreEqual(0, entity.Position.X);
|
||||
Assert.AreEqual(0, entity.Position.Z);
|
||||
Assert.AreEqual(-1, entity.Position.X);
|
||||
Assert.AreEqual(-1, entity.Position.Z);
|
||||
Assert.AreEqual(0, entity.Velocity.X);
|
||||
Assert.AreEqual(0, entity.Velocity.Z);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
namespace TrueCraft.API.Test
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestBoundingCylinder
|
||||
{
|
||||
[Test]
|
||||
public void TestIntersectsPoint()
|
||||
{
|
||||
// x
|
||||
// /
|
||||
// x
|
||||
var cylinder = new BoundingCylinder(Vector3.Zero, Vector3.One, 1);
|
||||
Assert.IsTrue(cylinder.Intersects(cylinder.Min));
|
||||
Assert.IsTrue(cylinder.Intersects(cylinder.Max));
|
||||
Assert.IsTrue(cylinder.Intersects(cylinder.Min + (Vector3.One / 2)));
|
||||
Assert.IsTrue(cylinder.Intersects(cylinder.Max - (Vector3.One / 2)));
|
||||
Assert.IsTrue(cylinder.Intersects(new Vector3(0.25, 0, 0)));
|
||||
Assert.IsFalse(cylinder.Intersects(new Vector3(5, 5, 5)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIntersectsBox()
|
||||
{
|
||||
// x
|
||||
// /
|
||||
// x
|
||||
var cylinder = new BoundingCylinder(Vector3.Zero, Vector3.One * 10, 3);
|
||||
var doesNotIntersect = new BoundingBox(Vector3.One * 10 + 5, Vector3.One * 10 + 5);
|
||||
Assert.IsFalse(cylinder.Intersects(doesNotIntersect));
|
||||
var intersects = new BoundingBox(Vector3.Zero, Vector3.One);
|
||||
Assert.IsTrue(cylinder.Intersects(intersects));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{90712322-5904-4BCE-8606-C662706B8EAE}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>TrueCraft.API.Test</RootNamespace>
|
||||
<AssemblyName>TrueCraft.API.Test</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="nunit.framework">
|
||||
<HintPath>..\..\packages\NUnit.3.4.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="TestBoundingCylinder.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\TrueCraft.API\TrueCraft.API.csproj">
|
||||
<Project>{FEE55B54-91B0-4325-A2C3-D576C0B7A81F}</Project>
|
||||
<Name>TrueCraft.API</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
4
TrueCraft.Core.Test/TrueCraft.API.Test/packages.config
Normal file
4
TrueCraft.Core.Test/TrueCraft.API.Test/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NUnit" version="3.4.1" targetFramework="net45" />
|
||||
</packages>
|
@ -70,15 +70,91 @@ namespace TrueCraft.Core.Physics
|
||||
aabbEntity.TerrainCollision(collision, before.X < 0 ? Vector3.Left : Vector3.Right);
|
||||
if (TestTerrainCollisionZ(aabbEntity, out collision))
|
||||
aabbEntity.TerrainCollision(collision, before.Z < 0 ? Vector3.Backwards : Vector3.Forwards);
|
||||
|
||||
if (TestTerrainCollisionCylinder(aabbEntity, out collision))
|
||||
aabbEntity.TerrainCollision(collision, before);
|
||||
}
|
||||
|
||||
entity.EndUpdate(entity.Position + entity.Velocity);
|
||||
TestTerrainCollisionY(aabbEntity, out collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BoundingBox GetAABBVelocityBox(IAABBEntity entity)
|
||||
{
|
||||
var min = new Vector3(
|
||||
Math.Min(entity.BoundingBox.Min.X, entity.BoundingBox.Min.X + entity.Velocity.X),
|
||||
Math.Min(entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Y + entity.Velocity.Y),
|
||||
Math.Min(entity.BoundingBox.Min.Z, entity.BoundingBox.Min.Z + entity.Velocity.Z)
|
||||
);
|
||||
var max = new Vector3(
|
||||
Math.Max(entity.BoundingBox.Max.X, entity.BoundingBox.Max.X + entity.Velocity.X),
|
||||
Math.Max(entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Y + entity.Velocity.Y),
|
||||
Math.Max(entity.BoundingBox.Max.Z, entity.BoundingBox.Max.Z + entity.Velocity.Z)
|
||||
);
|
||||
return new BoundingBox(min, max);
|
||||
}
|
||||
|
||||
private void AdjustVelocityForCollision(IAABBEntity entity, BoundingBox problem)
|
||||
{
|
||||
var velocity = entity.Velocity;
|
||||
if (entity.Velocity.X < 0)
|
||||
velocity.X = entity.BoundingBox.Min.X - problem.Max.X;
|
||||
if (entity.Velocity.X > 0)
|
||||
velocity.X = entity.BoundingBox.Max.X - problem.Min.X;
|
||||
if (entity.Velocity.Y < 0)
|
||||
velocity.Y = entity.BoundingBox.Min.Y - problem.Max.Y;
|
||||
if (entity.Velocity.Y > 0)
|
||||
velocity.Y = entity.BoundingBox.Max.Y - problem.Min.Y;
|
||||
if (entity.Velocity.Z < 0)
|
||||
velocity.Z = entity.BoundingBox.Min.Z - problem.Max.Z;
|
||||
if (entity.Velocity.Z > 0)
|
||||
velocity.Z = entity.BoundingBox.Max.Z - problem.Min.Z;
|
||||
entity.Velocity = velocity;
|
||||
}
|
||||
|
||||
public bool TestTerrainCollisionCylinder(IAABBEntity entity, out Vector3 collisionPoint)
|
||||
{
|
||||
collisionPoint = Vector3.Zero;
|
||||
var testBox = GetAABBVelocityBox(entity);
|
||||
var testCylinder = new BoundingCylinder(testBox.Min, testBox.Max,
|
||||
entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max));
|
||||
|
||||
bool collision = false;
|
||||
for (int x = (int)(Math.Floor(testBox.Min.X)); x <= (int)(Math.Ceiling(testBox.Max.X)); x++)
|
||||
{
|
||||
for (int z = (int)(Math.Floor(testBox.Min.Z)); z <= (int)(Math.Ceiling(testBox.Max.Z)); z++)
|
||||
{
|
||||
for (int y = (int)(Math.Floor(testBox.Min.Y)); y <= (int)(Math.Ceiling(testBox.Max.Y)); y++)
|
||||
{
|
||||
var coords = new Coordinates3D(x, y, z);
|
||||
if (!World.IsValidPosition(coords))
|
||||
continue;
|
||||
|
||||
var _box = BlockPhysicsProvider.GetBoundingBox(World, coords);
|
||||
if (_box == null)
|
||||
continue;
|
||||
|
||||
var box = _box.Value.OffsetBy(coords);
|
||||
if (testCylinder.Intersects(box))
|
||||
{
|
||||
if (testBox.Intersects(box))
|
||||
{
|
||||
collision = true;
|
||||
AdjustVelocityForCollision(entity, box);
|
||||
testBox = GetAABBVelocityBox(entity);
|
||||
testCylinder = new BoundingCylinder(testBox.Min, testBox.Max,
|
||||
entity.BoundingBox.Min.DistanceTo(entity.BoundingBox.Max));
|
||||
collisionPoint = coords;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return collision;
|
||||
}
|
||||
|
||||
public bool TestTerrainCollisionY(IAABBEntity entity, out Vector3 collisionPoint)
|
||||
{
|
||||
// Things we need to do:
|
||||
|
@ -371,16 +371,6 @@
|
||||
<Name>TrueCraft.Profiling</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<MonoDevelop>
|
||||
<Properties>
|
||||
<Policies>
|
||||
<TextStylePolicy inheritsSet="VisualStudio" inheritsScope="text/plain" scope="text/x-csharp" />
|
||||
<CSharpFormattingPolicy inheritsSet="Mono" inheritsScope="text/x-csharp" scope="text/x-csharp" />
|
||||
</Policies>
|
||||
</Properties>
|
||||
</MonoDevelop>
|
||||
</ProjectExtensions>
|
||||
<ItemGroup>
|
||||
<Folder Include="Lighting\" />
|
||||
<Folder Include="AI\" />
|
||||
|
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft.Core.Test", "True
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft.Profiling", "TrueCraft.Profiling\TrueCraft.Profiling.csproj", "{BCA0E139-CF47-43B3-9DC9-D4611C0A2AAD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCraft.API.Test", "TrueCraft.Core.Test\TrueCraft.API.Test\TrueCraft.API.Test.csproj", "{90712322-5904-4BCE-8606-C662706B8EAE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -76,9 +78,16 @@ Global
|
||||
{FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Optimized Debug|Any CPU.Build.0 = Optimized Debug|Any CPU
|
||||
{FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FEE55B54-91B0-4325-A2C3-D576C0B7A81F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Optimized Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Optimized Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{BCFDCD93-C23E-49E6-9767-A887B3C2A709} = {6756B61E-5856-4CA7-90B5-6053763FE7BA}
|
||||
{90712322-5904-4BCE-8606-C662706B8EAE} = {6756B61E-5856-4CA7-90B5-6053763FE7BA}
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
@ -114,6 +123,26 @@ Global
|
||||
$2.inheritsSet = Mono
|
||||
$2.inheritsScope = text/x-csharp
|
||||
$2.scope = text/x-csharp
|
||||
$2.IndentSwitchSection = True
|
||||
$2.NewLinesForBracesInProperties = True
|
||||
$2.NewLinesForBracesInAccessors = True
|
||||
$2.NewLinesForBracesInAnonymousMethods = True
|
||||
$2.NewLinesForBracesInControlBlocks = True
|
||||
$2.NewLinesForBracesInAnonymousTypes = True
|
||||
$2.NewLinesForBracesInObjectCollectionArrayInitializers = True
|
||||
$2.NewLinesForBracesInLambdaExpressionBody = True
|
||||
$2.NewLineForElse = True
|
||||
$2.NewLineForCatch = True
|
||||
$2.NewLineForFinally = True
|
||||
$2.NewLineForMembersInObjectInit = True
|
||||
$2.NewLineForMembersInAnonymousTypes = True
|
||||
$2.NewLineForClausesInQuery = True
|
||||
$2.SpacingAfterMethodDeclarationName = False
|
||||
$2.SpaceAfterMethodCallName = False
|
||||
$2.SpaceBeforeOpenSquareBracket = False
|
||||
$0.DotNetNamingPolicy = $3
|
||||
$3.DirectoryNamespaceAssociation = PrefixedHierarchical
|
||||
$3.ResourceNamePolicy = FileFormatDefault
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
Loading…
x
Reference in New Issue
Block a user