//
        // Processes a ray-line intersection to see if it's a valid hit.
        // 
        // Shares some code with ValidateRayHit
        //
        private void ValidateLineHit(
            RayHitTestParameters rayParams, 
            FaceType facesToHit,
            int i0,
            int i1,
            int i2,
            ref Point3D v0,
            ref Point3D v1,
            ref Point3D v2,
            ref Point barycentric
            )
        {
            Matrix3D worldTransformMatrix = rayParams.HasWorldTransformMatrix ? rayParams.WorldTransformMatrix : Matrix3D.Identity;
                    
            // OK, we have an intersection with the LINE but that could be wrong on three
            // accounts:
            //   1. We could have hit the line on the wrong side of the ray's origin.
            //   2. We may need to cull the intersection if it's beyond the far clipping
            //      plane (only if the hit test originated from a Viewport3DVisual.)
            //   3. We could have hit a back-facing triangle
            // We will transform the hit point back into world space to check these
            // things & compute the correct distance from the origin to the hit point.
            
            // Hit point in model space
            Point3D pointHit = M3DUtil.Interpolate(ref v0, ref v1, ref v2, ref barycentric);
            
            Point3D worldPointHit = pointHit;
            worldTransformMatrix.MultiplyPoint(ref worldPointHit);
            
            // Vector from origin to hit point
            Vector3D hitVector = worldPointHit - rayParams.Origin;
            Vector3D originalDirection = rayParams.Direction;
            double rayDistanceUnnormalized = Vector3D.DotProduct(originalDirection, hitVector);

            if (rayDistanceUnnormalized > 0)
            {
                // If we have a HitTestProjectionMatrix than this hit test originated
                // at a Viewport3DVisual.
                if (rayParams.HasHitTestProjectionMatrix)
                {
                    // To test if we are in front of the far clipping plane what we
                    // do conceptually is project our hit point in world space into
                    // homogenous space and verify that it is on the correct side of
                    // the Z=1 plane.
                    //
                    // To save some cycles we only bother computing Z and W of the
                    // projected point and use a simple Z/W > 1 test to see if we
                    // are past the far plane.
                    //
                    // NOTE: HitTestProjectionMatrix is not just the camera matrices.
                    //       It has an additional translation to move the ray to the
                    //       origin.  This extra translation does not effect this test.
                    
                    Matrix3D m = rayParams.HitTestProjectionMatrix;

                    // We directly substitute 1 for p.W below:
                    double pz = worldPointHit.X * m.M13 + worldPointHit.Y * m.M23 + worldPointHit.Z * m.M33 + m.OffsetZ;
                    double pw = worldPointHit.X * m.M14 + worldPointHit.Y * m.M24 + worldPointHit.Z * m.M34 + m.M44;

                    // Early exit if pz/pw > 1.  The negated logic is to reject NaNs.
                    if (!(pz / pw <= 1))
                    {
                        return;
                    }

                    Debug.Assert(!double.IsInfinity(pz / pw) && !double.IsNaN(pz / pw),
                        "Expected near/far tests to cull -Inf/+Inf and NaN.");
                }

                Point3D a = v0, b = v1, c = v2;

                worldTransformMatrix.MultiplyPoint(ref a);
                worldTransformMatrix.MultiplyPoint(ref b);
                worldTransformMatrix.MultiplyPoint(ref c);

                Vector3D normal = Vector3D.CrossProduct(b - a, c - a);

                double cullSign = -Vector3D.DotProduct(normal, hitVector);
                double det = worldTransformMatrix.Determinant;
                bool frontFace = (cullSign > 0) == (det >= 0);
            
                if (((facesToHit & FaceType.Front) == FaceType.Front && frontFace) || ((facesToHit & FaceType.Back) == FaceType.Back && !frontFace))
                {
                    double dist = hitVector.Length;
                    if (rayParams.HasModelTransformMatrix)
                    {
                        rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit);
                    }
                    
                    rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric);
                }
            }          
        }
