Esempio n. 1
0
        //Transform the convex into the space of something else.
        /// <summary>
        /// Gets the bounding box of the convex shape transformed first into world space, and then into the local space of another affine transform.
        /// </summary>
        /// <param name="shapeTransform">Transform to use to put the shape into world space.</param>
        /// <param name="spaceTransform">Used as the frame of reference to compute the bounding box.
        /// In effect, the shape is transformed by the inverse of the space transform to compute its bounding box in local space.</param>
        /// <param name="boundingBox">Bounding box in the local space.</param>
        public void GetLocalBoundingBox(ref RigidTransform shapeTransform, ref AffineTransform spaceTransform, out BoundingBox boundingBox)
        {
#if !WINDOWS
            boundingBox = new BoundingBox();
#endif
            //TODO: This method peforms quite a few sqrts because the collision margin can get scaled, and so cannot be applied as a final step.
            //There should be a better way to do this.
            //Additionally, this bounding box is not consistent in all cases with the post-add version.  Adding the collision margin at the end can
            //slightly overestimate the size of a margin expanded shape at the corners, which is fine (and actually important for the box-box special case).

            //Move forward into convex's space, backwards into the new space's local space.
            AffineTransform transform;
            AffineTransform.Invert(ref spaceTransform, out transform);
            AffineTransform.Multiply(ref shapeTransform, ref transform, out transform);

            //Sample the local directions from the orientation matrix, implicitly transposed.

            Vector3 right;
            var     direction = new Vector3(transform.LinearTransform.M11, transform.LinearTransform.M21, transform.LinearTransform.M31);
            GetLocalExtremePoint(direction, out right);

            Vector3 left;
            direction = new Vector3(-transform.LinearTransform.M11, -transform.LinearTransform.M21, -transform.LinearTransform.M31);
            GetLocalExtremePoint(direction, out left);

            Vector3 up;
            direction = new Vector3(transform.LinearTransform.M12, transform.LinearTransform.M22, transform.LinearTransform.M32);
            GetLocalExtremePoint(direction, out up);

            Vector3 down;
            direction = new Vector3(-transform.LinearTransform.M12, -transform.LinearTransform.M22, -transform.LinearTransform.M32);
            GetLocalExtremePoint(direction, out down);

            Vector3 backward;
            direction = new Vector3(transform.LinearTransform.M13, transform.LinearTransform.M23, transform.LinearTransform.M33);
            GetLocalExtremePoint(direction, out backward);

            Vector3 forward;
            direction = new Vector3(-transform.LinearTransform.M13, -transform.LinearTransform.M23, -transform.LinearTransform.M33);
            GetLocalExtremePoint(direction, out forward);


            //This could be optimized.  Unnecessary transformation information gets computed.
            Matrix3X3.Transform(ref right, ref transform.LinearTransform, out right);
            Matrix3X3.Transform(ref left, ref transform.LinearTransform, out left);
            Matrix3X3.Transform(ref up, ref transform.LinearTransform, out up);
            Matrix3X3.Transform(ref down, ref transform.LinearTransform, out down);
            Matrix3X3.Transform(ref backward, ref transform.LinearTransform, out backward);
            Matrix3X3.Transform(ref forward, ref transform.LinearTransform, out forward);

            //These right/up/backward represent the extreme points in world space along the world space axes.
            boundingBox.Max.X = transform.Translation.X + right.X;
            boundingBox.Max.Y = transform.Translation.Y + up.Y;
            boundingBox.Max.Z = transform.Translation.Z + backward.Z;

            boundingBox.Min.X = transform.Translation.X + left.X;
            boundingBox.Min.Y = transform.Translation.Y + down.Y;
            boundingBox.Min.Z = transform.Translation.Z + forward.Z;
        }
