Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Core / CSharp / System / Windows / Media3D / MeshGeometry3D.cs / 1 / MeshGeometry3D.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: 3D mesh implementation. // // See spec at http://avalon/medialayer/Specifications/Avalon3D%20API%20Spec.mht // // History: // 06/10/2004 : [....] - Created from Mesh3D.cs (deprecated) // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.Media3D; using MS.Internal.PresentationCore; using MS.Utility; using System; using System.Diagnostics; using System.Windows.Markup; using System.Windows.Media.Composition; namespace System.Windows.Media.Media3D { ////// MeshGeometry3D a straightforward triangle primitive. /// public sealed partial class MeshGeometry3D : Geometry3D { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Default Constructor. /// public MeshGeometry3D() {} #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods ////// Get bounds for this MeshGeometry3D. /// public override Rect3D Bounds { get { ReadPreamble(); if (_cachedBounds.IsEmpty) { UpdateCachedBounds(); } Debug_VerifyCachedBounds(); return _cachedBounds; } } #endregion Public Methods //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ #region Protected Methods ////// Overriden to clear our bounds cache. /// protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { if (e.IsAValueChange || e.IsASubPropertyChange) { DependencyProperty dp = e.Property; // We invalidate the cache here rather than in the InvalidateResourcePositions method // because the later is not invoked in the event that the Point3DCollection is swapped // out from underneath us. (In that case, the resource invalidation takes a different // code path.) if (dp == MeshGeometry3D.PositionsProperty) { SetCachedBoundsDirty(); } } base.OnPropertyChanged(e); } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal Rect GetTextureCoordinateBounds() { PointCollection tx = TextureCoordinates; int count = (tx == null) ? 0 : tx.Count; if (count > 0) { Point ptMin = tx[0]; Point ptMax = tx[0]; for (int i = 1; i < count; i++) { Point txPt = tx.Internal_GetItem(i); double txx = txPt.X; if (ptMin.X > txx) { ptMin.X = txx; } else if (ptMax.X < txx) { ptMax.X = txx; } double txy = txPt.Y; if (ptMin.Y > txy) { ptMin.Y = txy; } else if (ptMax.Y < txy) { ptMax.Y = txy; } } return new Rect(ptMin, ptMax); } else { return Rect.Empty; } } // // Hits the ray against the mesh // internal override void RayHitTestCore( RayHitTestParameters rayParams, FaceType hitTestableFaces) { Debug.Assert(hitTestableFaces != FaceType.None, "Caller should make sure we're trying to hit something"); Point3DCollection positions = Positions; if (positions == null) { return; } Point3D origin; Vector3D direction; rayParams.GetLocalLine(out origin, out direction); Int32Collection indices = TriangleIndices; // In the line case, we want to hit test all faces because we don't // have a direction. This may differ from what faces we want to // accept. FaceType facesToHit; if (rayParams.IsRay) { facesToHit = hitTestableFaces; } else { facesToHit = FaceType.Front | FaceType.Back; } // // This code duplication is unfortunate but necessary. Breaking it down into methods // further significantly impacts performance. About 5% improvement could be made // by unrolling this code below even more. // // If futher perf investigation is done with this code, be sure to test NGEN assemblies only // as JIT produces different, faster code than NGEN. // if (indices == null || indices.Count == 0) { FrugalStructListps = positions._collection; int count = ps.Count - (ps.Count % 3); for (int i = count - 1; i >= 2; i -= 3) { int i0 = i - 2; int i1 = i - 1; int i2 = i; Point3D v0 = ps[i0]; Point3D v1 = ps[i1]; Point3D v2 = ps[i2]; double hitTime; Point barycentric; // The line hit test is equivalent to a double sided // triangle hit because it doesn't cull triangles based // on winding if (LineUtil.ComputeLineTriangleIntersection( facesToHit, ref origin, ref direction, ref v0, ref v1, ref v2, out barycentric, out hitTime ) ) { if (rayParams.IsRay) { ValidateRayHit( rayParams, ref origin, ref direction, hitTime, i0, i1, i2, ref barycentric ); } else { ValidateLineHit( rayParams, hitTestableFaces, i0, i1, i2, ref v0, ref v1, ref v2, ref barycentric ); } } } } else // indexed mesh { FrugalStructList ps = positions._collection; FrugalStructList idcs = indices._collection; int count = idcs.Count; int limit = ps.Count; for (int i = 2; i < count; i += 3) { int i0 = idcs[i - 2]; int i1 = idcs[i - 1]; int i2 = idcs[i]; // Quit if we encounter an index out of range. // This is okay because the triangles we ignore are not rendered. // (see: CMilMeshGeometry3DDuce::Realize) if ((0 > i0 || i0 >= limit) || (0 > i1 || i1 >= limit) || (0 > i2 || i2 >= limit)) { break; } Point3D v0 = ps[i0]; Point3D v1 = ps[i1]; Point3D v2 = ps[i2]; double hitTime; Point barycentric; if (LineUtil.ComputeLineTriangleIntersection( facesToHit, ref origin, ref direction, ref v0, ref v1, ref v2, out barycentric, out hitTime ) ) { if (rayParams.IsRay) { ValidateRayHit( rayParams, ref origin, ref direction, hitTime, i0, i1, i2, ref barycentric ); } else { ValidateLineHit( rayParams, hitTestableFaces, i0, i1, i2, ref v0, ref v1, ref v2, ref barycentric ); } } } } } #endregion Internal Methods //----------------------------------------------------- // // 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, String.Format("Distance is negative: {0}", dist)); if (rayParams.HasModelTransformMatrix) { rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit); } rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric); } } // // 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); } } } // Updates the _cachedBounds member to the current bounds of the mesh. // This method must be called before accessing _cachedBounds if // _cachedBounds.IsEmpty is true. Otherwise the _cachedBounds are // current and do not need to be recomputed. See also Debug_VerifyCachedBounds. private void UpdateCachedBounds() { Debug.Assert(_cachedBounds.IsEmpty, "PERF: Caller should verify that bounds are dirty before recomputing."); _cachedBounds = M3DUtil.ComputeAxisAlignedBoundingBox(Positions); } // Sets _cachedBounds to Rect3D.Empty (indicating that the bounds are no // longer valid.) private void SetCachedBoundsDirty() { _cachedBounds = Rect3D.Empty; } #endregion Private Methods //----------------------------------------------------- // // DEBUG // //------------------------------------------------------ #region DEBUG // Always call this method before accessing _cachedBounds. On [Conditional("DEBUG")] private void Debug_VerifyCachedBounds() { Rect3D actualBounds = M3DUtil.ComputeAxisAlignedBoundingBox(Positions); // The funny boolean logic below avoids asserts when the cached // bounds contain NaNs. (NaN != NaN) bool areEqual = !(_cachedBounds.X < actualBounds.X || _cachedBounds.X > actualBounds.X) && !(_cachedBounds.Y < actualBounds.Y || _cachedBounds.Y > actualBounds.Y) && !(_cachedBounds.Z < actualBounds.Z || _cachedBounds.Z > actualBounds.Z) && !(_cachedBounds.SizeX < actualBounds.SizeX || _cachedBounds.SizeX > actualBounds.SizeX) && !(_cachedBounds.SizeY < actualBounds.SizeY || _cachedBounds.SizeY > actualBounds.SizeY) && !(_cachedBounds.SizeZ < actualBounds.SizeZ || _cachedBounds.SizeZ > actualBounds.SizeZ); if (!areEqual) { if (_cachedBounds == Rect3D.Empty) { Debug.Fail("Cached bounds are invalid. Caller needs to check for IsEmpty and call UpdateCachedBounds."); } else { Debug.Fail("Cached bounds are invalid. We missed a call to SetCachedBoundsDirty."); } } } #endregion DEBUG //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // If the _cachedBounds are empty it means that the cache is invalid. The user must // check for this case and call UpdateCachedBounds if the cache is invalid. (There // is no way to distinguish between actually caching "Empty" when there are no // positions and the cache being invalid - but computing bounds in this case is // very fast.) private Rect3D _cachedBounds = Rect3D.Empty; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // // // Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: 3D mesh implementation. // // See spec at http://avalon/medialayer/Specifications/Avalon3D%20API%20Spec.mht // // History: // 06/10/2004 : [....] - Created from Mesh3D.cs (deprecated) // //--------------------------------------------------------------------------- using MS.Internal; using MS.Internal.Media3D; using MS.Internal.PresentationCore; using MS.Utility; using System; using System.Diagnostics; using System.Windows.Markup; using System.Windows.Media.Composition; namespace System.Windows.Media.Media3D { ////// MeshGeometry3D a straightforward triangle primitive. /// public sealed partial class MeshGeometry3D : Geometry3D { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Default Constructor. /// public MeshGeometry3D() {} #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods ////// Get bounds for this MeshGeometry3D. /// public override Rect3D Bounds { get { ReadPreamble(); if (_cachedBounds.IsEmpty) { UpdateCachedBounds(); } Debug_VerifyCachedBounds(); return _cachedBounds; } } #endregion Public Methods //------------------------------------------------------ // // Public Properties // //------------------------------------------------------ //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ #region Protected Methods ////// Overriden to clear our bounds cache. /// protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { if (e.IsAValueChange || e.IsASubPropertyChange) { DependencyProperty dp = e.Property; // We invalidate the cache here rather than in the InvalidateResourcePositions method // because the later is not invoked in the event that the Point3DCollection is swapped // out from underneath us. (In that case, the resource invalidation takes a different // code path.) if (dp == MeshGeometry3D.PositionsProperty) { SetCachedBoundsDirty(); } } base.OnPropertyChanged(e); } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods internal Rect GetTextureCoordinateBounds() { PointCollection tx = TextureCoordinates; int count = (tx == null) ? 0 : tx.Count; if (count > 0) { Point ptMin = tx[0]; Point ptMax = tx[0]; for (int i = 1; i < count; i++) { Point txPt = tx.Internal_GetItem(i); double txx = txPt.X; if (ptMin.X > txx) { ptMin.X = txx; } else if (ptMax.X < txx) { ptMax.X = txx; } double txy = txPt.Y; if (ptMin.Y > txy) { ptMin.Y = txy; } else if (ptMax.Y < txy) { ptMax.Y = txy; } } return new Rect(ptMin, ptMax); } else { return Rect.Empty; } } // // Hits the ray against the mesh // internal override void RayHitTestCore( RayHitTestParameters rayParams, FaceType hitTestableFaces) { Debug.Assert(hitTestableFaces != FaceType.None, "Caller should make sure we're trying to hit something"); Point3DCollection positions = Positions; if (positions == null) { return; } Point3D origin; Vector3D direction; rayParams.GetLocalLine(out origin, out direction); Int32Collection indices = TriangleIndices; // In the line case, we want to hit test all faces because we don't // have a direction. This may differ from what faces we want to // accept. FaceType facesToHit; if (rayParams.IsRay) { facesToHit = hitTestableFaces; } else { facesToHit = FaceType.Front | FaceType.Back; } // // This code duplication is unfortunate but necessary. Breaking it down into methods // further significantly impacts performance. About 5% improvement could be made // by unrolling this code below even more. // // If futher perf investigation is done with this code, be sure to test NGEN assemblies only // as JIT produces different, faster code than NGEN. // if (indices == null || indices.Count == 0) { FrugalStructListps = positions._collection; int count = ps.Count - (ps.Count % 3); for (int i = count - 1; i >= 2; i -= 3) { int i0 = i - 2; int i1 = i - 1; int i2 = i; Point3D v0 = ps[i0]; Point3D v1 = ps[i1]; Point3D v2 = ps[i2]; double hitTime; Point barycentric; // The line hit test is equivalent to a double sided // triangle hit because it doesn't cull triangles based // on winding if (LineUtil.ComputeLineTriangleIntersection( facesToHit, ref origin, ref direction, ref v0, ref v1, ref v2, out barycentric, out hitTime ) ) { if (rayParams.IsRay) { ValidateRayHit( rayParams, ref origin, ref direction, hitTime, i0, i1, i2, ref barycentric ); } else { ValidateLineHit( rayParams, hitTestableFaces, i0, i1, i2, ref v0, ref v1, ref v2, ref barycentric ); } } } } else // indexed mesh { FrugalStructList ps = positions._collection; FrugalStructList idcs = indices._collection; int count = idcs.Count; int limit = ps.Count; for (int i = 2; i < count; i += 3) { int i0 = idcs[i - 2]; int i1 = idcs[i - 1]; int i2 = idcs[i]; // Quit if we encounter an index out of range. // This is okay because the triangles we ignore are not rendered. // (see: CMilMeshGeometry3DDuce::Realize) if ((0 > i0 || i0 >= limit) || (0 > i1 || i1 >= limit) || (0 > i2 || i2 >= limit)) { break; } Point3D v0 = ps[i0]; Point3D v1 = ps[i1]; Point3D v2 = ps[i2]; double hitTime; Point barycentric; if (LineUtil.ComputeLineTriangleIntersection( facesToHit, ref origin, ref direction, ref v0, ref v1, ref v2, out barycentric, out hitTime ) ) { if (rayParams.IsRay) { ValidateRayHit( rayParams, ref origin, ref direction, hitTime, i0, i1, i2, ref barycentric ); } else { ValidateLineHit( rayParams, hitTestableFaces, i0, i1, i2, ref v0, ref v1, ref v2, ref barycentric ); } } } } } #endregion Internal Methods //----------------------------------------------------- // // 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, String.Format("Distance is negative: {0}", dist)); if (rayParams.HasModelTransformMatrix) { rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit); } rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric); } } // // 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); } } } // Updates the _cachedBounds member to the current bounds of the mesh. // This method must be called before accessing _cachedBounds if // _cachedBounds.IsEmpty is true. Otherwise the _cachedBounds are // current and do not need to be recomputed. See also Debug_VerifyCachedBounds. private void UpdateCachedBounds() { Debug.Assert(_cachedBounds.IsEmpty, "PERF: Caller should verify that bounds are dirty before recomputing."); _cachedBounds = M3DUtil.ComputeAxisAlignedBoundingBox(Positions); } // Sets _cachedBounds to Rect3D.Empty (indicating that the bounds are no // longer valid.) private void SetCachedBoundsDirty() { _cachedBounds = Rect3D.Empty; } #endregion Private Methods //----------------------------------------------------- // // DEBUG // //------------------------------------------------------ #region DEBUG // Always call this method before accessing _cachedBounds. On [Conditional("DEBUG")] private void Debug_VerifyCachedBounds() { Rect3D actualBounds = M3DUtil.ComputeAxisAlignedBoundingBox(Positions); // The funny boolean logic below avoids asserts when the cached // bounds contain NaNs. (NaN != NaN) bool areEqual = !(_cachedBounds.X < actualBounds.X || _cachedBounds.X > actualBounds.X) && !(_cachedBounds.Y < actualBounds.Y || _cachedBounds.Y > actualBounds.Y) && !(_cachedBounds.Z < actualBounds.Z || _cachedBounds.Z > actualBounds.Z) && !(_cachedBounds.SizeX < actualBounds.SizeX || _cachedBounds.SizeX > actualBounds.SizeX) && !(_cachedBounds.SizeY < actualBounds.SizeY || _cachedBounds.SizeY > actualBounds.SizeY) && !(_cachedBounds.SizeZ < actualBounds.SizeZ || _cachedBounds.SizeZ > actualBounds.SizeZ); if (!areEqual) { if (_cachedBounds == Rect3D.Empty) { Debug.Fail("Cached bounds are invalid. Caller needs to check for IsEmpty and call UpdateCachedBounds."); } else { Debug.Fail("Cached bounds are invalid. We missed a call to SetCachedBoundsDirty."); } } } #endregion DEBUG //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // If the _cachedBounds are empty it means that the cache is invalid. The user must // check for this case and call UpdateCachedBounds if the cache is invalid. (There // is no way to distinguish between actually caching "Empty" when there are no // positions and the cache being invalid - but computing bounds in this case is // very fast.) private Rect3D _cachedBounds = Rect3D.Empty; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- StorageAssociationTypeMapping.cs
- Int32Collection.cs
- XmlAnyElementAttributes.cs
- IItemContainerGenerator.cs
- TypefaceMetricsCache.cs
- MediaTimeline.cs
- InternalBufferOverflowException.cs
- Ports.cs
- SByteStorage.cs
- SchemaNamespaceManager.cs
- SimpleHandlerBuildProvider.cs
- AttachedProperty.cs
- LongMinMaxAggregationOperator.cs
- sortedlist.cs
- ProxyFragment.cs
- SetterBase.cs
- AuthenticationModuleElementCollection.cs
- UInt32Converter.cs
- SerializationIncompleteException.cs
- sqlinternaltransaction.cs
- smtppermission.cs
- StaticFileHandler.cs
- StatusBarPanelClickEvent.cs
- TemplateBindingExtension.cs
- WmpBitmapDecoder.cs
- SchemaComplexType.cs
- SeekStoryboard.cs
- GeometryCollection.cs
- PlainXmlWriter.cs
- ApplicationFileParser.cs
- SqlGatherProducedAliases.cs
- Stroke.cs
- AppDomainInfo.cs
- MaterialGroup.cs
- _TLSstream.cs
- SectionInput.cs
- MsmqTransportElement.cs
- PipeStream.cs
- DataServiceResponse.cs
- WebPartConnection.cs
- XmlBaseReader.cs
- HttpHandlerAction.cs
- FrameworkTextComposition.cs
- DLinqDataModelProvider.cs
- XNodeValidator.cs
- LogLogRecordHeader.cs
- ToolStripDesignerAvailabilityAttribute.cs
- TriggerActionCollection.cs
- XmlDataDocument.cs
- SqlParameter.cs
- ToolStripItemDataObject.cs
- ManagementOptions.cs
- ArrayWithOffset.cs
- EventEntry.cs
- AsyncDataRequest.cs
- UnsafePeerToPeerMethods.cs
- Empty.cs
- MulticastOption.cs
- ConfigurationSection.cs
- FlowPosition.cs
- WinFormsUtils.cs
- MethodImplAttribute.cs
- UriTemplateVariableQueryValue.cs
- DataGridComponentEditor.cs
- CodeGroup.cs
- CrossContextChannel.cs
- DebugManager.cs
- SqlDataSourceConfigureSelectPanel.cs
- Pair.cs
- XmlElementList.cs
- TransformedBitmap.cs
- ExpressionConverter.cs
- DataTransferEventArgs.cs
- MemberHolder.cs
- Point4DValueSerializer.cs
- PrintPreviewGraphics.cs
- TypeInfo.cs
- TypeUtils.cs
- OpenFileDialog.cs
- XmlEntityReference.cs
- SerialReceived.cs
- WebPartPersonalization.cs
- Perspective.cs
- CodeDOMProvider.cs
- TreeWalker.cs
- UnmanagedMemoryStream.cs
- OleDbFactory.cs
- SafeNativeMethodsMilCoreApi.cs
- DataGridBoolColumn.cs
- ValueHandle.cs
- LinqDataSourceView.cs
- SystemSounds.cs
- _IPv4Address.cs
- DataControlReference.cs
- SingleAnimationBase.cs
- MemoryPressure.cs
- CacheMemory.cs
- ValueOfAction.cs
- FileVersion.cs
- JumpPath.cs