private static bool ConstructPlaneIntersection(Vector3 Position, Matrix4x4 viewProjection, Ray ray, Vector3 perpendicularVector0, Vector3 perpendicularVector1, out Vector3 intersection) { // Choose the perpendicular plane that is more parallel to the camera plane to // maximize the available accuracy in the view space. Vector3 viewDirection = new Vector3(viewProjection.M31, viewProjection.M32, viewProjection.M33); float perpendicularVector0Dot = Math.Abs(Vector3.Dot(viewDirection, perpendicularVector0)); float perpendicularVector1Dot = Math.Abs(Vector3.Dot(viewDirection, perpendicularVector1)); Plane plane = MathC.CreatePlaneAtPoint(Position, perpendicularVector0Dot > perpendicularVector1Dot ? perpendicularVector0 : perpendicularVector1); // Construct intersection return(Collision.RayIntersectsPlane(ray, plane, out intersection)); }
public PickingResultGizmo DoPicking(Ray ray) { if (!DrawGizmo) { return(null); } bool upside = Orientation == GizmoOrientation.UpsideDown; // Check for translation if (SupportTranslateX) { float unused; BoundingSphere sphereX = new BoundingSphere(Position + Vector3.UnitX * (upside ? -Size : Size) * _arrowHeadOffsetMultiplier, TranslationConeSize / 1.5f); if (Collision.RayIntersectsSphere(ray, sphereX, out unused)) { return(new PickingResultGizmo(GizmoMode.TranslateX)); } } if (SupportTranslateY) { float unused; BoundingSphere sphereY = new BoundingSphere(Position + Vector3.UnitY * (upside ? -Size : Size) * _arrowHeadOffsetMultiplier, TranslationConeSize / 1.5f); if (Collision.RayIntersectsSphere(ray, sphereY, out unused)) { return(new PickingResultGizmo(GizmoMode.TranslateY)); } } if (SupportTranslateZ) { float unused; BoundingSphere sphereZ = new BoundingSphere(Position - Vector3.UnitZ * (upside ? -Size : Size) * _arrowHeadOffsetMultiplier, TranslationConeSize / 1.5f); if (Collision.RayIntersectsSphere(ray, sphereZ, out unused)) { return(new PickingResultGizmo(GizmoMode.TranslateZ)); } } // Check for scale if (SupportScale) { float unused; BoundingBox scaleX = new BoundingBox(Position + Vector3.UnitX * (upside ? -Size : Size) / 2.0f - new Vector3(ScaleCubeSize / 2.0f), Position + Vector3.UnitX * (upside ? -Size : Size) / 2.0f + new Vector3(ScaleCubeSize / 2.0f)); if (Collision.RayIntersectsBox(ray, scaleX, out unused)) { return(new PickingResultGizmo(GizmoMode.ScaleX)); } BoundingBox scaleY = new BoundingBox(Position + Vector3.UnitY * (upside ? -Size : Size) / 2.0f - new Vector3(ScaleCubeSize / 2.0f), Position + Vector3.UnitY * (upside ? -Size : Size) / 2.0f + new Vector3(ScaleCubeSize / 2.0f)); if (Collision.RayIntersectsBox(ray, scaleY, out unused)) { return(new PickingResultGizmo(GizmoMode.ScaleY)); } BoundingBox scaleZ = new BoundingBox(Position - Vector3.UnitZ * (upside ? -Size : Size) / 2.0f - new Vector3(ScaleCubeSize / 2.0f), Position - Vector3.UnitZ * (upside ? -Size : Size) / 2.0f + new Vector3(ScaleCubeSize / 2.0f)); if (Collision.RayIntersectsBox(ray, scaleZ, out unused)) { return(new PickingResultGizmo(GizmoMode.ScaleZ)); } } // Check for rotation float pickRadius = LineThickness / 2 + (Size * 0.045f); if (SupportRotationZ) { Plane planeZ = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitZ, RotateMatrixZ)); Vector3 intersectionPoint; if (Collision.RayIntersectsPlane(ray, planeZ, out intersectionPoint)) { var distance = (intersectionPoint - Position).Length(); if (distance >= Size - pickRadius && distance <= Size + pickRadius) { Vector3 startDirection = Vector3.Normalize(intersectionPoint - Position); float sin = Vector3.Dot(Vector3.UnitY, startDirection); float cos = Vector3.Dot(planeZ.Normal, Vector3.Cross(Vector3.UnitY, startDirection)); return(new PickingResultGizmo(GizmoMode.RotateZ, (float)Math.Atan2(-sin, cos), distance)); } } } if (SupportRotationX) { Plane planeX = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitX, RotateMatrixX)); Vector3 intersectionPoint; if (Collision.RayIntersectsPlane(ray, planeX, out intersectionPoint)) { var distance = (intersectionPoint - Position).Length(); if (distance >= Size - pickRadius && distance <= Size + pickRadius) { Vector3 startDirection = Vector3.Normalize(intersectionPoint - Position); float sin = Vector3.Dot(Vector3.UnitY, startDirection); float cos = Vector3.Dot(planeX.Normal, Vector3.Cross(Vector3.UnitY, startDirection)); return(new PickingResultGizmo(GizmoMode.RotateX, (float)Math.Atan2(-sin, cos), distance)); } } } if (SupportRotationY) { Plane planeY = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitY, RotateMatrixY)); Vector3 intersectionPoint; if (Collision.RayIntersectsPlane(ray, planeY, out intersectionPoint)) { var distance = (intersectionPoint - Position).Length(); if (distance >= Size - pickRadius && distance <= Size + pickRadius) { Vector3 startDirection = Vector3.Normalize(intersectionPoint - Position); float sin = Vector3.Dot(Vector3.UnitZ, startDirection); float cos = Vector3.Dot(planeY.Normal, Vector3.Cross(Vector3.UnitZ, startDirection)); return(new PickingResultGizmo(GizmoMode.RotateY, (float)Math.Atan2(-sin, cos), distance)); } } } return(null); }
/// <returns>true, if an iteraction with the gizmo is happening</returns> public bool MouseMoved(Matrix4x4 viewProjection, Ray ray) { if (!DrawGizmo || _mode == GizmoMode.None) { return(false); } bool upside = Orientation == GizmoOrientation.UpsideDown; bool flippedScale = false; // Flip sizing dimensions if object is rotateable on Y axis if ((_mode == GizmoMode.ScaleX || _mode == GizmoMode.ScaleZ) && SupportRotationY) { flippedScale = MathC.RadToDeg(RotationY) % 180.0f >= 45.0f; } // First get the ray in 3D space from X, Y mouse coordinates switch (_mode) { case GizmoMode.TranslateX: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitY, Vector3.UnitZ, out intersection)) { GizmoMove(new Vector3(intersection.X - (upside ? -Size : Size) * _arrowHeadOffsetMultiplier, Position.Y, Position.Z)); GizmoMoveDelta(new Vector3(intersection.X - (upside ? -Size : Size) * _arrowHeadOffsetMultiplier - Position.X, 0.0f, 0.0f)); } } break; case GizmoMode.TranslateY: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitX, Vector3.UnitZ, out intersection)) { GizmoMove(new Vector3(Position.X, intersection.Y - (upside ? -Size : Size) * _arrowHeadOffsetMultiplier, Position.Z)); GizmoMoveDelta(new Vector3(0.0f, intersection.Y - (upside ? -Size : Size) * _arrowHeadOffsetMultiplier - Position.Y, 0.0f)); } } break; case GizmoMode.TranslateZ: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitX, Vector3.UnitY, out intersection)) { GizmoMove(new Vector3(Position.X, Position.Y, intersection.Z + (upside ? -Size : Size) * _arrowHeadOffsetMultiplier)); GizmoMoveDelta(new Vector3(0.0f, 0.0f, intersection.Z + (upside ? -Size : Size) * _arrowHeadOffsetMultiplier - Position.Z)); } } break; case GizmoMode.ScaleX: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitY, Vector3.UnitZ, out intersection)) { if (flippedScale) { GizmoScaleZ(_scaleBase.Z * (float)Math.Exp(_scaleSpeed * (intersection.X - Position.X))); } else { GizmoScaleX(_scaleBase.X * (float)Math.Exp(_scaleSpeed * (intersection.X - Position.X))); } } } break; case GizmoMode.ScaleY: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitX, Vector3.UnitZ, out intersection)) { GizmoScaleY(_scaleBase.Y * (float)Math.Exp(_scaleSpeed * (intersection.Y - Position.Y))); } } break; case GizmoMode.ScaleZ: { Vector3 intersection; if (ConstructPlaneIntersection(Position, viewProjection, ray, Vector3.UnitX, Vector3.UnitY, out intersection)) { if (flippedScale) { GizmoScaleX(_scaleBase.X * (float)Math.Exp(_scaleSpeed * -(intersection.Z - Position.Z))); } else { GizmoScaleZ(_scaleBase.Z * (float)Math.Exp(_scaleSpeed * -(intersection.Z - Position.Z))); } } } break; case GizmoMode.RotateY: { Plane rotationPlane = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitY, RotateMatrixY)); Vector3 rotationIntersection; if (Collision.RayIntersectsPlane(ray, rotationPlane, out rotationIntersection)) { Vector3 direction = rotationIntersection - Position; _rotationLastMouseRadius = direction.Length(); direction = Vector3.Normalize(direction); float sin = Vector3.Dot(Vector3.UnitZ, direction); float cos = Vector3.Dot(rotationPlane.Normal, Vector3.Cross(Vector3.UnitZ, direction)); _rotationLastMouseAngle = (float)Math.Atan2(-sin, cos); GizmoRotateY(SimplifyAngle(_rotationPickAngleOffset + _rotationLastMouseAngle)); } } break; case GizmoMode.RotateX: { Plane rotationPlane = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitX, RotateMatrixX)); Vector3 rotationIntersection; if (Collision.RayIntersectsPlane(ray, rotationPlane, out rotationIntersection)) { Vector3 direction = rotationIntersection - Position; _rotationLastMouseRadius = direction.Length(); direction = Vector3.Normalize(direction); float sin = Vector3.Dot(Vector3.UnitY, direction); float cos = Vector3.Dot(rotationPlane.Normal, Vector3.Cross(Vector3.UnitY, direction)); _rotationLastMouseAngle = (float)Math.Atan2(-sin, cos); GizmoRotateX(SimplifyAngle(_rotationPickAngleOffset + _rotationLastMouseAngle)); } } break; case GizmoMode.RotateZ: { Plane rotationPlane = MathC.CreatePlaneAtPoint(Position, MathC.HomogenousTransform(Vector3.UnitZ, RotateMatrixZ)); Vector3 rotationIntersection; if (Collision.RayIntersectsPlane(ray, rotationPlane, out rotationIntersection)) { Vector3 direction = rotationIntersection - Position; _rotationLastMouseRadius = direction.Length(); direction = Vector3.Normalize(direction); float sin = Vector3.Dot(Vector3.UnitY, direction); float cos = Vector3.Dot(rotationPlane.Normal, Vector3.Cross(Vector3.UnitY, direction)); _rotationLastMouseAngle = (float)Math.Atan2(-sin, cos); GizmoRotateZ(SimplifyAngle(_rotationPickAngleOffset + _rotationLastMouseAngle)); } } break; } return(true); }