public void AddLine(Point3D p1, Point3D p2) { int i0 = positions.Count; this.positions.Add(p1); this.positions.Add(p2); this.lineListIndices.Add(i0); this.lineListIndices.Add(i0 + 1); }
public void AddBox(Point3D center, double xlength, double ylength, double zlength) { int i0 = positions.Count; var dx = new Vector3D((float)xlength/2f, 0, 0); var dy = new Vector3D(0, (float)ylength/2f, 0); var dz = new Vector3D(0, 0, (float)zlength/2f); this.Add(true, center - dx - dy - dz, center + dx - dy - dz, center + dx + dy - dz, center - dx + dy - dz); this.Add(true, center - dx - dy + dz, center + dx - dy + dz, center + dx + dy + dz, center - dx + dy + dz); lineListIndices.AddRange(new[] { i0 + 0, i0 + 4, i0 + 1, i0 + 5, i0 + 2, i0 + 6, i0 + 3, i0 + 7 }); }
/// <summary> /// Un-projects a point from the screen (2D) to a point on plane (3D) /// </summary> /// <param name="p"> /// The 2D point. /// </param> /// <param name="position"> /// plane position /// </param> /// <param name="normal"> /// plane normal /// </param> /// <returns> /// A 3D point. /// </returns> public Vector3?UnProject(Point p, Vector3 position, Vector3 normal) { var ray = this.GetRay(p); if (ray == null) { return(null); } var plane = new Plane(position, normal); if (ray.Intersects(ref plane, out Vector3 point)) { return(point); }
private void Timer_Tick(object sender, EventArgs e) { double angle = (0.05f * frame) * Math.PI / 180; var xAxis = new Vector3(1, 0, 0); var zAxis = new Vector3(0, 0, 1); var yAxis = new Vector3(0, 1, 0); var rotation = Matrix.RotationAxis(xAxis, 0); double angleEach = 0; int counter = 0; for (int i = 0; i < NumSegments && i < numBonesInModel; ++i, counter += numSegmentPerBone) { if (i == 0) { boneInternal[0] = rotation; } else { var vp = Vector3.Transform(path[counter - numSegmentPerBone], Matrix.RotationAxis(xAxis, (float)angleEach)).ToVector3(); angleEach += angle; var v = Vector3.Transform(path[counter], Matrix.RotationAxis(xAxis, (float)angleEach)).ToVector3(); var rad = Math.Acos(Vector3.Dot(yAxis, (v - vp).Normalized())); if (angleEach < 0) { rad = -rad; } var rot = Matrix.RotationAxis(xAxis, (float)rad); var trans = Matrix.Translation(v); boneInternal[i] = rot * trans; } } Bones = new BoneMatricesStruct() { Bones = boneInternal.ToArray() }; if (frame > 40 || frame < -40) { direction = !direction; } if (direction) { ++frame; } else { --frame; } }
/// <summary> /// Zooms the camera to the specified rectangle. /// </summary> /// <param name="camera"> /// The camera. /// </param> /// <param name="viewport"> /// The viewport. /// </param> /// <param name="zoomRectangle"> /// The zoom rectangle. /// </param> public static void ZoomToRectangle(this Camera camera, Viewport3DX viewport, Rect zoomRectangle) { var topLeftRay = viewport.UnProjectToRay(zoomRectangle.TopLeft); var topRightRay = viewport.UnProjectToRay(zoomRectangle.TopRight); var centerRay = viewport.UnProjectToRay( new Point( (zoomRectangle.Left + zoomRectangle.Right) * 0.5, (zoomRectangle.Top + zoomRectangle.Bottom) * 0.5)); if (topLeftRay == null || topRightRay == null || centerRay == null) { // could not invert camera matrix return; } var u = Vector3.Normalize(topLeftRay.Direction); var v = Vector3.Normalize(topRightRay.Direction); var w = Vector3.Normalize(centerRay.Direction); if (camera is IPerspectiveCameraModel perspectiveCamera) { var distance = camera.LookDirection.Length; // option 1: change distance var newDistance = distance * zoomRectangle.Width / viewport.ActualWidth; var newLookDirection = (float)newDistance * w; var newPosition = camera.CameraInternal.Position + ((float)(distance - newDistance) * w); var newTarget = newPosition + newLookDirection; LookAt(camera, newTarget.ToPoint3D(), newLookDirection.ToVector3D(), 200); // option 2: change fov // double newFieldOfView = Math.Acos(Vector3D.DotProduct(u, v)); // var newTarget = camera.Position + distance * w; // pcamera.FieldOfView = newFieldOfView * 180 / Math.PI; // LookAt(camera, newTarget, distance * w, 0); } else if (camera is IOrthographicCameraModel orthographicCamera) { orthographicCamera.Width *= zoomRectangle.Width / viewport.ActualWidth; var oldTarget = camera.CameraInternal.Position + camera.CameraInternal.LookDirection; var distance = camera.CameraInternal.LookDirection.Length(); var newTarget = centerRay.PlaneIntersection(oldTarget, w); if (newTarget != null) { LookAt(orthographicCamera, newTarget.Value.ToPoint3D(), 200); } } }
public static void ExtrudeText(this MeshBuilder builder, string text, string font, FontStyle fontStyle, FontWeight fontWeight, double fontSize, Vector3D textDirection, Point3D p0, Point3D p1) { var outlineList = GetTextOutlines(text, font, fontStyle, fontWeight, fontSize); // Build the polygon to mesh (using Triangle.NET to triangulate) var polygon = new TriangleNet.Geometry.Polygon(); int marker = 0; foreach (var outlines in outlineList) { var outerOutline = outlines.OrderBy(x => x.AreaOfSegment()).Last(); for (int i = 0; i < outlines.Count; i++) { var outline = outlines[i]; var isHole = i != outlines.Count - 1 && IsPointInPolygon(outerOutline, outline[0]); polygon.AddContour(outline.Select(p => new Vertex(p.X, p.Y)), marker++, isHole); builder.AddExtrudedSegments(outline.ToSegments().Select(x => new SharpDX.Vector2((float)x.X, (float)x.Y)).ToList(), textDirection, p0, p1); } } var mesher = new GenericMesher(); var options = new ConstraintOptions(); var mesh = mesher.Triangulate(polygon, options); var u = textDirection; u.Normalize(); var z = p1 - p0; z.Normalize(); var v = Vector3D.Cross(z, u); // Convert the triangles foreach (var t in mesh.Triangles) { var v0 = t.GetVertex(2); var v1 = t.GetVertex(1); var v2 = t.GetVertex(0); // Add the top triangle. // Project the X/Y vertices onto a plane defined by textdirection, p0 and p1. builder.AddTriangle(v0.Project(p0, u, v, z, 1), v1.Project(p0, u, v, z, 1), v2.Project(p0, u, v, z, 1)); // Add the bottom triangle. builder.AddTriangle(v2.Project(p0, u, v, z, 0), v1.Project(p0, u, v, z, 0), v0.Project(p0, u, v, z, 0)); } }
/// <summary> /// Rotate around three axes. /// </summary> /// <param name="p1"> /// The previous mouse position. /// </param> /// <param name="p2"> /// The current mouse position. /// </param> /// <param name="rotateAround"> /// The point to rotate around. /// </param> public void RotateTurnball(Vector2 p1, Vector2 p2, Vector3 rotateAround) { this.InitTurnballRotationAxes(p1); Vector2 delta = p2 - p1; var relativeTarget = rotateAround - this.Camera.CameraInternal.Target; var relativePosition = rotateAround - this.Camera.CameraInternal.Position; float d = -1; if (this.CameraMode != CameraMode.Inspect) { d = 0.2f; } d *= (float)RotationSensitivity; var q1 = Quaternion.RotationAxis(this.rotationAxisX, d * Inv * delta.X / 180 * (float)Math.PI); var q2 = Quaternion.RotationAxis(this.rotationAxisY, d * delta.Y / 180 * (float)Math.PI); Quaternion q = q1 * q2; var m = Matrix.RotationQuaternion(q); Vector3 newLookDir = Vector3.TransformNormal(this.Camera.CameraInternal.LookDirection, m); Vector3 newUpDirection = Vector3.TransformNormal(this.Camera.CameraInternal.UpDirection, m); Vector3 newRelativeTarget = Vector3.TransformCoordinate(relativeTarget, m); Vector3 newRelativePosition = Vector3.TransformCoordinate(relativePosition, m); var newRightVector = Vector3.Normalize(Vector3.Cross(newLookDir, newUpDirection)); var modUpDir = Vector3.Normalize(Vector3.Cross(newRightVector, newLookDir)); if ((newUpDirection - modUpDir).Length() > 1e-8) { newUpDirection = modUpDir; } var newTarget = rotateAround - newRelativeTarget; var newPosition = rotateAround - newRelativePosition; var newLookDirection = newTarget - newPosition; this.Camera.LookDirection = newLookDirection.ToVector3D(); if (this.CameraMode == CameraMode.Inspect) { this.Camera.Position = newPosition.ToPoint3D(); } this.Camera.UpDirection = newUpDirection.ToVector3D(); }
/// <summary> /// Finds the bounding box of the viewport. /// </summary> /// <param name="viewport">The viewport.</param> /// <returns>The bounding box.</returns> public static Rect3D FindBounds(this Viewport3DX viewport) { var maxVector = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); var firstModel = viewport.Renderables.PreorderDFT((r) => { if (r.Visible && !(r is ScreenSpacedNode)) { return(true); } return(false); }).Where(x => { if (x is IBoundable b) { return(b.HasBound && b.BoundsWithTransform.Maximum != b.BoundsWithTransform.Minimum && b.BoundsWithTransform.Maximum != Vector3.Zero && b.BoundsWithTransform.Maximum != maxVector); } else { return(false); } }).FirstOrDefault(); if (firstModel == null) { return(new Rect3D()); } var bounds = firstModel.BoundsWithTransform; foreach (var renderable in viewport.Renderables.PreorderDFT((r) => { if (r.Visible && !(r is ScreenSpacedNode)) { return(true); } return(false); })) { if (renderable is IBoundable r) { if (r.HasBound && r.BoundsWithTransform.Maximum != maxVector) { bounds = global::SharpDX.BoundingBox.Merge(bounds, r.BoundsWithTransform); } } } return(new Rect3D(bounds.Minimum.ToPoint3D(), (bounds.Maximum - bounds.Minimum).ToSize3D())); }
/// <summary> /// changes in lookdirection set the rotation point to wrong position /// Ref: https://github.com/helix-toolkit/helix-toolkit/issues/1068 /// </summary> /// <param name="newRelativeTarget"></param> /// <param name="relativeTarget"></param> /// <returns></returns> private Vector3 CalcNewRelativeTargetOrthogonalToLookDirection(Vector3 newRelativeTarget, Vector3 relativeTarget) { var relativeTargetDiff = newRelativeTarget - relativeTarget; var lookDir = Camera.CameraInternal.LookDirection; var crossProduct = Vector3.Cross(lookDir, relativeTargetDiff); var orthogonalRelativeTargetDiff = Vector3.Cross(crossProduct, lookDir).Normalized(); // correct length var angle = orthogonalRelativeTargetDiff.AngleBetween(relativeTargetDiff); orthogonalRelativeTargetDiff *= (float)(Math.Cos(angle) * relativeTargetDiff.Length()); newRelativeTarget = relativeTarget + orthogonalRelativeTargetDiff; return(newRelativeTarget); }
/// <summary> /// Zooms the view around the specified point. /// </summary> /// <param name="delta"> /// The delta. /// </param> /// <param name="zoomAround"> /// The zoom around. /// </param> /// <param name="isTouch"></param> /// <param name="stopOther">Stop other manipulation</param> public void Zoom(double delta, Vector3 zoomAround, bool isTouch = false, bool stopOther = true) { if (!this.Controller.IsZoomEnabled) { return; } if (stopOther) { this.Controller.StopSpin(); this.Controller.StopPanning(); } if (this.Camera is IPerspectiveCameraModel) { if (!isTouch) { if (delta < -0.5) { delta = -0.5; } delta *= this.ZoomSensitivity; } if (this.CameraMode == CameraMode.FixedPosition || this.changeFieldOfView) { this.Viewport.ZoomByChangingFieldOfView(delta); } else { switch (this.CameraMode) { case CameraMode.Inspect: this.ChangeCameraDistance(ref delta, zoomAround); break; case CameraMode.WalkAround: this.Camera.Position -= this.Camera.LookDirection * delta; break; } } return; } else if (this.Camera is IOrthographicCameraModel) { this.ZoomByChangingCameraWidth(delta, zoomAround); } }
/// <summary> /// Tries to parse a vertex from a string. /// </summary> /// <param name="line"> /// The input string. /// </param> /// <param name="point"> /// The vertex point. /// </param> /// <returns> /// True if parsing was successful. /// </returns> private static bool TryParseVertex(string line, out Point3D point) { var match = VertexRegex.Match(line); if (!match.Success) { point = new Point3D(); return(false); } float x = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture); float y = float.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture); float z = float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture); point = new Point3D(x, y, z); return(true); }
/// <summary> /// Occurs when the manipulation is started. /// </summary> /// <param name="e">The <see cref="Point"/> instance containing the event data.</param> public override void Started(Point e) { base.Started(e); this.zoomPoint = new Point(this.Viewport.ActualWidth / 2, this.Viewport.ActualHeight / 2); this.zoomPoint3D = this.Camera.CameraInternal.Target; if (this.Controller.ZoomAroundMouseDownPoint && this.MouseDownNearestPoint3D != null) { this.zoomPoint = this.MouseDownPoint; this.zoomPoint3D = this.MouseDownNearestPoint3D.Value; } if (!this.changeFieldOfView) { this.Viewport.ShowTargetAdorner(this.zoomPoint); } }
/// <summary> /// Pans the camera by the specified 3D vector (world coordinates). /// </summary> /// <param name="delta"> /// The panning vector. /// </param> /// <param name="stopOther">Stop other manipulation</param> public void Pan(Vector3 delta, bool stopOther = true) { if (!this.Controller.IsPanEnabled) { return; } if (stopOther) { this.Controller.StopSpin(); this.Controller.StopZooming(); } if (this.CameraMode == CameraMode.FixedPosition) { return; } this.Camera.Position += delta.ToVector3D(); }
/// <summary> /// Finds the nearest connected segment to the specified point. /// </summary> /// <param name="segments"> /// The segments. /// </param> /// <param name="point"> /// The point. /// </param> /// <param name="eps"> /// The tolerance. /// </param> /// <returns> /// The index of the nearest point. /// </returns> private static int FindConnectedSegment(IList <Point3D> segments, Point3D point, DoubleOrSingle eps) { var best = eps; int result = -1; for (int i = 0; i < segments.Count; i++) { var v = point - segments[i]; var ls0 = SharedFunctions.LengthSquared(ref v); if (ls0 < best) { result = i; best = ls0; } } return(result); }
/// <summary> /// The add pan force. /// </summary> /// <param name="pan"> /// The pan. /// </param> public void AddPanForce(Vector3 pan) { if (!this.IsPanEnabled) { return; } this.PushCameraSetting(); if (this.IsInertiaEnabled) { this.panSpeed += pan * 40; } else { this.panHandler.Pan(pan); } Viewport.InvalidateRender(); }
private double CalculateError(int id_v1, int id_v2, out Vector3D p_result) { p_result = new Vector3D(); // compute interpolated vertex var q = vertices[id_v1].q + vertices[id_v2].q; bool border = vertices[id_v1].border & vertices[id_v2].border; double error = 0; double det = q.det(0, 1, 2, 1, 4, 5, 2, 5, 7); if (det != 0 && !border) { // q_delta is invertible p_result.X = (float)(-1 / det * (q.det(1, 2, 3, 4, 5, 6, 5, 7, 8))); // vx = A41/det(q_delta) p_result.Y = (float)(1 / det * (q.det(0, 2, 3, 1, 5, 6, 2, 7, 8))); // vy = A42/det(q_delta) p_result.Z = (float)(-1 / det * (q.det(0, 1, 3, 1, 4, 6, 2, 5, 8))); // vz = A43/det(q_delta) error = VertexError(ref q, p_result.X, p_result.Y, p_result.Z); } else { // det = 0 -> try to find best result var p1 = vertices[id_v1].p; var p2 = vertices[id_v2].p; var p3 = (p1 + p2) / 2; double error1 = VertexError(ref q, p1.X, p1.Y, p1.Z); double error2 = VertexError(ref q, p2.X, p2.Y, p2.Z); double error3 = VertexError(ref q, p3.X, p3.Y, p3.Z); error = Math.Min(error1, Math.Min(error2, error3)); if (error1 == error) { p_result = p1; } if (error2 == error) { p_result = p2; } if (error3 == error) { p_result = p3; } } return(error); }
/// <summary> /// Adds the zoom force. /// </summary> /// <param name="delta"> /// The delta. /// </param> /// <param name="zoomOrigin"> /// The zoom origin. /// </param> public void AddZoomForce(float delta, Vector3 zoomOrigin) { if (!this.IsZoomEnabled) { return; } this.PushCameraSetting(); if (this.IsInertiaEnabled) { this.zoomPoint3D = zoomOrigin; this.zoomSpeed += delta * 8; } else { this.zoomHandler.Zoom(delta, zoomOrigin); } Viewport.InvalidateRender(); }
/// <summary> /// Rotates the specified p0. /// </summary> /// <param name="p0">The p0.</param> /// <param name="p1">The p1.</param> /// <param name="rotateAround">The rotate around.</param> /// <param name="stopOther">if set to <c>true</c> [stop other].</param> public void Rotate(Vector2 p0, Vector2 p1, Vector3 rotateAround, bool stopOther = true) { if (!this.Controller.IsRotationEnabled) { return; } if (stopOther) { Controller.StopZooming(); Controller.StopPanning(); } p0 = Vector2.Multiply(p0, Controller.AllowRotateXY); p1 = Vector2.Multiply(p1, Controller.AllowRotateXY); Vector3 newPos = Camera.CameraInternal.Position; Vector3 newLook = Camera.CameraInternal.LookDirection; Vector3 newUp = Vector3.Normalize(Camera.CameraInternal.UpDirection); switch (this.Controller.CameraRotationMode) { case CameraRotationMode.Trackball: CameraMath.RotateTrackball(CameraMode, ref p0, ref p1, ref rotateAround, (float)RotationSensitivity, Controller.Width, Controller.Height, Camera, Inv, out newPos, out newLook, out newUp); break; case CameraRotationMode.Turntable: var p = p1 - p0; CameraMath.RotateTurntable(CameraMode, ref p, ref rotateAround, (float)RotationSensitivity, Controller.Width, Controller.Height, Camera, Inv, ModelUpDirection, out newPos, out newLook, out newUp); break; case CameraRotationMode.Turnball: CameraMath.RotateTurnball(CameraMode, ref p0, ref p1, ref rotateAround, (float)RotationSensitivity, Controller.Width, Controller.Height, Camera, Inv, out newPos, out newLook, out newUp); break; default: break; } Camera.LookDirection = newLook.ToVector3D(); Camera.Position = newPos.ToPoint3D(); Camera.UpDirection = newUp.ToVector3D(); }
/// <summary> /// Un-projects a 2D screen point. /// </summary> /// <param name="viewport">The viewport.</param> /// <param name="pointIn">The input point.</param> /// <param name="pointNear">The point at the near clipping plane.</param> /// <param name="pointFar">The point at the far clipping plane.</param> /// <returns>The ray.</returns> public static Ray UnProject(this Viewport3DX viewport, Vector2 point2d)//, out Vector3 pointNear, out Vector3 pointFar) { var camera = viewport.Camera as ProjectionCamera; if (camera != null) { var p = new Vector3((float)point2d.X, (float)point2d.Y, 1); //var wvp = GetViewProjectionMatrix(viewport); //Vector3 r = Vector3.Unproject(p, 0f, 0f, (float)viewport.ActualWidth, (float)viewport.ActualHeight, 0f, 1f, wvp); //r.Normalize(); var vp = GetScreenViewProjectionMatrix(viewport); var vpi = Matrix.Invert(vp); var test = 1f / ((p.X * vpi.M14) + (p.Y * vpi.M24) + (p.Z * vpi.M34) + vpi.M44); if (double.IsInfinity(test)) { vpi.M44 = vpi.M44 + 0.000001f; } Vector3 zn, zf; p.Z = 0; Vector3.TransformCoordinate(ref p, ref vpi, out zn); p.Z = 1; Vector3.TransformCoordinate(ref p, ref vpi, out zf); Vector3 r = zf - zn; r.Normalize(); if (camera is PerspectiveCamera) { return(new Ray(camera.Position.ToVector3(), r)); } else if (camera is OrthographicCamera) { return(new Ray(zn, r)); } } throw new HelixToolkitException("Unproject camera error."); }
/// <summary> /// Generates a square grid with a step of 1.0 /// </summary> /// <returns></returns> public static LineGeometry3D GenerateGrid(Vector3 plane, int min0 = 0, int max0 = 10, int min1 = 0, int max1 = 10) { var grid = new LineBuilder(); //int width = max - min; if (plane == Vector3.UnitX) { for (int i = min0; i <= max0; i++) { grid.AddLine(new Vector3(0, i, min1), new Vector3(0, i, max1)); } for (int i = min1; i <= max1; i++) { grid.AddLine(new Vector3(0, min0, i), new Vector3(0, max0, i)); } } else if (plane == Vector3.UnitY) { for (int i = min0; i <= max0; i++) { grid.AddLine(new Vector3(i, 0, min1), new Vector3(i, 0, max1)); } for (int i = min1; i <= max1; i++) { grid.AddLine(new Vector3(min0, 0, i), new Vector3(max0, 0, i)); } } else { for (int i = min0; i <= max0; i++) { grid.AddLine(new Vector3(i, min1, 0), new Vector3(i, max1, 0)); } for (int i = min1; i <= max1; i++) { grid.AddLine(new Vector3(min0, i, 0), new Vector3(max0, i, 0)); } } return(grid.ToLineGeometry3D()); }
/// <summary> /// Determines whether this polygon is planar. /// </summary> /// <returns> /// The is planar. /// </returns> public bool IsPlanar() { Vector3D v1 = this.Points[1] - this.Points[0]; var normal = new Vector3D(); for (int i = 2; i < this.Points.Count; i++) { var n = Vector3D.Cross(v1, this.Points[i] - this.Points[0]); n.Normalize(); if (i == 2) { normal = n; } else if (Math.Abs(Vector3D.Dot(n, normal) - 1) > 1e-8) { return(false); } } return(true); }
/// <summary> /// Gets the normal of the polygon. /// </summary> /// <returns> /// The normal. /// </returns> public Vector3D GetNormal() { if (this.Points.Count < 3) { throw new InvalidOperationException("At least three points required in the polygon to find a normal."); } Vector3D v1 = this.Points[1] - this.Points[0]; for (int i = 2; i < this.Points.Count; i++) { var n = Vector3D.Cross(v1, this.Points[i] - this.Points[0]); if (n.LengthSquared() > 1e-8) { n.Normalize(); return(n); } } throw new InvalidOperationException("Invalid polygon."); }
/// <summary> /// Un-projects a 2D screen point. /// </summary> /// <param name="viewport">The viewport.</param> /// <param name="point2d">The input point.</param> /// <returns>The ray.</returns> public static Ray UnProject(this Viewport3DX viewport, Vector2 point2d)//, out Vector3 pointNear, out Vector3 pointFar) { var camera = viewport.CameraCore as ProjectionCameraCore; if (camera != null) { var px = (float)point2d.X; var py = (float)point2d.Y; var viewMatrix = camera.GetViewMatrix(); Vector3 v = new Vector3(); var matrix = MatrixExtensions.PsudoInvert(ref viewMatrix); float w = (float)viewport.ActualWidth; float h = (float)viewport.ActualHeight; var aspectRatio = w / h; var projMatrix = camera.GetProjectionMatrix(aspectRatio); Vector3 zn, zf; v.X = (2 * px / w - 1) / projMatrix.M11; v.Y = -(2 * py / h - 1) / projMatrix.M22; v.Z = 1 / projMatrix.M33; Vector3.TransformCoordinate(ref v, ref matrix, out zf); if (camera is PerspectiveCameraCore) { zn = camera.Position; } else { v.Z = 0; Vector3.TransformCoordinate(ref v, ref matrix, out zn); } Vector3 r = zf - zn; r.Normalize(); return(new Ray(zn + r * camera.NearPlaneDistance, r)); } throw new HelixToolkitException("Unproject camera error."); }
/// <summary> /// Rotate camera using 'Turntable' rotation. /// </summary> /// <param name="delta"> /// The relative change in position. /// </param> /// <param name="rotateAround"> /// The point to rotate around. /// </param> public void RotateTurntable(Vector2 delta, Vector3 rotateAround) { var relativeTarget = rotateAround - this.Camera.CameraInternal.Target; var relativePosition = rotateAround - this.Camera.CameraInternal.Position; var cUp = Camera.CameraInternal.UpDirection; var up = this.ModelUpDirection; var dir = Vector3.Normalize(Camera.CameraInternal.LookDirection); var right = Vector3.Normalize(Vector3.Cross(dir, cUp)); float d = -0.5f; if (this.CameraMode != CameraMode.Inspect) { d *= -0.2f; } d *= (float)this.RotationSensitivity; var q1 = Quaternion.RotationAxis(up, d * Inv * delta.X / 180 * (float)Math.PI); var q2 = Quaternion.RotationAxis(right, d * delta.Y / 180 * (float)Math.PI); Quaternion q = q1 * q2; var m = Matrix.RotationQuaternion(q); var newUpDirection = Vector3.TransformNormal(cUp, m); var newRelativeTarget = Vector3.TransformCoordinate(relativeTarget, m); var newRelativePosition = Vector3.TransformCoordinate(relativePosition, m); var newTarget = rotateAround - newRelativeTarget; var newPosition = rotateAround - newRelativePosition; this.Camera.LookDirection = (newTarget - newPosition).ToVector3D(); if (this.CameraMode == CameraMode.Inspect) { this.Camera.Position = newPosition.ToPoint3D(); } this.Camera.UpDirection = newUpDirection.ToVector3D(); }
/// <summary> /// Zooms the camera to the specified rectangle. /// </summary> /// <param name="camera"> /// The camera. /// </param> /// <param name="viewport"> /// The viewport. /// </param> /// <param name="zoomRectangle"> /// The zoom rectangle. /// </param> public static void ZoomToRectangle(this Camera camera, Viewport3DX viewport, Rect zoomRectangle) { if (viewport.UnProject(zoomRectangle.TopLeft.ToVector2(), out var topLeftRay) && viewport.UnProject(zoomRectangle.TopRight.ToVector2(), out var topRightRay) && viewport.UnProject(new global::SharpDX.Vector2( (float)(zoomRectangle.Left + zoomRectangle.Right) * 0.5f, (float)(zoomRectangle.Top + zoomRectangle.Bottom) * 0.5f), out var centerRay)) { var u = Vector3.Normalize(topLeftRay.Direction); var v = Vector3.Normalize(topRightRay.Direction); var w = Vector3.Normalize(centerRay.Direction); if (camera is IPerspectiveCameraModel perspectiveCamera) { var distance = camera.LookDirection.Length; // option 1: change distance var newDistance = distance * zoomRectangle.Width / viewport.ActualWidth; var newLookDirection = (float)newDistance * w; var newPosition = camera.CameraInternal.Position + ((float)(distance - newDistance) * w); var newTarget = newPosition + newLookDirection; LookAt(camera, newTarget.ToPoint3D(), newLookDirection.ToVector3D(), 200); // option 2: change fov // double newFieldOfView = Math.Acos(Vector3D.DotProduct(u, v)); // var newTarget = camera.Position + distance * w; // pcamera.FieldOfView = newFieldOfView * 180 / Math.PI; // LookAt(camera, newTarget, distance * w, 0); } else if (camera is IOrthographicCameraModel orthographicCamera) { orthographicCamera.Width *= zoomRectangle.Width / viewport.ActualWidth; var oldTarget = camera.CameraInternal.Position + camera.CameraInternal.LookDirection; var distance = camera.CameraInternal.LookDirection.Length(); if (centerRay.PlaneIntersection(oldTarget, w, out var newTarget)) { LookAt(orthographicCamera, newTarget.ToPoint3D(), 200); } } } }
/// <summary> /// Initializes a new instance of the <see cref="ContourHelper" /> class. /// </summary> /// <param name="planeOrigin">The plane origin.</param> /// <param name="planeNormal">The plane normal.</param> /// <param name="originalMesh">The original mesh.</param> public ContourHelper(Point3D planeOrigin, Vector3D planeNormal, MeshGeometry3D originalMesh) { var hasNormals = originalMesh.Normals != null && originalMesh.Normals.Count > 0; var hasTextureCoordinates = originalMesh.TextureCoordinates != null && originalMesh.TextureCoordinates.Count > 0; this.normals = hasNormals ? new Vector3D[3] : null; this.textures = hasTextureCoordinates ? new Point[3] : null; this.positionCount = originalMesh.Positions.Count; this.meshPositions = originalMesh.Positions.ToArray(); this.meshNormals = hasNormals ? originalMesh.Normals.ToArray() : null; this.meshTextureCoordinates = hasTextureCoordinates ? originalMesh.TextureCoordinates.ToArray() : null; // Determine the equation of the plane as // ax + by + cz + d = 0 var l = (float)Math.Sqrt((planeNormal.X * planeNormal.X) + (planeNormal.Y * planeNormal.Y) + (planeNormal.Z * planeNormal.Z)); this.a = planeNormal.X / l; this.b = planeNormal.Y / l; this.c = planeNormal.Z / l; this.d = -(float)((planeNormal.X * planeOrigin.X) + (planeNormal.Y * planeOrigin.Y) + (planeNormal.Z * planeOrigin.Z)); }
/// <summary> /// Changes the camera position by the specified vector. /// </summary> /// <param name="delta">The translation vector in camera space (z in look direction, y in up direction, and x perpendicular to the two others)</param> /// <param name="stopOther">Stop other manipulation</param> public void MoveCameraPosition(Vector3 delta, bool stopOther = true) { if (stopOther) { Controller.StopPanning(); Controller.StopSpin(); } var z = Vector3.Normalize(this.Camera.CameraInternal.LookDirection); var x = Vector3.Cross(this.Camera.CameraInternal.LookDirection, this.Camera.CameraInternal.UpDirection); var y = Vector3.Normalize(Vector3.Cross(x, z)); x = Vector3.Cross(z, y); // delta *= this.ZoomSensitivity; switch (this.CameraMode) { case CameraMode.Inspect: case CameraMode.WalkAround: this.Camera.Position += ((x * delta.X) + (y * delta.Y) + (z * delta.Z)).ToVector3D(); break; } }
/// <summary> /// Occurs when the position is changed during a manipulation. /// </summary> /// <param name="e">The <see cref="Point"/> instance containing the event data.</param> public override void Delta(Point e) { base.Delta(e); var thisPoint3D = this.UnProject(e, this.panPoint3D, this.Camera.CameraInternal.LookDirection); if (Camera.CameraInternal.LookDirection.LengthSquared() < 1f && MouseDownNearestPoint3D.HasValue) { var look = Camera.CameraInternal.LookDirection.Normalized(); var v = MouseDownNearestPoint3D.Value - Camera.CameraInternal.Position; Camera.CameraInternal.LookDirection = look * Math.Max(1, Vector3.Dot(v, look)); } if (this.LastPoint3D == null || thisPoint3D == null) { return; } var delta3D = this.LastPoint3D.Value - thisPoint3D.Value; this.Pan(delta3D); this.LastPoint = e; this.LastPoint3D = this.UnProject(e, this.panPoint3D, this.Camera.CameraInternal.LookDirection); }
/// <summary> /// The change camera width. /// </summary> /// <param name="delta"> /// The delta. /// </param> /// <param name="zoomAround"> /// The zoom around. /// </param> public void ZoomByChangingCameraWidth(double delta, Vector3 zoomAround) { if (delta < -0.5) { delta = -0.5; } switch (this.CameraMode) { case CameraMode.WalkAround: case CameraMode.Inspect: case CameraMode.FixedPosition: if (ChangeCameraDistance(ref delta, zoomAround)) { // Modify the camera width if (Camera is IOrthographicCameraModel ocamera) { ocamera.Width *= Math.Pow(2.5, delta); } } break; } }
private bool Flipped(ref Vector3D p, int i0, int i1, ref Vertex v0, ref Vertex v1, IList <bool> deleted) { for (int i = 0; i < v0.tCount; ++i) { var t = triangles[refs[v0.tStart + i].tid]; if (t.deleted) { continue; } int s = refs[v0.tStart + i].tvertex; int id1 = t.v[(s + 1) % 3]; int id2 = t.v[(s + 2) % 3]; if (id1 == i1 || id2 == i1) { deleted[i] = true; continue; } Vector3D d1 = vertices[id1].p - p; d1.Normalize(); Vector3D d2 = vertices[id2].p - p; d2.Normalize(); if (SharedFunctions.DotProduct(ref d1, ref d2) > 0.999) { return(true); } var n = SharedFunctions.CrossProduct(ref d1, ref d2); n.Normalize(); deleted[i] = false; if (SharedFunctions.DotProduct(ref n, ref t.normal) < 0.2) { return(true); } } return(false); }
/// <summary> /// Adds a sphere. /// </summary> /// <param name="center"> /// The center of the sphere. /// </param> /// <param name="radius"> /// The radius of the sphere. /// </param> /// <param name="thetaDiv"> /// The number of divisions around the sphere. /// </param> /// <param name="phiDiv"> /// The number of divisions from top to bottom of the sphere. /// </param> public void AddSphere(Point3D center, double radius = 1, int thetaDiv = 32, int phiDiv = 32) { int index0 = this.positions.Count; float dt = (float)(2 * Math.PI / thetaDiv); float dp = (float)(Math.PI / phiDiv); for (int pi = 0; pi <= phiDiv; pi++) { float phi = pi * dp; for (int ti = 0; ti <= thetaDiv; ti++) { // we want to start the mesh on the x axis float theta = ti * dt; // Spherical coordinates // http://mathworld.wolfram.com/SphericalCoordinates.html //float x = (float)(Math.Cos(theta) * Math.Sin(phi)); //float y = (float)(Math.Sin(theta) * Math.Sin(phi)); //float z = (float)(Math.Cos(phi)); float x = (float)(Math.Sin(theta) * Math.Sin(phi)); float y = (float)(Math.Cos(phi)); float z = (float)(Math.Cos(theta) * Math.Sin(phi)); var p = new Point3D(center.X + ((float)radius * x), center.Y + ((float)radius * y), center.Z + ((float)radius * z)); this.positions.Add(p); if (this.normals != null) { var n = new Vector3D(x, y, z); this.normals.Add(n); } if (this.textureCoordinates != null) { var uv = new Point((float)(theta / (2 * Math.PI)), (float)(phi / Math.PI)); this.textureCoordinates.Add(uv); } } } this.AddRectangularMeshTriangleIndices(index0, phiDiv + 1, thetaDiv + 1, true); Console.WriteLine(); }
/// <summary> /// Adds a triangle. /// </summary> /// <param name="p0"> /// The first point. /// </param> /// <param name="p1"> /// The second point. /// </param> /// <param name="p2"> /// The third point. /// </param> /// <param name="uv0"> /// The first texture coordinate. /// </param> /// <param name="uv1"> /// The second texture coordinate. /// </param> /// <param name="uv2"> /// The third texture coordinate. /// </param> public void AddTriangle(Point3D p0, Point3D p1, Point3D p2, Point uv0, Point uv1, Point uv2) { int i0 = this.positions.Count; this.positions.Add(p0); this.positions.Add(p1); this.positions.Add(p2); if (this.textureCoordinates != null) { this.textureCoordinates.Add(uv0); this.textureCoordinates.Add(uv1); this.textureCoordinates.Add(uv2); } if (this.normals != null) { var w = Vector3D.Cross(p1 - p0, p2 - p0); w.Normalize(); this.normals.Add(w); this.normals.Add(w); this.normals.Add(w); } this.triangleIndices.Add(i0 + 0); this.triangleIndices.Add(i0 + 1); this.triangleIndices.Add(i0 + 2); }
/// <summary> /// Adds a triangle. /// </summary> /// <param name="p0"> /// The first point. /// </param> /// <param name="p1"> /// The second point. /// </param> /// <param name="p2"> /// The third point. /// </param> public void AddTriangle(Point3D p0, Point3D p1, Point3D p2) { var uv0 = new Point(0, 0); var uv1 = new Point(1, 0); var uv2 = new Point(0, 1); this.AddTriangle(p0, p1, p2, uv0, uv1, uv2); }
/// <summary> /// /// </summary> /// <param name="viewport"></param> /// <param name="p"></param> /// <param name="position"></param> /// <param name="normal"></param> /// <returns></returns> public static Vector3?UnProjectOnPlane(this Viewport3DX viewport, Vector2 p, Vector3 position, Vector3 normal) { var plane = new Plane(position, normal); return(UnProjectOnPlane(viewport, p, plane)); }
private static void ComputeNormals(List<Vector3D> positions, List<int> triangleIndices, out Vector3D[] normals) { //var normals = new List<Vector3D>(positions.Count); normals = new Point3D[positions.Count]; for (int t = 0; t < triangleIndices.Count; t += 3) { var i1 = triangleIndices[t]; var i2 = triangleIndices[t + 1]; var i3 = triangleIndices[t + 2]; var v1 = positions[i1]; var v2 = positions[i2]; var v3 = positions[i3]; var p1 = v2 - v1; var p2 = v3 - v1; var n = Vector3D.Cross(p1, p2); // angle p1.Normalize(); p2.Normalize(); var a = (float)Math.Acos(Vector3D.Dot(p1, p2)); n.Normalize(); normals[i1] += (a * n); normals[i2] += (a * n); normals[i3] += (a * n); } for (int i = 0; i < normals.Length; i++) { normals[i].Normalize(); } }
/// <summary> /// Adds a rectangular mesh defined by a two-dimensional arrary of points. /// </summary> /// <param name="points"> /// The points. /// </param> /// <param name="texCoords"> /// The texture coordinates (optional). /// </param> /// <param name="closed0"> /// set to <c>true</c> if the mesh is closed in the 1st dimension. /// </param> /// <param name="closed1"> /// set to <c>true</c> if the mesh is closed in the 2nd dimension. /// </param> public void AddRectangularMesh(Point3D[,] points, Point[,] texCoords = null, bool closed0 = false, bool closed1 = false) { if (points == null) { throw new ArgumentNullException("points"); } int rows = points.GetUpperBound(0) + 1; int columns = points.GetUpperBound(1) + 1; int index0 = this.positions.Count; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { this.positions.Add(points[i, j]); } } this.AddRectangularMeshTriangleIndices(index0, rows, columns, closed0, closed1); if (this.normals != null) { this.AddRectangularMeshNormals(index0, rows, columns); } if (this.textureCoordinates != null) { if (texCoords != null) { for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { this.textureCoordinates.Add(texCoords[i, j]); } } } else { this.AddRectangularMeshTextureCoordinates(rows, columns); } } }
/// <summary> /// Adds a regular icosahedron. /// </summary> /// <param name="center"> /// The center. /// </param> /// <param name="radius"> /// The radius. /// </param> /// <param name="shareVertices"> /// share vertices if set to <c>true</c> . /// </param> /// <remarks> /// See http://en.wikipedia.org/wiki/Icosahedron and http://www.gamedev.net/community/forums/topic.asp?topic_id=283350. /// </remarks> public void AddRegularIcosahedron(Point3D center, double radius, bool shareVertices) { float a = (float)Math.Sqrt(2.0 / (5.0 + Math.Sqrt(5.0))); float b = (float)Math.Sqrt(2.0 / (5.0 - Math.Sqrt(5.0))); var icosahedronIndices = new[] { 1, 4, 0, 4, 9, 0, 4, 5, 9, 8, 5, 4, 1, 8, 4, 1, 10, 8, 10, 3, 8, 8, 3, 5, 3, 2, 5, 3, 7, 2, 3, 10, 7, 10, 6, 7, 6, 11, 7, 6, 0, 11, 6, 1, 0, 10, 1, 6, 11, 0, 9, 2, 11, 9, 5, 2, 9, 11, 2, 7 }; var icosahedronVertices = new[] { new Vector3D(-a, 0, b), new Vector3D(a, 0, b), new Vector3D(-a, 0, -b), new Vector3D(a, 0, -b), new Vector3D(0, b, a), new Vector3D(0, b, -a), new Vector3D(0, -b, a), new Vector3D(0, -b, -a), new Vector3D(b, a, 0), new Vector3D(-b, a, 0), new Vector3D(b, -a, 0), new Vector3D(-b, -a, 0) }; if (shareVertices) { int index0 = this.positions.Count; foreach (var v in icosahedronVertices) { this.positions.Add(center + (v * (float)radius)); } foreach (int i in icosahedronIndices) { this.triangleIndices.Add(index0 + i); } } else { for (int i = 0; i + 2 < icosahedronIndices.Length; i += 3) { this.AddTriangle( center + (icosahedronVertices[icosahedronIndices[i]] * (float)radius), center + (icosahedronVertices[icosahedronIndices[i + 1]] * (float)radius), center + (icosahedronVertices[icosahedronIndices[i + 2]] * (float)radius)); } } }
private static void ComputeTangentsQuads(List<Point3D> positions, List<Point3D> normals, List<Point> textureCoordinates, List<int> indices, out List<Point3D> tangents, out List<Point3D> bitangents) { var tan1 = new Point3D[positions.Count]; for (int t = 0; t < indices.Count; t += 4) { var i1 = indices[t]; var i2 = indices[t + 1]; var i3 = indices[t + 2]; var i4 = indices[t + 3]; var v1 = positions[i1]; var v2 = positions[i2]; var v3 = positions[i3]; var v4 = positions[i4]; var w1 = textureCoordinates[i1]; var w2 = textureCoordinates[i2]; var w3 = textureCoordinates[i3]; var w4 = textureCoordinates[i4]; float x1 = v2.X - v1.X; float x2 = v4.X - v1.X; float y1 = v2.Y - v1.Y; float y2 = v4.Y - v1.Y; float z1 = v2.Z - v1.Z; float z2 = v4.Z - v1.Z; float s1 = w2.X - w1.X; float s2 = w4.X - w1.X; float t1 = w2.Y - w1.Y; float t2 = w4.Y - w1.Y; float r = 1.0f / (s1 * t2 - s2 * t1); var udir = new Vector3D((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); //var vdir = new Vector3D((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += udir; tan1[i2] += udir; tan1[i3] += udir; tan1[i4] += udir; //tan2[i1] += vdir; //tan2[i2] += vdir; //tan2[i3] += vdir; } tangents = new List<Point3D>(positions.Count); bitangents = new List<Point3D>(positions.Count); for (int i = 0; i < positions.Count; i++) { var n = normals[i]; var t = tan1[i]; t = (t - n * Vector3D.Dot(n, t)); t.Normalize(); var b = Vector3D.Cross(n, t); tangents.Add(t); bitangents.Add(b); } }
/// <summary> /// Adds a sphere (by subdiving a regular icosahedron). /// </summary> /// <param name="center"> /// The center of the sphere. /// </param> /// <param name="radius"> /// The radius of the sphere. /// </param> /// <param name="subdivisions"> /// The number of triangular subdivisions of the original icosahedron. /// </param> /// <remarks> /// See http://www.fho-emden.de/~hoffmann/ikos27042002.pdf. /// </remarks> public void AddSubdivisionSphere(Point3D center, double radius, int subdivisions) { int p0 = this.positions.Count; this.Append(GetUnitSphere(subdivisions)); int p1 = this.positions.Count; for (int i = p0; i < p1; i++) { this.positions[i] = center + ((float)radius * this.positions[i]); } }
private static void AppendSphere(Point3D center, double radius, int thetaSteps, int phiSteps, out List<Point3D> positions, out List<Point3D> normals, out List<Point> textureCoordinates, out List<int> triangleIndices) { positions = new List<Point3D>(); normals = new List<Point3D>(); textureCoordinates = new List<Point>(); triangleIndices = new List<int>(); double dt = DegToRad(360.0) / thetaSteps; double dp = DegToRad(180.0) / phiSteps; for (int pi = 0; pi <= phiSteps; pi++) { double phi = pi * dp; for (int ti = 0; ti <= thetaSteps; ti++) { // we want to start the mesh on the x axis double theta = ti * dt; positions.Add(GetPosition(theta, phi, radius) + center); normals.Add(GetNormal(theta, phi)); textureCoordinates.Add(GetTextureCoordinate(theta, phi)); } } for (int pi = 0; pi < phiSteps; pi++) { for (int ti = 0; ti < thetaSteps; ti++) { int x0 = ti; int x1 = ti + 1; int y0 = pi * (thetaSteps + 1); int y1 = (pi + 1) * (thetaSteps + 1); triangleIndices.Add(x0 + y0); triangleIndices.Add(x0 + y1); triangleIndices.Add(x1 + y0); triangleIndices.Add(x1 + y0); triangleIndices.Add(x0 + y1); triangleIndices.Add(x1 + y1); } } }
/// <summary> /// Tesselates the element and returns a MeshGeometry3D representing the /// tessellation based on the parameters given /// </summary> public void AppendSphere(Point3D center, double radius = 1, int thetaSteps = 64, int phiSteps = 64) { List<Point3D> pos, nor; List<Point> tcoord; List<int> tind; AppendSphere(center, radius, thetaSteps, phiSteps, out pos, out nor, out tcoord, out tind); int i0 = positions.Count; this.positions.AddRange(pos); this.normals.AddRange(nor); this.textureCoordinates.AddRange(tcoord); this.triangleIndices.AddRange(tind.Select(x => x + i0)); }
/// <summary> /// Adds an arrow to the mesh. /// </summary> /// <param name="point1"> /// The start point. /// </param> /// <param name="point2"> /// The end point. /// </param> /// <param name="diameter"> /// The diameter of the arrow cylinder. /// </param> /// <param name="headLength"> /// Length of the head (relative to diameter). /// </param> /// <param name="thetaDiv"> /// The number of divisions around the arrow. /// </param> public void AddArrow(Point3D point1, Point3D point2, double diameter, double headLength = 3, int thetaDiv = 18) { var dir = point2 - point1; var length = dir.Length(); var r = (float)diameter / 2; var pc = new PointCollection { new Point(0, 0), new Point(0, r), new Point(length - (float)(diameter * headLength), r), new Point(length - (float)(diameter * headLength), r * 2), new Point(length, 0) }; this.AddRevolvedGeometry(pc, point1, dir, thetaDiv); }
/// <summary> /// Adds a pyramid. /// </summary> /// <param name="center"> /// The center. /// </param> /// <param name="sideLength"> /// Length of the sides of the pyramid. /// </param> /// <param name="height"> /// The height of the pyramid. /// </param> /// <remarks> /// See http://en.wikipedia.org/wiki/Pyramid_(geometry). /// </remarks> public void AddPyramid(Point3D center, double sideLength, double height) { var p1 = new Point3D(center.X - (float)(sideLength * 0.5), center.Y - (float)(sideLength * 0.5), center.Z); var p2 = new Point3D(center.X + (float)(sideLength * 0.5), center.Y - (float)(sideLength * 0.5), center.Z); var p3 = new Point3D(center.X + (float)(sideLength * 0.5), center.Y + (float)(sideLength * 0.5), center.Z); var p4 = new Point3D(center.X - (float)(sideLength * 0.5), center.Y + (float)(sideLength * 0.5), center.Z); var p5 = new Point3D(center.X, center.Y, center.Z + (float)height); this.AddTriangle(p1, p2, p5); this.AddTriangle(p2, p3, p5); this.AddTriangle(p3, p4, p5); this.AddTriangle(p4, p1, p5); }
/// <summary> /// Adds a quadrilateral polygon. /// </summary> /// <param name="p0"> /// The first point. /// </param> /// <param name="p1"> /// The second point. /// </param> /// <param name="p2"> /// The third point. /// </param> /// <param name="p3"> /// The fourth point. /// </param> /// <remarks> /// See http://en.wikipedia.org/wiki/Quadrilateral. /// </remarks> public void AddQuad(Point3D p0, Point3D p1, Point3D p2, Point3D p3) { //// The nodes are arranged in counter-clockwise order //// p3 p2 //// +---------------+ //// | | //// | | //// +---------------+ //// p0 p1 var uv0 = new Point(0, 0); var uv1 = new Point(1, 0); var uv2 = new Point(0, 1); var uv3 = new Point(1, 1); this.AddQuad(p0, p1, p2, p3, uv0, uv1, uv2, uv3); }
/// <summary> /// Un-projects a 2D screen point. /// </summary> /// <param name="viewport">The viewport.</param> /// <param name="pointIn">The input point.</param> /// <param name="pointNear">The point at the near clipping plane.</param> /// <param name="pointFar">The point at the far clipping plane.</param> /// <returns>The ray.</returns> public static Ray UnProject(this Viewport3DX viewport, Vector2 point2d)//, out Vector3 pointNear, out Vector3 pointFar) { var camera = viewport.Camera as ProjectionCamera; if (camera != null) { var p = new Vector3((float)point2d.X, (float)point2d.Y, 1); //var wvp = GetViewProjectionMatrix(viewport); //Vector3 r = Vector3.Unproject(p, 0f, 0f, (float)viewport.ActualWidth, (float)viewport.ActualHeight, 0f, 1f, wvp); //r.Normalize(); var vp = GetScreenViewProjectionMatrix(viewport); var vpi = Matrix.Invert(vp); Vector3 zn, zf; p.Z = 0; Vector3.TransformCoordinate(ref p, ref vpi, out zn); p.Z = 1; Vector3.TransformCoordinate(ref p, ref vpi, out zf); Vector3 r = zf - zn; r.Normalize(); if (camera is PerspectiveCamera) { return new Ray(camera.Position.ToVector3(), r); } else if (camera is OrthographicCamera) { return new Ray(zn, r); } } throw new HelixToolkitException("Unproject camera error."); }
/// <summary> /// Adds a cube face. /// </summary> /// <param name="center"> /// The center of the cube. /// </param> /// <param name="normal"> /// The normal vector for the face. /// </param> /// <param name="up"> /// The up vector for the face. /// </param> /// <param name="dist"> /// The dist from the center of the cube to the face. /// </param> /// <param name="width"> /// The width of the face. /// </param> /// <param name="height"> /// The height of the face. /// </param> private void AddCubeFace(Point3D center, Vector3D normal, Vector3D up, double dist, double width, double height) { var right = Vector3D.Cross(normal, up); var n = normal * (float)dist / 2; up *= (float)height / 2f; right *= (float)width / 2f; var p1 = center + n - up - right; var p2 = center + n - up + right; var p3 = center + n + up + right; var p4 = center + n + up - right; int i0 = this.positions.Count; this.positions.Add(p1); this.positions.Add(p2); this.positions.Add(p3); this.positions.Add(p4); if (this.normals != null) { this.normals.Add(normal); this.normals.Add(normal); this.normals.Add(normal); this.normals.Add(normal); } if (this.textureCoordinates != null) { this.textureCoordinates.Add(new Point(1, 1)); this.textureCoordinates.Add(new Point(0, 1)); this.textureCoordinates.Add(new Point(0, 0)); this.textureCoordinates.Add(new Point(1, 0)); } this.triangleIndices.Add(i0 + 2); this.triangleIndices.Add(i0 + 1); this.triangleIndices.Add(i0 + 0); this.triangleIndices.Add(i0 + 0); this.triangleIndices.Add(i0 + 3); this.triangleIndices.Add(i0 + 2); }
public static Vector3? UnProjectOnPlane(this Viewport3DX viewport, Vector2 p, Vector3 position, Vector3 normal) { var plane = new Plane(position, normal); return UnProjectOnPlane(viewport, p, plane); }
/// <summary> /// Adds the edges of a bounding box as cylinders. /// </summary> /// <param name="boundingBox"> /// The bounding box. /// </param> /// <param name="diameter"> /// The diameter of the cylinders. /// </param> public void AddBoundingBox(System.Windows.Media.Media3D.Rect3D boundingBox, double diameter) { var p0 = new Point3D((float)boundingBox.X, (float)boundingBox.Y, (float)boundingBox.Z); var p1 = new Point3D((float)boundingBox.X, (float)boundingBox.Y + (float)boundingBox.SizeY, (float)boundingBox.Z); var p2 = new Point3D((float)boundingBox.X + (float)boundingBox.SizeX, (float)boundingBox.Y + (float)boundingBox.SizeY, (float)boundingBox.Z); var p3 = new Point3D((float)boundingBox.X + (float)boundingBox.SizeX, (float)boundingBox.Y, (float)boundingBox.Z); var p4 = new Point3D((float)boundingBox.X, (float)boundingBox.Y, (float)boundingBox.Z + (float)boundingBox.SizeZ); var p5 = new Point3D((float)boundingBox.X, (float)boundingBox.Y + (float)boundingBox.SizeY, (float)boundingBox.Z + (float)boundingBox.SizeZ); var p6 = new Point3D((float)boundingBox.X + (float)boundingBox.SizeX, (float)boundingBox.Y + (float)boundingBox.SizeY, (float)boundingBox.Z + (float)boundingBox.SizeZ); var p7 = new Point3D((float)boundingBox.X + (float)boundingBox.SizeX, (float)boundingBox.Y, (float)boundingBox.Z + (float)boundingBox.SizeZ); Action<Point3D, Point3D> addEdge = (c1, c2) => this.AddCylinder(c1, c2, diameter, 10); addEdge(p0, p1); addEdge(p1, p2); addEdge(p2, p3); addEdge(p3, p0); addEdge(p4, p5); addEdge(p5, p6); addEdge(p6, p7); addEdge(p7, p4); addEdge(p0, p4); addEdge(p1, p5); addEdge(p2, p6); addEdge(p3, p7); }
/// <summary> /// Adds a surface of revolution. /// </summary> /// <param name="points"> /// The points (y coordinates are radius, x coordinates are distance from the origin along the axis of revolution) /// </param> /// <param name="origin"> /// The origin of the revolution axis. /// </param> /// <param name="direction"> /// The direction of the revolution axis. /// </param> /// <param name="thetaDiv"> /// The number of divisions around the mesh. /// </param> /// <remarks> /// See http://en.wikipedia.org/wiki/Surface_of_revolution. /// </remarks> public void AddRevolvedGeometry(IList<Point> points, Point3D origin, Vector3D direction, int thetaDiv) { direction.Normalize(); // Find two unit vectors orthogonal to the specified direction var u = direction.FindAnyPerpendicular(); var v = Vector3D.Cross(direction, u); u.Normalize(); v.Normalize(); var circle = GetCircle(thetaDiv); int index0 = this.positions.Count; int n = points.Count; int totalNodes = (points.Count - 1) * 2 * thetaDiv; int rowNodes = (points.Count - 1) * 2; for (int i = 0; i < thetaDiv; i++) { var w = (v * circle[i].X) + (u * circle[i].Y); for (int j = 0; j + 1 < n; j++) { // Add segment var q1 = origin + (direction * points[j].X) + (w * points[j].Y); var q2 = origin + (direction * points[j + 1].X) + (w * points[j + 1].Y); // todo:should not add segment if q1==q2 (corner point) // const double eps = 1e-6; // if (Point3D.Subtract(q1, q2).LengthSquared < eps) // continue; float tx = points[j + 1].X - points[j].X; float ty = points[j + 1].Y - points[j].Y; var normal = (-direction * ty) + (w * tx); normal.Normalize(); this.positions.Add(q1); this.positions.Add(q2); if (this.normals != null) { this.normals.Add(normal); this.normals.Add(normal); } if (this.textureCoordinates != null) { this.textureCoordinates.Add(new Point((float)i / (thetaDiv - 1), (float)j / (n - 1))); this.textureCoordinates.Add(new Point((float)i / (thetaDiv - 1), (float)(j + 1) / (n - 1))); } int i0 = index0 + (i * rowNodes) + (j * 2); int i1 = i0 + 1; int i2 = index0 + ((((i + 1) * rowNodes) + (j * 2)) % totalNodes); int i3 = i2 + 1; this.triangleIndices.Add(i1); this.triangleIndices.Add(i0); this.triangleIndices.Add(i2); this.triangleIndices.Add(i1); this.triangleIndices.Add(i2); this.triangleIndices.Add(i3); } } }
public void AddFaceNY() { var positions = new Vector3D[] { new Vector3D(0,0,0), //p0 new Vector3D(0,0,1), //p1 new Vector3D(1,0,1), //p2 new Vector3D(1,0,0), //p3 }; var normals = new Vector3D[] { -Vector3D.UnitY, -Vector3D.UnitY, -Vector3D.UnitY, -Vector3D.UnitY, }; int i0 = this.positions.Count; var indices = new int[] { i0+0,i0+3,i0+2, i0+0,i0+2,i0+1, }; var texcoords = new Point[] { new Point(0,1), new Point(1,1), new Point(1,0), new Point(0,0), }; this.positions.AddRange(positions); this.normals.AddRange(normals); this.triangleIndices.AddRange(indices); this.textureCoordinates.AddRange(texcoords); }
/// <summary> /// Determines whether this polygon is planar. /// </summary> /// <returns> /// The is planar. /// </returns> public bool IsPlanar() { Vector3D v1 = this.Points[1] - this.Points[0]; var normal = new Vector3D(); for (int i = 2; i < this.Points.Count; i++) { var n = Vector3D.Cross(v1, this.Points[i] - this.Points[0]); n.Normalize(); if (i == 2) { normal = n; } else if (Math.Abs(Vector3D.Dot(n, normal) - 1) > 1e-8) { return false; } } return true; }
/// <summary> /// Chamfers the specified corner (experimental code). /// </summary> /// <param name="p"> /// The corner point. /// </param> /// <param name="d"> /// The chamfer distance. /// </param> /// <param name="eps"> /// The corner search limit distance. /// </param> /// <param name="chamferPoints"> /// If this parameter is provided, the collection will be filled with the generated chamfer points. /// </param> public void ChamferCorner(Point3D p, double d, double eps = 1e-6, IList<Point3D> chamferPoints = null) { this.NoSharedVertices(); this.normals = null; this.textureCoordinates = null; var cornerNormal = this.FindCornerNormal(p, eps); var newCornerPoint = p - (cornerNormal * (float)d); int index0 = this.positions.Count; this.positions.Add(newCornerPoint); var plane = new Plane3D(newCornerPoint, cornerNormal); int ntri = this.triangleIndices.Count; for (int i = 0; i < ntri; i += 3) { int i0 = i; int i1 = i + 1; int i2 = i + 2; var p0 = this.positions[this.triangleIndices[i0]]; var p1 = this.positions[this.triangleIndices[i1]]; var p2 = this.positions[this.triangleIndices[i2]]; var d0 = (p - p0).LengthSquared(); var d1 = (p - p1).LengthSquared(); var d2 = (p - p2).LengthSquared(); var mind = Math.Min(d0, Math.Min(d1, d2)); if (mind > eps) { continue; } if (d1 < eps) { i0 = i + 1; i1 = i + 2; i2 = i; } if (d2 < eps) { i0 = i + 2; i1 = i; i2 = i + 1; } p0 = this.positions[this.triangleIndices[i0]]; p1 = this.positions[this.triangleIndices[i1]]; p2 = this.positions[this.triangleIndices[i2]]; // p0 is the corner vertex (at index i0) // find the intersections between the chamfer plane and the two edges connected to the corner var line1 = new Ray(p0, p1 - p0); var line2 = new Ray(p0, p2 - p0); Point3D p01, p02; if (!plane.Intersects(ref line1, out p01)) { continue; } if (!plane.Intersects(ref line2, out p02)) { continue; } if (chamferPoints != null) { // add the chamfered points if (!chamferPoints.Contains(p01)) { chamferPoints.Add(p01); } if (!chamferPoints.Contains(p02)) { chamferPoints.Add(p02); } } int i01 = i0; // change the original triangle to use the first chamfer point this.positions[this.triangleIndices[i01]] = p01; int i02 = this.positions.Count; this.positions.Add(p02); // add a new triangle for the other chamfer point this.triangleIndices.Add(i01); this.triangleIndices.Add(i2); this.triangleIndices.Add(i02); // add a triangle connecting the chamfer points and the new corner point this.triangleIndices.Add(index0); this.triangleIndices.Add(i01); this.triangleIndices.Add(i02); } this.NoSharedVertices(); }
/// <summary> /// Subdivides each triangle into six triangles. Adds a vertex at the midpoint of each triangle. /// </summary> /// <remarks> /// See http://en.wikipedia.org/wiki/Barycentric_subdivision /// </remarks> private void SubdivideBarycentric() { // The BCS of a triangle S divides it into six triangles; each part has one vertex v2 at the // barycenter of S, another one v1 at the midpoint of some side, and the last one v0 at one // of the original vertices. int im = this.positions.Count; int ntri = this.triangleIndices.Count; for (int i = 0; i < ntri; i += 3) { int i0 = this.triangleIndices[i]; int i1 = this.triangleIndices[i + 1]; int i2 = this.triangleIndices[i + 2]; var p0 = this.positions[i0]; var p1 = this.positions[i1]; var p2 = this.positions[i2]; var v01 = p1 - p0; var v12 = p2 - p1; var v20 = p0 - p2; var p01 = p0 + (v01 * 0.5f); var p12 = p1 + (v12 * 0.5f); var p20 = p2 + (v20 * 0.5f); var m = new Point3D((p0.X + p1.X + p2.X) / 3, (p0.Y + p1.Y + p2.Y) / 3, (p0.Z + p1.Z + p2.Z) / 3); int i01 = im + 1; int i12 = im + 2; int i20 = im + 3; this.positions.Add(m); this.positions.Add(p01); this.positions.Add(p12); this.positions.Add(p20); if (this.normals != null) { var n = this.normals[i0]; this.normals.Add(n); this.normals.Add(n); this.normals.Add(n); this.normals.Add(n); } if (this.textureCoordinates != null) { var uv0 = this.textureCoordinates[i0]; var uv1 = this.textureCoordinates[i0 + 1]; var uv2 = this.textureCoordinates[i0 + 2]; var t01 = uv1 - uv0; var t12 = uv2 - uv1; var t20 = uv0 - uv2; var u01 = uv0 + (t01 * 0.5f); var u12 = uv1 + (t12 * 0.5f); var u20 = uv2 + (t20 * 0.5f); var uvm = new Point((uv0.X + uv1.X) * 0.5f, (uv0.Y + uv1.Y) * 0.5f); this.textureCoordinates.Add(uvm); this.textureCoordinates.Add(u01); this.textureCoordinates.Add(u12); this.textureCoordinates.Add(u20); } // TriangleIndices[i ] = i0; this.triangleIndices[i + 1] = i01; this.triangleIndices[i + 2] = im; this.triangleIndices.Add(i01); this.triangleIndices.Add(i1); this.triangleIndices.Add(im); this.triangleIndices.Add(i1); this.triangleIndices.Add(i12); this.triangleIndices.Add(im); this.triangleIndices.Add(i12); this.triangleIndices.Add(i2); this.triangleIndices.Add(im); this.triangleIndices.Add(i2); this.triangleIndices.Add(i20); this.triangleIndices.Add(im); this.triangleIndices.Add(i20); this.triangleIndices.Add(i0); this.triangleIndices.Add(im); im += 4; } }
/// <summary> /// Adds a box with the specifed faces, aligned with the X, Y and Z axes. /// </summary> /// <param name="center"> /// The center point of the box. /// </param> /// <param name="xlength"> /// The length of the box along the X axis. /// </param> /// <param name="ylength"> /// The length of the box along the Y axis. /// </param> /// <param name="zlength"> /// The length of the box along the Z axis. /// </param> /// <param name="faces"> /// The faces to include. /// </param> public void AddBox(Point3D center, double xlength, double ylength, double zlength, BoxFaces faces = BoxFaces.All) { if ((faces & BoxFaces.PositiveX) == BoxFaces.PositiveX) { this.AddCubeFace(center, new Vector3D(1, 0, 0), new Vector3D(0, 0, 1), xlength, ylength, zlength); } if ((faces & BoxFaces.NegativeX) == BoxFaces.NegativeX) { this.AddCubeFace(center, new Vector3D(-1, 0, 0), new Vector3D(0, 0, 1), xlength, ylength, zlength); } if ((faces & BoxFaces.NegativeY) == BoxFaces.NegativeY) { this.AddCubeFace(center, new Vector3D(0, -1, 0), new Vector3D(0, 0, 1), ylength, xlength, zlength); } if ((faces & BoxFaces.PositiveY) == BoxFaces.PositiveY) { this.AddCubeFace(center, new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), ylength, xlength, zlength); } if ((faces & BoxFaces.PositiveZ) == BoxFaces.PositiveZ) { this.AddCubeFace(center, new Vector3D(0, 0, 1), new Vector3D(0, 1, 0), zlength, xlength, ylength); } if ((faces & BoxFaces.NegativeZ) == BoxFaces.NegativeZ) { this.AddCubeFace(center, new Vector3D(0, 0, -1), new Vector3D(0, 1, 0), zlength, xlength, ylength); } }
/// <summary> /// Finds the average normal to the specified corner (experimental code). /// </summary> /// <param name="p"> /// The corner point. /// </param> /// <param name="eps"> /// The corner search limit distance. /// </param> /// <returns> /// The normal. /// </returns> private Vector3D FindCornerNormal(Point3D p, double eps) { var sum = new Vector3D(); int count = 0; var addedNormals = new HashSet<Vector3D>(); for (int i = 0; i < this.triangleIndices.Count; i += 3) { int i0 = i; int i1 = i + 1; int i2 = i + 2; var p0 = this.positions[this.triangleIndices[i0]]; var p1 = this.positions[this.triangleIndices[i1]]; var p2 = this.positions[this.triangleIndices[i2]]; // check if any of the vertices are on the corner double d0 = (p - p0).LengthSquared(); double d1 = (p - p1).LengthSquared(); double d2 = (p - p2).LengthSquared(); double mind = Math.Min(d0, Math.Min(d1, d2)); if (mind > eps) { continue; } // calculate the triangle normal and check if this face is already added var normal = Vector3D.Cross(p1 - p0, p2 - p0); normal.Normalize(); // todo: need to use the epsilon value to compare the normals? if (addedNormals.Contains(normal)) { continue; } // todo: this does not work yet // double dp = 1; // foreach (var n in addedNormals) // { // dp = Math.Abs(Vector3D.DotProduct(n, normal) - 1); // if (dp < eps) // continue; // } // if (dp < eps) // { // continue; // } count++; sum += normal; addedNormals.Add(normal); } if (count == 0) { return new Vector3D(); } return sum * (1.0f / count); }
/// <summary> /// Adds a box aligned with the X, Y and Z axes. /// </summary> /// <param name="center"> /// The center point of the box. /// </param> /// <param name="xlength"> /// The length of the box along the X axis. /// </param> /// <param name="ylength"> /// The length of the box along the Y axis. /// </param> /// <param name="zlength"> /// The length of the box along the Z axis. /// </param> public void AddBox(Point3D center, double xlength, double ylength, double zlength) { this.AddBox(center, (float)xlength, (float)ylength, (float)zlength, BoxFaces.All); }
/// <summary> /// Returns a line geometry of the axis-aligned bounding-box of the given mesh. /// </summary> /// <param name="mesh">Input mesh for the computation of the b-box</param> /// <returns></returns> public static LineGeometry3D GenerateBoundingBox(Vector3[] points) { var bb = global::SharpDX.BoundingBox.FromPoints(points); return GenerateBoundingBox(bb); }
/// <summary> /// Adds a quadrilateral polygon. /// </summary> /// <param name="p0"> /// The first point. /// </param> /// <param name="p1"> /// The second point. /// </param> /// <param name="p2"> /// The third point. /// </param> /// <param name="p3"> /// The fourth point. /// </param> /// <param name="uv0"> /// The first texture coordinate. /// </param> /// <param name="uv1"> /// The second texture coordinate. /// </param> /// <param name="uv2"> /// The third texture coordinate. /// </param> /// <param name="uv3"> /// The fourth texture coordinate. /// </param> /// <remarks> /// See http://en.wikipedia.org/wiki/Quadrilateral. /// </remarks> public void AddQuad(Point3D p0, Point3D p1, Point3D p2, Point3D p3, Point uv0, Point uv1, Point uv2, Point uv3) { //// The nodes are arranged in counter-clockwise order //// p3 p2 //// +---------------+ //// | | //// | | //// +---------------+ //// p0 p1 int i0 = this.positions.Count; this.positions.Add(p0); this.positions.Add(p1); this.positions.Add(p2); this.positions.Add(p3); if (this.textureCoordinates != null) { this.textureCoordinates.Add(uv0); this.textureCoordinates.Add(uv1); this.textureCoordinates.Add(uv2); this.textureCoordinates.Add(uv3); } if (this.normals != null) { var w = Vector3D.Cross(p3 - p0, p1 - p0); w.Normalize(); this.normals.Add(w); this.normals.Add(w); this.normals.Add(w); this.normals.Add(w); } this.triangleIndices.Add(i0 + 0); this.triangleIndices.Add(i0 + 1); this.triangleIndices.Add(i0 + 2); this.triangleIndices.Add(i0 + 2); this.triangleIndices.Add(i0 + 3); this.triangleIndices.Add(i0 + 0); }
/// <summary> /// Rotate the camera around the specified point. /// </summary> /// <param name="p0"> /// The p 0. /// </param> /// <param name="p1"> /// The p 1. /// </param> /// <param name="rotateAround"> /// The rotate around. /// </param> public void Rotate(Point p0, Point p1, Vector3 rotateAround) { Rotate(p0.ToVector2(), p1.ToVector2(), rotateAround); }
/// <summary> /// Generates a square grid with a step of 1.0 /// </summary> /// <returns></returns> public static LineGeometry3D GenerateGrid(Vector3 plane, int min = 0, int max = 10) { var grid = new LineBuilder(); //int width = max - min; if (plane == Vector3.UnitX) { for (int i = min; i <= max; i++) { grid.AddLine(new Vector3(0, i, min), new Vector3(0, i, max)); grid.AddLine(new Vector3(0, min, i), new Vector3(0, max, i)); } } else if (plane == Vector3.UnitY) { for (int i = min; i <= max; i++) { grid.AddLine(new Vector3(i, 0, min), new Vector3(i, 0, max)); grid.AddLine(new Vector3(min, 0, i), new Vector3(max, 0, i)); } } else { for (int i = min; i <= max; i++) { grid.AddLine(new Vector3(i, min, 0), new Vector3(i, max, 0)); grid.AddLine(new Vector3(min, i, 0), new Vector3(max, i, 0)); } } return grid.ToLineGeometry3D(); }