Esempio n. 2
0
        //Transform the convex into the space of something else.
        /// <summary>
        /// Gets the bounding box of the convex shape transformed first into world space, and then into the local space of another affine transform.
        /// </summary>
        /// <param name="shapeTransform">Transform to use to put the shape into world space.</param>
        /// <param name="spaceTransform">Used as the frame of reference to compute the bounding box.
        /// In effect, the shape is transformed by the inverse of the space transform to compute its bounding box in local space.</param>
        /// <param name="boundingBox">Bounding box in the local space.</param>
        public void GetLocalBoundingBox(ref RigidTransform shapeTransform, ref AffineTransform spaceTransform, out BoundingBox boundingBox)
        {
#if !WINDOWS
            boundingBox = new BoundingBox();
#endif
            //TODO: This method peforms quite a few sqrts because the collision margin can get scaled, and so cannot be applied as a final step.
            //There should be a better way to do this. At the very least, it should be possible to avoid the 6 square roots involved currently.
            //If this shows a a bottleneck, it might be best to virtualize this function and implement a per-shape variant.
            //Also... It might be better just to have the internal function be a GetBoundingBox that takes an AffineTransform, and an outer function
            //does the local space fiddling.

            //Move forward into convex's space, backwards into the new space's local space.
            AffineTransform transform;
            AffineTransform.Invert(ref spaceTransform, out transform);
            AffineTransform.Multiply(ref shapeTransform, ref transform, out transform);

            //Sample the local directions from the orientation matrix, implicitly transposed.

            Vector3 right;
            var     direction = new Vector3(transform.LinearTransform.M11, transform.LinearTransform.M21, transform.LinearTransform.M31);
            GetLocalExtremePoint(direction, out right);

            Vector3 left;
            direction = new Vector3(-transform.LinearTransform.M11, -transform.LinearTransform.M21, -transform.LinearTransform.M31);
            GetLocalExtremePoint(direction, out left);

            Vector3 up;
            direction = new Vector3(transform.LinearTransform.M12, transform.LinearTransform.M22, transform.LinearTransform.M32);
            GetLocalExtremePoint(direction, out up);

            Vector3 down;
            direction = new Vector3(-transform.LinearTransform.M12, -transform.LinearTransform.M22, -transform.LinearTransform.M32);
            GetLocalExtremePoint(direction, out down);

            Vector3 backward;
            direction = new Vector3(transform.LinearTransform.M13, transform.LinearTransform.M23, transform.LinearTransform.M33);
            GetLocalExtremePoint(direction, out backward);

            Vector3 forward;
            direction = new Vector3(-transform.LinearTransform.M13, -transform.LinearTransform.M23, -transform.LinearTransform.M33);
            GetLocalExtremePoint(direction, out forward);

            //Rather than transforming each axis independently (and doing three times as many operations as required), just get the 6 required values directly.
            Vector3 positive, negative;
            TransformLocalExtremePoints(ref right, ref up, ref backward, ref transform.LinearTransform, out positive);
            TransformLocalExtremePoints(ref left, ref down, ref forward, ref transform.LinearTransform, out negative);

            //The positive and negative vectors represent the X, Y and Z coordinates of the extreme points in world space along the world space axes.
            boundingBox.Max.X = transform.Translation.X + positive.X;
            boundingBox.Max.Y = transform.Translation.Y + positive.Y;
            boundingBox.Max.Z = transform.Translation.Z + positive.Z;

            boundingBox.Min.X = transform.Translation.X + negative.X;
            boundingBox.Min.Y = transform.Translation.Y + negative.Y;
            boundingBox.Min.Z = transform.Translation.Z + negative.Z;
        }
Esempio n. 3
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);
        }
