/// <summary> /// Transforms from local to world coordinates. /// </summary> /// <param name="vector"> /// The vector (local coordinates). /// </param> /// <returns> /// Transformed vector (world coordinates). /// </returns> protected Vector3D ToWorld(Vector3D vector) { var mat = Visual3DHelper.GetTransform(this); var t = new MatrixTransform3D(mat); return(t.Transform(vector)); }
/// <summary> /// Updates the transforms. /// </summary> /// <returns> /// True if the transform was changed. /// </returns> public bool UpdateTransforms() { var transform = Visual3DHelper.GetViewportTransform(this.visual); if (!transform.HasValue) { return(false); } var newTransform = transform.Value; if (double.IsNaN(newTransform.M11)) { return(false); } if (!newTransform.HasInverse) { return(false); } if (newTransform == this.visualToScreen) { return(false); } this.visualToScreen = this.screenToVisual = newTransform; this.screenToVisual.Invert(); return(true); }
/// <summary> /// Gets the distance from the camera for the specified visual. /// </summary> /// <param name="c"> /// The visual. /// </param> /// <param name="cameraPos"> /// The camera position. /// </param> /// <param name="transform"> /// The total transform of the visual. /// </param> /// <returns> /// The camera distance. /// </returns> private double GetCameraDistance(Visual3D c, Point3D cameraPos, Transform3D transform) { var bounds = Visual3DHelper.FindBounds(c, transform); switch (this.Method) { case SortingMethod.BoundingBoxCenter: var mid = new Point3D( bounds.X + bounds.SizeX * 0.5, bounds.Y + bounds.SizeY * 0.5, bounds.Z + bounds.SizeZ * 0.5); return((mid - cameraPos).LengthSquared); case SortingMethod.BoundingBoxCorners: double d = double.MaxValue; d = Math.Min(d, cameraPos.DistanceTo(new Point3D(bounds.X, bounds.Y, bounds.Z))); d = Math.Min(d, cameraPos.DistanceTo(new Point3D(bounds.X + bounds.SizeX, bounds.Y, bounds.Z))); d = Math.Min( d, cameraPos.DistanceTo(new Point3D(bounds.X + bounds.SizeX, bounds.Y + bounds.SizeY, bounds.Z))); d = Math.Min(d, cameraPos.DistanceTo(new Point3D(bounds.X, bounds.Y + bounds.SizeY, bounds.Z))); d = Math.Min(d, cameraPos.DistanceTo(new Point3D(bounds.X, bounds.Y, bounds.Z + bounds.SizeZ))); d = Math.Min( d, cameraPos.DistanceTo(new Point3D(bounds.X + bounds.SizeX, bounds.Y, bounds.Z + bounds.SizeZ))); d = Math.Min( d, cameraPos.DistanceTo( new Point3D(bounds.X + bounds.SizeX, bounds.Y + bounds.SizeY, bounds.Z + bounds.SizeZ))); d = Math.Min( d, cameraPos.DistanceTo(new Point3D(bounds.X, bounds.Y + bounds.SizeY, bounds.Z + bounds.SizeZ))); return(d); default: var boundingSphere = BoundingSphere.CreateFromRect3D(bounds); return(boundingSphere.DistanceFrom(cameraPos)); } }
/// <summary> /// Transforms from local to world coordinates. /// </summary> /// <param name="point"> /// The point (local coordinates). /// </param> /// <returns> /// Transformed point (world coordinates). /// </returns> protected Point3D ToWorld(Point3D point) { var mat = Visual3DHelper.GetTransform(this); var t = new MatrixTransform3D(mat); return(t.Transform(point)); }
/// <summary> /// The sort children. /// </summary> private void SortChildren() { var vp = Visual3DHelper.GetViewport3D(this); if (vp == null) { return; } var cam = vp.Camera as ProjectionCamera; if (cam == null) { return; } var cameraPos = cam.Position; var transform = new MatrixTransform3D(Visual3DHelper.GetTransform(this)); IList <Visual3D> transparentChildren = new List <Visual3D>(); IList <Visual3D> opaqueChildren = new List <Visual3D>(); if (this.CheckForOpaqueVisuals) { foreach (var child in this.Children) { if (this.IsVisualTransparent(child)) { transparentChildren.Add(child); } else { opaqueChildren.Add(child); } } } else { transparentChildren = this.Children; } // sort the children by distance from camera (note that OrderBy is a stable sort algorithm) var sortedTransparentChildren = transparentChildren.OrderBy(item => - this.GetCameraDistance(item, cameraPos, transform)).ToList(); this.Children.Clear(); // add the opaque children foreach (var c in opaqueChildren) { this.Children.Add(c); } // add the sorted transparent children foreach (var c in sortedTransparentChildren) { this.Children.Add(c); } }
/// <summary> /// Transforms from world to local coordinates. /// </summary> /// <param name="worldPoint"> /// The point (world coordinates). /// </param> /// <returns> /// Transformed vector (local coordinates). /// </returns> protected Point3D ToLocal(Point3D worldPoint) { var mat = Visual3DHelper.GetTransform(this); mat.Invert(); var t = new MatrixTransform3D(mat); return(t.Transform(worldPoint)); }
/// <summary> /// Updates the clipping object. /// </summary> private void UpdateClipping() { var vp = Visual3DHelper.GetViewport3D(this); if (vp == null) { return; } this.Clipping = new CohenSutherlandClipping(10, vp.ActualWidth - 20, 10, vp.ActualHeight - 20); }
/// <summary> /// Zooms to fit the extents of the specified viewport. /// </summary> /// <param name="camera"> /// The actual camera. /// </param> /// <param name="viewport"> /// The viewport. /// </param> /// <param name="animationTime"> /// The animation time. /// </param> public static void ZoomExtents(this ProjectionCamera camera, Viewport3D viewport, double animationTime = 0) { var bounds = Visual3DHelper.FindBounds(viewport.Children); var diagonal = new Vector3D(bounds.SizeX, bounds.SizeY, bounds.SizeZ); if (bounds.IsEmpty || diagonal.LengthSquared < double.Epsilon) { return; } ZoomExtents(camera, viewport, bounds, animationTime); }
/// <summary> /// Applies the cutting planes. /// </summary> /// <param name="forceUpdate"> /// if set to <c>true</c> [force update]. /// </param> private void ApplyCuttingPlanes(bool forceUpdate = false) { lock (this) { this.NewCutGeometries = new Dictionary <Model3D, Geometry3D>(); this.NewOriginalGeometries = new Dictionary <Model3D, Geometry3D>(); this.forceUpdate = forceUpdate; Visual3DHelper.Traverse <GeometryModel3D>(this.Children, this.ApplyCuttingPlanesToModel); this.CutGeometries = this.NewCutGeometries; this.OriginalGeometries = this.NewOriginalGeometries; } }
/// <summary> /// Invoked when an unhandled <see cref="E:System.Windows.Input.Mouse.MouseDown" /> attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event. /// </summary> /// <param name="e">The <see cref="T:System.Windows.Input.MouseButtonEventArgs" /> that contains the event data. This event data reports details about the mouse button that was pressed and the handled state.</param> protected override void OnMouseDown(MouseButtonEventArgs e) { base.OnMouseDown(e); this.ParentViewport = Visual3DHelper.GetViewport3D(this); this.Camera = this.ParentViewport.Camera as ProjectionCamera; var projectionCamera = this.Camera; if (projectionCamera != null) { this.HitPlaneNormal = projectionCamera.LookDirection; } this.CaptureMouse(); }
/// <summary> /// Exports the specified viewport. /// Exports model, camera and lights. /// </summary> /// <param name="viewport"> /// The viewport. /// </param> public virtual void Export(Viewport3D viewport) { this.ExportHeader(); this.ExportViewport(viewport); // Export objects Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportModel); // Export camera this.ExportCamera(viewport.Camera); // Export lights Visual3DHelper.Traverse <Light>(viewport.Children, this.ExportLight); }
/// <summary> /// The composition target_ rendering. /// </summary> /// <param name="sender"> /// The sender. /// </param> /// <param name="e"> /// The event arguments. /// </param> protected override void OnCompositionTargetRendering(object sender, RenderingEventArgs e) { if (this.isRendering) { if (!Visual3DHelper.IsAttachedToViewport3D(this)) { return; } if (this.UpdateTransforms()) { this.UpdateGeometry(); } } }
/// <summary> /// Exports the specified viewport. /// Exports model, camera and lights. /// </summary> /// <param name="viewport"> /// The viewport. /// </param> public override void Export(Viewport3D viewport) { this.ExportViewport(viewport); // Export camera // Export lights Visual3DHelper.Traverse <Light>(viewport.Children, this.ExportLight); this.writer.WriteStartElement("library_materials"); Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportMaterial); this.writer.WriteEndElement(); this.writer.WriteStartElement("library_effects"); Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportEffect); this.writer.WriteEndElement(); // writer.WriteStartElement("library_cameras"); // this.ExportCamera(viewport.Camera); // writer.WriteEndElement(); // writer.WriteStartElement("library_lights"); // Visual3DHelper.Traverse<Light>(viewport.Children, this.ExportLight); // writer.WriteEndElement(); this.writer.WriteStartElement("library_geometries"); Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportGeometry); this.writer.WriteEndElement(); this.writer.WriteStartElement("library_nodes"); Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportNode); this.writer.WriteEndElement(); this.writer.WriteStartElement("library_visual_scenes"); this.writer.WriteStartElement("visual_scene"); this.writer.WriteAttributeString("id", "RootNode"); this.writer.WriteAttributeString("name", "RootNode"); // this.ExportCameraNode(viewport.Camera); Visual3DHelper.Traverse <GeometryModel3D>(viewport.Children, this.ExportSceneNode); this.writer.WriteEndElement(); this.writer.WriteEndElement(); this.writer.WriteStartElement("scene"); this.writer.WriteStartElement("instance_visual_scene"); this.writer.WriteAttributeString("url", "#RootNode"); this.writer.WriteEndElement(); this.writer.WriteEndElement(); }
/// <summary> /// Gets the total number of triangles in the viewport. /// </summary> /// <param name="viewport">The viewport.</param> /// <returns>The total number of triangles</returns> public static int GetTotalNumberOfTriangles(Viewport3D viewport) { int count = 0; Visual3DHelper.Traverse <GeometryModel3D>( viewport.Children, (m, t) => { var geometry = m.Geometry as MeshGeometry3D; if (geometry != null && geometry.TriangleIndices != null) { count += geometry.TriangleIndices.Count / 3; } }); return(count); }
/// <summary> /// The expand. /// </summary> protected virtual void OnExpansionChanged() { if (!this.ExpandOrigin.HasValue) { if (this.Content != null) { this.actualExpandOrigin = this.Content.Bounds.Location; } } else { this.actualExpandOrigin = this.ExpandOrigin.Value; } Visual3DHelper.TraverseModel <GeometryModel3D>(this.Content, this.Expand); }
/// <summary> /// The sort children. /// </summary> private void SortChildren() { var vp = Visual3DHelper.GetViewport3D(this); if (vp == null) { return; } var cam = vp.Camera as ProjectionCamera; if (cam == null) { return; } var cameraPos = cam.Position; var transform = new MatrixTransform3D(Visual3DHelper.GetTransform(this)); IList <Visual3D> transparentChildren = new List <Visual3D>(); IList <Visual3D> opaqueChildren = new List <Visual3D>(); if (this.CheckForOpaqueVisuals) { foreach (var child in this.Children) { if (this.IsVisualTransparent(child)) { transparentChildren.Add(child); } else { opaqueChildren.Add(child); } } } else { transparentChildren = this.Children; } // sort the children by distance from camera (note that OrderBy is a stable sort algorithm) var sortedTransparentChildren = transparentChildren.OrderBy(item => - this.GetCameraDistance(item, cameraPos, transform)).ToList(); // Now that opaqueChildren and sortedTransparentChildren describe our desired ordering, we need sort the current children in the new order. // To optimize the efficiency of this procedure we want to change the children list as little as possible. // Unfortunatally the Visual3DCollection does not have a swap method and we always need to remove an item before we can add it again as // temporary duplicates result in exceptions in the visual tree. // Due to this set of considerations we use selection sort to sort the current Children. (if we could swap without removal, cycle sort might be a small improvement) for (int desiredIndex = 0; desiredIndex < opaqueChildren.Count; desiredIndex++) { Visual3D currentChild = opaqueChildren[desiredIndex]; int currentIndex = Children.IndexOf(currentChild); //Insert in the proper spot if not contained: if (currentIndex == -1) { Children.Insert(desiredIndex, currentChild); continue; } //Do nothing if it is in the correct spot; //The order of the opaque children does not matter as long as they are before the transparent children: if (currentIndex < opaqueChildren.Count) { continue; } //remove from old spot and insert to the new correct spot: Children.RemoveAt(currentIndex); Children.Insert(desiredIndex, currentChild); } for (int desiredIndex = opaqueChildren.Count; desiredIndex < opaqueChildren.Count + sortedTransparentChildren.Count; desiredIndex++) { Visual3D currentChild = sortedTransparentChildren[desiredIndex - opaqueChildren.Count]; int currentIndex = Children.IndexOf(currentChild); //Insert in the proper spot if not contained: if (currentIndex == -1) { Children.Insert(desiredIndex, currentChild); continue; } //Do nothing if it is in the correct spot: if (currentIndex == desiredIndex) { continue; } //remove from old spot and insert to the new correct spot: Children.RemoveAt(currentIndex); Children.Insert(desiredIndex, currentChild); } }
/// <summary> /// Exports the specified visual. /// </summary> /// <param name="visual"> /// The visual. /// </param> public void Export(Visual3D visual) { this.ExportHeader(); Visual3DHelper.Traverse <GeometryModel3D>(visual, this.ExportModel); }
/// <summary> /// Exports the specified model. /// </summary> /// <param name="model"> /// The model. /// </param> public void Export(Model3D model) { this.ExportHeader(); Visual3DHelper.TraverseModel <GeometryModel3D>(model, this.ExportModel); }
/// <summary> /// Gets the distance squared. /// </summary> /// <param name="position"> /// The position. /// </param> /// <param name="visual"> /// The visual. /// </param> /// <returns> /// The get distance squared. /// </returns> public static double GetDistanceSquared(Point3D position, Visual3D visual) { var bounds = Visual3DHelper.FindBounds(visual, Transform3D.Identity); return(Point3D.Subtract(bounds.Location, position).LengthSquared); }