void AddLocalContact(ref ContactData contact, ref Matrix3x3 orientation, ref QuickList <ContactData> candidatesToAdd)
 {
     //Put the contact into world space.
     Matrix3x3.Transform(ref contact.Position, ref orientation, out contact.Position);
     Vector3.Add(ref contact.Position, ref convex.worldTransform.Position, out contact.Position);
     Matrix3x3.Transform(ref contact.Normal, ref orientation, out contact.Normal);
     //Check to see if the contact is unique before proceeding.
     if (IsContactUnique(ref contact, ref candidatesToAdd))
     {
         candidatesToAdd.Add(ref contact);
     }
 }
Beispiel #2
0
        protected override void ProcessCandidates(ref QuickList <ContactData> candidates)
        {
            //If the candidates list is empty, then let's see if the convex is in the 'thickness' of the terrain.
            if (candidates.Count == 0 & terrain.thickness > 0)
            {
                RayHit rayHit;
                Ray    ray = new Ray {
                    Position = convex.worldTransform.Position, Direction = terrain.worldTransform.LinearTransform.Up
                };
                ray.Direction.Normalize();
                //The raycast has to use doublesidedness, since we're casting from the bottom up.
                if (terrain.Shape.RayCast(ref ray, terrain.thickness, ref terrain.worldTransform, TriangleSidedness.DoubleSided, out rayHit))
                {
                    //Found a hit!
                    rayHit.Normal.Normalize();
                    float dot;
                    Vector3.Dot(ref ray.Direction, ref rayHit.Normal, out dot);

                    var newContact = new ContactData
                    {
                        Normal           = rayHit.Normal,
                        Position         = convex.worldTransform.Position,
                        Id               = 2,
                        PenetrationDepth = -rayHit.T * dot + convex.Shape.MinimumRadius
                    };
                    newContact.Validate();
                    bool found = false;
                    for (int i = 0; i < contacts.Count; i++)
                    {
                        if (contacts.Elements[i].Id == 2)
                        {
                            //As set above, an id of 2 corresponds to a contact created from this raycast process.
                            contacts.Elements[i].Normal                     = newContact.Normal;
                            contacts.Elements[i].Position                   = newContact.Position;
                            contacts.Elements[i].PenetrationDepth           = newContact.PenetrationDepth;
                            supplementData.Elements[i].BasePenetrationDepth = newContact.PenetrationDepth;
                            supplementData.Elements[i].LocalOffsetA         = new Vector3();
                            supplementData.Elements[i].LocalOffsetB         = ray.Position; //convex local position in mesh.
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        candidates.Add(ref newContact);
                    }
                }
            }
        }
        //This works in the general case where there can be any  number of contacts and candidates.  Could specialize it as an optimization to single-contact added incremental manifolds.
        ///<summary>
        /// Reduces the contact manifold to a good subset.
        ///</summary>
        ///<param name="contacts">Contacts to reduce.</param>
        ///<param name="contactCandidates">Contact candidates to include in the reduction process.</param>
        ///<param name="contactsToRemove">Contacts that need to removed to reach the reduced state.</param>
        ///<param name="toAdd">Contact candidates that should be added to reach the reduced state.</param>
        ///<exception cref="InvalidOperationException">Thrown when the set being reduced is empty.</exception>
        public static void ReduceContacts(RawList <Contact> contacts, ref QuickList <ContactData> contactCandidates, RawList <int> contactsToRemove, ref QuickList <ContactData> toAdd)
        {
            //Find the deepest point of all contacts/candidates, as well as a compounded 'normal' vector.
            float   maximumDepth = -float.MaxValue;
            int     deepestIndex = -1;
            Vector3 normal       = Toolbox.ZeroVector;

            for (int i = 0; i < contacts.Count; i++)
            {
                Vector3.Add(ref normal, ref contacts.Elements[i].Normal, out normal);
                if (contacts.Elements[i].PenetrationDepth > maximumDepth)
                {
                    deepestIndex = i;
                    maximumDepth = contacts.Elements[i].PenetrationDepth;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                Vector3.Add(ref normal, ref contactCandidates.Elements[i].Normal, out normal);
                if (contactCandidates.Elements[i].PenetrationDepth > maximumDepth)
                {
                    deepestIndex = contacts.Count + i;
                    maximumDepth = contactCandidates.Elements[i].PenetrationDepth;
                }
            }
            //If the normals oppose each other, this can happen.  It doesn't need to be normalized, but having SOME normal is necessary.
            if (normal.LengthSquared() < Toolbox.Epsilon)
            {
                if (contacts.Count > 0)
                {
                    normal = contacts.Elements[0].Normal;
                }
                else if (contactCandidates.Count > 0)
                {
                    normal = contactCandidates.Elements[0].Normal; //This method is only called when there's too many contacts, so if contacts is empty, the candidates must NOT be empty.
                }
                else //This method should not have been called at all if it gets here.
                {
                    throw new ArgumentException("Cannot reduce an empty contact set.");
                }
            }


            //Find the contact (candidate) that is furthest away from the deepest contact (candidate).
            Vector3 deepestPosition;

            if (deepestIndex < contacts.Count)
            {
                deepestPosition = contacts.Elements[deepestIndex].Position;
            }
            else
            {
                deepestPosition = contactCandidates.Elements[deepestIndex - contacts.Count].Position;
            }
            float distanceSquared;
            float furthestDistance = 0;
            int   furthestIndex    = -1;

            for (int i = 0; i < contacts.Count; i++)
            {
                Vector3.DistanceSquared(ref contacts.Elements[i].Position, ref deepestPosition, out distanceSquared);
                if (distanceSquared > furthestDistance)
                {
                    furthestDistance = distanceSquared;
                    furthestIndex    = i;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                Vector3.DistanceSquared(ref contactCandidates.Elements[i].Position, ref deepestPosition, out distanceSquared);
                if (distanceSquared > furthestDistance)
                {
                    furthestDistance = distanceSquared;
                    furthestIndex    = contacts.Count + i;
                }
            }
            if (furthestIndex == -1)
            {
                //Either this method was called when it shouldn't have been, or all contacts and contact candidates are at the same location.
                if (contacts.Count > 0)
                {
                    for (int i = 1; i < contacts.Count; i++)
                    {
                        contactsToRemove.Add(i);
                    }
                    return;
                }
                if (contactCandidates.Count > 0)
                {
                    toAdd.Add(ref contactCandidates.Elements[0]);
                    return;
                }
                throw new ArgumentException("Cannot reduce an empty contact set.");
            }
            Vector3 furthestPosition;

            if (furthestIndex < contacts.Count)
            {
                furthestPosition = contacts.Elements[furthestIndex].Position;
            }
            else
            {
                furthestPosition = contactCandidates.Elements[furthestIndex - contacts.Count].Position;
            }
            Vector3 xAxis;

            Vector3.Subtract(ref deepestPosition, ref furthestPosition, out xAxis);

            //Create the second axis of the 2d 'coordinate system' of the manifold.
            Vector3 yAxis;

            Vector3.Cross(ref xAxis, ref normal, out yAxis);

            //Determine the furthest points along the axis.
            float minYAxisDot = float.MaxValue, maxYAxisDot = -float.MaxValue;
            int   minYAxisIndex = -1, maxYAxisIndex = -1;

            for (int i = 0; i < contacts.Count; i++)
            {
                float dot;
                Vector3.Dot(ref contacts.Elements[i].Position, ref yAxis, out dot);
                if (dot < minYAxisDot)
                {
                    minYAxisIndex = i;
                    minYAxisDot   = dot;
                }
                if (dot > maxYAxisDot)
                {
                    maxYAxisIndex = i;
                    maxYAxisDot   = dot;
                }
            }
            for (int i = 0; i < contactCandidates.Count; i++)
            {
                float dot;
                Vector3.Dot(ref contactCandidates.Elements[i].Position, ref yAxis, out dot);
                if (dot < minYAxisDot)
                {
                    minYAxisIndex = i + contacts.Count;
                    minYAxisDot   = dot;
                }
                if (dot > maxYAxisDot)
                {
                    maxYAxisIndex = i + contacts.Count;
                    maxYAxisDot   = dot;
                }
            }

            //the deepestIndex, furthestIndex, minYAxisIndex, and maxYAxisIndex are the extremal points.
            //Cycle through the existing contacts.  If any DO NOT MATCH the existing candidates, add them to the toRemove list.
            //Cycle through the candidates.  If any match, add them to the toAdd list.

            //Repeated entries in the reduced manifold aren't a problem.
            //-Contacts list does not include repeats with itself.
            //-A contact is only removed if it doesn't match anything.

            //-Contact candidates do not repeat with themselves.
            //-Contact candidates do not repeat with contacts.
            //-Contact candidates are added if they match any of the indices.

            for (int i = 0; i < contactCandidates.Count; i++)
            {
                int totalIndex = i + contacts.Count;
                if (totalIndex == deepestIndex || totalIndex == furthestIndex || totalIndex == minYAxisIndex || totalIndex == maxYAxisIndex)
                {
                    //This contact is present in the new manifold.  Add it.
                    toAdd.Add(ref contactCandidates.Elements[i]);
                }
            }
            for (int i = 0; i < contacts.Count; i++)
            {
                if (!(i == deepestIndex || i == furthestIndex || i == minYAxisIndex || i == maxYAxisIndex))
                {
                    //This contact is not present in the new manifold.  Remove it.
                    contactsToRemove.Add(i);
                }
            }
        }