Esempio n. 4
0
        //public unsafe static void TestMultiplyCorrectness()
        //{
        //    const int iterationCount = 100000;
        //    Random random = new Random(5);
        //    for (int iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
        //    {
        //        AffineTransform simdA, simdB;
        //        bAffineTransform scalarA, scalarB;
        //        var simdPointerA = (float*)&simdA;
        //        var scalarPointerA = (float*)&scalarA;
        //        var simdPointerB = (float*)&simdB;
        //        var scalarPointerB = (float*)&scalarB;
        //        for (int i = 0; i < 12; ++i)
        //        {
        //            scalarPointerA[i] = simdPointerA[i] = (float)(random.NextDouble() * 4 - 2);
        //            scalarPointerB[i] = simdPointerB[i] = (float)(random.NextDouble() * 4 - 2);
        //        }

        //        AffineTransform simdResult;
        //        AffineTransform.Multiply(ref simdA, ref simdB, out simdResult);
        //        bAffineTransform scalarResult;
        //        bAffineTransform.Multiply(ref scalarA, ref scalarB, out scalarResult);
        //        var simdPointerResult = (float*)&simdResult;
        //        var scalarPointerResult = (float*)&scalarResult;

        //        for (int i = 0; i < 12; ++i)
        //        {
        //            const float threshold = 1e-5f;
        //            var simdScalarError = Math.Abs(simdPointerResult[i] - scalarPointerResult[i]);
        //            if (simdScalarError > threshold)
        //            {
        //                Console.WriteLine($"Excess error for {i}");
        //            }
        //        }
        //    }

        //    for (int iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
        //    {
        //        AffineTransform simdA, simdB;
        //        bAffineTransform scalarA, scalarB;
        //        var simdPointerA = (float*)&simdA;
        //        var scalarPointerA = (float*)&scalarA;
        //        var simdPointerB = (float*)&simdB;
        //        var scalarPointerB = (float*)&scalarB;
        //        for (int i = 0; i < 12; ++i)
        //        {
        //            scalarPointerA[i] = simdPointerA[i] = (float)(random.NextDouble() * 4 - 2);
        //            scalarPointerB[i] = simdPointerB[i] = (float)(random.NextDouble() * 4 - 2);
        //        }

        //        AffineTransform.Multiply(ref simdA, ref simdB, out simdA);
        //        bAffineTransform.Multiply(ref scalarA, ref scalarB, out scalarA);


        //        for (int i = 0; i < 12; ++i)
        //        {
        //            const float threshold = 1e-5f;
        //            var simdScalarError = Math.Abs(simdPointerA[i] - scalarPointerA[i]);
        //            if (simdScalarError > threshold)
        //            {
        //                Console.WriteLine($"Excess error for {i}");
        //            }
        //        }
        //    }

        //    for (int iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
        //    {
        //        AffineTransform simdA, simdB;
        //        bAffineTransform scalarA, scalarB;
        //        var simdPointerA = (float*)&simdA;
        //        var scalarPointerA = (float*)&scalarA;
        //        var simdPointerB = (float*)&simdB;
        //        var scalarPointerB = (float*)&scalarB;
        //        for (int i = 0; i < 12; ++i)
        //        {
        //            scalarPointerA[i] = simdPointerA[i] = (float)(random.NextDouble() * 4 - 2);
        //            scalarPointerB[i] = simdPointerB[i] = (float)(random.NextDouble() * 4 - 2);
        //        }

        //        AffineTransform.Multiply(ref simdA, ref simdB, out simdB);
        //        bAffineTransform.Multiply(ref scalarA, ref scalarB, out scalarB);


        //        for (int i = 0; i < 12; ++i)
        //        {
        //            const float threshold = 1e-5f;
        //            var simdScalarError = Math.Abs(simdPointerB[i] - scalarPointerB[i]);
        //            if (simdScalarError > threshold)
        //            {
        //                Console.WriteLine($"Excess error for {i}");
        //            }
        //        }
        //    }


        //    for (int iterationIndex = 0; iterationIndex < iterationCount; ++iterationIndex)
        //    {
        //        AffineTransform simd;
        //        bAffineTransform scalar;
        //        var simdPointer = (float*)&simd;
        //        var scalarPointer = (float*)&scalar;
        //        for (int i = 0; i < 12; ++i)
        //        {
        //            scalarPointer[i] = simdPointer[i] = (float)(random.NextDouble() * 4 - 2);
        //        }

        //        AffineTransform.Multiply(ref simd, ref simd, out simd);
        //        bAffineTransform.Multiply(ref scalar, ref scalar, out scalar);

        //        for (int i = 0; i < 12; ++i)
        //        {
        //            const float threshold = 1e-5f;
        //            var simdScalarError = Math.Abs(simdPointer[i] - scalarPointer[i]);
        //            if (simdScalarError > threshold)
        //            {
        //                Console.WriteLine($"Excess error for {i}");
        //            }
        //        }
        //    }
        //}



        //public static float TestScalarInvert(int iterationCount)
        //{
        //    bAffineTransform m = bAffineTransform.Identity;
        //    float accumulator = 0;
        //    for (int i = 0; i < iterationCount; ++i)
        //    {
        //        bAffineTransform r0, r1;
        //        bAffineTransform.Invert(ref m, out r0);
        //        bAffineTransform.Invert(ref r0, out r1);
        //        bAffineTransform.Invert(ref r1, out r0);
        //        bAffineTransform.Invert(ref r0, out r1);
        //        bAffineTransform.Invert(ref r1, out r0);
        //        bAffineTransform.Invert(ref r0, out r1);
        //        bAffineTransform.Invert(ref r1, out r0);
        //        bAffineTransform.Invert(ref r0, out r1);
        //        bAffineTransform.Invert(ref r1, out r0);
        //        bAffineTransform.Invert(ref r0, out r1);
        //        accumulator += r1.Translation.X;

        //    }
        //    return accumulator;
        //}
        public static float TestSIMDInvert(int iterationCount)
        {
            AffineTransform m           = AffineTransform.Identity;
            float           accumulator = 0;

            for (int i = 0; i < iterationCount; ++i)
            {
                AffineTransform r0, r1;
                AffineTransform.Invert(m, out r0);
                AffineTransform.Invert(r0, out r1);
                AffineTransform.Invert(r1, out r0);
                AffineTransform.Invert(r0, out r1);
                AffineTransform.Invert(r1, out r0);
                AffineTransform.Invert(r0, out r1);
                AffineTransform.Invert(r1, out r0);
                AffineTransform.Invert(r0, out r1);
                AffineTransform.Invert(r1, out r0);
                AffineTransform.Invert(r0, out r1);
                accumulator += r1.Translation.X;
            }
            return(accumulator);
        }
