private void RandomCamera_Click(object sender, RoutedEventArgs e) { try { // Position Vector3D position = Math3D.GetRandomVector_Spherical(CAMERADISTANCE / 2, CAMERADISTANCE * 2); // Look Direction Vector3D lookDirection = position * -1; Vector3D rotateAxis = Math3D.GetRandomVector_Cone(Math3D.GetArbitraryOrhonganal(lookDirection), 0, 20, 1, 1); Quaternion rotate = new Quaternion(rotateAxis, Math1D.GetNearZeroValue(20)); lookDirection = rotate.GetRotatedVector(lookDirection); // Up Vector Vector3D up = Math3D.GetArbitraryOrhonganal(lookDirection); // Commit _camera.Position = position.ToPoint(); _camera.LookDirection = lookDirection; _camera.UpDirection = up; } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
private void ChangeDragPlane(RayHitTestParameters clickRay) { _dragHistory.Clear(); if (_selectedItem == null) { _dragPlane = null; return; } //NOTE: This was copied from Game.Newt.v2.GameItems.ShipEditor.Editor.ChangeDragHitShape() Point3D point = _selectedItem.Item.PositionWorld; RayHitTestParameters cameraLookCenter = UtilityWPF.RayFromViewportPoint(_camera, _viewport, new Point(_viewport.ActualWidth * .5d, _viewport.ActualHeight * .5d)); // Come up with the right plane Vector3D standard = Math3D.GetArbitraryOrhonganal(cameraLookCenter.Direction); Vector3D orth = Vector3D.CrossProduct(standard, cameraLookCenter.Direction); ITriangle plane = new Triangle(point, point + standard, point + orth); _dragPlane = new DragHitShape(); _dragPlane.SetShape_Plane(plane); _dragMouseDownClickRay = clickRay; _dragMouseDownCenterRay = new RayHitTestParameters(point, clickRay.Direction); //TODO: the ray through the center of the part really isn't parallel to the click ray (since the perspective camera sees in a cone) }
/// <summary> /// This is used to rotate a 2D icon onto 4 evenly distributed points around a sphere /// </summary> private static Transform3D[] GetRotations_Tetrahedron(double radius) { List <Transform3D> retVal = new List <Transform3D>(); Vector3D position = new Vector3D(radius, 0, 0); Vector3D right = new Vector3D(0, 1, 0); Vector3D up = new Vector3D(0, 0, 1); Tetrahedron tetra = UtilityWPF.GetTetrahedron(1); foreach (Point3D dir in tetra.AllPoints) { Quaternion randRot = Math3D.GetRotation(right, Math3D.GetArbitraryOrhonganal(position)); // give it a random spin so that the final icons aren't semi lined up Quaternion majorRot = Math3D.GetRotation(position, dir.ToVector()); Transform3DGroup transform = new Transform3DGroup(); transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(randRot))); transform.Children.Add(new RotateTransform3D(new QuaternionRotation3D(majorRot))); retVal.Add(transform); } return(retVal.ToArray()); }
private void grdViewPort_MouseDown(object sender, MouseButtonEventArgs e) { const double CLICKDISTANCE = BOUNDRYSIZE / 8; try { if (e.ChangedButton != MouseButton.Left) { // All the special logic in this method is for the left button return; } // Fire a ray at the mouse point Point clickPoint2D = e.GetPosition(grdViewPort); RayHitTestParameters clickRay; List <MyHitTestResult> hits = UtilityWPF.CastRay(out clickRay, clickPoint2D, _viewport, _camera, _viewport, false); // Figure out where to place the point Point3D clickPoint3D; ITriangle clickPlane = null; if (hits != null && hits.Count > 0) { // They clicked on something, so use that clickPoint3D = hits[0].Point; } else { //TODO: If there is a mothership, choose a point in a plane that goes through it (orth to the click ray) // // If there isn't, but they click near a swarm, use that plane // // Or if they click near something (like an asteroid) double?clickDistNearSwarm = GetClickDistanceNearSwarm(clickRay); double clickDist; if (clickDistNearSwarm == null) { clickDist = Math.Max(CLICKDISTANCE, _camera.Position.ToVector().Length / 1.1); } else { clickDist = clickDistNearSwarm.Value; } clickPoint3D = clickRay.Origin + clickRay.Direction.ToUnit() * clickDist; } // Update the click plane if (clickPlane == null) { Vector3D standard = Math3D.GetArbitraryOrhonganal(clickRay.Direction); Vector3D orth = Vector3D.CrossProduct(standard, clickRay.Direction); clickPlane = new Triangle(clickPoint3D, clickPoint3D + standard, clickPoint3D + orth); } // Store the point and plane _strokes.AddPointToStroke(clickPoint3D); _clickPlane = clickPlane; } catch (Exception ex) { MessageBox.Show(ex.ToString(), this.Title, MessageBoxButton.OK, MessageBoxImage.Error); } }
/// <summary> /// This overload is the same as the previous, but if the camera is looking along the plane/cylinder wall, then the output will be /// constrained to a line/circle /// NOTE: Sphere and circle will never be constrained, the lesser overload will be used instead /// </summary> public Point3D?CastRay(RayHitTestParameters mouseDownClickRay, RayHitTestParameters mouseDownCenterRay, RayHitTestParameters currentClickRay, PerspectiveCamera camera, Viewport3D viewport) { if (_shape == ShapeType.None) { return(null); } else if (_shape == ShapeType.Sphere || _shape == ShapeType.Circle || _shape == ShapeType.Circles) { return(CastRay(mouseDownClickRay, mouseDownCenterRay, currentClickRay)); } #region Get dot product //NOTE: _camera.LookDirection and _camera.UpDirection are really screwed up (I think that the trackball messed them up), so fire a ray instead // I'm not using the mouse click point, because that can change as they drag, and the inconsistency would be jarring RayHitTestParameters cameraLook = UtilityWPF.RayFromViewportPoint(camera, viewport, new Point(viewport.ActualWidth / 2d, viewport.ActualHeight / 2d)); double dot = 0; double[] dots = null; Vector3D cameraLookUnit = cameraLook.Direction.ToUnit(); switch (_shape) { case ShapeType.Plane: dot = Vector3D.DotProduct(_plane.NormalUnit, cameraLookUnit); // the dot is against the normal break; case ShapeType.Line: case ShapeType.Cylinder: //NOTE: They cylinder only limits movement if they are looking along the line. dot = Vector3D.DotProduct(_direction.ToUnit(), cameraLookUnit); // the dot is along the drag line break; case ShapeType.Lines: case ShapeType.LinesCircles: // I don't care about the dot product for the circles, they aren't limited dots = _lines.Select(o => Vector3D.DotProduct(o.Direction.ToUnit(), cameraLookUnit)).ToArray(); break; default: throw new ApplicationException("finish this"); } #endregion Point3D?retVal = null; switch (_shape) { case ShapeType.Line: #region Line if (Math.Abs(dot) > _constrainMaxDotProduct) { retVal = null; } else { double dummy1; retVal = CastRay_Line(out dummy1, _point, _direction, mouseDownClickRay, mouseDownCenterRay, currentClickRay); } #endregion break; case ShapeType.Lines: case ShapeType.Circles: case ShapeType.LinesCircles: #region Lines/Circles //NOTE: These have to look at _shape instead of null checks, because the values may be non null from a previous use List <RayHitTestParameters> usableLines = new List <RayHitTestParameters>(); if (_shape == ShapeType.Lines || _shape == ShapeType.LinesCircles) { for (int cntr = 0; cntr < _lines.Length; cntr++) { if (Math.Abs(dots[cntr]) <= _constrainMaxDotProduct) { usableLines.Add(_lines[cntr]); } } } CircleDefinition[] usableCircles = null; if (_shape == ShapeType.Circles || _shape == ShapeType.LinesCircles) { usableCircles = _circles; // all circles are always used } retVal = CastRay_LinesCircles(usableLines, usableCircles, mouseDownClickRay, mouseDownCenterRay, currentClickRay); #endregion break; case ShapeType.Plane: #region Plane if (Math.Abs(dot) < 1d - _constrainMaxDotProduct) { retVal = CastRay_PlaneLimited(_plane, mouseDownClickRay, mouseDownCenterRay, currentClickRay, cameraLook); } else { retVal = CastRay_Plane(_plane, mouseDownClickRay, mouseDownCenterRay, currentClickRay); } #endregion break; case ShapeType.Cylinder: #region Cylinder if (Math.Abs(dot) > _constrainMaxDotProduct) { // Constrain to a circle Vector3D circleVector1 = Math3D.GetArbitraryOrhonganal(_direction); Vector3D circleVector2 = Vector3D.CrossProduct(circleVector1, _direction); Triangle plane = new Triangle(_point, _point + circleVector1, _point + circleVector2); double dummy1; retVal = CastRay_Circle(out dummy1, plane, _point, _radius, mouseDownClickRay, mouseDownCenterRay, currentClickRay); } else { retVal = CastRay_Cylinder(_point, _direction, _radius, mouseDownClickRay, mouseDownCenterRay, currentClickRay); } #endregion break; case ShapeType.Mesh: throw new ApplicationException("finish this"); case ShapeType.None: retVal = null; break; default: throw new ApplicationException("Unknown ShapeType: " + _shape.ToString()); } return(retVal); }
private static MeshGeometry3D GetArrowMesh(Point3D from, Point3D to, double thickness) { double half = thickness / 2d; Vector3D line = to - from; if (line.X == 0 && line.Y == 0 && line.Z == 0) { line.X = 0.000000001d; } Vector3D orth1 = Math3D.GetArbitraryOrhonganal(line); orth1 = Math3D.RotateAroundAxis(orth1, line, StaticRandom.NextDouble() * Math.PI * 2d); // give it a random rotation so that if many lines are created by this method, they won't all be oriented the same orth1 = orth1.ToUnit() * half; Vector3D orth2 = Vector3D.CrossProduct(line, orth1); orth2 = orth2.ToUnit() * half; // Define 3D mesh object MeshGeometry3D retVal = new MeshGeometry3D(); // Arrow Base retVal.Positions.Add(from - orth1); // 0 retVal.Positions.Add(from + orth1); // 1 retVal.Positions.Add(from - orth2); // 2 retVal.Positions.Add(from + orth2); // 3 // Arrow Tip retVal.Positions.Add(to); // 4 // Tip Faces retVal.TriangleIndices.Add(0); retVal.TriangleIndices.Add(3); retVal.TriangleIndices.Add(4); retVal.TriangleIndices.Add(3); retVal.TriangleIndices.Add(1); retVal.TriangleIndices.Add(4); retVal.TriangleIndices.Add(1); retVal.TriangleIndices.Add(2); retVal.TriangleIndices.Add(4); retVal.TriangleIndices.Add(2); retVal.TriangleIndices.Add(0); retVal.TriangleIndices.Add(4); // Base Faces //NOTE: These lines need to use as few triangles as possible, and they will almost certainly leave IsShiny false. So a backplate isn't really needed //retVal.TriangleIndices.Add(0); //retVal.TriangleIndices.Add(2); //retVal.TriangleIndices.Add(1); //retVal.TriangleIndices.Add(1); //retVal.TriangleIndices.Add(3); //retVal.TriangleIndices.Add(0); // shouldn't I set normals? //retVal.Normals //retVal.Freeze(); return(retVal); }
private void DrawLines_Plate(int numSamples, double half, double lineThickness, AxisFor axisX, AxisFor axisY, AxisFor axisZ) { const double ELAPSEDURATIONSECONDS = 1; // Figure out how wide to make the plate int totalSamples = numSamples * numSamples * numSamples; // numsamples is per axis, so cube it int cellsPerSlice = _field.Size * _field.Size; int numSlices = Convert.ToInt32(Math.Round(Convert.ToDouble(totalSamples) / Convert.ToDouble(cellsPerSlice))); if (numSlices == 0) { numSlices = 1; } int toOffset = numSlices / 2; int fromOffset = numSlices - toOffset - 1; DateTime now = DateTime.UtcNow; bool isOverField = false; if (_mousePoint != null) { #region Snap to mouse // Cast a ray (Copied this from ItemSelectDragLogic.ChangeDragPlane, DragItem) Point3D point = new Point3D(0, 0, 0); RayHitTestParameters cameraLookCenter = UtilityWPF.RayFromViewportPoint(_camera, _viewport, new Point(_viewport.ActualWidth * .5d, _viewport.ActualHeight * .5d)); // Come up with a snap plane Vector3D standard = Math3D.GetArbitraryOrhonganal(cameraLookCenter.Direction); Vector3D orth = Vector3D.CrossProduct(standard, cameraLookCenter.Direction); ITriangle plane = new Triangle(point, point + standard, point + orth); DragHitShape dragPlane = new DragHitShape(); dragPlane.SetShape_Plane(plane); // Cast a ray onto that plane from the current mouse position RayHitTestParameters mouseRay = UtilityWPF.RayFromViewportPoint(_camera, _viewport, _mousePoint.Value); Point3D?hitPoint = dragPlane.CastRay(mouseRay); if (hitPoint != null) { // Find the nearest Z cell double halfSize = (_field.Size * _sizeMult) / 2d; double cellSize = (_field.Size * _sizeMult) / _field.Size; int zIndex = Convert.ToInt32((halfSize - axisZ.GetValue(hitPoint.Value)) / cellSize); if (zIndex >= 0 && zIndex < _field.Size) { isOverField = true; // Cap to field _plateCurrentIndex = _field.Size - zIndex; // it's actually the opposite if (_plateCurrentIndex - fromOffset < 0) { _plateCurrentIndex = fromOffset; } else if (_plateCurrentIndex + toOffset > _field.Size - 1) { _plateCurrentIndex = _field.Size - toOffset - 1; } _sceneRemaining = now + TimeSpan.FromSeconds(ELAPSEDURATIONSECONDS); } } #endregion } if (!isOverField) { #region Shift the plate if (_plateCurrentIndex + toOffset > _field.Size - 1) { _plateCurrentIndex = _field.Size - toOffset - 1; _sceneRemaining = now + TimeSpan.FromSeconds(ELAPSEDURATIONSECONDS); } else if (now > _sceneRemaining) { _plateCurrentIndex--; if (_plateCurrentIndex - fromOffset <= 0) { _plateCurrentIndex = _field.Size - toOffset - 1; } _sceneRemaining = now + TimeSpan.FromSeconds(ELAPSEDURATIONSECONDS); } #endregion } double[] velX = _field.VelocityX; double[] velY = _field.VelocityY; double[] velZ = _field.VelocityZ; bool[] blocked = _field.Blocked; _velocityLines.BeginAddingLines(); for (int z = _plateCurrentIndex - fromOffset; z <= _plateCurrentIndex + toOffset; z++) { for (int x = 0; x < _field.Size; x++) { for (int y = 0; y < _field.Size; y++) { int xRef = -1; int yRef = -1; int zRef = -1; axisX.Set3DIndex(ref xRef, ref yRef, ref zRef, x); axisY.Set3DIndex(ref xRef, ref yRef, ref zRef, y); axisZ.Set3DIndex(ref xRef, ref yRef, ref zRef, z); int index1D = _field.Get1DIndex(xRef, yRef, zRef); if (blocked[index1D]) { continue; } DrawLinesSprtAddLine(xRef, yRef, zRef, index1D, half, lineThickness, velX, velY, velZ); } } } _velocityLines.EndAddingLines(); }