void ComputeSolidSidedness()
        {
            //Raycast against the mesh.
            //If there's an even number of hits, then the ray start point is outside.
            //If there's an odd number of hits, then the ray start point is inside.

            //If the start is outside, then take the earliest toi hit and calibrate sidedness based on it.
            //If the start is inside, then take the latest toi hit and calibrate sidedness based on it.

            //This test assumes consistent winding across the entire mesh as well as a closed surface.
            //If those assumptions are not correct, then the raycast cannot determine inclusion or exclusion,
            //or there exists no calibration that will work across the entire surface.

            //Pick a ray direction that goes to a random location on the mesh.
            //A vertex would work, but targeting the middle of a triangle avoids some edge cases.
            var     ray = new Ray();
            Vector3 vA, vB, vC;

            triangleMesh.Data.GetTriangle(((triangleMesh.Data.indices.Length / 3) / 2) * 3, out vA, out vB, out vC);
            ray.Direction = (vA + vB + vC) / F64.C3;
            ray.Direction.Normalize();

            SidednessWhenSolid = ComputeSolidSidednessHelper(ray);
            //ComputeSolidSidednessHelper is separated into another function just in case multiple queries were desired for validation.
            //If multiple rays returned different sidednesses, the shape would be inconsistent.
        }
 /// <summary>
 /// Creates and instance of InstancedTriangleMesh
 /// Call the Static Method GetInstacedMesh to get the InstancedMeshShape obj
 /// </summary>
 /// <param name="InstancedMeshShape">The instanced mesh shape.</param>
 /// <param name="pos">The pos.</param>
 /// <param name="rotation">The rotation.</param>
 /// <param name="scale">The scale.</param>
 /// <param name="materialDescription">The material description.</param>
 /// <param name="TriangleSidedness">The triangle sidedness.</param>
 public InstancedTriangleMeshObject(InstancedMeshShape InstancedMeshShape, Vector3 pos, Matrix rotation, Vector3 scale, MaterialDescription materialDescription, TriangleSidedness TriangleSidedness = TriangleSidedness.Counterclockwise)
 {
     instancedMesh = new InstancedMesh(InstancedMeshShape,new BEPUphysics.MathExtensions.AffineTransform(scale,Quaternion.CreateFromRotationMatrix(rotation),pos));
     instancedMesh.Material = new BEPUphysics.Materials.Material(materialDescription.StaticFriction, materialDescription.DynamicFriction, materialDescription.Bounciness);
     instancedMesh.Sidedness = TriangleSidedness;
     this.rotation = rotation;
     this.scale = scale;
 }
        ///<summary>
        /// Constructs a new mobile mesh shape from cached data.
        ///</summary>
        ///<param name="meshData">Mesh data reprsenting the shape. Should already be properly centered.</param>
        /// <param name="hullVertices">Outer hull vertices of the mobile mesh shape used to quickly compute the bounding box.</param>
        ///<param name="solidity">Solidity state of the shape.</param>
        /// <param name="sidednessWhenSolid">Triangle sidedness to use when the shape is solid.</param>
        /// <param name="collisionMargin">Collision margin used to expand the mesh triangles.</param>
        /// <param name="volumeDescription">Description of the volume and its distribution in the shape. Assumed to be correct; no processing or validation is performed.</param>
        public MobileMeshShape(TransformableMeshData meshData, IList <Vector3> hullVertices, MobileMeshSolidity solidity, TriangleSidedness sidednessWhenSolid, Fix64 collisionMargin, EntityShapeVolumeDescription volumeDescription)
        {
            triangleMesh        = new TriangleMesh(meshData);
            this.hullVertices   = new RawList <Vector3>(hullVertices);
            meshCollisionMargin = collisionMargin;
            this.solidity       = solidity;
            SidednessWhenSolid  = sidednessWhenSolid;

            UpdateEntityShapeVolume(volumeDescription);
        }
Example #4
0
        void ComputeSolidSidedness()
        {
            //Raycast against the mesh.
            //If there's an even number of hits, then the ray start point is outside.
            //If there's an odd number of hits, then the ray start point is inside.

            //If the start is outside, then take the earliest toi hit and calibrate sidedness based on it.
            //If the start is inside, then take the latest toi hit and calibrate sidedness based on it.

            //This test assumes consistent winding across the entire mesh as well as a closed surface.
            //If those assumptions are not correct, then the raycast cannot determine inclusion or exclusion,
            //or there exists no calibration that will work across the entire surface.

            //Pick a ray direction that goes to a random location on the mesh.
            //A vertex would work, but targeting the middle of a triangle avoids some edge cases.
            var     ray = new Ray();
            Vector3 vA, vB, vC;

            triangleMesh.Data.GetTriangle(((triangleMesh.Data.indices.Length / 3) / 2) * 3, out vA, out vB, out vC);
            ray.Direction = (vA + vB + vC) / 3;
            ray.Direction.Normalize();

            solidSidedness = ComputeSolidSidednessHelper(ray);
            //TODO: Positions need to be valid for the verifying directions to work properly.
            ////Find another direction and test it to corroborate the first test.
            //Ray alternateRay;
            //alternateRay.Position = ray.Position;
            //Vector3.Cross(ref ray.Direction, ref Toolbox.UpVector, out alternateRay.Direction);
            //float lengthSquared = alternateRay.Direction.LengthSquared();
            //if (lengthSquared < Toolbox.Epsilon)
            //{
            //    Vector3.Cross(ref ray.Direction, ref Toolbox.RightVector, out alternateRay.Direction);
            //    lengthSquared = alternateRay.Direction.LengthSquared();
            //}
            //Vector3.Divide(ref alternateRay.Direction, (float)Math.Sqrt(lengthSquared), out alternateRay.Direction);
            //var sidednessCandidate2 = ComputeSolidSidednessHelper(alternateRay);
            //if (sidednessCandidate == sidednessCandidate2)
            //{
            //    //The two tests agreed! It's very likely that the sidedness is, in fact, in this direction.
            //    solidSidedness = sidednessCandidate;
            //}
            //else
            //{
            //    //The two tests disagreed.  Tiebreaker!
            //    Vector3.Cross(ref alternateRay.Direction, ref ray.Direction, out alternateRay.Direction);
            //    solidSidedness = ComputeSolidSidednessHelper(alternateRay);
            //}
        }
        //TODO: Having a specialized triangle-triangle pair test would be nice.  Even if it didn't use an actual triangle-triangle test, certain assumptions could still make it speedier and more elegant.
        //"Closest points between triangles" + persistent manifolding would probably be the best approach (a lot faster than the triangle-convex general case anyway).
        public override bool GenerateContactCandidates(TriangleShape triangle,
                                                       out TinyStructList <ContactData> contactList)
        {
            if (base.GenerateContactCandidates(triangle, out contactList))
            {
                //The triangle-convex pair test has already rejected contacts whose normals would violate the first triangle's sidedness.
                //However, since it's a vanilla triangle-convex test, it doesn't know about the sidedness of the other triangle!
                TriangleShape shape = (TriangleShape)convex;
                Vector3       normal;
                //Lots of recalculating ab-bc!
                Vector3 ab, ac;
                Vector3.Subtract(ref shape.vB, ref shape.vA, out ab);
                Vector3.Subtract(ref shape.vC, ref shape.vA, out ac);
                Vector3.Cross(ref ab, ref ac, out normal);
                TriangleSidedness sidedness = shape.sidedness;
                if (sidedness != TriangleSidedness.DoubleSided)
                {
                    for (int i = contactList.Count - 1; i >= 0; i--)
                    {
                        ContactData item;
                        contactList.Get(i, out item);

                        float dot;
                        Vector3.Dot(ref item.Normal, ref normal, out dot);
                        if (sidedness == TriangleSidedness.Clockwise)
                        {
                            if (dot < 0)
                            {
                                contactList.RemoveAt(i);
                            }
                        }
                        else
                        {
                            if (dot > 0)
                            {
                                contactList.RemoveAt(i);
                            }
                        }
                    }
                }

                return(contactList.Count > 0);
            }

            return(false);
        }
