Do more accurate model picking with rotated AABBs, make sure to only return closest player in model picking.

This commit is contained in:
UnknownShadow200 2015-07-04 14:26:19 +10:00
parent e23b5aeda5
commit 43a7e35d64
6 changed files with 35 additions and 31 deletions

View File

@ -42,23 +42,6 @@ namespace ClassicalSharp {
}
}
public virtual BoundingBox PickingBounds {
get {
BoundingBox bb = Model.PickingBounds;
float angle = YawRadians;
// TODO: This would be a lot simpler and more accurate if we just did ray-oobb intersection.
Vector3 x1z1 = Utils.RotateY( bb.Min.X, 0, bb.Min.Z, angle );
Vector3 x1z2 = Utils.RotateY( bb.Min.X, 0, bb.Max.Z, angle );
Vector3 x2z1 = Utils.RotateY( bb.Max.X, 0, bb.Min.Z, angle );
Vector3 x2z2 = Utils.RotateY( bb.Max.X, 0, bb.Max.Z, angle );
float minX = Math.Min( x1z1.X, Math.Min( x1z2.X, Math.Min( x2z2.X, x2z1.X ) ) );
float maxX = Math.Max( x1z1.X, Math.Max( x1z2.X, Math.Max( x2z2.X, x2z1.X ) ) );
float minZ = Math.Min( x1z1.Z, Math.Min( x1z2.Z, Math.Min( x2z2.Z, x2z1.Z ) ) );
float maxZ = Math.Max( x1z1.Z, Math.Max( x1z2.Z, Math.Max( x2z2.Z, x2z1.Z ) ) );
return new BoundingBox( minX, bb.Min.Y, minZ, maxX, bb.Max.Y, maxZ ).Offset( Position );
}
}
public abstract void Despawn();
public abstract void Render( double deltaTime, float t );

View File

@ -33,17 +33,18 @@ namespace ClassicalSharp {
}
public byte GetClosetPlayer( Vector3 eyePos, Vector3 dir ) {
float dist = float.PositiveInfinity;
float closestDist = float.PositiveInfinity;
byte targetId = 255;
for( int i = 0; i < Players.Length - 1; i++ ) { // -1 because we don't want to pick against local player
Player p = Players[i];
if( p == null ) continue;
BoundingBox bounds = p.PickingBounds;
float t0, t1;
if( IntersectionUtils.RayIntersectsBox( eyePos, dir, bounds.Min, bounds.Max, out t0, out t1 ) ) {
dist = t0;
targetId = (byte)i;
if( Intersection.RayIntersectsRotatedBox( eyePos, dir, p, out t0, out t1 ) ) {
if( t0 < closestDist ) {
closestDist = t0;
targetId = (byte)i;
}
}
}
return targetId;

View File

@ -4,7 +4,21 @@ using OpenTK;
namespace ClassicalSharp {
public static class IntersectionUtils {
public static class Intersection {
internal static bool RayIntersectsRotatedBox( Vector3 origin, Vector3 dir, Player target, out float tMin, out float tMax ) {
// This is the rotated AABB of the model
// *
// / \ we then perform a counter *---* and we can then do
// ====>* x * rotation to the rotated AABB | x | a standard AABB test
// \ / and ray origin and direction *---* with the rotated ray
// * /
// /
Vector3 rotatedOrigin = target.Position + Utils.RotateY( origin - target.Position, -target.YawRadians );
Vector3 rotatedDir = Utils.RotateY( dir, -target.YawRadians );
BoundingBox bb = target.Model.PickingBounds.Offset( target.Position );
return RayIntersectsBox( rotatedOrigin, rotatedDir, bb.Min, bb.Max, out tMin, out tMax );
}
//http://www.cs.utah.edu/~awilliam/box/box.pdf
public static bool RayIntersectsBox( Vector3 origin, Vector3 dir, Vector3 min, Vector3 max,

View File

@ -90,7 +90,7 @@ namespace ClassicalSharp {
// This cell falls on the path of the ray. Now perform an additional bounding box test,
// since some blocks do not occupy a whole cell.
float t0, t1;
if( IntersectionUtils.RayIntersectsBox( origin, dir, min, max, out t0, out t1 ) ) {
if( Intersection.RayIntersectsBox( origin, dir, min, max, out t0, out t1 ) ) {
pickedPos.UpdateBlockPos( min, max, origin, dir, t0, t1 );
return;
}

View File

@ -44,7 +44,7 @@ namespace ClassicalSharp {
}
public override void GetPickedBlock( PickedPos pos ) {
Vector3 dir = Utils.GetDirectionVector( player.YawRadians, player.PitchRadians + Math.PI );
Vector3 dir = Utils.GetDirVector( player.YawRadians, player.PitchRadians );
Vector3 eyePos = player.EyePosition;
float reach = Window.LocalPlayer.ReachDistance;
Picking.GetPickedBlockPos( Window, eyePos, dir, reach, pos );
@ -108,7 +108,7 @@ namespace ClassicalSharp {
public override Matrix4 GetView() {
Vector3 eyePos = player.EyePosition;
Vector3 cameraPos = eyePos - Utils.GetDirectionVector( player.YawRadians, player.PitchRadians + Math.PI ) * distance;
Vector3 cameraPos = eyePos - Utils.GetDirVector( player.YawRadians, player.PitchRadians ) * distance;
return Matrix4.LookAt( cameraPos, eyePos, Vector3.UnitY );
}
@ -124,7 +124,7 @@ namespace ClassicalSharp {
public override Matrix4 GetView() {
Vector3 eyePos = player.EyePosition;
Vector3 cameraDir = Utils.GetDirectionVector( player.YawRadians, player.PitchRadians + Math.PI );
Vector3 cameraDir = Utils.GetDirVector( player.YawRadians, player.PitchRadians );
return Matrix4.LookAt( eyePos, eyePos + cameraDir, Vector3.UnitY );
}

View File

@ -111,6 +111,12 @@ namespace ClassicalSharp {
return packed * 360.0 / 256.0;
}
public static Vector3 RotateY( Vector3 v, float angle ) {
float cosA = (float)Math.Cos( angle );
float sinA = (float)Math.Sin( angle );
return new Vector3( cosA * v.X - sinA * v.Z, v.Y, sinA * v.X + cosA * v.Z );
}
public static Vector3 RotateY( float x, float y, float z, float angle ) {
float cosA = (float)Math.Cos( angle );
float sinA = (float)Math.Sin( angle );
@ -138,10 +144,10 @@ namespace ClassicalSharp {
return dx * dx + dy * dy + dz * dz;
}
public static Vector3 GetDirectionVector( double yawRad, double pitchRad ) {
double x = Math.Cos( pitchRad ) * -Math.Sin( yawRad );
double y = Math.Sin( pitchRad );
double z = Math.Cos( pitchRad ) * Math.Cos( yawRad );
public static Vector3 GetDirVector( double yawRad, double pitchRad ) {
double x = -Math.Cos( pitchRad ) * -Math.Sin( yawRad );
double y = -Math.Sin( pitchRad );
double z = -Math.Cos( pitchRad ) * Math.Cos( yawRad );
return new Vector3( (float)x, (float)y, (float)z );
}