1 module sbylib.collision.narrow.polygonsphere; 2 3 import sbylib.collision.shape.polygon : CollisionPolygon; 4 import sbylib.collision.shape.sphere : CollisionSphere; 5 import sbylib.math; 6 import std.typecons : Nullable, nullable; 7 8 enum Type { Face, Edge, Vertex } 9 10 struct PolygonSphereResult { 11 vec3 _pushVector; 12 Type type; 13 14 vec3 pushVector(CollisionPolygon) { 15 return _pushVector; 16 } 17 18 vec3 pushVector(CollisionSphere) { 19 return -_pushVector; 20 } 21 } 22 23 auto detect(Polygon : CollisionPolygon, Sphere : CollisionSphere) (Sphere sphere, Polygon polygon) { 24 return detect(polygon, sphere); 25 } 26 27 auto detect(Polygon : CollisionPolygon, Sphere : CollisionSphere) (Polygon polygon, Sphere sphere) { 28 auto pos = inverseSignPos(polygon.vertices, sphere.center); 29 if (pos[0] == -1) 30 return toFace(polygon.vertices, sphere); 31 if (pos[1] == -1) 32 return toEdge([polygon.vertices[pos[0]], polygon.vertices[(pos[0]+1)%$]], sphere); 33 if (pos[0] + 1 == pos[1]) 34 return toVertex(polygon.vertices[pos[1]], sphere); 35 if ((pos[1] + 1) % polygon.vertices.length == pos[0]) 36 return toVertex(polygon.vertices[pos[0]], sphere); 37 assert(false); 38 } 39 40 private int[2] inverseSignPos(vec3[] v, vec3 p) { 41 auto base = getNormal(v[0], v[1], v[2]); 42 int pos = 0; 43 int[2] res = [-1,-1]; 44 foreach (i; 0..v.length) { 45 if (dot(base, getNormal(v[i], v[(i+1)%v.length], p)) < 0) { 46 assert(pos < 2); 47 res[pos++] = cast(int)i; 48 } 49 } 50 return res; 51 } 52 53 private auto toFace(Sphere)(vec3[] v, Sphere s) { 54 auto n = getNormal(v[0], v[1], v[2]).normalize; 55 auto d = dot(n, s.center - v[0]); 56 return wrapResult(n * d, s, Type.Face); 57 } 58 59 private auto toEdge(Sphere)(vec3[] v, Sphere s) { 60 const vec = v[1] - v[0]; 61 const l2 = lengthSq(vec); 62 const t = dot(vec, s.center - v[0]) / l2; 63 if (t < 0) return wrapResult(v[0] - s.center, s, Type.Edge); 64 if (t > 1) return wrapResult(v[1] - s.center, s, Type.Edge); 65 return wrapResult(v[0] + vec * t - s.center, s, Type.Edge); 66 } 67 68 private auto toVertex(Sphere)(vec3 v, Sphere s) { 69 return wrapResult(v - s.center, s, Type.Vertex); 70 } 71 72 private Nullable!PolygonSphereResult wrapResult(Sphere)(vec3 d, Sphere s, Type t) { 73 auto l = length(d); 74 if (l > s.radius) return typeof(return).init; 75 return nullable(PolygonSphereResult(safeNormalize(d) * (l - s.radius), t)); 76 } 77 78 private vec3 getNormal(vec3 a, vec3 b, vec3 c) { 79 return cross(a-b, b-c); 80 }