Example #6
0
        ///<summary>
        /// Tests a ray against the triangle mesh.
        ///</summary>
        ///<param name="ray">Ray to test against the mesh.</param>
        /// <param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
        ///<param name="hits">Hit data for the ray, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, IList <RayHit> hits)
        {
            var hitElements = Resources.GetIntList();

            tree.GetOverlaps(ray, maximumLength, hitElements);
            for (int i = 0; i < hitElements.Count; i++)
            {
                Vector3 v1, v2, v3;
                data.GetTriangle(hitElements[i], out v1, out v2, out v3);
                RayHit hit;
                if (Toolbox.FindRayTriangleIntersection(ref ray, maximumLength, sidedness, ref v1, ref v2, ref v3, out hit))
                {
                    hits.Add(hit);
                }
            }
            Resources.GiveBack(hitElements);
            return(hits.Count > 0);
        }
Example #7
0
        ///<summary>
        /// Tests a ray against the instance.
        ///</summary>
        ///<param name="ray">Ray to test.</param>
        ///<param name="maximumLength">Maximum length of the ray to test; in units of the ray's direction's length.</param>
        ///<param name="sidedness">Sidedness to use during the ray cast.  This does not have to be the same as the mesh's sidedness.</param>
        ///<param name="rayHit">The hit location of the ray on the mesh, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
        {
            //Put the ray into local space.
            Ray             localRay;
            AffineTransform inverse;

            AffineTransform.Invert(ref worldTransform, out inverse);
            Matrix3x3.Transform(ref ray.Direction, ref inverse.LinearTransform, out localRay.Direction);
            AffineTransform.Transform(ref ray.Position, ref inverse, out localRay.Position);

            if (Shape.TriangleMesh.RayCast(localRay, maximumLength, sidedness, out rayHit))
            {
                //Transform the hit into world space.
                Vector3.Multiply(ref ray.Direction, rayHit.T, out rayHit.Location);
                Vector3.Add(ref rayHit.Location, ref ray.Position, out rayHit.Location);
                Matrix3x3.TransformTranspose(ref rayHit.Normal, ref inverse.LinearTransform, out rayHit.Normal);
                return(true);
            }
            rayHit = new RayHit();
            return(false);
        }
        } // Uninitialize

        #endregion

        #region Create Static Collidable From Model Filter

        /// <summary>
        /// Creates and assign a static mesh usign the model stored in the model filter component.
        /// </summary>
        /// <param name="triangleSidedness">A triangle can be double sided, or allow one of its sides to let interacting objects through.</param>
        public void CreateStaticCollidableFromModelFilter(TriangleSidedness triangleSidedness = TriangleSidedness.Counterclockwise)
        {
            ModelFilter modelFilter = ((GameObject3D)Owner).ModelFilter;

            if (modelFilter != null && modelFilter.Model != null && modelFilter.Model is FileModel)
            {
                Vector3[] vertices;
                int[]     indices;
                TriangleMesh.GetVerticesAndIndicesFromModel(((FileModel)modelFilter.Model).Resource, out vertices, out indices);
                StaticMesh staticMesh = new StaticMesh(vertices, indices)
                {
                    Sidedness = triangleSidedness
                };

                StaticCollidable = staticMesh;
            }
            else
            {
                throw new InvalidOperationException("Static Collider: Model filter not present, model not present or model is not a FileModel.");
            }
        } // CreateStaticCollidableFromModelFilter
Example #9
0
        ///<summary>
        /// Tests a ray against the surface of the mesh.  This does not take into account solidity.
        ///</summary>
        ///<param name="ray">Ray to test.</param>
        ///<param name="maximumLength">Maximum length of the ray to test; in units of the ray's direction's length.</param>
        ///<param name="sidedness">Sidedness to use during the ray cast.  This does not have to be the same as the mesh's sidedness.</param>
        ///<param name="rayHit">The hit location of the ray on the mesh, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
        {
            //Put the ray into local space.
            Ray       localRay;
            Matrix3x3 orientation;

            Matrix3x3.CreateFromQuaternion(ref worldTransform.Orientation, out orientation);
            Matrix3x3.TransformTranspose(ref ray.Direction, ref orientation, out localRay.Direction);
            Vector3.Subtract(ref ray.Position, ref worldTransform.Position, out localRay.Position);
            Matrix3x3.TransformTranspose(ref localRay.Position, ref orientation, out localRay.Position);

            if (Shape.TriangleMesh.RayCast(localRay, maximumLength, sidedness, out rayHit))
            {
                //Transform the hit into world space.
                Vector3.Multiply(ref ray.Direction, rayHit.T, out rayHit.Location);
                Vector3.Add(ref rayHit.Location, ref ray.Position, out rayHit.Location);
                Matrix3x3.Transform(ref rayHit.Normal, ref orientation, out rayHit.Normal);
                return(true);
            }
            rayHit = new RayHit();
            return(false);
        }
Example #10
0
        ///<summary>
        /// Tests a ray against the triangle mesh.
        ///</summary>
        ///<param name="ray">Ray to test against the mesh.</param>
        /// <param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
        ///<param name="rayHit">Hit data for the ray, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
        {
            var  rayHits  = Resources.GetRayHitList();
            bool toReturn = RayCast(ray, maximumLength, sidedness, rayHits);

            if (toReturn)
            {
                rayHit = rayHits[0];
                for (int i = 1; i < rayHits.Count; i++)
                {
                    RayHit hit = rayHits[i];
                    if (hit.T < rayHit.T)
                    {
                        rayHit = hit;
                    }
                }
            }
            else
            {
                rayHit = new RayHit();
            }
            Resources.GiveBack(rayHits);
            return(toReturn);
        }
Example #11
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="hits">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, TriangleSidedness sidedness, IList <RayHit> hits)
 {
     return(RayCast(ray, float.MaxValue, sidedness, hits));
 }
