Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media3D / MeshGeometry3D.cs / 1305600 / 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, "Distance is negative: " + 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, "Distance is negative: " + 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
- HttpListenerPrefixCollection.cs
- Context.cs
- TimerElapsedEvenArgs.cs
- SmiXetterAccessMap.cs
- WindowsListBox.cs
- CaseExpr.cs
- HandleRef.cs
- AssemblyUtil.cs
- PriorityRange.cs
- DbParameterCollection.cs
- DateBoldEvent.cs
- StorageAssociationSetMapping.cs
- Base64Decoder.cs
- _ConnectOverlappedAsyncResult.cs
- ModifierKeysValueSerializer.cs
- _IPv6Address.cs
- PrintDialogException.cs
- AnnotationDocumentPaginator.cs
- AmbientProperties.cs
- CachedRequestParams.cs
- FilterableAttribute.cs
- RadioButtonPopupAdapter.cs
- UdpReplyToBehavior.cs
- EmissiveMaterial.cs
- PrivilegedConfigurationManager.cs
- GridViewEditEventArgs.cs
- MediaSystem.cs
- FixUpCollection.cs
- OperationContextScope.cs
- LayoutManager.cs
- FormatSelectingMessageInspector.cs
- HtmlEmptyTagControlBuilder.cs
- MissingMemberException.cs
- initElementDictionary.cs
- DownloadProgressEventArgs.cs
- UITypeEditor.cs
- Geometry.cs
- MultipartIdentifier.cs
- SoapProcessingBehavior.cs
- XmlLoader.cs
- AudioSignalProblemOccurredEventArgs.cs
- ReadOnlyNameValueCollection.cs
- FileLogRecordEnumerator.cs
- DataGridViewCheckBoxCell.cs
- SetIterators.cs
- RotateTransform.cs
- SerializationSectionGroup.cs
- TabItem.cs
- ReflectionTypeLoadException.cs
- HtmlImage.cs
- SafeLibraryHandle.cs
- DeferrableContentConverter.cs
- SymbolDocumentGenerator.cs
- DayRenderEvent.cs
- Fonts.cs
- SqlAliasesReferenced.cs
- TextServicesContext.cs
- ClientUtils.cs
- BinaryFormatter.cs
- DataGridTextBox.cs
- Deflater.cs
- XDRSchema.cs
- XamlBrushSerializer.cs
- Content.cs
- SHA256Managed.cs
- SplineKeyFrames.cs
- DropDownHolder.cs
- TreeNode.cs
- ListViewCancelEventArgs.cs
- KeyValuePairs.cs
- MethodBody.cs
- FileUpload.cs
- ObjectStateFormatter.cs
- ClientBuildManager.cs
- ScriptIgnoreAttribute.cs
- SiteIdentityPermission.cs
- ExpressionVisitor.cs
- StrongNamePublicKeyBlob.cs
- Executor.cs
- PublisherMembershipCondition.cs
- ConfigsHelper.cs
- IPipelineRuntime.cs
- LingerOption.cs
- GetReadStreamResult.cs
- UriTemplateClientFormatter.cs
- CallbackBehaviorAttribute.cs
- RecognitionEventArgs.cs
- ClosureBinding.cs
- IDataContractSurrogate.cs
- TableCell.cs
- CalendarDesigner.cs
- SafeEventHandle.cs
- List.cs
- FrameworkTemplate.cs
- ConfigXmlText.cs
- QueryCacheKey.cs
- KeySplineConverter.cs
- StateInitialization.cs
- VectorCollectionConverter.cs
- Point4DConverter.cs