123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- using OpenMetaverse;
- using OpenSim.Region.PhysicsModules.SharedBase;
- using System;
- using System.Runtime.CompilerServices;
- namespace OpenSim.Region.PhysicsModule.ubOde
- {
- public partial class ODEScene
- {
- internal bool CollideSpheres(Vector3 p1, float r1, Vector3 p2, float r2,
- ref UBOdeNative.ContactGeom c)
- {
- float rsum = r1 + r2;
- Vector3 delta = p1 - p2;
- float d = delta.LengthSquared();
- if (d >= rsum * rsum)
- return false;
- if (d < 1e-6f)
- {
- c.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref p1);
- c.normal.X = 1;
- c.normal.Y = 0;
- c.normal.Z = 0;
- c.depth = rsum;
- return true;
- }
- d = MathF.Sqrt(d);
- c.depth = rsum - d;
- float k = r1 - 0.5f * c.depth;
- delta *= 1.0f / d;
- c.normal = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref delta);
- delta *= k;
- delta = p1 - delta;
- c.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref delta);
- return true;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Vector3 minusXRotateByShortQZ(Vector2 shortQuaternion)
- {
- float num = shortQuaternion.X + shortQuaternion.X;
- return new(shortQuaternion.X * num - 1f, shortQuaternion.Y * num, 0);
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static bool CollideSimpleChars(OdeCharacter p1, OdeCharacter p2, ref ContactPoint cp, ref int feet)
- {
- Vector3 tt;
- tt.Z = p1._position.Z - p2._position.Z;
- float absdz = Math.Abs(tt.Z);
- float r = p1.CapsuleSizeZ + p2.CapsuleSizeZ;
- if (absdz >= r)
- return false;
- tt.X = p1._position.X - p2._position.X;
- tt.Y = p1._position.Y - p2._position.Y;
- Vector3 v;
- float r1, r2, rhsum, d;
- float rsum = p1.CapsuleRadius + p2.CapsuleRadius;
- r -= rsum;
- if (absdz < r)
- {
- float hd = tt.X * tt.X + tt.Y * tt.Y;
- if (hd < 1e-6f)
- {
- cp.Position = p1._position;
- cp.Position.Z -= 0.5f * tt.Z;
- cp.SurfaceNormal = minusXRotateByShortQZ(p1.Orientation2D);
- cp.PenetrationDepth = rsum + .05f;
- return true;
- }
- hd = MathF.Sqrt(hd);
- if(hd > rsum)
- return false;
- d = hd;
- hd = 1.0f / hd;
- tt.X *= hd;
- tt.Y *= hd;
- v = Vector3.InverseRotateByShortQZ(tt, p1.Orientation2D);
- r1 = 1.0f / MathF.Sqrt(v.X * v.X / p1.AvaSizeXsq + v.Y * v.Y / p1.AvaSizeYsq);
- v = Vector3.InverseRotateByShortQZ(tt, p2.Orientation2D);
- r2 = 1.0f / MathF.Sqrt(v.X * v.X / p2.AvaSizeXsq + v.Y * v.Y / p2.AvaSizeYsq);
- rhsum = r1 + r2 - d;
- if (rhsum < 0.5e-3f)
- return false;
- cp.SurfaceNormal.X = tt.X;
- cp.SurfaceNormal.Y = tt.Y;
- cp.SurfaceNormal.Z = 0;
- cp.Position = p1._position;
- cp.Position.Z -= 0.5f * tt.Z;
- float hk = r1 - 0.5f * rhsum;
- if (hk <= 0)
- cp.PenetrationDepth = r1;
- else
- {
- cp.PenetrationDepth = rhsum;
- cp.Position.X -= hk * tt.X;
- cp.Position.Y -= hk * tt.Y;
- }
- return true;
- }
- if (tt.Z > 0)
- {
- tt.Z -= r;
- feet = 1;
- }
- else
- {
- tt.Z += r;
- feet = -1;
- }
- d = tt.LengthSquared();
- if (d < 1e-6f)
- {
- cp.Position = p1._position;
- cp.SurfaceNormal.X = 0;
- cp.SurfaceNormal.Y = 0;
- cp.SurfaceNormal.Z = tt.Z > 0 ? 1.0f : -1.0f;
- cp.PenetrationDepth = rsum;
- return true;
- }
- d = MathF.Sqrt(d);
- float k = rsum - d;
- if (k < 0.5e-4f)
- return false;
- tt *= 1.0f / d;
- v = Vector3.InverseRotateByShortQZ(tt, p1.Orientation2D);
- r1 = v.X * v.X / p1.AvaSizeXsq + v.Y * v.Y / p1.AvaSizeYsq;
- v = Vector3.InverseRotateByShortQZ(tt, p2.Orientation2D);
- r2 = v.X * v.X / p2.AvaSizeXsq + v.Y * v.Y / p2.AvaSizeYsq;
- if (d > 1.0f / MathF.Sqrt(r1) + 1.0f / MathF.Sqrt(r2))
- return false;
- cp.SurfaceNormal = tt;
- cp.PenetrationDepth = k;
- k = p1.CapsuleRadius - 0.5f * k;
- tt *= k;
- cp.Position = p1._position - tt;
- return true;
- }
- internal ContactPoint SharedChrContact = new();
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void CollideCharChar(OdeCharacter p1, OdeCharacter p2)
- {
- if (ContactJointCount >= maxContactJoints)
- return;
- int feetcollision = 0;
- if (!CollideSimpleChars(p1, p2, ref SharedChrContact, ref feetcollision))
- return;
- p1.CollidingObj = true;
- p2.CollidingObj = true;
- if (feetcollision > 0)
- {
- p1.CollideNormal = SharedChrContact.SurfaceNormal;
- p1.IsColliding = true;
- }
- else if (feetcollision < 0)
- {
- p2.CollideNormal = -SharedChrContact.SurfaceNormal;
- p2.IsColliding = true;
- SharedChrContact.CharacterFeet = true;
- }
- contactSharedForJoints.surface.mu = 0;
- contactSharedForJoints.surface.bounce = 0;
- contactSharedForJoints.geom.pos = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref SharedChrContact.Position);
- contactSharedForJoints.geom.normal = Unsafe.As<Vector3, UBOdeNative.Vector3>(ref SharedChrContact.SurfaceNormal);
- contactSharedForJoints.geom.depth = SharedChrContact.PenetrationDepth;
- IntPtr Joint = CreateCharContacJoint();
- if (Joint == IntPtr.Zero)
- return;
- UBOdeNative.JointAttach(Joint, p1.Body, p2.Body);
- if (p1.CollisionScore < float.MaxValue)
- p1.CollisionScore += 1.0f;
- if (p2.CollisionScore < float.MaxValue)
- p2.CollisionScore += 1.0f;
- bool p1events = p1.SubscribedEvents();
- bool p2events = p2.SubscribedEvents();
- if (!p2events && !p1events)
- return;
- Vector3 vel = Vector3.Zero;
- if (p2.IsPhysical)
- vel = p2.rootVelocity;
- if (p1.IsPhysical)
- vel -= p1.rootVelocity;
- SharedChrContact.RelativeSpeed = Vector3.Dot(vel, SharedChrContact.SurfaceNormal);
- if (p2events)
- {
- p2.AddCollisionEvent(p1.ParentActor.m_baseLocalID, SharedChrContact);
- }
- if (p1events)
- {
- SharedChrContact.SurfaceNormal = -SharedChrContact.SurfaceNormal;
- SharedChrContact.RelativeSpeed = -SharedChrContact.RelativeSpeed;
- SharedChrContact.CharacterFeet = feetcollision > 0;
- p1.AddCollisionEvent(p2.ParentActor.m_baseLocalID, SharedChrContact);
- }
- }
- // if mode==1 then use the sphere exit contact, not the entry contact
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool ray_sphere_helper(Vector3 pos, Vector3 dir, float len, Vector3 sphere_pos, float radius,
- ref ContactResult cp)
- {
- Vector3 q = pos - sphere_pos;
- float B = q.Dot(dir);
- float C = q.LengthSquared() - radius * radius;
- // note: if C <= 0 then the start of the ray is inside the sphere
- float k = B * B - C;
- if (k < 1e-6f)
- return false;
- k = MathF.Sqrt(k);
- float alpha;
- if (C >= 0)
- {
- alpha = -B + k;
- if (alpha < 0)
- return false;
- }
- else
- {
- alpha = -B - k;
- if (alpha < 0)
- {
- alpha = -B + k;
- if (alpha < 0)
- return false;
- }
- }
- if (alpha > len)
- return false;
- cp.Pos = pos + dir * alpha;
- cp.Normal = sphere_pos - cp.Pos;
- cp.Normal.Normalize();
- cp.Depth = alpha;
- return true;
- }
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool CollideRaySimpleCapsule(OdeCharacter p1, Vector3 RayStart, Vector3 RayDirection, float RayLength,
- ref ContactResult cp)
- {
- Vector3 cs;
- cs.Z = RayStart.Z - p1._position.Z;
- float distZ = Math.Abs(cs.Z) - p1.CapsuleSizeZ;
- if (distZ >= RayLength)
- return false;
- cs.X = RayStart.X - p1._position.X;
- cs.Y = RayStart.Y - p1._position.Y;
- float C = cs.X * cs.X + cs.Y * cs.Y - p1.CapsuleRadius * p1.CapsuleRadius;
- if(C > RayLength * RayLength)
- return false;
- if (C <= 0.5e-4f)
- {
- if (distZ < 0.5e-4f)
- return false;
- else
- {
- cp.Pos.X = p1._position.X;
- cp.Pos.Y = p1._position.Y;
- cp.Normal.X = 0;
- cp.Normal.Y = 0;
- cp.Depth = distZ;
- if (cs.Z < 0)
- {
- cp.Pos.Z = p1._position.Z - p1.CapsuleSizeZ;
- cp.Normal.Z = -1f;
- }
- else
- {
- cp.Pos.Z = p1._position.Z + p1.CapsuleSizeZ;
- cp.Normal.Z = 1f;
- }
- return true;
- }
- }
- float k;
- float lz2 = p1.CapsuleSizeZ - p1.CapsuleRadius;
- // compute ray collision with infinite cylinder, except for the case where
- // the ray is outside the capped cylinder but within the infinite cylinder
- // (it that case the ray can only hit endcaps)
- float A = RayDirection.X * RayDirection.X + RayDirection.Y * RayDirection.Y;
- // A == 0 means that the ray and ccylinder axes are parallel
- if (A == 0)
- goto _checkends;
- float B = 2f * (cs.X * RayDirection.X + cs.Y * RayDirection.Y);
- k = B * B - 4f * A * C;
- if (k < 1e-6f)
- return false;
- k = MathF.Sqrt(k);
- A = 0.5f / A;
- float alpha = (-B - k) * A;
- if (alpha < 0)
- {
- alpha = (-B + k) * A;
- if (alpha < 0)
- return false;
- }
- if (alpha > RayLength)
- return false;
- // the ray intersects the infinite cylinder. check to see if the
- // intersection point is between the caps
- cp.Pos = RayStart + RayDirection * alpha;
- Vector3 q = cp.Pos - p1._position;
- if (q.Z >= -lz2 && q.Z <= lz2)
- {
- cp.Normal.X = q.X;
- cp.Normal.Y = q.Y;
- cp.Normal.Z = 0;
-
- cp.Normal.Normalize();
- cp.Depth = alpha;
- return true;
- }
- // the infinite cylinder intersection point is not between the caps.
- // set k to cap position to check.
- _checkends:
- k = cs.Z < 0 ? -lz2 : lz2;
- return ray_sphere_helper(RayStart, RayDirection, RayLength,
- new(p1._position.X, p1._position.Y, p1._position.Z + k),
- p1.CapsuleRadius, ref cp);
- }
- }
- }
|