Example #12
0
        ///<summary>
        /// Tests a ray against the surface of the mesh.  This does not take into account solidity.
        ///</summary>
        ///<param name="ray">Ray to test.</param>
        ///<param name="maximumLength">Maximum length of the ray to test; in units of the ray's direction's length.</param>
        ///<param name="sidedness">Sidedness to use during the ray cast.  This does not have to be the same as the mesh's sidedness.</param>
        ///<param name="rayHit">The hit location of the ray on the mesh, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
        {
            //Put the ray into local space.
            Ray localRay;
            Matrix3x3 orientation;
            Matrix3x3.CreateFromQuaternion(ref worldTransform.Orientation, out orientation);
            Matrix3x3.TransformTranspose(ref ray.Direction, ref orientation, out localRay.Direction);
            Vector3.Subtract(ref ray.Position, ref worldTransform.Position, out localRay.Position);
            Matrix3x3.TransformTranspose(ref localRay.Position, ref orientation, out localRay.Position);

            if (Shape.TriangleMesh.RayCast(localRay, maximumLength, sidedness, out rayHit))
            {
                //Transform the hit into world space.
                Vector3.Multiply(ref ray.Direction, rayHit.T, out rayHit.Location);
                Vector3.Add(ref rayHit.Location, ref ray.Position, out rayHit.Location);
                Matrix3x3.Transform(ref rayHit.Normal, ref orientation, out rayHit.Normal);
                return true;
            }
            rayHit = new RayHit();
            return false;
        }
 ///<summary>
 /// Tests a ray against the mesh.
 ///</summary>
 ///<param name="ray">Ray to test.</param>
 ///<param name="maximumLength">Maximum length to test in units of the ray direction's length.</param>
 ///<param name="sidedness">Sidedness to use when raycasting.  Doesn't have to be the same as the mesh's own sidedness.</param>
 ///<param name="rayHit">Data about the ray's intersection with the mesh, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
 {
     return(mesh.RayCast(ray, maximumLength, sidedness, out rayHit));
 }
Example #14
0
        ///<summary>
        /// Updates the time of impact for the pair.
        ///</summary>
        ///<param name="requester">Collidable requesting the update.</param>
        ///<param name="dt">Timestep duration.</param>
        public override void UpdateTimeOfImpact(Collidable requester, float dt)
        {
            //TODO: This conditional early outing stuff could be pulled up into a common system, along with most of the pair handler.
            var overlap    = BroadPhaseOverlap;
            var convexMode = convex.entity == null ? PositionUpdateMode.Discrete : convex.entity.PositionUpdateMode;

            if (
                (mobileMesh.IsActive || (convex.entity == null ? false : convex.entity.activityInformation.IsActive)) &&     //At least one has to be active.
                (
                    (
                        convexMode == PositionUpdateMode.Continuous &&       //If both are continuous, only do the process for A.
                        mobileMesh.entity.PositionUpdateMode == PositionUpdateMode.Continuous &&
                        overlap.entryA == requester
                    ) ||
                    (
                        convexMode == PositionUpdateMode.Continuous ^       //If only one is continuous, then we must do it.
                        mobileMesh.entity.PositionUpdateMode == PositionUpdateMode.Continuous
                    )
                )
                )
            {
                //TODO: This system could be made more robust by using a similar region-based rejection of edges.
                //CCD events are awfully rare under normal circumstances, so this isn't usually an issue.

                //Only perform the test if the minimum radii are small enough relative to the size of the velocity.
                Vector3 velocity;
                if (convex.entity != null)
                {
                    Vector3.Subtract(ref convex.entity.linearVelocity, ref mobileMesh.entity.linearVelocity, out velocity);
                }
                else
                {
                    Vector3.Negate(ref mobileMesh.entity.linearVelocity, out velocity);
                }
                Vector3.Multiply(ref velocity, dt, out velocity);
                float velocitySquared = velocity.LengthSquared();

                var minimumRadius = convex.Shape.minimumRadius * MotionSettings.CoreShapeScaling;
                timeOfImpact = 1;
                if (minimumRadius * minimumRadius < velocitySquared)
                {
                    TriangleSidedness sidedness = mobileMesh.Shape.Sidedness;
                    Matrix3X3         orientation;
                    Matrix3X3.CreateFromQuaternion(ref mobileMesh.worldTransform.Orientation, out orientation);
                    var triangle = Resources.GetTriangle();
                    triangle.collisionMargin = 0;
                    //Spherecast against all triangles to find the earliest time.
                    for (int i = 0; i < MeshManifold.overlappedTriangles.count; i++)
                    {
                        MeshBoundingBoxTreeData data = mobileMesh.Shape.TriangleMesh.Data;
                        int triangleIndex            = MeshManifold.overlappedTriangles.Elements[i];
                        data.GetTriangle(triangleIndex, out triangle.vA, out triangle.vB, out triangle.vC);
                        Matrix3X3.Transform(ref triangle.vA, ref orientation, out triangle.vA);
                        Matrix3X3.Transform(ref triangle.vB, ref orientation, out triangle.vB);
                        Matrix3X3.Transform(ref triangle.vC, ref orientation, out triangle.vC);
                        Vector3.Add(ref triangle.vA, ref mobileMesh.worldTransform.Position, out triangle.vA);
                        Vector3.Add(ref triangle.vB, ref mobileMesh.worldTransform.Position, out triangle.vB);
                        Vector3.Add(ref triangle.vC, ref mobileMesh.worldTransform.Position, out triangle.vC);
                        //Put the triangle into 'localish' space of the convex.
                        Vector3.Subtract(ref triangle.vA, ref convex.worldTransform.Position, out triangle.vA);
                        Vector3.Subtract(ref triangle.vB, ref convex.worldTransform.Position, out triangle.vB);
                        Vector3.Subtract(ref triangle.vC, ref convex.worldTransform.Position, out triangle.vC);

                        RayHit rayHit;
                        if (GJKToolbox.CCDSphereCast(new Ray(Toolbox.ZeroVector, velocity), minimumRadius, triangle, ref Toolbox.RigidIdentity, timeOfImpact, out rayHit) &&
                            rayHit.T > Toolbox.BigEpsilon)
                        {
                            if (sidedness != TriangleSidedness.DoubleSided)
                            {
                                Vector3 AB, AC;
                                Vector3.Subtract(ref triangle.vB, ref triangle.vA, out AB);
                                Vector3.Subtract(ref triangle.vC, ref triangle.vA, out AC);
                                Vector3 normal;
                                Vector3.Cross(ref AB, ref AC, out normal);
                                float dot;
                                Vector3.Dot(ref normal, ref rayHit.Normal, out dot);
                                //Only perform sweep if the object is in danger of hitting the object.
                                //Triangles can be one sided, so check the impact normal against the triangle normal.
                                if (sidedness == TriangleSidedness.Counterclockwise && dot < 0 ||
                                    sidedness == TriangleSidedness.Clockwise && dot > 0)
                                {
                                    timeOfImpact = rayHit.T;
                                }
                            }
                            else
                            {
                                timeOfImpact = rayHit.T;
                            }
                        }
                    }
                    Resources.GiveBack(triangle);
                }
            }
        }
