Normal from Shape and Contact Point
When doing raycasts, they do not provide contact normal.
You can use this code to get it.
C#
public static unsafe class DynamicHitExts {
public static FPVector2 normal(
this DynamicHit hit, Frame f
) {
var entity = (Entity*) hit.Entity;
if (entity == null) {
var collider = f.Map.StaticColliders[hit.StaticData.ColliderId - 1];
return collider.normal(hit.Point);
}
else {
var body = Entity.GetDynamicBody(entity);
var t2d = Entity.GetTransform2D(entity);
return body->Shape.normal(t2d, hit.Point);
}
}
}
public static class MapStaticColliderExts {
/// <param name="collider"></param>
/// <param name="contactPoint">world position</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static FPVector2 normal(this MapStaticCollider collider, FPVector2 contactPoint) {
FPVector2 rot(FPVector2 v) => FPVector2.Rotate(v, collider.Rotation);
var relativeContactPoint = FPVector2.Rotate(contactPoint - collider.Position, -collider.Rotation);
switch (collider.ShapeType) {
case DynamicShapeType.Circle:
return rot(DynamicShapeExts.circleNormal(relativeContactPoint));
case DynamicShapeType.Polygon:
return rot(DynamicShapeExts.polygonNormal(collider.PolygonCollider, relativeContactPoint));
case DynamicShapeType.Box:
return rot(DynamicShapeExts.boxNormal(collider.BoxExtents, relativeContactPoint));
case DynamicShapeType.None:
default:
throw new ArgumentOutOfRangeException(nameof(collider.ShapeType), collider.ShapeType, "unknown shape type");
}
}
}
public static unsafe class DynamicShapeExts {
static bool checkNormal(
FPVector2 start, FPVector2 end, FPVector2 contactPoint, ref FP currentCross
) {
var vCross = FPMath.Abs(FPVector2.Cross(end - contactPoint, end - start));
if (vCross < currentCross) {
currentCross = vCross;
return true;
}
return false;
}
public static FPVector2 circleNormal(FPVector2 contactPoint) => contactPoint.Normalized;
public static FPVector2 boxNormal(FPVector2 extents, FPVector2 contactPoint) {
extents.extentsPoints(out var bottomLeft, out var bottomRight, out var topLeft, out var topRight);
var normal = FPVector2.Zero;
var currentCross = FP.MaxValue;
if (checkNormal(bottomLeft, topLeft, contactPoint, ref currentCross)) normal = FPVector2.Left;
if (checkNormal(bottomLeft, bottomRight, contactPoint, ref currentCross)) normal = FPVector2.Down;
if (checkNormal(topLeft, topRight, contactPoint, ref currentCross)) normal = FPVector2.Up;
if (checkNormal(bottomRight, topRight, contactPoint, ref currentCross)) normal = FPVector2.Right;
return normal;
}
public static FPVector2 polygonNormal(PolygonCollider collider, FPVector2 contactPoint) {
var maxIdx = collider.Vertices.Length - 1;
var normal = FPVector2.Zero;
var currentCross = FP.MaxValue;
for (var idx = 0; idx < maxIdx; idx++) {
if (checkNormal(collider.Vertices[idx], collider.Vertices[idx + 1], contactPoint, ref currentCross))
normal = collider.Normals[idx];
}
if (checkNormal(collider.Vertices[maxIdx], collider.Vertices[0], contactPoint, ref currentCross))
normal = collider.Normals[maxIdx];
return normal;
}
/// <param name="shape"></param>
/// <param name="contactPoint">relative to this shape</param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static FPVector2 normal(this DynamicShape shape, FPVector2 contactPoint) {
var normal = FPVector2.Zero;
var currentCross = FP.MaxValue;
switch (shape.Type) {
case DynamicShapeType.Circle: return circleNormal(contactPoint);
case DynamicShapeType.Box: return boxNormal(shape.Box.Extents, contactPoint);
case DynamicShapeType.Polygon:
var collider = (PolygonCollider) DB.FastUnsafe[shape.Polygon.AssetId];
return polygonNormal(collider, contactPoint);
case DynamicShapeType.None:
default:
throw new ArgumentOutOfRangeException(nameof(shape.Type), shape.Type, "Unknown shape type");
}
}
/// <param name="shape"></param>
/// <param name="t2d"></param>
/// <param name="contactPoint">in world space</param>
/// <returns></returns>
public static FPVector2 normal(
this DynamicShape shape, Transform2D* t2d, FPVector2 contactPoint
) {
var relativeContactPoint = FPVector2.Rotate(contactPoint - t2d->Position, -t2d->Rotation);
return FPVector2.Rotate(shape.normal(relativeContactPoint), t2d->Rotation);
}
}
Back to top