Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / Media3D / MeshGeometry3D.cs / 2 / MeshGeometry3D.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: 3D mesh implementation. // // See spec at [....]/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
- ErrorRuntimeConfig.cs
- XmlSchemaSet.cs
- ParseNumbers.cs
- MenuCommand.cs
- AtomContentProperty.cs
- SizeValueSerializer.cs
- TopClause.cs
- validationstate.cs
- CodeTypeParameterCollection.cs
- DtcInterfaces.cs
- StringSource.cs
- ModifierKeysValueSerializer.cs
- Renderer.cs
- DbProviderManifest.cs
- LayoutSettings.cs
- EntityAdapter.cs
- ConfigXmlSignificantWhitespace.cs
- BitmapEffectGroup.cs
- DesignerToolboxInfo.cs
- RefreshInfo.cs
- PathGeometry.cs
- WebPartsSection.cs
- DataSet.cs
- FragmentQueryKB.cs
- ScriptIgnoreAttribute.cs
- XmlCharCheckingWriter.cs
- ToggleButtonAutomationPeer.cs
- PartialCachingControl.cs
- NullExtension.cs
- SiteMapNode.cs
- XmlNamespaceMappingCollection.cs
- ColumnResizeUndoUnit.cs
- MethodMessage.cs
- RoleManagerSection.cs
- RangeValidator.cs
- Quack.cs
- WebPartHelpVerb.cs
- CrossContextChannel.cs
- BaseEntityWrapper.cs
- LineInfo.cs
- ScrollProviderWrapper.cs
- SqlFacetAttribute.cs
- AccessorTable.cs
- GroupQuery.cs
- CacheEntry.cs
- XmlSchemaSimpleType.cs
- DbReferenceCollection.cs
- VisualBrush.cs
- ServiceDurableInstanceContextProvider.cs
- BaseParaClient.cs
- EdmError.cs
- SafeLibraryHandle.cs
- FlowDocumentPaginator.cs
- DetailsViewDeleteEventArgs.cs
- ParameterElementCollection.cs
- ProxyWebPartConnectionCollection.cs
- InstallHelper.cs
- DataGridBoolColumn.cs
- OrderedHashRepartitionEnumerator.cs
- DataGridViewColumn.cs
- PaginationProgressEventArgs.cs
- CodeDomLoader.cs
- EntityClassGenerator.cs
- SimpleType.cs
- GacUtil.cs
- Walker.cs
- DriveNotFoundException.cs
- FlowDocumentPaginator.cs
- EmptyCollection.cs
- WindowsIdentity.cs
- ProcessModelInfo.cs
- DialogWindow.cs
- XamlSerializer.cs
- WinEventQueueItem.cs
- DateTime.cs
- Attributes.cs
- PartialTrustVisibleAssembly.cs
- ValidationErrorEventArgs.cs
- EncoderFallback.cs
- MenuScrollingVisibilityConverter.cs
- MsmqIntegrationBindingElement.cs
- DataGridrowEditEndingEventArgs.cs
- EntityConnectionStringBuilder.cs
- TransformPattern.cs
- ConnectionPointCookie.cs
- UserInitiatedNavigationPermission.cs
- ExternalException.cs
- OleDbConnectionInternal.cs
- SmtpNegotiateAuthenticationModule.cs
- TransformerTypeCollection.cs
- GlyphRunDrawing.cs
- InfoCardHelper.cs
- Style.cs
- PrintDialog.cs
- Propagator.Evaluator.cs
- LoginDesignerUtil.cs
- CodeArrayIndexerExpression.cs
- HtmlElementEventArgs.cs
- SinglePageViewer.cs
- PropertyToken.cs