Example #15
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="rayHit">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, TriangleSidedness sidedness, out RayHit rayHit)
 {
     return RayCast(ray, float.MaxValue, sidedness, out rayHit);
 }
 /// <summary>
 /// Creates and instance of InstancedTriangleMesh
 /// Call the Static Method GetInstacedMesh to get the InstancedMeshShape obj
 /// </summary>
 /// <param name="InstancedMeshShape">The instanced mesh shape.</param>
 /// <param name="pos">The pos.</param>
 /// <param name="rotation">The rotation.</param>
 /// <param name="scale">The scale.</param>
 /// <param name="materialDescription">The material description.</param>
 /// <param name="TriangleSidedness">The triangle sidedness.</param>
 public InstancedTriangleMeshObject(InstancedMeshShape InstancedMeshShape, Vector3 pos, Matrix rotation, Vector3 scale, MaterialDescription materialDescription, TriangleSidedness TriangleSidedness = TriangleSidedness.Counterclockwise)
 {
     instancedMesh           = new InstancedMesh(InstancedMeshShape, new BEPUphysics.MathExtensions.AffineTransform(scale, Quaternion.CreateFromRotationMatrix(rotation), pos));
     instancedMesh.Material  = new BEPUphysics.Materials.Material(materialDescription.StaticFriction, materialDescription.DynamicFriction, materialDescription.Bounciness);
     instancedMesh.Sidedness = TriangleSidedness;
     this.rotation           = rotation;
     this.scale = scale;
 }