Esempio n. 5
0
        /// <summary>
        /// Gets the bounding box of the mesh transformed first into world space, and then into the local space of another affine transform.
        /// </summary>
        /// <param name="shapeTransform">Transform to use to put the shape into world space.</param>
        /// <param name="spaceTransform">Used as the frame of reference to compute the bounding box.
        /// In effect, the shape is transformed by the inverse of the space transform to compute its bounding box in local space.</param>
        /// <param name="boundingBox">Bounding box in the local space.</param>
        public void GetLocalBoundingBox(ref RigidTransform shapeTransform, ref AffineTransform spaceTransform, out BoundingBox boundingBox)
        {
#if !WINDOWS
            boundingBox = new BoundingBox();
#endif
            //TODO: This method peforms quite a few sqrts because the collision margin can get scaled, and so cannot be applied as a final step.
            //There should be a better way to do this.
            //Additionally, this bounding box is not consistent in all cases with the post-add version.  Adding the collision margin at the end can
            //slightly overestimate the size of a margin expanded shape at the corners, which is fine (and actually important for the box-box special case).

            //Move forward into convex's space, backwards into the new space's local space.
            AffineTransform transform;
            AffineTransform.Invert(ref spaceTransform, out transform);
            AffineTransform.Multiply(ref shapeTransform, ref transform, out transform);

            GetBoundingBox(ref transform.LinearTransform, out boundingBox);
            boundingBox.Max.X += transform.Translation.X;
            boundingBox.Max.Y += transform.Translation.Y;
            boundingBox.Max.Z += transform.Translation.Z;

            boundingBox.Min.X += transform.Translation.X;
            boundingBox.Min.Y += transform.Translation.Y;
            boundingBox.Min.Z += transform.Translation.Z;
        }
