ODECollision.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. using OpenMetaverse;
  2. using OpenSim.Region.PhysicsModules.SharedBase;
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. namespace OpenSim.Region.PhysicsModule.ubOde
  6. {
  7. public partial class ODEScene
  8. {
  9. internal bool CollideSpheres(Vector3 p1, float r1, Vector3 p2, float r2,
  10. ref UBOdeNative.ContactGeom c)
  11. {
  12. float rsum = r1 + r2;
  13. Vector3 delta = p1 - p2;
  14. float d = delta.LengthSquared();
  15. if (d >= rsum * rsum)
  16. return false;
  17. if (d < 1e-6f)
  18. {
  19. c.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref p1);
  20. c.normal.X = 1;
  21. c.normal.Y = 0;
  22. c.normal.Z = 0;
  23. c.depth = rsum;
  24. return true;
  25. }
  26. d = MathF.Sqrt(d);
  27. c.depth = rsum - d;
  28. float k = r1 - 0.5f * c.depth;
  29. delta *= 1.0f / d;
  30. c.normal = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref delta);
  31. delta *= k;
  32. delta = p1 - delta;
  33. c.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref delta);
  34. return true;
  35. }
  36. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  37. public static Vector3 minusXRotateByShortQZ(Vector2 shortQuaternion)
  38. {
  39. float num = shortQuaternion.X + shortQuaternion.X;
  40. return new(shortQuaternion.X * num - 1f, shortQuaternion.Y * num, 0);
  41. }
  42. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  43. internal static bool CollideSimpleChars(OdeCharacter p1, OdeCharacter p2, ref ContactPoint cp, ref int feet)
  44. {
  45. Vector3 tt;
  46. tt.Z = p1._position.Z - p2._position.Z;
  47. float absdz = Math.Abs(tt.Z);
  48. float r = p1.CapsuleSizeZ + p2.CapsuleSizeZ;
  49. if (absdz >= r)
  50. return false;
  51. tt.X = p1._position.X - p2._position.X;
  52. tt.Y = p1._position.Y - p2._position.Y;
  53. Vector3 v;
  54. float r1, r2, rhsum, d;
  55. float rsum = p1.CapsuleRadius + p2.CapsuleRadius;
  56. r -= rsum;
  57. if (absdz < r)
  58. {
  59. float hd = tt.X * tt.X + tt.Y * tt.Y;
  60. if (hd < 1e-6f)
  61. {
  62. cp.Position = p1._position;
  63. cp.Position.Z -= 0.5f * tt.Z;
  64. cp.SurfaceNormal = minusXRotateByShortQZ(p1.Orientation2D);
  65. cp.PenetrationDepth = rsum + .05f;
  66. return true;
  67. }
  68. hd = MathF.Sqrt(hd);
  69. if(hd > rsum)
  70. return false;
  71. d = hd;
  72. hd = 1.0f / hd;
  73. tt.X *= hd;
  74. tt.Y *= hd;
  75. v = Vector3.InverseRotateByShortQZ(tt, p1.Orientation2D);
  76. r1 = 1.0f / MathF.Sqrt(v.X * v.X / p1.AvaSizeXsq + v.Y * v.Y / p1.AvaSizeYsq);
  77. v = Vector3.InverseRotateByShortQZ(tt, p2.Orientation2D);
  78. r2 = 1.0f / MathF.Sqrt(v.X * v.X / p2.AvaSizeXsq + v.Y * v.Y / p2.AvaSizeYsq);
  79. rhsum = r1 + r2 - d;
  80. if (rhsum < 0.5e-3f)
  81. return false;
  82. cp.SurfaceNormal.X = tt.X;
  83. cp.SurfaceNormal.Y = tt.Y;
  84. cp.SurfaceNormal.Z = 0;
  85. cp.Position = p1._position;
  86. cp.Position.Z -= 0.5f * tt.Z;
  87. float hk = r1 - 0.5f * rhsum;
  88. if (hk <= 0)
  89. cp.PenetrationDepth = r1;
  90. else
  91. {
  92. cp.PenetrationDepth = rhsum;
  93. cp.Position.X -= hk * tt.X;
  94. cp.Position.Y -= hk * tt.Y;
  95. }
  96. return true;
  97. }
  98. if (tt.Z > 0)
  99. {
  100. tt.Z -= r;
  101. feet = 1;
  102. }
  103. else
  104. {
  105. tt.Z += r;
  106. feet = -1;
  107. }
  108. d = tt.LengthSquared();
  109. if (d < 1e-6f)
  110. {
  111. cp.Position = p1._position;
  112. cp.SurfaceNormal.X = 0;
  113. cp.SurfaceNormal.Y = 0;
  114. cp.SurfaceNormal.Z = tt.Z > 0 ? 1.0f : -1.0f;
  115. cp.PenetrationDepth = rsum;
  116. return true;
  117. }
  118. d = MathF.Sqrt(d);
  119. float k = rsum - d;
  120. if (k < 0.5e-4f)
  121. return false;
  122. tt *= 1.0f / d;
  123. v = Vector3.InverseRotateByShortQZ(tt, p1.Orientation2D);
  124. r1 = v.X * v.X / p1.AvaSizeXsq + v.Y * v.Y / p1.AvaSizeYsq;
  125. v = Vector3.InverseRotateByShortQZ(tt, p2.Orientation2D);
  126. r2 = v.X * v.X / p2.AvaSizeXsq + v.Y * v.Y / p2.AvaSizeYsq;
  127. if (d > 1.0f / MathF.Sqrt(r1) + 1.0f / MathF.Sqrt(r2))
  128. return false;
  129. cp.SurfaceNormal = tt;
  130. cp.PenetrationDepth = k;
  131. k = p1.CapsuleRadius - 0.5f * k;
  132. tt *= k;
  133. cp.Position = p1._position - tt;
  134. return true;
  135. }
  136. internal ContactPoint SharedChrContact = new();
  137. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  138. internal void CollideCharChar(OdeCharacter p1, OdeCharacter p2)
  139. {
  140. if (ContactJointCount >= maxContactJoints)
  141. return;
  142. int feetcollision = 0;
  143. if (!CollideSimpleChars(p1, p2, ref SharedChrContact, ref feetcollision))
  144. return;
  145. p1.CollidingObj = true;
  146. p2.CollidingObj = true;
  147. if (feetcollision > 0)
  148. {
  149. p1.CollideNormal = SharedChrContact.SurfaceNormal;
  150. p1.IsColliding = true;
  151. }
  152. else if (feetcollision < 0)
  153. {
  154. p2.CollideNormal = -SharedChrContact.SurfaceNormal;
  155. p2.IsColliding = true;
  156. SharedChrContact.CharacterFeet = true;
  157. }
  158. contactSharedForJoints.surface.mu = 0;
  159. contactSharedForJoints.surface.bounce = 0;
  160. contactSharedForJoints.geom.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref SharedChrContact.Position);
  161. contactSharedForJoints.geom.normal = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref SharedChrContact.SurfaceNormal);
  162. contactSharedForJoints.geom.depth = SharedChrContact.PenetrationDepth;
  163. IntPtr Joint = CreateCharContacJoint();
  164. if (Joint == IntPtr.Zero)
  165. return;
  166. UBOdeNative.JointAttach(Joint, p1.Body, p2.Body);
  167. if (p1.CollisionScore < float.MaxValue)
  168. p1.CollisionScore += 1.0f;
  169. if (p2.CollisionScore < float.MaxValue)
  170. p2.CollisionScore += 1.0f;
  171. bool p1events = p1.SubscribedEvents();
  172. bool p2events = p2.SubscribedEvents();
  173. if (!p2events && !p1events)
  174. return;
  175. Vector3 vel = Vector3.Zero;
  176. if (p2.IsPhysical)
  177. vel = p2.rootVelocity;
  178. if (p1.IsPhysical)
  179. vel -= p1.rootVelocity;
  180. SharedChrContact.RelativeSpeed = Vector3.Dot(vel, SharedChrContact.SurfaceNormal);
  181. if (p2events)
  182. {
  183. p2.AddCollisionEvent(p1.ParentActor.m_baseLocalID, SharedChrContact);
  184. }
  185. if (p1events)
  186. {
  187. SharedChrContact.SurfaceNormal = -SharedChrContact.SurfaceNormal;
  188. SharedChrContact.RelativeSpeed = -SharedChrContact.RelativeSpeed;
  189. SharedChrContact.CharacterFeet = feetcollision > 0;
  190. p1.AddCollisionEvent(p2.ParentActor.m_baseLocalID, SharedChrContact);
  191. }
  192. }
  193. // if mode==1 then use the sphere exit contact, not the entry contact
  194. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  195. private static bool ray_sphere_helper(Vector3 pos, Vector3 dir, float len, Vector3 sphere_pos, float radius,
  196. ref ContactResult cp)
  197. {
  198. Vector3 q = pos - sphere_pos;
  199. float B = q.Dot(dir);
  200. float C = q.LengthSquared() - radius * radius;
  201. // note: if C <= 0 then the start of the ray is inside the sphere
  202. float k = B * B - C;
  203. if (k < 1e-6f)
  204. return false;
  205. k = MathF.Sqrt(k);
  206. float alpha;
  207. if (C >= 0)
  208. {
  209. alpha = -B + k;
  210. if (alpha < 0)
  211. return false;
  212. }
  213. else
  214. {
  215. alpha = -B - k;
  216. if (alpha < 0)
  217. {
  218. alpha = -B + k;
  219. if (alpha < 0)
  220. return false;
  221. }
  222. }
  223. if (alpha > len)
  224. return false;
  225. cp.Pos = pos + dir * alpha;
  226. cp.Normal = sphere_pos - cp.Pos;
  227. cp.Normal.Normalize();
  228. cp.Depth = alpha;
  229. return true;
  230. }
  231. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  232. internal bool CollideRaySimpleCapsule(OdeCharacter p1, Vector3 RayStart, Vector3 RayDirection, float RayLength,
  233. ref ContactResult cp)
  234. {
  235. Vector3 cs;
  236. cs.Z = RayStart.Z - p1._position.Z;
  237. float distZ = Math.Abs(cs.Z) - p1.CapsuleSizeZ;
  238. if (distZ >= RayLength)
  239. return false;
  240. cs.X = RayStart.X - p1._position.X;
  241. cs.Y = RayStart.Y - p1._position.Y;
  242. float C = cs.X * cs.X + cs.Y * cs.Y - p1.CapsuleRadius * p1.CapsuleRadius;
  243. if(C > RayLength * RayLength)
  244. return false;
  245. if (C <= 0.5e-4f)
  246. {
  247. if (distZ < 0.5e-4f)
  248. return false;
  249. else
  250. {
  251. cp.Pos.X = p1._position.X;
  252. cp.Pos.Y = p1._position.Y;
  253. cp.Normal.X = 0;
  254. cp.Normal.Y = 0;
  255. cp.Depth = distZ;
  256. if (cs.Z < 0)
  257. {
  258. cp.Pos.Z = p1._position.Z - p1.CapsuleSizeZ;
  259. cp.Normal.Z = -1f;
  260. }
  261. else
  262. {
  263. cp.Pos.Z = p1._position.Z + p1.CapsuleSizeZ;
  264. cp.Normal.Z = 1f;
  265. }
  266. return true;
  267. }
  268. }
  269. float k;
  270. float lz2 = p1.CapsuleSizeZ - p1.CapsuleRadius;
  271. // compute ray collision with infinite cylinder, except for the case where
  272. // the ray is outside the capped cylinder but within the infinite cylinder
  273. // (it that case the ray can only hit endcaps)
  274. float A = RayDirection.X * RayDirection.X + RayDirection.Y * RayDirection.Y;
  275. // A == 0 means that the ray and ccylinder axes are parallel
  276. if (A == 0)
  277. goto _checkends;
  278. float B = 2f * (cs.X * RayDirection.X + cs.Y * RayDirection.Y);
  279. k = B * B - 4f * A * C;
  280. if (k < 1e-6f)
  281. return false;
  282. k = MathF.Sqrt(k);
  283. A = 0.5f / A;
  284. float alpha = (-B - k) * A;
  285. if (alpha < 0)
  286. {
  287. alpha = (-B + k) * A;
  288. if (alpha < 0)
  289. return false;
  290. }
  291. if (alpha > RayLength)
  292. return false;
  293. // the ray intersects the infinite cylinder. check to see if the
  294. // intersection point is between the caps
  295. cp.Pos = RayStart + RayDirection * alpha;
  296. Vector3 q = cp.Pos - p1._position;
  297. if (q.Z >= -lz2 && q.Z <= lz2)
  298. {
  299. cp.Normal.X = q.X;
  300. cp.Normal.Y = q.Y;
  301. cp.Normal.Z = 0;
  302. cp.Normal.Normalize();
  303. cp.Depth = alpha;
  304. return true;
  305. }
  306. // the infinite cylinder intersection point is not between the caps.
  307. // set k to cap position to check.
  308. _checkends:
  309. k = cs.Z < 0 ? -lz2 : lz2;
  310. return ray_sphere_helper(RayStart, RayDirection, RayLength,
  311. new(p1._position.X, p1._position.Y, p1._position.Z + k),
  312. p1.CapsuleRadius, ref cp);
  313. }
  314. }
  315. }