Major collision refactor to correct AABB system
This commit is contained in:
231
AABB.cs
231
AABB.cs
@@ -1,9 +1,4 @@
|
|||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Voxel
|
namespace Voxel
|
||||||
{
|
{
|
||||||
@@ -18,12 +13,53 @@ namespace Voxel
|
|||||||
Max = max;
|
Max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AABB Copy()
|
||||||
|
{
|
||||||
|
return new AABB(Min, Max);
|
||||||
|
}
|
||||||
|
|
||||||
public static AABB FromCenter(Vector3 center, float width, float height, float depth)
|
public static AABB FromCenter(Vector3 center, float width, float height, float depth)
|
||||||
{
|
{
|
||||||
Vector3 half = new Vector3(width / 2f, 0, depth / 2f);
|
Vector3 half = new Vector3(width / 2f, 0, depth / 2f);
|
||||||
return new AABB(center - half, center + new Vector3(half.X, height, half.Z));
|
return new AABB(center - half, center + new Vector3(half.X, height, half.Z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AABB Expand(float x, float y, float z)
|
||||||
|
{
|
||||||
|
AABB returnValue = Copy();
|
||||||
|
|
||||||
|
if (x > 0)
|
||||||
|
returnValue.Max.X += x;
|
||||||
|
else
|
||||||
|
returnValue.Min.X += x;
|
||||||
|
|
||||||
|
if (y > 0)
|
||||||
|
returnValue.Max.Y += y;
|
||||||
|
else
|
||||||
|
returnValue.Min.Y += y;
|
||||||
|
|
||||||
|
if (z > 0)
|
||||||
|
returnValue.Max.Z += z;
|
||||||
|
else
|
||||||
|
returnValue.Min.Z += z;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AABB Grow(float x, float y, float z)
|
||||||
|
{
|
||||||
|
AABB returnValue = Copy();
|
||||||
|
|
||||||
|
returnValue.Min.X -= x;
|
||||||
|
returnValue.Min.Y -= y;
|
||||||
|
returnValue.Min.Z -= z;
|
||||||
|
returnValue.Max.X += x;
|
||||||
|
returnValue.Max.Y += y;
|
||||||
|
returnValue.Max.Z += z;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
public void Move(float x, float y, float z)
|
public void Move(float x, float y, float z)
|
||||||
{
|
{
|
||||||
Min.X += x;
|
Min.X += x;
|
||||||
@@ -34,26 +70,6 @@ namespace Voxel
|
|||||||
Max.Z += z;
|
Max.Z += z;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Intersects(AABB other)
|
|
||||||
{
|
|
||||||
return IntersectsX(other) && IntersectsY(other) && IntersectsZ(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IntersectsX(AABB other)
|
|
||||||
{
|
|
||||||
return Min.X < other.Max.X && Max.X > other.Max.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IntersectsY(AABB other)
|
|
||||||
{
|
|
||||||
return Min.Y < other.Max.Y && Max.Y > other.Max.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IntersectsZ(AABB other)
|
|
||||||
{
|
|
||||||
return Min.Z < other.Max.Z && Max.Z > other.Max.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Contains(Vector3 point)
|
public bool Contains(Vector3 point)
|
||||||
{
|
{
|
||||||
return (point.X >= Min.X && point.X <= Max.X) &&
|
return (point.X >= Min.X && point.X <= Max.X) &&
|
||||||
@@ -61,151 +77,90 @@ namespace Voxel
|
|||||||
(point.Z >= Min.Z && point.Z <= Max.Z);
|
(point.Z >= Min.Z && point.Z <= Max.Z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetClipX(AABB other, float deltaX)
|
public bool IntersectsX(AABB against)
|
||||||
{
|
{
|
||||||
if (IntersectsY(other) && IntersectsZ(other))
|
return Min.X < against.Max.X && Max.X > against.Min.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IntersectsY(AABB against)
|
||||||
|
{
|
||||||
|
return Min.Y < against.Max.Y && Max.Y > against.Min.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IntersectsZ(AABB against)
|
||||||
|
{
|
||||||
|
return Min.Z < against.Max.Z && Max.Z > against.Min.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Intersects(AABB against)
|
||||||
|
{
|
||||||
|
return IntersectsX(against) && IntersectsY(against) && IntersectsZ(against);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetClipX(AABB against, float deltaX)
|
||||||
|
{
|
||||||
|
if (IntersectsY(against) && IntersectsZ(against))
|
||||||
{
|
{
|
||||||
if (deltaX > 0 && Max.X <= other.Min.X)
|
if (deltaX > 0 && Max.X <= against.Min.X)
|
||||||
{
|
{
|
||||||
float clip = other.Min.X - other.Min.X;
|
float clip = against.Min.X - Max.X;
|
||||||
|
if (deltaX > clip) deltaX = clip;
|
||||||
if (deltaX > clip)
|
|
||||||
deltaX = clip;
|
|
||||||
}
|
}
|
||||||
|
if (deltaX < 0 && Min.X >= against.Max.X)
|
||||||
if (deltaX < 0 && Min.X >= other.Max.X)
|
|
||||||
{
|
{
|
||||||
float clip = other.Max.X - Max.X;
|
float clip = against.Max.X - Min.X;
|
||||||
if (deltaX < clip)
|
if (deltaX < clip) deltaX = clip;
|
||||||
deltaX = clip;
|
|
||||||
}
|
}
|
||||||
return deltaX;
|
return deltaX;
|
||||||
}
|
}
|
||||||
return deltaX;
|
return deltaX;
|
||||||
}
|
}
|
||||||
public float GetClipY(AABB other, float deltaY)
|
|
||||||
|
public float GetClipY(AABB against, float deltaY)
|
||||||
{
|
{
|
||||||
if (IntersectsX(other) && IntersectsZ(other))
|
if (IntersectsX(against) && IntersectsZ(against))
|
||||||
{
|
{
|
||||||
if (deltaY > 0 && Max.Y <= other.Min.Y)
|
if (deltaY > 0 && Max.Y <= against.Min.Y)
|
||||||
{
|
{
|
||||||
float clip = other.Min.Y - other.Min.Y;
|
float clip = against.Min.Y - Max.Y;
|
||||||
|
if (deltaY > clip) deltaY = clip;
|
||||||
if (deltaY > clip)
|
|
||||||
deltaY = clip;
|
|
||||||
}
|
}
|
||||||
|
if (deltaY < 0 && Min.Y >= against.Max.Y)
|
||||||
if (deltaY < 0 && Min.X >= other.Max.X)
|
|
||||||
{
|
{
|
||||||
float clip = other.Max.X - Max.X;
|
float clip = against.Max.Y - Min.Y;
|
||||||
if (deltaY < clip)
|
if (deltaY < clip) deltaY = clip;
|
||||||
deltaY = clip;
|
|
||||||
}
|
}
|
||||||
return deltaY;
|
return deltaY;
|
||||||
}
|
}
|
||||||
return deltaY;
|
return deltaY;
|
||||||
}
|
}
|
||||||
public float GetClipZ(AABB other, float deltaZ)
|
|
||||||
|
public float GetClipZ(AABB against, float deltaZ)
|
||||||
{
|
{
|
||||||
if (IntersectsX(other) && IntersectsY(other))
|
if (IntersectsX(against) && IntersectsY(against))
|
||||||
{
|
{
|
||||||
if (deltaZ > 0 && Max.Z <= other.Min.Z)
|
if (deltaZ > 0 && Max.Z <= against.Min.Z)
|
||||||
{
|
{
|
||||||
float clip = other.Min.Z - other.Min.Z;
|
float clip = against.Min.Z - Max.Z;
|
||||||
|
if (deltaZ > clip) deltaZ = clip;
|
||||||
if (deltaZ > clip)
|
|
||||||
deltaZ = clip;
|
|
||||||
}
|
}
|
||||||
|
if (deltaZ < 0 && Min.Z >= against.Max.Z)
|
||||||
if (deltaZ < 0 && Min.Z >= other.Max.Z)
|
|
||||||
{
|
{
|
||||||
float clip = other.Max.Z - Max.Z;
|
float clip = against.Max.Z - Min.Z;
|
||||||
if (deltaZ < clip)
|
if (deltaZ < clip) deltaZ = clip;
|
||||||
deltaZ = clip;
|
|
||||||
}
|
}
|
||||||
return deltaZ;
|
return deltaZ;
|
||||||
}
|
}
|
||||||
return deltaZ;
|
return deltaZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetBlockClipX(int blockX, int blockY, int blockZ, float deltaX)
|
public Vector3 GetCenter()
|
||||||
{
|
{
|
||||||
// Use the current AABB as the collider (no new allocation)
|
return new Vector3(
|
||||||
AABB collider = this;
|
(Min.X + Max.X) / 2,
|
||||||
|
(Min.Y + Max.Y) / 2,
|
||||||
// Block bounds
|
(Min.Z + Max.Z) / 2
|
||||||
float blockMinX = blockX;
|
);
|
||||||
float blockMaxX = blockX + 1;
|
|
||||||
|
|
||||||
// Quick reject: if player is not overlapping the block in Y and Z, no collision.
|
|
||||||
if (!(collider.Min.Y < blockY + 1 && collider.Max.Y > blockY) ||
|
|
||||||
!(collider.Min.Z < blockZ + 1 && collider.Max.Z > blockZ))
|
|
||||||
return deltaX;
|
|
||||||
|
|
||||||
// Moving right (positive delta)
|
|
||||||
if (deltaX > 0 && collider.Max.X <= blockMinX)
|
|
||||||
{
|
|
||||||
float clip = blockMinX - collider.Max.X;
|
|
||||||
if (deltaX > clip) deltaX = clip;
|
|
||||||
}
|
|
||||||
// Moving left (negative delta)
|
|
||||||
else if (deltaX < 0 && collider.Min.X >= blockMaxX)
|
|
||||||
{
|
|
||||||
float clip = blockMaxX - collider.Min.X; // negative distance
|
|
||||||
if (deltaX < clip) deltaX = clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
return deltaX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetBlockClipY(int blockX, int blockY, int blockZ, float deltaY)
|
|
||||||
{
|
|
||||||
AABB collider = this;
|
|
||||||
|
|
||||||
float blockMinY = blockY;
|
|
||||||
float blockMaxY = blockY + 1;
|
|
||||||
|
|
||||||
if (!(collider.Min.X < blockX + 1 && collider.Max.X > blockX) ||
|
|
||||||
!(collider.Min.Z < blockZ + 1 && collider.Max.Z > blockZ))
|
|
||||||
return deltaY;
|
|
||||||
|
|
||||||
if (deltaY > 0 && collider.Max.Y <= blockMinY)
|
|
||||||
{
|
|
||||||
float clip = blockMinY - collider.Max.Y;
|
|
||||||
if (deltaY > clip) deltaY = clip;
|
|
||||||
}
|
|
||||||
else if (deltaY < 0 && collider.Min.Y >= blockMaxY)
|
|
||||||
{
|
|
||||||
float clip = blockMaxY - collider.Min.Y;
|
|
||||||
if (deltaY < clip) deltaY = clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
return deltaY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetBlockClipZ(int blockX, int blockY, int blockZ, float deltaZ)
|
|
||||||
{
|
|
||||||
AABB collider = this;
|
|
||||||
|
|
||||||
float blockMinZ = blockZ;
|
|
||||||
float blockMaxZ = blockZ + 1;
|
|
||||||
|
|
||||||
if (!(collider.Min.X < blockX + 1 && collider.Max.X > blockX) ||
|
|
||||||
!(collider.Min.Y < blockY + 1 && collider.Max.Y > blockY))
|
|
||||||
return deltaZ;
|
|
||||||
|
|
||||||
if (deltaZ > 0 && collider.Max.Z <= blockMinZ)
|
|
||||||
{
|
|
||||||
float clip = blockMinZ - collider.Max.Z;
|
|
||||||
if (deltaZ > clip) deltaZ = clip;
|
|
||||||
}
|
|
||||||
else if (deltaZ < 0 && collider.Min.Z >= blockMaxZ)
|
|
||||||
{
|
|
||||||
float clip = blockMaxZ - collider.Min.Z;
|
|
||||||
if (deltaZ < clip) deltaZ = clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
return deltaZ;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Blocks.cs
12
Blocks.cs
@@ -93,12 +93,12 @@
|
|||||||
|
|
||||||
{ Voxel.Blocks.TNT, new BlockDefinition(
|
{ Voxel.Blocks.TNT, new BlockDefinition(
|
||||||
Voxel.Blocks.TNT,
|
Voxel.Blocks.TNT,
|
||||||
Voxel.Textures.TntSide, // West
|
Voxel.Textures.TntSide, // West
|
||||||
Voxel.Textures.TntSide, // East
|
Voxel.Textures.TntSide, // East
|
||||||
Voxel.Textures.TntTop, // Top
|
Voxel.Textures.TntTop, // Top
|
||||||
Voxel.Textures.TntBottom, // Bottom
|
Voxel.Textures.TntBottom, // Bottom
|
||||||
Voxel.Textures.TntSide, // North
|
Voxel.Textures.TntSide, // North
|
||||||
Voxel.Textures.TntSide // South
|
Voxel.Textures.TntSide // South
|
||||||
)},
|
)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
389
Entity.cs
389
Entity.cs
@@ -24,11 +24,6 @@ namespace Voxel
|
|||||||
private float _yMultiplier = 0.98f;
|
private float _yMultiplier = 0.98f;
|
||||||
private float _groundMultiplier = 0.6f;
|
private float _groundMultiplier = 0.6f;
|
||||||
|
|
||||||
private const float COLLISION_EPSILON = 0.1f;
|
|
||||||
|
|
||||||
private static int BlockCoordMin(float coord) => (int)MathF.Floor(coord - 1e-2f);
|
|
||||||
private static int BlockCoordMax(float coord) => (int)MathF.Floor(coord + 1e-2f);
|
|
||||||
|
|
||||||
protected World _world;
|
protected World _world;
|
||||||
|
|
||||||
public Entity(Vector3 position, float width, float height, World world)
|
public Entity(Vector3 position, float width, float height, World world)
|
||||||
@@ -41,12 +36,7 @@ namespace Voxel
|
|||||||
|
|
||||||
public void Tick()
|
public void Tick()
|
||||||
{
|
{
|
||||||
var newPosition = Position;
|
Move();
|
||||||
var desiredMovement = Velocity;
|
|
||||||
CheckAndResolveCollisions(ref newPosition, desiredMovement);
|
|
||||||
Position = newPosition;
|
|
||||||
|
|
||||||
//Move();
|
|
||||||
|
|
||||||
if (!OnGround)
|
if (!OnGround)
|
||||||
{
|
{
|
||||||
@@ -66,398 +56,61 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
Velocity = new Vector3(Velocity.X * _groundMultiplier, 0f, Velocity.Z * _groundMultiplier);
|
Velocity = new Vector3(Velocity.X * _groundMultiplier, 0f, Velocity.Z * _groundMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateOnGround();
|
|
||||||
|
|
||||||
Console.WriteLine(Position.Y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Move()
|
public void Move()
|
||||||
{
|
{
|
||||||
Vector3 moveVector = Velocity;
|
Vector3 moveVector = Velocity;
|
||||||
Vector3 original = moveVector;
|
Vector3 originalVector = moveVector;
|
||||||
|
|
||||||
AABB collider = new AABB(
|
AABB body = AABB.FromCenter(Position, Width, Height, Width);
|
||||||
Position - new Vector3(Position.X - Width / 2, 0, Position.Z - Width / 2),
|
|
||||||
Position - new Vector3(Position.X + Width / 2, Height, Position.Z + Width / 2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CheckAndResolveCollisions(ref Vector3 position, Vector3 movement)
|
List<AABB> collisionChecks = _world.GetColliders(body.Expand(Velocity.X, Velocity.Y, Velocity.Z).Grow(1,1,1));
|
||||||
{
|
|
||||||
Vector3 originalPosition = position;
|
|
||||||
AABB futureBox = GetBoundingBoxAt(originalPosition + movement);
|
|
||||||
|
|
||||||
if (!HasCollision(futureBox))
|
foreach (AABB collider in collisionChecks)
|
||||||
{
|
{
|
||||||
position = originalPosition + movement;
|
moveVector.X = body.GetClipX(collider, moveVector.X);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
position = originalPosition;
|
body.Move(moveVector.X, 0, 0);
|
||||||
|
|
||||||
ResolveYCollisionIndependent(ref position, movement.Y);
|
foreach (AABB collider in collisionChecks)
|
||||||
|
|
||||||
Vector3 tempPosX = new Vector3(position.X + movement.X, position.Y, position.Z);
|
|
||||||
if (!HasCollision(GetBoundingBoxAt(tempPosX)))
|
|
||||||
{
|
{
|
||||||
position.X = tempPosX.X;
|
moveVector.Y = body.GetClipY(collider, moveVector.Y);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResolveXCollisionIndependent(ref position, movement.X);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 tempPosZ = new Vector3(position.X, position.Y, position.Z + movement.Z);
|
body.Move(0, moveVector.Y, 0);
|
||||||
if (!HasCollision(GetBoundingBoxAt(tempPosZ)))
|
|
||||||
{
|
|
||||||
position.Z = tempPosZ.Z;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResolveZCollisionIndependent(ref position, movement.Z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResolveYCollisionIndependent(ref Vector3 position, float velocityY)
|
foreach (AABB collider in collisionChecks)
|
||||||
{
|
|
||||||
if (velocityY == 0) return;
|
|
||||||
|
|
||||||
Vector3 testPos = new Vector3(position.X, position.Y + velocityY, position.Z);
|
|
||||||
AABB testBox = GetBoundingBoxAt(testPos);
|
|
||||||
|
|
||||||
if (HasCollision(testBox))
|
|
||||||
{
|
{
|
||||||
if (velocityY > 0)
|
moveVector.Z = body.GetClipZ(collider, moveVector.Z);
|
||||||
{
|
|
||||||
float ceilingY = GetCeilingHeight(testBox);
|
|
||||||
position.Y = ceilingY - (Height / 2);
|
|
||||||
Velocity.Y = 0;
|
|
||||||
}
|
|
||||||
else // Hitting floor
|
|
||||||
{
|
|
||||||
float floorY = GetFloorHeight(testBox);
|
|
||||||
position.Y = floorY + (Height / 2);
|
|
||||||
Velocity.Y = 0;
|
|
||||||
OnGround = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
position.Y += velocityY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResolveXCollisionIndependent(ref Vector3 position, float velocityX)
|
|
||||||
{
|
|
||||||
if (velocityX == 0) return;
|
|
||||||
|
|
||||||
// Check if we're already inside a block at current position
|
|
||||||
AABB currentBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, position.Z));
|
|
||||||
if (HasCollision(currentBox))
|
|
||||||
{
|
|
||||||
// Already inside a block, allow movement to escape
|
|
||||||
position.X += velocityX;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float direction = Math.Sign(velocityX);
|
body.Move(0, 0, moveVector.Z);
|
||||||
float checkDistance = Math.Abs(velocityX) + COLLISION_EPSILON;
|
|
||||||
|
|
||||||
for (float offset = 0; offset <= checkDistance; offset += 0.1f)
|
if (moveVector.X != originalVector.X)
|
||||||
{
|
{
|
||||||
float testX = position.X + (offset * direction);
|
Velocity.X = 0; Console.WriteLine("Collided X");
|
||||||
AABB testBox = GetBoundingBoxAt(new Vector3(testX, position.Y, position.Z));
|
|
||||||
|
|
||||||
if (HasCollision(testBox))
|
|
||||||
{
|
|
||||||
if (direction > 0)
|
|
||||||
{
|
|
||||||
position.X = GetRightWallPosition(testBox) - (Width / 2) - COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
position.X = GetLeftWallPosition(testBox) + (Width / 2) + COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
Velocity.X = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No collision found, apply full movement
|
if (moveVector.Y != originalVector.Y)
|
||||||
position.X += velocityX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResolveZCollisionIndependent(ref Vector3 position, float velocityZ)
|
|
||||||
{
|
|
||||||
if (velocityZ == 0) return;
|
|
||||||
|
|
||||||
// Check if we're already inside a block at current position
|
|
||||||
AABB currentBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, position.Z));
|
|
||||||
if (HasCollision(currentBox))
|
|
||||||
{
|
{
|
||||||
// Already inside a block, allow movement to escape
|
Velocity.Y = 0;
|
||||||
position.Z += velocityZ;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float direction = Math.Sign(velocityZ);
|
if (moveVector.Z != originalVector.Z)
|
||||||
float checkDistance = Math.Abs(velocityZ) + COLLISION_EPSILON;
|
|
||||||
|
|
||||||
for (float offset = 0; offset <= checkDistance; offset += 0.01f)
|
|
||||||
{
|
{
|
||||||
float testZ = position.Z + (offset * direction);
|
Velocity.Z = 0; Console.WriteLine("Collided Z");
|
||||||
AABB testBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, testZ));
|
|
||||||
|
|
||||||
if (HasCollision(testBox))
|
|
||||||
{
|
|
||||||
if (direction > 0) // Moving forward
|
|
||||||
{
|
|
||||||
position.Z = GetFrontWallPosition(testBox) - (Width / 2);
|
|
||||||
}
|
|
||||||
else // Moving backward
|
|
||||||
{
|
|
||||||
position.Z = GetBackWallPosition(testBox) + (Width / 2);
|
|
||||||
}
|
|
||||||
Velocity.Z = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No collision found, apply full movement
|
OnGround = moveVector.Y != originalVector.Y && originalVector.Y < 0;
|
||||||
position.Z += velocityZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetFloorHeight(AABB box)
|
Position = new Vector3(body.GetCenter().X, body.Min.Y, body.GetCenter().Z);
|
||||||
{
|
|
||||||
int minX = BlockCoordMin(box.Min.X);
|
|
||||||
int maxX = BlockCoordMax(box.Max.X);
|
|
||||||
int minZ = BlockCoordMin(box.Min.Z);
|
|
||||||
int maxZ = BlockCoordMax(box.Max.Z);
|
|
||||||
int checkY = BlockCoordMin(box.Min.Y);
|
|
||||||
|
|
||||||
float highestFloor = float.MinValue;
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, checkY, z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
highestFloor = Math.Max(highestFloor, checkY + 1); // Top of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return highestFloor != float.MinValue ? highestFloor : box.Min.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetCeilingHeight(AABB box)
|
|
||||||
{
|
|
||||||
int minX = BlockCoordMin(box.Min.X);
|
|
||||||
int maxX = BlockCoordMax(box.Max.X);
|
|
||||||
int minZ = BlockCoordMin(box.Min.Z);
|
|
||||||
int maxZ = BlockCoordMax(box.Max.Z);
|
|
||||||
int checkY = BlockCoordMax(box.Max.Y);
|
|
||||||
|
|
||||||
float lowestCeiling = float.MaxValue;
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, checkY, z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
lowestCeiling = Math.Min(lowestCeiling, checkY); // Bottom of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lowestCeiling != float.MaxValue ? lowestCeiling : box.Max.Y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetLeftWallPosition(AABB box)
|
|
||||||
{
|
|
||||||
int minY = BlockCoordMin(box.Min.Y);
|
|
||||||
int maxY = BlockCoordMax(box.Max.Y);
|
|
||||||
int minZ = BlockCoordMin(box.Min.Z);
|
|
||||||
int maxZ = BlockCoordMax(box.Max.Z);
|
|
||||||
int checkX = BlockCoordMin(box.Min.X);
|
|
||||||
|
|
||||||
float rightmostWall = float.MinValue;
|
|
||||||
|
|
||||||
for (int y = minY; y <= maxY; y++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(checkX, y, z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
rightmostWall = Math.Max(rightmostWall, checkX + 1); // Right side of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rightmostWall != float.MinValue ? rightmostWall : box.Min.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetRightWallPosition(AABB box)
|
|
||||||
{
|
|
||||||
int minY = BlockCoordMin(box.Min.Y);
|
|
||||||
int maxY = BlockCoordMax(box.Max.Y);
|
|
||||||
int minZ = BlockCoordMin(box.Min.Z);
|
|
||||||
int maxZ = BlockCoordMax(box.Max.Z);
|
|
||||||
int checkX = BlockCoordMax(box.Max.X);
|
|
||||||
|
|
||||||
float leftmostWall = float.MaxValue;
|
|
||||||
|
|
||||||
for (int y = minY; y <= maxY; y++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(checkX, y, z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
leftmostWall = Math.Min(leftmostWall, checkX); // Left side of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return leftmostWall != float.MaxValue ? leftmostWall : box.Max.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetBackWallPosition(AABB box)
|
|
||||||
{
|
|
||||||
int minX = BlockCoordMin(box.Min.X);
|
|
||||||
int maxX = BlockCoordMax(box.Max.X);
|
|
||||||
int minY = BlockCoordMin(box.Min.Y);
|
|
||||||
int maxY = BlockCoordMax(box.Max.Y);
|
|
||||||
int checkZ = BlockCoordMin(box.Min.Z);
|
|
||||||
|
|
||||||
float frontmostWall = float.MinValue;
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int y = minY; y <= maxY; y++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, y, checkZ);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
frontmostWall = Math.Max(frontmostWall, checkZ + 1); // Front side of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return frontmostWall != float.MinValue ? frontmostWall : box.Min.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetFrontWallPosition(AABB box)
|
|
||||||
{
|
|
||||||
int minX = BlockCoordMin(box.Min.X);
|
|
||||||
int maxX = BlockCoordMax(box.Max.X);
|
|
||||||
int minY = BlockCoordMin(box.Min.Y);
|
|
||||||
int maxY = BlockCoordMax(box.Max.Y);
|
|
||||||
int checkZ = BlockCoordMax(box.Max.Z);
|
|
||||||
|
|
||||||
float backmostWall = float.MaxValue;
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int y = minY; y <= maxY; y++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, y, checkZ);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
backmostWall = Math.Min(backmostWall, checkZ); // Back side of the block
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return backmostWall != float.MaxValue ? backmostWall : box.Max.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasCollision(AABB box)
|
|
||||||
{
|
|
||||||
int minX = BlockCoordMin(box.Min.X);
|
|
||||||
int maxX = BlockCoordMax(box.Max.X);
|
|
||||||
int minY = BlockCoordMin(box.Min.Y);
|
|
||||||
int maxY = BlockCoordMax(box.Max.Y);
|
|
||||||
int minZ = BlockCoordMin(box.Min.Z);
|
|
||||||
int maxZ = BlockCoordMax(box.Max.Z);
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int y = minY; y <= maxY; y++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, y, z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyImpulse(Vector3 force)
|
public void ApplyImpulse(Vector3 force)
|
||||||
{
|
{
|
||||||
Velocity += force;
|
Velocity += force;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AABB GetBoundingBox()
|
|
||||||
{
|
|
||||||
return GetBoundingBoxAt(Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AABB GetBoundingBoxAt(Vector3 position)
|
|
||||||
{
|
|
||||||
float halfWidth = Width / 2;
|
|
||||||
float halfHeight = Height / 2;
|
|
||||||
|
|
||||||
Vector3 min = new Vector3(
|
|
||||||
position.X - halfWidth,
|
|
||||||
position.Y - halfHeight, // Center Y minus half height
|
|
||||||
position.Z - halfWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
Vector3 max = new Vector3(
|
|
||||||
position.X + halfWidth,
|
|
||||||
position.Y + halfHeight, // Center Y plus half height
|
|
||||||
position.Z + halfWidth
|
|
||||||
);
|
|
||||||
|
|
||||||
return new AABB(min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateOnGround()
|
|
||||||
{
|
|
||||||
AABB box = GetBoundingBox();
|
|
||||||
float yCheck = box.Min.Y - COLLISION_EPSILON;
|
|
||||||
|
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
|
||||||
|
|
||||||
OnGround = false;
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
|
||||||
{
|
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
|
||||||
{
|
|
||||||
Blocks block = _world.GetBlock(x, (int)MathF.Floor(yCheck), z);
|
|
||||||
if (block != Blocks.Air)
|
|
||||||
{
|
|
||||||
OnGround = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ namespace Voxel
|
|||||||
|
|
||||||
public void Update(float deltaTime, float alpha)
|
public void Update(float deltaTime, float alpha)
|
||||||
{
|
{
|
||||||
Camera.Position = Vector3.Lerp(previousPosition, Position, alpha) + Vector3.UnitY * 0.62f;
|
Camera.Position = Vector3.Lerp(previousPosition, Position, alpha) + Vector3.UnitY * 1.62f;
|
||||||
Rotation = Camera.Yaw;
|
Rotation = Camera.Yaw;
|
||||||
|
|
||||||
if (lastClick > 0)
|
if (lastClick > 0)
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ namespace Voxel
|
|||||||
Renderer.Render();
|
Renderer.Render();
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
|
Thread.Sleep(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
|
protected override void OnFramebufferResize(FramebufferResizeEventArgs e)
|
||||||
|
|||||||
35
World.cs
35
World.cs
@@ -1,6 +1,4 @@
|
|||||||
using OpenTK.Mathematics;
|
using OpenTK.Mathematics;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
|
|
||||||
namespace Voxel
|
namespace Voxel
|
||||||
{
|
{
|
||||||
@@ -62,22 +60,17 @@ namespace Voxel
|
|||||||
int centerX = (int)Math.Floor(playerPosition.X / Chunk.Size);
|
int centerX = (int)Math.Floor(playerPosition.X / Chunk.Size);
|
||||||
int centerZ = (int)Math.Floor(playerPosition.Z / Chunk.Size);
|
int centerZ = (int)Math.Floor(playerPosition.Z / Chunk.Size);
|
||||||
|
|
||||||
// Quick check - skip if still in same chunk
|
|
||||||
if ((centerX == _lastCenter.x && centerZ == _lastCenter.z) && chunkLoadingInitialized)
|
if ((centerX == _lastCenter.x && centerZ == _lastCenter.z) && chunkLoadingInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_lastCenter = (centerX, centerZ);
|
_lastCenter = (centerX, centerZ);
|
||||||
|
|
||||||
// Calculate bounds
|
|
||||||
int minX = centerX - _loadDistance;
|
int minX = centerX - _loadDistance;
|
||||||
int maxX = centerX + _loadDistance;
|
int maxX = centerX + _loadDistance;
|
||||||
int minZ = centerZ - _loadDistance;
|
int minZ = centerZ - _loadDistance;
|
||||||
int maxZ = centerZ + _loadDistance;
|
int maxZ = centerZ + _loadDistance;
|
||||||
|
|
||||||
// Unload chunks outside range
|
|
||||||
UnloadDistantChunks(minX, maxX, minZ, maxZ);
|
UnloadDistantChunks(minX, maxX, minZ, maxZ);
|
||||||
|
|
||||||
// Load chunks inside range
|
|
||||||
LoadChunksInRange(minX, maxX, minZ, maxZ);
|
LoadChunksInRange(minX, maxX, minZ, maxZ);
|
||||||
|
|
||||||
chunkLoadingInitialized = true;
|
chunkLoadingInitialized = true;
|
||||||
@@ -347,9 +340,35 @@ namespace Voxel
|
|||||||
return (false, Blocks.Air, 0, 0, 0, Vector3i.Zero);
|
return (false, Blocks.Air, 0, 0, 0, Vector3i.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public List<AABB> GetColliders(AABB body)
|
||||||
{
|
{
|
||||||
|
List<AABB> collisions = new List<AABB>();
|
||||||
|
|
||||||
|
int minX = (int)Math.Floor(body.Min.X);
|
||||||
|
int maxX = (int)Math.Ceiling(body.Max.X) - 1;
|
||||||
|
int minY = (int)Math.Floor(body.Min.Y);
|
||||||
|
int maxY = (int)Math.Ceiling(body.Max.Y) - 1;
|
||||||
|
int minZ = (int)Math.Floor(body.Min.Z);
|
||||||
|
int maxZ = (int)Math.Ceiling(body.Max.Z) - 1;
|
||||||
|
|
||||||
|
for (int x = minX; x <= maxX; x++)
|
||||||
|
{
|
||||||
|
for (int y = minY; y <= maxY; y++)
|
||||||
|
{
|
||||||
|
for (int z = minZ; z <= maxZ; z++)
|
||||||
|
{
|
||||||
|
if (GetBlock(x, y, z) != Blocks.Air)
|
||||||
|
{
|
||||||
|
collisions.Add(new AABB(
|
||||||
|
new Vector3(x, y, z),
|
||||||
|
new Vector3(x + 1, y + 1, z + 1)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return collisions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user