collision.d 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. module graphics.collision;
  2. import raylib;
  3. import raylib.raymath;
  4. import std.math;
  5. import variables;
  6. struct OBB
  7. {
  8. Quaternion rotation;
  9. Vector3 center;
  10. Vector3 halfExtents;
  11. }
  12. void getAxes(ref const OBB obb, out Vector3 right, out Vector3 up, out Vector3 forward)
  13. {
  14. Matrix rot = QuaternionToMatrix(obb.rotation);
  15. right = Vector3(rot.m0, rot.m1, rot.m2);
  16. up = Vector3(rot.m4, rot.m5, rot.m6);
  17. forward = Vector3(rot.m8, rot.m9, rot.m10);
  18. }
  19. void getCorners(ref const OBB obb, out Vector3[8] corners)
  20. {
  21. Vector3 right, up, forward;
  22. getAxes(obb, right, up, forward);
  23. right = Vector3Scale(right, obb.halfExtents.x);
  24. up = Vector3Scale(up, obb.halfExtents.y);
  25. forward = Vector3Scale(forward, obb.halfExtents.z);
  26. corners[0] = Vector3Add(Vector3Add(Vector3Add(obb.center, right), up), forward);
  27. corners[1] = Vector3Add(Vector3Add(Vector3Subtract(obb.center, right), up), forward);
  28. corners[2] = Vector3Add(Vector3Add(Vector3Subtract(obb.center, right), up), Vector3Negate(
  29. forward));
  30. corners[3] = Vector3Add(Vector3Add(Vector3Add(obb.center, right), up), Vector3Negate(forward));
  31. corners[4] = Vector3Add(Vector3Add(Vector3Add(obb.center, right), Vector3Negate(up)), forward);
  32. corners[5] = Vector3Add(Vector3Add(Vector3Subtract(obb.center, right), Vector3Negate(up)), forward);
  33. corners[6] = Vector3Add(Vector3Add(Vector3Subtract(obb.center, right), Vector3Negate(up)), Vector3Negate(
  34. forward));
  35. corners[7] = Vector3Add(Vector3Add(Vector3Add(obb.center, right), Vector3Negate(up)), Vector3Negate(
  36. forward));
  37. }
  38. void drawWireframe(ref const OBB obb, Color color)
  39. {
  40. Vector3[8] c;
  41. getCorners(obb, c);
  42. DrawLine3D(c[0], c[1], color);
  43. DrawLine3D(c[1], c[2], color);
  44. DrawLine3D(c[2], c[3], color);
  45. DrawLine3D(c[3], c[0], color);
  46. DrawLine3D(c[4], c[5], color);
  47. DrawLine3D(c[5], c[6], color);
  48. DrawLine3D(c[6], c[7], color);
  49. DrawLine3D(c[7], c[4], color);
  50. DrawLine3D(c[0], c[4], color);
  51. DrawLine3D(c[1], c[5], color);
  52. DrawLine3D(c[2], c[6], color);
  53. DrawLine3D(c[3], c[7], color);
  54. }
  55. bool containsPoint(ref const OBB obb, Vector3 point)
  56. {
  57. Vector3 local = Vector3Subtract(point, obb.center);
  58. Quaternion inverseRot = QuaternionInvert(obb.rotation);
  59. local = Vector3RotateByQuaternion(local, inverseRot);
  60. return fabs(local.x) <= obb.halfExtents.x &&
  61. fabs(local.y) <= obb.halfExtents.y &&
  62. fabs(local.z) <= obb.halfExtents.z;
  63. }
  64. void projectBoundingBoxOntoAxis(ref const BoundingBox box, Vector3 axis, out float outMin, out float outMax)
  65. {
  66. Vector3[8] corners = [
  67. Vector3(box.min.x, box.min.y, box.min.z),
  68. Vector3(box.max.x, box.min.y, box.min.z),
  69. Vector3(box.max.x, box.max.y, box.min.z),
  70. Vector3(box.min.x, box.max.y, box.min.z),
  71. Vector3(box.min.x, box.min.y, box.max.z),
  72. Vector3(box.max.x, box.min.y, box.max.z),
  73. Vector3(box.max.x, box.max.y, box.max.z),
  74. Vector3(box.min.x, box.max.y, box.max.z)
  75. ];
  76. float min = Vector3DotProduct(corners[0], axis);
  77. float max = min;
  78. for (int i = 1; i < 8; ++i)
  79. {
  80. float projection = Vector3DotProduct(corners[i], axis);
  81. if (projection < min)
  82. min = projection;
  83. if (projection > max)
  84. max = projection;
  85. }
  86. outMin = min;
  87. outMax = max;
  88. }
  89. void projectOBBOntoAxis(ref const OBB obb, Vector3 axis, out float outMin, out float outMax)
  90. {
  91. Vector3 right, up, forward;
  92. getAxes(obb, right, up, forward);
  93. float r =
  94. fabs(Vector3DotProduct(right, axis)) * obb.halfExtents.x +
  95. fabs(Vector3DotProduct(up, axis)) * obb.halfExtents.y +
  96. fabs(
  97. Vector3DotProduct(forward, axis)) * obb.halfExtents.z;
  98. float centerProj = Vector3DotProduct(obb.center, axis);
  99. outMin = centerProj - r;
  100. outMax = centerProj + r;
  101. }
  102. bool checkCollisionBoundingBoxVsOBB(ref const BoundingBox box, ref const OBB obb)
  103. {
  104. Vector3[3] aabbAxes = [
  105. Vector3(1, 0, 0),
  106. Vector3(0, 1, 0),
  107. Vector3(0, 0, 1)
  108. ];
  109. Vector3[3] obbAxes;
  110. getAxes(obb, obbAxes[0], obbAxes[1], obbAxes[2]);
  111. Vector3[15] testAxes;
  112. int axisCount = 0;
  113. for (int i = 0; i < 3; i++)
  114. testAxes[axisCount++] = aabbAxes[i];
  115. for (int i = 0; i < 3; i++)
  116. testAxes[axisCount++] = obbAxes[i];
  117. for (int i = 0; i < 3; i++)
  118. {
  119. for (int j = 0; j < 3; j++)
  120. {
  121. Vector3 cross = Vector3CrossProduct(aabbAxes[i], obbAxes[j]);
  122. if (Vector3LengthSqr(cross) > 0.000001f)
  123. {
  124. testAxes[axisCount++] = Vector3Normalize(cross);
  125. }
  126. }
  127. }
  128. for (int i = 0; i < axisCount; ++i)
  129. {
  130. Vector3 axis = testAxes[i];
  131. float minA, maxA, minB, maxB;
  132. projectBoundingBoxOntoAxis(box, axis, minA, maxA);
  133. projectOBBOntoAxis(obb, axis, minB, maxB);
  134. if (maxA < minB || maxB < minA)
  135. {
  136. return false;
  137. }
  138. }
  139. return true;
  140. }
  141. bool checkCollisionOBBvsOBB(ref const OBB a, ref const OBB b)
  142. {
  143. Vector3[3] axesA, axesB;
  144. getAxes(a, axesA[0], axesA[1], axesA[2]);
  145. getAxes(b, axesB[0], axesB[1], axesB[2]);
  146. Vector3[15] testAxes;
  147. int axisCount = 0;
  148. for (int i = 0; i < 3; ++i)
  149. testAxes[axisCount++] = axesA[i];
  150. for (int i = 0; i < 3; ++i)
  151. testAxes[axisCount++] = axesB[i];
  152. for (int i = 0; i < 3; ++i)
  153. {
  154. for (int j = 0; j < 3; ++j)
  155. {
  156. Vector3 cross = Vector3CrossProduct(axesA[i], axesB[j]);
  157. float len = Vector3Length(cross);
  158. if (len > 0.0001f)
  159. {
  160. testAxes[axisCount++] = Vector3Scale(cross, 1.0f / len);
  161. }
  162. }
  163. }
  164. for (int i = 0; i < axisCount; ++i)
  165. {
  166. Vector3 axis = testAxes[i];
  167. float minA, maxA, minB, maxB;
  168. projectOBBOntoAxis(a, axis, minA, maxA);
  169. projectOBBOntoAxis(b, axis, minB, maxB);
  170. if (maxA < minB || maxB < minA)
  171. {
  172. return false;
  173. }
  174. }
  175. return true;
  176. }
  177. RayCollision getRayCollisionOBB(Ray ray, ref const OBB obb)
  178. {
  179. RayCollision result;
  180. result.hit = false;
  181. result.distance = 0;
  182. result.normal = Vector3(0.0f, 0.0f, 0.0f);
  183. result.point = Vector3(0.0f, 0.0f, 0.0f);
  184. // Move ray into OBB's local space
  185. Vector3 localOrigin = Vector3Subtract(ray.position, obb.center);
  186. Quaternion inverseRot = QuaternionInvert(obb.rotation);
  187. Vector3 localRayOrigin = Vector3RotateByQuaternion(localOrigin, inverseRot);
  188. Vector3 localRayDir = Vector3RotateByQuaternion(ray.direction, inverseRot);
  189. Vector3 boxMin = Vector3Negate(obb.halfExtents);
  190. Vector3 boxMax = obb.halfExtents;
  191. // Ray vs AABB in OBB-local space
  192. float tmin = -float.infinity;
  193. float tmax = float.infinity;
  194. Vector3 normal = Vector3(0);
  195. for (int i = 0; i < 3; ++i)
  196. {
  197. float origin;
  198. float dir;
  199. float min;
  200. float max;
  201. switch (i)
  202. {
  203. case 0:
  204. origin = localRayOrigin.x;
  205. dir = localRayDir.x;
  206. min = boxMin.x;
  207. max = boxMax.x;
  208. break;
  209. case 1:
  210. origin = localRayOrigin.y;
  211. dir = localRayDir.y;
  212. min = boxMin.y;
  213. max = boxMax.y;
  214. break;
  215. case 2:
  216. origin = localRayOrigin.z;
  217. dir = localRayDir.z;
  218. min = boxMin.z;
  219. max = boxMax.z;
  220. break;
  221. default:
  222. assert(false);
  223. }
  224. if (fabs(dir) < 0.0001f)
  225. {
  226. if (origin < min || origin > max)
  227. return result;
  228. }
  229. else
  230. {
  231. float ood = 1.0f / dir;
  232. float t1 = (min - origin) * ood;
  233. float t2 = (max - origin) * ood;
  234. int axis = i;
  235. if (t1 > t2)
  236. {
  237. float temp = t1;
  238. t1 = t2;
  239. t2 = temp;
  240. axis = -axis;
  241. }
  242. if (t1 > tmin)
  243. {
  244. tmin = t1;
  245. normal = Vector3(0);
  246. switch (abs(axis))
  247. {
  248. case 0:
  249. normal.x = axis >= 0 ? -1.0f : 1.0f;
  250. break;
  251. case 1:
  252. normal.y = axis >= 0 ? -1.0f : 1.0f;
  253. break;
  254. case 2:
  255. normal.z = axis >= 0 ? -1.0f : 1.0f;
  256. break;
  257. default:
  258. break;
  259. }
  260. }
  261. if (t2 < tmax)
  262. {
  263. tmax = t2;
  264. }
  265. if (tmin > tmax)
  266. return result;
  267. }
  268. }
  269. // Convert result to world space
  270. result.hit = true;
  271. result.distance = tmin;
  272. result.point = Vector3Add(ray.position, Vector3Scale(ray.direction, tmin));
  273. result.normal = Vector3RotateByQuaternion(normal, obb.rotation);
  274. return result;
  275. }
  276. bool checkCollisionSphereVsOBB(Vector3 sphereCenter, float radius, ref const OBB obb)
  277. {
  278. Vector3 localCenter = Vector3Subtract(sphereCenter, obb.center);
  279. Quaternion invRot = QuaternionInvert(obb.rotation);
  280. localCenter = Vector3RotateByQuaternion(localCenter, invRot);
  281. Vector3 Clamped = Vector3(
  282. Clamp(localCenter.x, -obb.halfExtents.x, obb.halfExtents.x),
  283. Clamp(localCenter.y, -obb.halfExtents.y, obb.halfExtents.y),
  284. Clamp(localCenter.z, -obb.halfExtents.z, obb.halfExtents.z)
  285. );
  286. Vector3 worldClamped = Vector3RotateByQuaternion(Clamped, obb.rotation);
  287. worldClamped = Vector3Add(worldClamped, obb.center);
  288. float distSq = Vector3DistanceSqr(sphereCenter, worldClamped);
  289. return distSq <= radius * radius;
  290. }
  291. void placeOBB(int index, Vector3 position, Vector3 size, Vector3 rotation) {
  292. OBB newOBB;
  293. newOBB.center = position;
  294. newOBB.halfExtents = size;
  295. newOBB.rotation = QuaternionFromEuler(
  296. rotation.x * raylib.DEG2RAD,
  297. rotation.y * raylib.DEG2RAD,
  298. rotation.z * raylib.DEG2RAD
  299. );
  300. if (collisions.length <= index) {
  301. collisions.length += 1;
  302. }
  303. collisions[index] = newOBB;
  304. }
  305. void removeOBB(int index) {
  306. collisions = collisions[0 .. index] ~ collisions[index + 1 .. $];
  307. }
  308. void updatePlayerOBB(ref OBB playerOBB, Vector3 playerPosition, Vector3 modelCharacterSize, float playerRotation)
  309. {
  310. playerOBB.center = Vector3(playerPosition.x,
  311. playerPosition.y + modelCharacterSize.y / 2,
  312. playerPosition.z);
  313. playerOBB.halfExtents = Vector3(modelCharacterSize.x / 2,
  314. modelCharacterSize.y / 2,
  315. modelCharacterSize.z / 2);
  316. playerOBB.rotation = QuaternionFromAxisAngle(Vector3(0, 1, 0), playerRotation * raylib.raymath.DEG2RAD);
  317. }