Example #17
0
        ///<summary>
        /// Tests a ray against the terrain shape.
        ///</summary>
        ///<param name="ray">Ray to test against the shape.</param>
        ///<param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        ///<param name="transform">Transform to apply to the terrain shape during the test.</param>
        ///<param name="sidedness">Sidedness of the triangles to use when raycasting.</param>
        ///<param name="hit">Hit data of the ray cast, if any.</param>
        ///<returns>Whether or not the ray hit the transformed terrain shape.</returns>
        public bool RayCast(ref Ray ray, float maximumLength, ref AffineTransform transform, TriangleSidedness sidedness, out RayHit hit)
        {
            hit = new RayHit();
            //Put the ray into local space.
            Ray localRay;
            AffineTransform inverse;
            AffineTransform.Invert(ref transform, out inverse);
            Matrix3x3.Transform(ref ray.Direction, ref inverse.LinearTransform, out localRay.Direction);
            AffineTransform.Transform(ref ray.Position, ref inverse, out localRay.Position);

            //Use rasterizey traversal.
            //The origin is at 0,0,0 and the map goes +X, +Y, +Z.
            //if it's before the origin and facing away, or outside the max and facing out, early out.
            float maxX = heights.GetLength(0) - 1;
            float maxZ = heights.GetLength(1) - 1;

            Vector3 progressingOrigin = localRay.Position;
            float distance = 0;
            //Check the outside cases first.
            if (progressingOrigin.X < 0)
            {
                if (localRay.Direction.X > 0)
                {
                    //Off the left side.
                    float timeToMinX = -progressingOrigin.X / localRay.Direction.X;
                    distance += timeToMinX;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                    return false; //Outside and pointing away from the terrain.
            }
            else if (progressingOrigin.X > maxX)
            {
                if (localRay.Direction.X < 0)
                {
                    //Off the left side.
                    float timeToMinX = -(progressingOrigin.X - maxX) / localRay.Direction.X;
                    distance += timeToMinX;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                    return false; //Outside and pointing away from the terrain.
            }

            if (progressingOrigin.Z < 0)
            {
                if (localRay.Direction.Z > 0)
                {
                    float timeToMinZ = -progressingOrigin.Z / localRay.Direction.Z;
                    distance += timeToMinZ;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                    return false;
            }
            else if (progressingOrigin.Z > maxZ)
            {
                if (localRay.Direction.Z < 0)
                {
                    float timeToMinZ = -(progressingOrigin.Z - maxZ) / localRay.Direction.Z;
                    distance += timeToMinZ;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                    return false;
            }

            if (distance > maximumLength)
                return false;

            //By now, we should be entering the main body of the terrain.

            int xCell = (int)progressingOrigin.X;
            int zCell = (int)progressingOrigin.Z;
            //If it's hitting the border and going in, then correct the index
            //so that it will initially target a valid quad.
            //Without this, a quad beyond the border would be tried and failed.
            if (xCell == heights.GetLength(0) - 1 && localRay.Direction.X < 0)
                xCell = heights.GetLength(0) - 2;
            if (zCell == heights.GetLength(1) - 1 && localRay.Direction.Z < 0)
                zCell = heights.GetLength(1) - 2;

            while (true)
            {
                //Check for a miss.
                if (xCell < 0 ||
                    zCell < 0 ||
                    xCell >= heights.GetLength(0) - 1 ||
                    zCell >= heights.GetLength(1) - 1)
                    return false;

                //Test the triangles of this cell.
                Vector3 v1, v2, v3, v4;
                // v3 v4
                // v1 v2
                GetLocalPosition(xCell, zCell, out v1);
                GetLocalPosition(xCell + 1, zCell, out v2);
                GetLocalPosition(xCell, zCell + 1, out v3);
                GetLocalPosition(xCell + 1, zCell + 1, out v4);
                RayHit hit1, hit2;
                bool didHit1;
                bool didHit2;

                //Don't bother doing ray intersection tests if the ray can't intersect it.

                float highest = v1.Y;
                float lowest = v1.Y;
                if (v2.Y > highest)
                    highest = v2.Y;
                else if (v2.Y < lowest)
                    lowest = v2.Y;
                if (v3.Y > highest)
                    highest = v3.Y;
                else if (v3.Y < lowest)
                    lowest = v3.Y;
                if (v4.Y > highest)
                    highest = v4.Y;
                else if (v4.Y < lowest)
                    lowest = v4.Y;

                if (!(progressingOrigin.Y > highest && localRay.Direction.Y > 0 ||
                    progressingOrigin.Y < lowest && localRay.Direction.Y < 0))
                {

                    if (quadTriangleOrganization == QuadTriangleOrganization.BottomLeftUpperRight)
                    {
                        //Always perform the raycast as if Y+ in local space is the way the triangles are facing.
                        didHit1 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v2, ref v3, out hit1);
                        didHit2 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v2, ref v4, ref v3, out hit2);
                    }
                    else //if (quadTriangleOrganization == CollisionShapes.QuadTriangleOrganization.BottomRightUpperLeft)
                    {
                        didHit1 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v2, ref v4, out hit1);
                        didHit2 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v4, ref v3, out hit2);
                    }
                    if (didHit1 && didHit2)
                    {
                        if (hit1.T < hit2.T)
                        {
                            Vector3.Multiply(ref ray.Direction, hit1.T, out hit.Location);
                            Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                            Matrix3x3.TransformTranspose(ref hit1.Normal, ref inverse.LinearTransform, out hit.Normal);
                            hit.T = hit1.T;
                            return true;
                        }
                        Vector3.Multiply(ref ray.Direction, hit2.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3x3.TransformTranspose(ref hit2.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit2.T;
                        return true;
                    }
                    else if (didHit1)
                    {
                        Vector3.Multiply(ref ray.Direction, hit1.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3x3.TransformTranspose(ref hit1.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit1.T;
                        return true;
                    }
                    else if (didHit2)
                    {
                        Vector3.Multiply(ref ray.Direction, hit2.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3x3.TransformTranspose(ref hit2.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit2.T;
                        return true;
                    }
                }

                //Move to the next cell.

                float timeToX;
                if (localRay.Direction.X < 0)
                    timeToX = -(progressingOrigin.X - xCell) / localRay.Direction.X;
                else if (ray.Direction.X > 0)
                    timeToX = (xCell + 1 - progressingOrigin.X) / localRay.Direction.X;
                else
                    timeToX = float.MaxValue;

                float timeToZ;
                if (localRay.Direction.Z < 0)
                    timeToZ = -(progressingOrigin.Z - zCell) / localRay.Direction.Z;
                else if (localRay.Direction.Z > 0)
                    timeToZ = (zCell + 1 - progressingOrigin.Z) / localRay.Direction.Z;
                else
                    timeToZ = float.MaxValue;

                //Move to the next cell.
                if (timeToX < timeToZ)
                {
                    if (localRay.Direction.X < 0)
                        xCell--;
                    else
                        xCell++;

                    distance += timeToX;
                    if (distance > maximumLength)
                        return false;

                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    if (localRay.Direction.Z < 0)
                        zCell--;
                    else
                        zCell++;

                    distance += timeToZ;
                    if (distance > maximumLength)
                        return false;

                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }

            }
        }
        ///<summary>
        /// Tests a ray against the instance.
        ///</summary>
        ///<param name="ray">Ray to test.</param>
        ///<param name="maximumLength">Maximum length of the ray to test; in units of the ray's direction's length.</param>
        ///<param name="sidedness">Sidedness to use during the ray cast.  This does not have to be the same as the mesh's sidedness.</param>
        ///<param name="rayHit">The hit location of the ray on the mesh, if any.</param>
        ///<returns>Whether or not the ray hit the mesh.</returns>
        public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
        {
            //Put the ray into local space.
            Ray localRay;
            AffineTransform inverse;
            AffineTransform.Invert(ref worldTransform, out inverse);
            Matrix3x3.Transform(ref ray.Direction, ref inverse.LinearTransform, out localRay.Direction);
            AffineTransform.Transform(ref ray.Position, ref inverse, out localRay.Position);

            if (Shape.TriangleMesh.RayCast(localRay, maximumLength, sidedness, out rayHit))
            {
                //Transform the hit into world space.
                Vector3.Multiply(ref ray.Direction, rayHit.T, out rayHit.Location);
                Vector3.Add(ref rayHit.Location, ref ray.Position, out rayHit.Location);
                Matrix3x3.TransformTranspose(ref rayHit.Normal, ref inverse.LinearTransform, out rayHit.Normal);
                return true;
            }
            rayHit = new RayHit();
            return false;
        }
Example #19
0
 ///<summary>
 /// Tests a ray against the mesh.
 ///</summary>
 ///<param name="ray">Ray to test.</param>
 ///<param name="maximumLength">Maximum length to test in units of the ray direction's length.</param>
 ///<param name="sidedness">Sidedness to use when raycasting.  Doesn't have to be the same as the mesh's own sidedness.</param>
 ///<param name="rayHit">Data about the ray's intersection with the mesh, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
 {
     return mesh.RayCast(ray, maximumLength, sidedness, out rayHit);
 }
Example #20
0
        void ComputeSolidSidedness()
        {
            //Raycast against the mesh.
            //If there's an even number of hits, then the ray start point is outside.
            //If there's an odd number of hits, then the ray start point is inside.

            //If the start is outside, then take the earliest toi hit and calibrate sidedness based on it.
            //If the start is inside, then take the latest toi hit and calibrate sidedness based on it.

            //This test assumes consistent winding across the entire mesh as well as a closed surface.
            //If those assumptions are not correct, then the raycast cannot determine inclusion or exclusion,
            //or there exists no calibration that will work across the entire surface.

            //Pick a ray direction that goes to a random location on the mesh.  
            //A vertex would work, but targeting the middle of a triangle avoids some edge cases.
            var ray = new Ray();
            Vector3 vA, vB, vC;
            triangleMesh.Data.GetTriangle(((triangleMesh.Data.indices.Length / 3) / 2) * 3, out vA, out vB, out vC);
			vA.Add2( ref vB, ref vC, out vA );
            vA.Mult( 1 / 3, out ray.Direction );
            ray.Direction.Normalize();

            SidednessWhenSolid = ComputeSolidSidednessHelper(ray);
            //ComputeSolidSidednessHelper is separated into another function just in case multiple queries were desired for validation.
            //If multiple rays returned different sidednesses, the shape would be inconsistent.

        }
Example #21
0
        ///<summary>
        /// Constructs a new mobile mesh shape from cached data.
        ///</summary>
        ///<param name="meshData">Mesh data reprsenting the shape. Should already be properly centered.</param>
        /// <param name="hullVertices">Outer hull vertices of the mobile mesh shape used to quickly compute the bounding box.</param>
        ///<param name="solidity">Solidity state of the shape.</param>
        /// <param name="sidednessWhenSolid">Triangle sidedness to use when the shape is solid.</param>
        /// <param name="collisionMargin">Collision margin used to expand the mesh triangles.</param>
        /// <param name="volumeDescription">Description of the volume and its distribution in the shape. Assumed to be correct; no processing or validation is performed.</param>
        public MobileMeshShape(TransformableMeshData meshData, IList<Vector3> hullVertices, MobileMeshSolidity solidity, TriangleSidedness sidednessWhenSolid, float collisionMargin, EntityShapeVolumeDescription volumeDescription)
        {
            triangleMesh = new TriangleMesh(meshData);
            this.hullVertices = new RawList<Vector3>(hullVertices);
            meshCollisionMargin = collisionMargin;
            this.solidity = solidity;
            SidednessWhenSolid = sidednessWhenSolid;

            UpdateEntityShapeVolume(volumeDescription);
        }
Example #22
0
        /// <summary>
        /// Determines the intersection between a ray and a triangle.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="maximumLength">Maximum length to travel in units of the direction's length.</param>
        /// <param name="sidedness">Sidedness of the triangle to test.</param>
        /// <param name="a">First vertex of the triangle.</param>
        /// <param name="b">Second vertex of the triangle.</param>
        /// <param name="c">Third vertex of the triangle.</param>
        /// <param name="hit">Hit data of the ray, if any</param>
        /// <returns>Whether or not the ray and triangle intersect.</returns>
        public static bool FindRayTriangleIntersection(ref Ray ray, float maximumLength, TriangleSidedness sidedness, ref Vector3 a, ref Vector3 b, ref Vector3 c, out RayHit hit)
        {
            hit = new RayHit();
            Vector3 ab = b - a;
            Vector3 ac = c - a;

            Vector3x.Cross(ref ab, ref ac, out hit.Normal);
            if (hit.Normal.LengthSquared() < Epsilon)
            {
                return(false); //Degenerate triangle!
            }
            float d = -Vector3.Dot(ray.Direction, hit.Normal);

            switch (sidedness)
            {
            case TriangleSidedness.DoubleSided:
                if (d <= 0)     //Pointing the wrong way.  Flip the normal.
                {
                    hit.Normal = -hit.Normal;
                    d          = -d;
                }
                break;

            case TriangleSidedness.Clockwise:
                if (d <= 0)     //Pointing the wrong way.  Can't hit.
                {
                    return(false);
                }

                break;

            case TriangleSidedness.Counterclockwise:
                if (d >= 0)     //Pointing the wrong way.  Can't hit.
                {
                    return(false);
                }

                hit.Normal = -hit.Normal;
                d          = -d;
                break;
            }

            Vector3 ap = ray.Position - a;

            hit.T  = Vector3.Dot(ap, hit.Normal);
            hit.T /= d;
            if (hit.T < 0 || hit.T > maximumLength)
            {
                return(false);//Hit is behind origin, or too far away.
            }
            hit.Location = ray.Position + hit.T * ray.Direction;

            // Compute barycentric coordinates
            ap = hit.Location - a;
            float ABdotAB, ABdotAC, ABdotAP;
            float ACdotAC, ACdotAP;

            ABdotAB = Vector3.Dot(ab, ab);
            ABdotAC = Vector3.Dot(ab, ac);
            ABdotAP = Vector3.Dot(ab, ap);
            ACdotAC = Vector3.Dot(ac, ac);
            ACdotAP = Vector3.Dot(ac, ap);

            float denom = 1 / (ABdotAB * ACdotAC - ABdotAC * ABdotAC);
            float u     = (ACdotAC * ABdotAP - ABdotAC * ACdotAP) * denom;
            float v     = (ABdotAB * ACdotAP - ABdotAC * ABdotAP) * denom;

            return((u >= -Toolbox.BigEpsilon) && (v >= -Toolbox.BigEpsilon) && (u + v <= 1 + Toolbox.BigEpsilon));
        }
Example #23
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="rayHit">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, TriangleSidedness sidedness, out RayHit rayHit)
 {
     return(RayCast(ray, float.MaxValue, sidedness, out rayHit));
 }
        void ComputeSolidSidedness()
        {
            //Raycast against the mesh.
            //If there's an even number of hits, then the ray start point is outside.
            //If there's an odd number of hits, then the ray start point is inside.

            //If the start is outside, then take the earliest toi hit and calibrate sidedness based on it.
            //If the start is inside, then take the latest toi hit and calibrate sidedness based on it.

            //This test assumes consistent winding across the entire mesh as well as a closed surface.
            //If those assumptions are not correct, then the raycast cannot determine inclusion or exclusion,
            //or there exists no calibration that will work across the entire surface.

            //Pick a ray direction that goes to a random location on the mesh.  
            //A vertex would work, but targeting the middle of a triangle avoids some edge cases.
            Ray ray = new Ray();
            Vector3 vA, vB, vC;
            triangleMesh.Data.GetTriangle(((triangleMesh.Data.indices.Length / 3) / 2) * 3, out vA, out vB, out vC);
            ray.Direction = (vA + vB + vC) / 3;
            ray.Direction.Normalize();

            solidSidedness = ComputeSolidSidednessHelper(ray);
            //TODO: Positions need to be valid for the verifying directions to work properly.
            ////Find another direction and test it to corroborate the first test.
            //Ray alternateRay;
            //alternateRay.Position = ray.Position;
            //Vector3.Cross(ref ray.Direction, ref Toolbox.UpVector, out alternateRay.Direction);
            //float lengthSquared = alternateRay.Direction.LengthSquared();
            //if (lengthSquared < Toolbox.Epsilon)
            //{
            //    Vector3.Cross(ref ray.Direction, ref Toolbox.RightVector, out alternateRay.Direction);
            //    lengthSquared = alternateRay.Direction.LengthSquared();
            //}
            //Vector3.Divide(ref alternateRay.Direction, (float)Math.Sqrt(lengthSquared), out alternateRay.Direction);
            //var sidednessCandidate2 = ComputeSolidSidednessHelper(alternateRay);
            //if (sidednessCandidate == sidednessCandidate2)
            //{
            //    //The two tests agreed! It's very likely that the sidedness is, in fact, in this direction.
            //    solidSidedness = sidednessCandidate;
            //}
            //else
            //{
            //    //The two tests disagreed.  Tiebreaker!
            //    Vector3.Cross(ref alternateRay.Direction, ref ray.Direction, out alternateRay.Direction);
            //    solidSidedness = ComputeSolidSidednessHelper(alternateRay);
            //}
        }
Example #25
0
        /// <summary>
        /// Determines the intersection between a ray and a triangle.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="maximumLength">Maximum length to travel in units of the direction's length.</param>
        /// <param name="sidedness">Sidedness of the triangle to test.</param>
        /// <param name="a">First vertex of the triangle.</param>
        /// <param name="b">Second vertex of the triangle.</param>
        /// <param name="c">Third vertex of the triangle.</param>
        /// <param name="hit">Hit data of the ray, if any</param>
        /// <returns>Whether or not the ray and triangle intersect.</returns>
        public static bool FindRayTriangleIntersection(ref Ray ray, float maximumLength, TriangleSidedness sidedness, ref Vector3 a, ref Vector3 b, ref Vector3 c, out RayHit hit)
        {
            hit = new RayHit();
            Vector3 ab, ac;
            Vector3.Subtract(ref b, ref a, out ab);
            Vector3.Subtract(ref c, ref a, out ac);

            Vector3.Cross(ref ab, ref ac, out hit.Normal);
            if (hit.Normal.LengthSquared() < Epsilon)
                return false; //Degenerate triangle!

            float d;
            Vector3.Dot(ref ray.Direction, ref hit.Normal, out d);
            d = -d;
            switch (sidedness)
            {
                case TriangleSidedness.DoubleSided:
                    if (d <= 0) //Pointing the wrong way.  Flip the normal.
                    {
                        Vector3.Negate(ref hit.Normal, out hit.Normal);
                        d = -d;
                    }
                    break;
                case TriangleSidedness.Clockwise:
                    if (d <= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    break;
                case TriangleSidedness.Counterclockwise:
                    if (d >= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    Vector3.Negate(ref hit.Normal, out hit.Normal);
                    d = -d;
                    break;
            }

            Vector3 ap;
            Vector3.Subtract(ref ray.Position, ref a, out ap);

            Vector3.Dot(ref ap, ref hit.Normal, out hit.T);
            hit.T /= d;
            if (hit.T < 0 || hit.T > maximumLength)
                return false;//Hit is behind origin, or too far away.

            Vector3.Multiply(ref ray.Direction, hit.T, out hit.Location);
            Vector3.Add(ref ray.Position, ref hit.Location, out hit.Location);

            // Compute barycentric coordinates
            Vector3.Subtract(ref hit.Location, ref a, out ap);
            float ABdotAB, ABdotAC, ABdotAP;
            float ACdotAC, ACdotAP;
            Vector3.Dot(ref ab, ref ab, out ABdotAB);
            Vector3.Dot(ref ab, ref ac, out ABdotAC);
            Vector3.Dot(ref ab, ref ap, out ABdotAP);
            Vector3.Dot(ref ac, ref ac, out ACdotAC);
            Vector3.Dot(ref ac, ref ap, out ACdotAP);

            float denom = 1 / (ABdotAB * ACdotAC - ABdotAC * ABdotAC);
            float u = (ACdotAC * ABdotAP - ABdotAC * ACdotAP) * denom;
            float v = (ABdotAB * ACdotAP - ABdotAC * ABdotAP) * denom;

            return (u >= -Toolbox.BigEpsilon) && (v >= -Toolbox.BigEpsilon) && (u + v <= 1 + Toolbox.BigEpsilon);

        }
Example #26
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="hits">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, TriangleSidedness sidedness, IList<RayHit> hits)
 {
     return RayCast(ray, float.MaxValue, sidedness, hits);
 }
Example #27
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="rayHit">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, out RayHit rayHit)
 {
     var rayHits = CommonResources.GetRayHitList();
     bool toReturn = RayCast(ray, maximumLength, sidedness, rayHits);
     if (toReturn)
     {
         rayHit = rayHits[0];
         for (int i = 1; i < rayHits.Count; i++)
         {
             RayHit hit = rayHits[i];
             if (hit.T < rayHit.T)
                 rayHit = hit;
         }
     }
     else
         rayHit = new RayHit();
     CommonResources.GiveBack(rayHits);
     return toReturn;
 }
Example #28
0
 ///<summary>
 /// Tests a ray against the triangle mesh.
 ///</summary>
 ///<param name="ray">Ray to test against the mesh.</param>
 /// <param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
 /// <param name="sidedness">Sidedness to apply to the mesh for the ray cast.</param>
 ///<param name="hits">Hit data for the ray, if any.</param>
 ///<returns>Whether or not the ray hit the mesh.</returns>
 public bool RayCast(Ray ray, float maximumLength, TriangleSidedness sidedness, IList<RayHit> hits)
 {
     var hitElements = CommonResources.GetIntList();
     tree.GetOverlaps(ray, maximumLength, hitElements);
     for (int i = 0; i < hitElements.Count; i++)
     {
         System.Numerics.Vector3 v1, v2, v3;
         data.GetTriangle(hitElements[i], out v1, out v2, out v3);
         RayHit hit;
         if (Toolbox.FindRayTriangleIntersection(ref ray, maximumLength, sidedness, ref v1, ref v2, ref v3, out hit))
         {
             hits.Add(hit);
         }
     }
     CommonResources.GiveBack(hitElements);
     return hits.Count > 0;
 }
Example #29
0
        ///<summary>
        /// Tests a ray against the terrain shape.
        ///</summary>
        ///<param name="ray">Ray to test against the shape.</param>
        ///<param name="maximumLength">Maximum length of the ray in units of the ray direction's length.</param>
        ///<param name="transform">Transform to apply to the terrain shape during the test.</param>
        ///<param name="sidedness">Sidedness of the triangles to use when raycasting.</param>
        ///<param name="hit">Hit data of the ray cast, if any.</param>
        ///<returns>Whether or not the ray hit the transformed terrain shape.</returns>
        public bool RayCast(ref Ray ray, float maximumLength, ref AffineTransform transform, TriangleSidedness sidedness, out RayHit hit)
        {
            hit = new RayHit();
            //Put the ray into local space.
            Ray             localRay;
            AffineTransform inverse;

            AffineTransform.Invert(ref transform, out inverse);
            Matrix3X3.Transform(ref ray.Direction, ref inverse.LinearTransform, out localRay.Direction);
            AffineTransform.Transform(ref ray.Position, ref inverse, out localRay.Position);

            //Use rasterizey traversal.
            //The origin is at 0,0,0 and the map goes +X, +Y, +Z.
            //if it's before the origin and facing away, or outside the max and facing out, early out.
            float maxX = heights.GetLength(0) - 1;
            float maxZ = heights.GetLength(1) - 1;

            Vector3 progressingOrigin = localRay.Position;
            float   distance          = 0;

            //Check the outside cases first.
            if (progressingOrigin.X < 0)
            {
                if (localRay.Direction.X > 0)
                {
                    //Off the left side.
                    float timeToMinX = -progressingOrigin.X / localRay.Direction.X;
                    distance += timeToMinX;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    return(false); //Outside and pointing away from the terrain.
                }
            }
            else if (progressingOrigin.X > maxX)
            {
                if (localRay.Direction.X < 0)
                {
                    //Off the left side.
                    float timeToMinX = -(progressingOrigin.X - maxX) / localRay.Direction.X;
                    distance += timeToMinX;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    return(false); //Outside and pointing away from the terrain.
                }
            }

            if (progressingOrigin.Z < 0)
            {
                if (localRay.Direction.Z > 0)
                {
                    float timeToMinZ = -progressingOrigin.Z / localRay.Direction.Z;
                    distance += timeToMinZ;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    return(false);
                }
            }
            else if (progressingOrigin.Z > maxZ)
            {
                if (localRay.Direction.Z < 0)
                {
                    float timeToMinZ = -(progressingOrigin.Z - maxZ) / localRay.Direction.Z;
                    distance += timeToMinZ;
                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToMinZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    return(false);
                }
            }

            if (distance > maximumLength)
            {
                return(false);
            }



            //By now, we should be entering the main body of the terrain.

            int xCell = (int)progressingOrigin.X;
            int zCell = (int)progressingOrigin.Z;

            //If it's hitting the border and going in, then correct the index
            //so that it will initially target a valid quad.
            //Without this, a quad beyond the border would be tried and failed.
            if (xCell == heights.GetLength(0) - 1 && localRay.Direction.X < 0)
            {
                xCell = heights.GetLength(0) - 2;
            }
            if (zCell == heights.GetLength(1) - 1 && localRay.Direction.Z < 0)
            {
                zCell = heights.GetLength(1) - 2;
            }

            while (true)
            {
                //Check for a miss.
                if (xCell < 0 ||
                    zCell < 0 ||
                    xCell >= heights.GetLength(0) - 1 ||
                    zCell >= heights.GetLength(1) - 1)
                {
                    return(false);
                }

                //Test the triangles of this cell.
                Vector3 v1, v2, v3, v4;
                // v3 v4
                // v1 v2
                GetLocalPosition(xCell, zCell, out v1);
                GetLocalPosition(xCell + 1, zCell, out v2);
                GetLocalPosition(xCell, zCell + 1, out v3);
                GetLocalPosition(xCell + 1, zCell + 1, out v4);
                RayHit hit1, hit2;
                bool   didHit1;
                bool   didHit2;

                //Don't bother doing ray intersection tests if the ray can't intersect it.

                float highest = v1.Y;
                float lowest  = v1.Y;
                if (v2.Y > highest)
                {
                    highest = v2.Y;
                }
                else if (v2.Y < lowest)
                {
                    lowest = v2.Y;
                }
                if (v3.Y > highest)
                {
                    highest = v3.Y;
                }
                else if (v3.Y < lowest)
                {
                    lowest = v3.Y;
                }
                if (v4.Y > highest)
                {
                    highest = v4.Y;
                }
                else if (v4.Y < lowest)
                {
                    lowest = v4.Y;
                }


                if (!(progressingOrigin.Y > highest && localRay.Direction.Y > 0 ||
                      progressingOrigin.Y < lowest && localRay.Direction.Y < 0))
                {
                    if (quadTriangleOrganization == QuadTriangleOrganization.BottomLeftUpperRight)
                    {
                        //Always perform the raycast as if Y+ in local space is the way the triangles are facing.
                        didHit1 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v2, ref v3, out hit1);
                        didHit2 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v2, ref v4, ref v3, out hit2);
                    }
                    else //if (quadTriangleOrganization == CollisionShapes.QuadTriangleOrganization.BottomRightUpperLeft)
                    {
                        didHit1 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v2, ref v4, out hit1);
                        didHit2 = Toolbox.FindRayTriangleIntersection(ref localRay, maximumLength, sidedness, ref v1, ref v4, ref v3, out hit2);
                    }
                    if (didHit1 && didHit2)
                    {
                        if (hit1.T < hit2.T)
                        {
                            Vector3.Multiply(ref ray.Direction, hit1.T, out hit.Location);
                            Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                            Matrix3X3.TransformTranspose(ref hit1.Normal, ref inverse.LinearTransform, out hit.Normal);
                            hit.T = hit1.T;
                            return(true);
                        }
                        Vector3.Multiply(ref ray.Direction, hit2.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3X3.TransformTranspose(ref hit2.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit2.T;
                    }
                    else if (didHit1)
                    {
                        Vector3.Multiply(ref ray.Direction, hit1.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3X3.TransformTranspose(ref hit1.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit1.T;
                        return(true);
                    }
                    else if (didHit2)
                    {
                        Vector3.Multiply(ref ray.Direction, hit2.T, out hit.Location);
                        Vector3.Add(ref hit.Location, ref ray.Position, out hit.Location);
                        Matrix3X3.TransformTranspose(ref hit2.Normal, ref inverse.LinearTransform, out hit.Normal);
                        hit.T = hit2.T;
                        return(true);
                    }
                }

                //Move to the next cell.

                float timeToX;
                if (localRay.Direction.X < 0)
                {
                    timeToX = -(progressingOrigin.X - xCell) / localRay.Direction.X;
                }
                else if (ray.Direction.X > 0)
                {
                    timeToX = (xCell + 1 - progressingOrigin.X) / localRay.Direction.X;
                }
                else
                {
                    timeToX = float.MaxValue;
                }

                float timeToZ;
                if (localRay.Direction.Z < 0)
                {
                    timeToZ = -(progressingOrigin.Z - zCell) / localRay.Direction.Z;
                }
                else if (localRay.Direction.Z > 0)
                {
                    timeToZ = (zCell + 1 - progressingOrigin.Z) / localRay.Direction.Z;
                }
                else
                {
                    timeToZ = float.MaxValue;
                }

                //Move to the next cell.
                if (timeToX < timeToZ)
                {
                    if (localRay.Direction.X < 0)
                    {
                        xCell--;
                    }
                    else
                    {
                        xCell++;
                    }

                    distance += timeToX;
                    if (distance > maximumLength)
                    {
                        return(false);
                    }

                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToX, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
                else
                {
                    if (localRay.Direction.Z < 0)
                    {
                        zCell--;
                    }
                    else
                    {
                        zCell++;
                    }

                    distance += timeToZ;
                    if (distance > maximumLength)
                    {
                        return(false);
                    }

                    Vector3 increment;
                    Vector3.Multiply(ref localRay.Direction, timeToZ, out increment);
                    Vector3.Add(ref increment, ref progressingOrigin, out progressingOrigin);
                }
            }
        }
Example #30
0
        /// <summary>
        /// Determines the intersection between a ray and a triangle.
        /// </summary>
        /// <param name="ray">Ray to test.</param>
        /// <param name="maximumLength">Maximum length to travel in units of the direction's length.</param>
        /// <param name="sidedness">Sidedness of the triangle to test.</param>
        /// <param name="a">First vertex of the triangle.</param>
        /// <param name="b">Second vertex of the triangle.</param>
        /// <param name="c">Third vertex of the triangle.</param>
        /// <param name="hit">Hit data of the ray, if any</param>
        /// <returns>Whether or not the ray and triangle intersect.</returns>
        public static bool FindRayTriangleIntersection(ref Ray ray, float maximumLength, TriangleSidedness sidedness, ref Vector3 a, ref Vector3 b, ref Vector3 c, out RayHit hit)
        {
            hit = new RayHit();
            Vector3 ab = b - a;
            Vector3 ac = c - a;
            Vector3x.Cross(ref ab, ref ac, out hit.Normal);
            if (hit.Normal.LengthSquared() < Epsilon)
                return false; //Degenerate triangle!

            float d = -Vector3.Dot(ray.Direction, hit.Normal);
            switch (sidedness)
            {
                case TriangleSidedness.DoubleSided:
                    if (d <= 0) //Pointing the wrong way.  Flip the normal.
                    {
                        hit.Normal = -hit.Normal;
                        d = -d;
                    }
                    break;
                case TriangleSidedness.Clockwise:
                    if (d <= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    break;
                case TriangleSidedness.Counterclockwise:
                    if (d >= 0) //Pointing the wrong way.  Can't hit.
                        return false;

                    hit.Normal = -hit.Normal;
                    d = -d;
                    break;
            }

            Vector3 ap = ray.Position - a;

            hit.T = Vector3.Dot(ap, hit.Normal);
            hit.T /= d;
            if (hit.T < 0 || hit.T > maximumLength)
                return false;//Hit is behind origin, or too far away.

            hit.Location = ray.Position + hit.T * ray.Direction;

            // Compute barycentric coordinates
            ap = hit.Location - a;
            float ABdotAB, ABdotAC, ABdotAP;
            float ACdotAC, ACdotAP;
            ABdotAB = Vector3.Dot(ab, ab);
            ABdotAC = Vector3.Dot(ab, ac);
            ABdotAP = Vector3.Dot(ab, ap);
            ACdotAC = Vector3.Dot(ac, ac);
            ACdotAP = Vector3.Dot(ac, ap);

            float denom = 1 / (ABdotAB * ACdotAC - ABdotAC * ABdotAC);
            float u = (ACdotAC * ABdotAP - ABdotAC * ACdotAP) * denom;
            float v = (ABdotAB * ACdotAP - ABdotAC * ABdotAP) * denom;

            return (u >= -Toolbox.BigEpsilon) && (v >= -Toolbox.BigEpsilon) && (u + v <= 1 + Toolbox.BigEpsilon);
        }