예제 #2
0
        //
        // Processes a ray-line intersection to see if it's a valid hit.
        //
        // Shares some code with ValidateRayHit
        //
        private void ValidateLineHit(
            RayHitTestParameters rayParams,
            FaceType facesToHit,
            int i0,
            int i1,
            int i2,
            ref Point3D v0,
            ref Point3D v1,
            ref Point3D v2,
            ref Point barycentric
            )
        {
            Matrix3D worldTransformMatrix = rayParams.HasWorldTransformMatrix ? rayParams.WorldTransformMatrix : Matrix3D.Identity;

            // OK, we have an intersection with the LINE but that could be wrong on three
            // accounts:
            //   1. We could have hit the line on the wrong side of the ray's origin.
            //   2. We may need to cull the intersection if it's beyond the far clipping
            //      plane (only if the hit test originated from a Viewport3DVisual.)
            //   3. We could have hit a back-facing triangle
            // We will transform the hit point back into world space to check these
            // things & compute the correct distance from the origin to the hit point.

            // Hit point in model space
            Point3D pointHit = M3DUtil.Interpolate(ref v0, ref v1, ref v2, ref barycentric);

            Point3D worldPointHit = pointHit;

            worldTransformMatrix.MultiplyPoint(ref worldPointHit);

            // Vector from origin to hit point
            Vector3D hitVector               = worldPointHit - rayParams.Origin;
            Vector3D originalDirection       = rayParams.Direction;
            double   rayDistanceUnnormalized = Vector3D.DotProduct(originalDirection, hitVector);

            if (rayDistanceUnnormalized > 0)
            {
                // If we have a HitTestProjectionMatrix than this hit test originated
                // at a Viewport3DVisual.
                if (rayParams.HasHitTestProjectionMatrix)
                {
                    // To test if we are in front of the far clipping plane what we
                    // do conceptually is project our hit point in world space into
                    // homogenous space and verify that it is on the correct side of
                    // the Z=1 plane.
                    //
                    // To save some cycles we only bother computing Z and W of the
                    // projected point and use a simple Z/W > 1 test to see if we
                    // are past the far plane.
                    //
                    // NOTE: HitTestProjectionMatrix is not just the camera matrices.
                    //       It has an additional translation to move the ray to the
                    //       origin.  This extra translation does not effect this test.

                    Matrix3D m = rayParams.HitTestProjectionMatrix;

                    // We directly substitute 1 for p.W below:
                    double pz = worldPointHit.X * m.M13 + worldPointHit.Y * m.M23 + worldPointHit.Z * m.M33 + m.OffsetZ;
                    double pw = worldPointHit.X * m.M14 + worldPointHit.Y * m.M24 + worldPointHit.Z * m.M34 + m.M44;

                    // Early exit if pz/pw > 1.  The negated logic is to reject NaNs.
                    if (!(pz / pw <= 1))
                    {
                        return;
                    }

                    Debug.Assert(!double.IsInfinity(pz / pw) && !double.IsNaN(pz / pw),
                                 "Expected near/far tests to cull -Inf/+Inf and NaN.");
                }

                Point3D a = v0, b = v1, c = v2;

                worldTransformMatrix.MultiplyPoint(ref a);
                worldTransformMatrix.MultiplyPoint(ref b);
                worldTransformMatrix.MultiplyPoint(ref c);

                Vector3D normal = Vector3D.CrossProduct(b - a, c - a);

                double cullSign  = -Vector3D.DotProduct(normal, hitVector);
                double det       = worldTransformMatrix.Determinant;
                bool   frontFace = (cullSign > 0) == (det >= 0);

                if (((facesToHit & FaceType.Front) == FaceType.Front && frontFace) || ((facesToHit & FaceType.Back) == FaceType.Back && !frontFace))
                {
                    double dist = hitVector.Length;
                    if (rayParams.HasModelTransformMatrix)
                    {
                        rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit);
                    }

                    rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric);
                }
            }
        }
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        
        #region Private Methods

        //
        // Processes a ray-triangle intersection to see if it's a valid hit. Unnecessary faces
        // have already been culled by the ray-triange intersection routines.
        // 
        // Shares some code with ValidateLineHit
        //
        private void ValidateRayHit(
            RayHitTestParameters rayParams, 
            ref Point3D origin, 
            ref Vector3D direction, 
            double hitTime,
            int i0,
            int i1,
            int i2,
            ref Point barycentric
            )
        {
            if (hitTime > 0)
            {
                Matrix3D worldTransformMatrix = rayParams.HasWorldTransformMatrix ? rayParams.WorldTransformMatrix : Matrix3D.Identity;

                Point3D pointHit = origin + hitTime * direction;
                
                Point3D worldPointHit = pointHit;
                worldTransformMatrix.MultiplyPoint(ref worldPointHit);
                
                // If we have a HitTestProjectionMatrix than this hit test originated
                // at a Viewport3DVisual.
                if (rayParams.HasHitTestProjectionMatrix)
                {
                    // To test if we are in front of the far clipping plane what we
                    // do conceptually is project our hit point in world space into
                    // homogenous space and verify that it is on the correct side of
                    // the Z=1 plane.
                    //
                    // To save some cycles we only bother computing Z and W of the
                    // projected point and use a simple Z/W > 1 test to see if we
                    // are past the far plane.
                    //
                    // NOTE: HitTestProjectionMatrix is not just the camera matrices.
                    //       It has an additional translation to move the ray to the
                    //       origin.  This extra translation does not effect this test.
                    
                    Matrix3D m = rayParams.HitTestProjectionMatrix;

                    // We directly substitute 1 for p.W below:
                    double pz = worldPointHit.X * m.M13 + worldPointHit.Y * m.M23 + worldPointHit.Z * m.M33 + m.OffsetZ;
                    double pw = worldPointHit.X * m.M14 + worldPointHit.Y * m.M24 + worldPointHit.Z * m.M34 + m.M44;

                    // Early exit if pz/pw > 1.  The negated logic is to reject NaNs.
                    if (!(pz / pw <= 1))
                    {
                        return;
                    }

                    Debug.Assert(!double.IsInfinity(pz / pw) && !double.IsNaN(pz / pw),
                        "Expected near/far tests to cull -Inf/+Inf and NaN.");
                }

                double dist = (worldPointHit - rayParams.Origin).Length;
                Debug.Assert(dist > 0, "Distance is negative: " + dist);

                if (rayParams.HasModelTransformMatrix)
                {
                    rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit);
                }
                
                rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric);
            }
        }