Esempio n. 6
0
        public unsafe static void TestInversionCorrectness()
        {
            Random random = new Random(5);

            for (int iterationIndex = 0; iterationIndex < 1000; ++iterationIndex)
            {
                bAffineTransform scalar;
                AffineTransform  simd;
                var scalarPointer = (float *)&scalar;
                var simdPointer   = (float *)&simd;

                //Create a guaranteed invertible transform.
                scalar.LinearTransform = bMatrix3x3.CreateFromAxisAngle(
                    bVector3.Normalize(new bVector3(
                                           0.1f + (float)random.NextDouble(),
                                           0.1f + (float)random.NextDouble(),
                                           0.1f + (float)random.NextDouble())),
                    (float)random.NextDouble());
                scalar.Translation = new bVector3((float)random.NextDouble() * 10, (float)random.NextDouble() * 10, (float)random.NextDouble() * 10);

                for (int i = 0; i < 12; ++i)
                {
                    simdPointer[i] = scalarPointer[i];
                }


                bAffineTransform.Invert(ref scalar, out scalar);
                AffineTransform.Invert(ref simd, out simd);

                for (int i = 0; i < 12; ++i)
                {
                    var errorSimd = Math.Abs(simdPointer[i] - scalarPointer[i]);
                    Assert.IsTrue(errorSimd < 1e-5f);
                }
            }
        }
Esempio n. 7
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);
                }
            }
        }
        /// <summary>
        /// Constructor for MultipleGradientPaintContext superclass.
        /// </summary>
        protected internal MultipleGradientPaintContext(MultipleGradientPaint mgp, ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform t, RenderingHints hints, float[] fractions, Color[] colors, CycleMethod cycleMethod, ColorSpaceType colorSpace)
        {
            if (deviceBounds == null)
            {
                throw new NullPointerException("Device bounds cannot be null");
            }

            if (userBounds == null)
            {
                throw new NullPointerException("User bounds cannot be null");
            }

            if (t == null)
            {
                throw new NullPointerException("Transform cannot be null");
            }

            if (hints == null)
            {
                throw new NullPointerException("RenderingHints cannot be null");
            }

            // The inverse transform is needed to go from device to user space.
            // Get all the components of the inverse transform matrix.
            AffineTransform tInv;

            try
            {
                // the following assumes that the caller has copied the incoming
                // transform and is not concerned about it being modified
                t.Invert();
                tInv = t;
            }
            catch (NoninvertibleTransformException)
            {
                // just use identity transform in this case; better to show
                // (incorrect) results than to throw an exception and/or no-op
                tInv = new AffineTransform();
            }
            double[] m = new double[6];
            tInv.GetMatrix(m);
            A00 = (float)m[0];
            A10 = (float)m[1];
            A01 = (float)m[2];
            A11 = (float)m[3];
            A02 = (float)m[4];
            A12 = (float)m[5];

            // copy some flags
            this.CycleMethod = cycleMethod;
            this.ColorSpace  = colorSpace;

            // we can avoid copying this array since we do not modify its values
            this.Fractions = fractions;

            // note that only one of these values can ever be non-null (we either
            // store the fast gradient array or the slow one, but never both
            // at the same time)
            int[]   gradient  = (mgp.Gradient != null) ? mgp.Gradient.get() : null;
            int[][] gradients = (mgp.Gradients != null) ? mgp.Gradients.get() : null;

            if (gradient == null && gradients == null)
            {
                // we need to (re)create the appropriate values
                CalculateLookupData(colors);

                // now cache the calculated values in the
                // MultipleGradientPaint instance for future use
                mgp.Model = this.Model;
                mgp.NormalizedIntervals = this.NormalizedIntervals;
                mgp.IsSimpleLookup      = this.IsSimpleLookup;
                if (IsSimpleLookup)
                {
                    // only cache the fast array
                    mgp.FastGradientArraySize = this.FastGradientArraySize;
                    mgp.Gradient = new SoftReference <int[]>(this.Gradient);
                }
                else
                {
                    // only cache the slow array
                    mgp.Gradients = new SoftReference <int[][]>(this.Gradients);
                }
            }
            else
            {
                // use the values cached in the MultipleGradientPaint instance
                this.Model = mgp.Model;
                this.NormalizedIntervals   = mgp.NormalizedIntervals;
                this.IsSimpleLookup        = mgp.IsSimpleLookup;
                this.Gradient              = gradient;
                this.FastGradientArraySize = mgp.FastGradientArraySize;
                this.Gradients             = gradients;
            }
        }