예제 #4
0
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------

        #region Private Methods

        //
        // Processes a ray-triangle intersection to see if it's a valid hit. Unnecessary faces
        // have already been culled by the ray-triange intersection routines.
        //
        // Shares some code with ValidateLineHit
        //
        private void ValidateRayHit(
            RayHitTestParameters rayParams,
            ref Point3D origin,
            ref Vector3D direction,
            double hitTime,
            int i0,
            int i1,
            int i2,
            ref Point barycentric
            )
        {
            if (hitTime > 0)
            {
                Matrix3D worldTransformMatrix = rayParams.HasWorldTransformMatrix ? rayParams.WorldTransformMatrix : Matrix3D.Identity;

                Point3D pointHit = origin + hitTime * direction;

                Point3D worldPointHit = pointHit;
                worldTransformMatrix.MultiplyPoint(ref worldPointHit);

                // If we have a HitTestProjectionMatrix than this hit test originated
                // at a Viewport3DVisual.
                if (rayParams.HasHitTestProjectionMatrix)
                {
                    // To test if we are in front of the far clipping plane what we
                    // do conceptually is project our hit point in world space into
                    // homogenous space and verify that it is on the correct side of
                    // the Z=1 plane.
                    //
                    // To save some cycles we only bother computing Z and W of the
                    // projected point and use a simple Z/W > 1 test to see if we
                    // are past the far plane.
                    //
                    // NOTE: HitTestProjectionMatrix is not just the camera matrices.
                    //       It has an additional translation to move the ray to the
                    //       origin.  This extra translation does not effect this test.

                    Matrix3D m = rayParams.HitTestProjectionMatrix;

                    // We directly substitute 1 for p.W below:
                    double pz = worldPointHit.X * m.M13 + worldPointHit.Y * m.M23 + worldPointHit.Z * m.M33 + m.OffsetZ;
                    double pw = worldPointHit.X * m.M14 + worldPointHit.Y * m.M24 + worldPointHit.Z * m.M34 + m.M44;

                    // Early exit if pz/pw > 1.  The negated logic is to reject NaNs.
                    if (!(pz / pw <= 1))
                    {
                        return;
                    }

                    Debug.Assert(!double.IsInfinity(pz / pw) && !double.IsNaN(pz / pw),
                                 "Expected near/far tests to cull -Inf/+Inf and NaN.");
                }

                double dist = (worldPointHit - rayParams.Origin).Length;
                Debug.Assert(dist > 0, "Distance is negative: " + dist);

                if (rayParams.HasModelTransformMatrix)
                {
                    rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit);
                }

                rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric);
            }
        }