private WActorNode Raycast(FRay ray) { if (m_world.Map == null) { return(null); } WActorNode closestResult = null; float closestDistance = float.MaxValue; foreach (var scene in m_world.Map.SceneList) { List <WActorNode> allActors = scene.GetChildrenOfType <WActorNode>(); foreach (WActorNode actorNode in allActors) { float intersectDistance; bool hitActor = actorNode.Raycast(ray, out intersectDistance); if (hitActor) { if (intersectDistance >= 0 && intersectDistance < closestDistance) { closestDistance = intersectDistance; closestResult = actorNode; } } } } return(closestResult); }
public bool Raycast(FRay ray, out float closestDistance) { // Convert the ray to local space of this node since all of our raycasts are local. FRay localRay = WMath.TransformRay(ray, Transform.Position, Transform.LocalScale, Transform.Rotation.Inverted()); bool bHit = false; if (m_actorMesh != null) { bHit = m_actorMesh.Raycast(localRay, out closestDistance, true); } else { bHit = WMath.RayIntersectsAABB(localRay, m_objRender.GetAABB().Min, m_objRender.GetAABB().Max, out closestDistance); } if (bHit) { // Convert the hit point back to world space... Vector3 localHitPoint = localRay.Origin + (localRay.Direction * closestDistance); localHitPoint = Vector3.Transform(localHitPoint + Transform.Position, Transform.Rotation); // Now get the distance from the original ray origin and the new worldspace hit point. closestDistance = (localHitPoint - ray.Origin).Length; } return(bHit); }
public static bool RayIntersectsAABB(FRay ray, Vector3 aabbMin, Vector3 aabbMax, out float intersectionDistance) { Vector3 t_1 = new Vector3(), t_2 = new Vector3(); float tNear = float.MinValue; float tFar = float.MaxValue; // Test infinite planes in each directin. for (int i = 0; i < 3; i++) { // Ray is parallel to planes in this direction. if (ray.Direction[i] == 0) { if ((ray.Origin[i] < aabbMin[i]) || (ray.Origin[i] > aabbMax[i])) { // Parallel and outside of the box, thus no intersection is possible. intersectionDistance = float.MinValue; return(false); } } else { t_1[i] = (aabbMin[i] - ray.Origin[i]) / ray.Direction[i]; t_2[i] = (aabbMax[i] - ray.Origin[i]) / ray.Direction[i]; // Ensure T_1 holds values for intersection with near plane. if (t_1[i] > t_2[i]) { Vector3 temp = t_2; t_2 = t_1; t_1 = temp; } if (t_1[i] > tNear) { tNear = t_1[i]; } if (t_2[i] < tFar) { tFar = t_2[i]; } if ((tNear > tFar) || (tFar < 0)) { intersectionDistance = float.MinValue; return(false); } } } intersectionDistance = tNear; return(true); }
private void CheckForObjectSelectionChange(WSceneView view) { // If we have a gizmo and we're transforming it, don't check for selection change. if (m_transformGizmo != null && m_transformGizmo.IsTransforming) { return; } if (WInput.GetMouseButtonDown(0) && !WInput.GetMouseButton(1)) { FRay mouseRay = view.ProjectScreenToWorld(WInput.MousePosition); WActorNode addedActor = Raycast(mouseRay); // Check the behaviour of this click to determine appropriate selection modification behaviour. // Click w/o Modifiers = Clear Selection, add result to selection // Click /w Ctrl = Toggle Selection State // Click /w Shift = Add to Selection bool ctrlPressed = WInput.GetKey(Key.LeftCtrl) || WInput.GetKey(Key.RightCtrl); bool shiftPressed = WInput.GetKey(Key.LeftShift) || WInput.GetKey(Key.RightShift); if (!ctrlPressed & !shiftPressed) { ModifySelection(SelectionType.Add, addedActor, true); //m_selectionList.Clear(); //if (addedActor != null) m_selectionList.Add(addedActor); } else if (addedActor != null && (ctrlPressed && !shiftPressed)) { if (m_selectionList.Contains(addedActor)) { ModifySelection(SelectionType.Remove, addedActor, false); } //m_selectionList.Remove(addedActor); else { ModifySelection(SelectionType.Add, addedActor, false); } //m_selectionList.Add(addedActor); } else if (addedActor != null && shiftPressed) { if (!m_selectionList.Contains(addedActor)) { ModifySelection(SelectionType.Add, addedActor, false); } //m_selectionList.Add(addedActor); } if (m_transformGizmo != null && m_selectionList.Count > 0) { m_transformGizmo.SetPosition(m_selectionList[0].Transform.Position); m_transformGizmo.SetLocalRotation(m_selectionList[0].Transform.Rotation); } } }
public bool RayIntersectsPlane(FRay ray, out float intersectDist) { float a = Vector3.Dot(ray.Direction, Normal); float num = -Vector3.Dot(ray.Origin, Normal) - Distance; if (Math.Abs(a) < float.Epsilon) { intersectDist = 0f; return(false); } intersectDist = num / a; return(intersectDist > 0f); }
public bool Raycast(FRay ray, out float closestDistance) { // Convert the ray to local space of this node since all of our raycasts are local. FRay localRay; if (DisableRotationAndScaleForRaycasting) { localRay = WMath.TransformRay(ray, Transform.Position, Vector3.One, Quaternion.Identity); } else { localRay = WMath.TransformRay(ray, Transform.Position, VisualScale, Transform.Rotation.Inverted().ToSinglePrecision()); } closestDistance = float.MaxValue; bool bHit = false; if (m_actorMeshes.Count > 0) { foreach (var actor_mesh in m_actorMeshes) { bHit = actor_mesh.Raycast(localRay, out closestDistance, true); if (bHit) { break; } } } else if (m_objRender != null) { if (m_objRender.FaceCullingEnabled && m_objRender.GetAABB().Contains(localRay.Origin)) { // If the camera is inside an OBJ render that has backface culling on, the actor won't actually be visible, so don't select it. return(false); } bHit = WMath.RayIntersectsAABB(localRay, m_objRender.GetAABB().Min, m_objRender.GetAABB().Max, out closestDistance); if (bHit) { // Convert the hit point back to world space... Vector3 localHitPoint = localRay.Origin + (localRay.Direction * closestDistance); Vector3 globalHitPoint = Transform.Position + Vector3.Transform(localHitPoint, Transform.Rotation.ToSinglePrecision()); // Now get the distance from the original ray origin and the new worldspace hit point. closestDistance = (globalHitPoint - ray.Origin).Length; } } return(bHit); }
private void CheckForObjectSelectionChange(WSceneView view) { // If we have a gizmo and we're transforming it, don't check for selection change. if (m_transformGizmo != null && m_transformGizmo.IsTransforming) { return; } if (WInput.GetMouseButtonDown(0) && !WInput.GetMouseButton(1)) { FRay mouseRay = view.ProjectScreenToWorld(WInput.MousePosition); var addedActor = Raycast(mouseRay); // Check the behaviour of this click to determine appropriate selection modification behaviour. // Click w/o Modifiers = Clear Selection, add result to selection // Click /w Ctrl = Toggle Selection State // Click /w Shift = Add to Selection bool ctrlPressed = WInput.GetKey(Key.LeftCtrl) || WInput.GetKey(Key.RightCtrl); bool shiftPressed = WInput.GetKey(Key.LeftShift) || WInput.GetKey(Key.RightShift); if (!ctrlPressed & !shiftPressed) { EditorSelection.ClearSelection(); if (addedActor != null) { EditorSelection.AddToSelection(addedActor); } } else if (addedActor != null && (ctrlPressed && !shiftPressed)) { if (addedActor.IsSelected) { EditorSelection.RemoveFromSelection(addedActor); } else { EditorSelection.AddToSelection(addedActor); } } else if (addedActor != null && shiftPressed) { if (!EditorSelection.SelectedObjects.Contains(addedActor)) { EditorSelection.AddToSelection(addedActor); } } UpdateGizmoTransform(); } }
public static FRay TransformRay(FRay ray, Vector3 position, Vector3 scale, Quaternion rotation) { FRay localRay = new FRay(); localRay.Direction = Vector3.Transform(ray.Direction, rotation); localRay.Origin = Vector3.Transform(ray.Origin - position, rotation); // We need to divide the origin and the direction by the scale. If you skip dividing the direction by the // scale, then it doesn't work on non-uniformly scaled objects. localRay.Origin.X /= scale.X; localRay.Origin.Y /= scale.Y; localRay.Origin.Z /= scale.Z; localRay.Direction.X /= scale.X; localRay.Direction.Y /= scale.Y; localRay.Direction.Z /= scale.Z; localRay.Direction.Normalize(); return localRay; }
public static FRay TransformRay(FRay ray, Vector3 position, Vector3 scale, Quaternion rotation) { FRay localRay = new FRay(); localRay.Direction = Vector3.Transform(ray.Direction, rotation); localRay.Origin = Vector3.Transform(ray.Origin - position, rotation); // We need to divide the origin and the direction by the scale. If you skip dividing the direction by the // scale, then it doesn't work on non-uniformly scaled objects. localRay.Origin.X /= scale.X; localRay.Origin.Y /= scale.Y; localRay.Origin.Z /= scale.Z; localRay.Direction.X /= scale.X; localRay.Direction.Y /= scale.Y; localRay.Direction.Z /= scale.Z; localRay.Direction.Normalize(); return(localRay); }
public static bool RayIntersectsTriangle(FRay ray, Vector3 v1, Vector3 v2, Vector3 v3, bool oneSided, out float intersectionDistance) { intersectionDistance = float.MinValue; // Find Vectors for two edges sharing v1 Vector3 e1 = v2 - v1; Vector3 e2 = v3 - v1; // Begin calculating determinant - also used to calculate 'u' parameter Vector3 p; Vector3.Cross(ref ray.Direction, ref e2, out p); // If determinant is near zero, ray lies in plane of triangle/ray is parallel to plane of triangle. float det = Vector3.Dot(e1, p); if (oneSided) { Vector3 n; Vector3.Cross(ref e2, ref e1, out n); n.NormalizeFast(); float dirToTri; Vector3.Dot(ref ray.Direction, ref n, out dirToTri); // Back-facing surface, early out. if (dirToTri > 0) { return(false); } } // No Collision if (det > -float.Epsilon && det < float.Epsilon) { return(false); } float inv_det = 1f / det; // Calculate distance from V1 to ray origin Vector3 t; Vector3.Subtract(ref ray.Origin, ref v1, out t); // Calculate 'u' parameter and test bound float u; Vector3.Dot(ref t, ref p, out u); u *= inv_det; // No Collision - Intersection lies outside of the triangle. if (u < 0f || u > 1f) { return(false); } // Prepare to test v parameter Vector3 q; Vector3.Cross(ref t, ref e1, out q); // Calculate 'v' parameter and test bound float v; Vector3.Dot(ref ray.Direction, ref q, out v); v *= inv_det; // No Collision - Intersection lies outside of the triangle. if (v < 0f || u + v > 1f) { return(false); } float dist; Vector3.Dot(ref e2, ref q, out dist); dist *= inv_det; if (dist > float.Epsilon) { intersectionDistance = dist; return(true); } // No hit, no win. return(false); }
public bool TransformFromInput(FRay ray, WSceneView view) { if (m_mode != FTransformMode.Translation) { WrapCursor(); } // Store the cursor position in viewport coordinates. Vector2 screenDimensions = App.GetScreenGeometry(); Vector2 cursorPos = App.GetCursorPosition(); Vector2 mouseCoords = new Vector2(((2f * cursorPos.X) / screenDimensions.X) - 1f, (1f - ((2f * cursorPos.Y) / screenDimensions.Y))); //[-1,1] range bool shiftPressed = WInput.GetKey(Key.LeftShift) || WInput.GetKey(Key.RightShift); if (m_mode == FTransformMode.Translation) { // Create a Translation Plane Vector3 axisA, axisB; if (GetNumSelectedAxes() == 1) { if (m_selectedAxes == FSelectedAxes.X) { axisB = Vector3.UnitX; } else if (m_selectedAxes == FSelectedAxes.Y) { axisB = Vector3.UnitY; } else { axisB = Vector3.UnitZ; } Vector3 dirToCamera = (m_position - view.GetCameraPos()).Normalized(); axisA = Vector3.Cross(axisB, dirToCamera); } else { axisA = ContainsAxis(m_selectedAxes, FSelectedAxes.X) ? Vector3.UnitX : Vector3.UnitZ; axisB = ContainsAxis(m_selectedAxes, FSelectedAxes.Y) ? Vector3.UnitY : Vector3.UnitZ; } Vector3 planeNormal = Vector3.Cross(axisA, axisB).Normalized(); m_translationPlane = new FPlane(planeNormal, m_position); float intersectDist; if (m_translationPlane.RayIntersectsPlane(ray, out intersectDist)) { Vector3 hitPos = ray.Origin + (ray.Direction * intersectDist); Vector3 localDelta = Vector3.Transform(hitPos - m_position, m_rotation.Inverted()); // Calculate a new position Vector3 newPos = m_position; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { newPos += Vector3.Transform(Vector3.UnitX, m_rotation) * localDelta.X; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { newPos += Vector3.Transform(Vector3.UnitY, m_rotation) * localDelta.Y; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { newPos += Vector3.Transform(Vector3.UnitZ, m_rotation) * localDelta.Z; } if (shiftPressed) { // Round to nearest 100 unit increment while shift is held down. newPos.X = (float)Math.Round(newPos.X / 100f) * 100f; newPos.Y = (float)Math.Round(newPos.Y / 100f) * 100f; newPos.Z = (float)Math.Round(newPos.Z / 100f) * 100f; } // Check the new location to see if it's skyrocked off into the distance due to near-plane raytracing issues. Vector3 newPosDirToCamera = (newPos - view.GetCameraPos()).Normalized(); float dot = Math.Abs(Vector3.Dot(planeNormal, newPosDirToCamera)); //Console.WriteLine("hitPos: {0} localOffset: {1} newPos: {2}, dotResult: {3}", hitPos, localOffset, newPos, dot); if (dot < 0.02f) { return(false); } // This is used to set the offset to the gizmo the mouse cursor is from the origin of the gizmo on the first frame // that you click on the gizmo. if (!m_hasSetMouseOffset) { m_translateOffset = m_position - newPos; m_deltaTranslation = Vector3.Zero; m_hasSetMouseOffset = true; return(false); } // Apply Translation m_deltaTranslation = Vector3.Transform(newPos - m_position + m_translateOffset, m_rotation.Inverted()); if (!ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { m_deltaTranslation.X = 0f; } if (!ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { m_deltaTranslation.Y = 0f; } if (!ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { m_deltaTranslation.Z = 0f; } m_totalTranslation += m_deltaTranslation; m_position += Vector3.Transform(m_deltaTranslation, m_rotation); if (!m_hasTransformed && (m_deltaTranslation != Vector3.Zero)) { m_hasTransformed = true; } return(m_hasTransformed); } else { // Our raycast missed the plane m_deltaTranslation = Vector3.Zero; return(false); } } else if (m_mode == FTransformMode.Rotation) { Vector3 rotationAxis; if (m_selectedAxes == FSelectedAxes.X) { rotationAxis = Vector3.UnitX; } else if (m_selectedAxes == FSelectedAxes.Y) { rotationAxis = Vector3.UnitY; } else { rotationAxis = Vector3.UnitZ; } // Convert these from [0-1] to [-1, 1] to match our mouse coords. Vector2 lineOrigin = (view.UnprojectWorldToViewport(m_hitPoint) * 2) - Vector2.One; Vector2 lineEnd = (view.UnprojectWorldToViewport(m_hitPoint + m_moveDir) * 2) - Vector2.One; lineOrigin.Y = -lineOrigin.Y; lineEnd.Y = -lineEnd.Y; Vector2 lineDir = (lineEnd - lineOrigin).Normalized(); float rotAmount = Vector2.Dot(lineDir, mouseCoords + m_wrapOffset - lineOrigin) * 180f; if (float.IsNaN(rotAmount)) { Console.WriteLine("rotAmountNaN!"); return(false); } if (!m_hasSetMouseOffset) { m_rotateOffset = -rotAmount; m_deltaRotation = Quaternion.Identity; m_hasSetMouseOffset = true; return(false); } // Apply Rotation rotAmount += m_rotateOffset; if (shiftPressed) { // Round to nearest 45 degree increment while shift is held down. rotAmount = (float)Math.Round(rotAmount / 45f) * 45f; } Quaternion oldRot = m_currentRotation; m_currentRotation = Quaternion.FromAxisAngle(rotationAxis, WMath.DegreesToRadians(rotAmount)); m_deltaRotation = m_currentRotation * oldRot.Inverted(); if (m_transformSpace == FTransformSpace.Local) { m_rotation *= m_deltaRotation; } // Add to Total Rotation recorded for UI. if (m_selectedAxes == FSelectedAxes.X) { m_totalRotation.X = rotAmount; } else if (m_selectedAxes == FSelectedAxes.Y) { m_totalRotation.Y = rotAmount; } else { m_totalRotation.Z = rotAmount; } if (!m_hasTransformed && rotAmount != 0f) { m_hasTransformed = true; } return(m_hasTransformed); } else if (m_mode == FTransformMode.Scale) { // Create a line in screen space. // Convert these from [0-1] to [-1, 1] to match our mouse coords. Vector2 lineOrigin = (view.UnprojectWorldToViewport(m_position) * 2) - Vector2.One; lineOrigin.Y = -lineOrigin.Y; // Determine the appropriate world space directoin using the selected axes and then conver this for use with // screen-space controlls. This has to be done every frame because the axes can be flipped while the gizmo // is transforming, so we can't pre-calculate this. Vector3 dirX = Vector3.Transform(mFlipScaleX ? -Vector3.UnitX : Vector3.UnitX, m_rotation); Vector3 dirY = Vector3.Transform(mFlipScaleY ? -Vector3.UnitY : Vector3.UnitY, m_rotation); Vector3 dirZ = Vector3.Transform(mFlipScaleZ ? -Vector3.UnitZ : Vector3.UnitZ, m_rotation); Vector2 lineDir; // If there is only one axis, then the world space direction is the selected axis. if (GetNumSelectedAxes() == 1) { Vector3 worldDir; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { worldDir = dirX; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { worldDir = dirY; } else { worldDir = dirZ; } Vector2 worldPoint = (view.UnprojectWorldToViewport(m_position + worldDir) * 2) - Vector2.One; worldPoint.Y = -lineOrigin.Y; lineDir = (worldPoint - lineOrigin).Normalized(); } // If there's two axii selected, then convert both to screen space and average them out to get the line direction. else if (GetNumSelectedAxes() == 2) { Vector3 axisA = ContainsAxis(m_selectedAxes, FSelectedAxes.X) ? dirX : dirY; Vector3 axisB = ContainsAxis(m_selectedAxes, FSelectedAxes.Z) ? dirZ : dirY; Vector2 screenA = (view.UnprojectWorldToViewport(m_position + axisA) * 2) - Vector2.One; screenA.Y = -screenA.Y; Vector2 screenB = (view.UnprojectWorldToViewport(m_position + axisB) * 2) - Vector2.One; screenB.Y = -screenB.Y; screenA = (screenA - lineOrigin).Normalized(); screenB = (screenB - lineOrigin).Normalized(); lineDir = ((screenA + screenB) / 2f).Normalized(); } // There's three axis, just use up. else { lineDir = Vector2.UnitY; } float scaleAmount = Vector2.Dot(lineDir, mouseCoords + m_wrapOffset - lineOrigin) * 5f; if (shiftPressed) { // Round to nearest whole number scale while shift is held down. scaleAmount = (float)Math.Round(scaleAmount); } // Set their initial offset if we haven't already if (!m_hasSetMouseOffset) { m_scaleOffset = -scaleAmount; m_deltaScale = Vector3.One; m_hasSetMouseOffset = true; return(false); } // Apply the scale scaleAmount = scaleAmount + m_scaleOffset + 1f; // A multiplier is applied to the scale amount if it's less than one to prevent it dropping into the negatives. // ??? if (scaleAmount < 1f) { scaleAmount = 1f / (-(scaleAmount - 1f) + 1f); } Vector3 oldScale = m_totalScale; m_totalScale = Vector3.One; if (ContainsAxis(m_selectedAxes, FSelectedAxes.X)) { m_totalScale.X = scaleAmount; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Y)) { m_totalScale.Y = scaleAmount; } if (ContainsAxis(m_selectedAxes, FSelectedAxes.Z)) { m_totalScale.Z = scaleAmount; } m_deltaScale = new Vector3(m_totalScale.X / oldScale.X, m_totalScale.Y / oldScale.Y, m_totalScale.Z / oldScale.Z); if (!m_hasTransformed && (scaleAmount != 1f)) { m_hasTransformed = true; } return(m_hasTransformed); } return(false); }
public bool CheckSelectedAxes(FRay ray) { // Convert the ray into local space so we can use axis-aligned checks, this solves the checking problem // when the gizmo is rotated due to being in Local mode. FRay localRay = new FRay(); localRay.Direction = Vector3.Transform(ray.Direction, m_rotation.Inverted()); localRay.Origin = Vector3.Transform(ray.Origin - m_position, m_rotation.Inverted()); //m_lineBatcher.DrawLine(localRay.Origin, localRay.Origin + (localRay.Direction * 10000), WLinearColor.White, 25, 5); List <AxisDistanceResult> results = new List <AxisDistanceResult>(); if (m_mode == FTransformMode.Translation) { FAABox[] translationAABB = GetAABBBoundsForMode(FTransformMode.Translation); for (int i = 0; i < translationAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, translationAABB[i].Min, translationAABB[i].Max, out intersectDist)) { results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } else if (m_mode == FTransformMode.Rotation) { // We'll use a combination of AABB and Distance checks to give us the quarter-circles we need. FAABox[] rotationAABB = GetAABBBoundsForMode(FTransformMode.Rotation); float screenScale = 0f; for (int i = 0; i < 3; i++) { screenScale += m_scale[i]; } screenScale /= 3f; for (int i = 0; i < rotationAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, rotationAABB[i].Min, rotationAABB[i].Max, out intersectDist)) { Vector3 intersectPoint = localRay.Origin + (localRay.Direction * intersectDist); // Convert this aabb check into a radius check so we clip it by the semi-circles // that the rotation tool actually is. if (intersectPoint.Length > 105f * screenScale) { continue; } results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } else if (m_mode == FTransformMode.Scale) { FAABox[] scaleAABB = GetAABBBoundsForMode(FTransformMode.Scale); for (int i = 0; i < scaleAABB.Length; i++) { float intersectDist; if (WMath.RayIntersectsAABB(localRay, scaleAABB[i].Min, scaleAABB[i].Max, out intersectDist)) { // Special-case here to give the center scale point overriding priority. Because we intersected // it, we can just override its distance to zero to make it clickable through the other bounding boxes. if ((FSelectedAxes)i + 1 == FSelectedAxes.All) { intersectDist = 0f; } results.Add(new AxisDistanceResult((FSelectedAxes)(i + 1), intersectDist)); } } } if (results.Count == 0) { m_selectedAxes = FSelectedAxes.None; return(false); } // If we get an intersection, sort them by the closest intersection distance. results.Sort((x, y) => x.Distance.CompareTo(y.Distance)); m_selectedAxes = results[0].Axis; // Store where the mouse hit on the first frame in world space. This means converting the ray back to worldspace. Vector3 localHitPoint = localRay.Origin + (localRay.Direction * results[0].Distance); m_hitPoint = Vector3.Transform(localHitPoint, m_rotation) + m_position; return(true); }
private void UpdateSelectionGizmo(WSceneView view) { if (!m_transformGizmo.Enabled && m_selectionList.Count > 0) { // Show the Transform Gizmo. m_transformGizmo.Enabled = true; m_transformGizmo.SetPosition(m_selectionList[0].Transform.Position); m_transformGizmo.SetLocalRotation(m_selectionList[0].Transform.Rotation); } else if (m_transformGizmo.Enabled && m_selectionList.Count == 0) { // Hide the Transform Gizmo. m_transformGizmo.Enabled = false; } if (!m_transformGizmo.Enabled) { return; } if (WInput.GetKeyDown(Key.Q) && !WInput.GetMouseButton(1)) { m_transformGizmo.SetMode(FTransformMode.None); } if (WInput.GetKeyDown(Key.W) && !WInput.GetMouseButton(1)) { m_transformGizmo.SetMode(FTransformMode.Translation); } if (WInput.GetKeyDown(Key.E) && !WInput.GetMouseButton(1)) { m_transformGizmo.SetMode(FTransformMode.Rotation); } if (WInput.GetKeyDown(Key.R) && !WInput.GetMouseButton(1)) { m_transformGizmo.SetMode(FTransformMode.Scale); } if (WInput.GetKeyDown(Key.OemOpenBrackets)) { m_transformGizmo.DecrementSize(); } if (WInput.GetKeyDown(Key.OemCloseBrackets)) { m_transformGizmo.IncrementSize(); } if (WInput.GetKeyDown(Key.OemTilde)) { if (m_transformGizmo.TransformSpace == FTransformSpace.World) { m_transformGizmo.SetTransformSpace(FTransformSpace.Local); } else { m_transformGizmo.SetTransformSpace(FTransformSpace.World); } UpdateGizmoTransform(); } if (WInput.GetMouseButtonDown(0)) { FRay mouseRay = view.ProjectScreenToWorld(WInput.MousePosition); if (m_transformGizmo.CheckSelectedAxes(mouseRay)) { m_transformGizmo.StartTransform(); } } if (WInput.GetMouseButtonUp(0)) { if (m_transformGizmo.IsTransforming) { // When we end let go of the gizmo, we want to make one last action which specifies that it is done, // so that the next gizmo move doesn't merge with the previous. WUndoCommand undoAction = CreateUndoActionForGizmo(true); if (undoAction != null) { m_world.UndoStack.Push(undoAction); } m_transformGizmo.EndTransform(); } } if (m_transformGizmo.IsTransforming) { FRay mouseRay = view.ProjectScreenToWorld(WInput.MousePosition); if (m_transformGizmo.TransformFromInput(mouseRay, view)) { WUndoCommand undoAction = CreateUndoActionForGizmo(false); if (undoAction != null) { m_world.UndoStack.Push(undoAction); } } } m_transformGizmo.UpdateForSceneView(view); }
public static bool RayIntersectsAABB(FRay ray, Vector3 aabbMin, Vector3 aabbMax, out float intersectionDistance) { Vector3 t_1 = new Vector3(), t_2 = new Vector3(); float tNear = float.MinValue; float tFar = float.MaxValue; // Test infinite planes in each directin. for (int i = 0; i < 3; i++) { // Ray is parallel to planes in this direction. if (ray.Direction[i] == 0) { if ((ray.Origin[i] < aabbMin[i]) || (ray.Origin[i] > aabbMax[i])) { // Parallel and outside of the box, thus no intersection is possible. intersectionDistance = float.MinValue; return false; } } else { t_1[i] = (aabbMin[i] - ray.Origin[i]) / ray.Direction[i]; t_2[i] = (aabbMax[i] - ray.Origin[i]) / ray.Direction[i]; // Ensure T_1 holds values for intersection with near plane. if (t_1[i] > t_2[i]) { Vector3 temp = t_2; t_2 = t_1; t_1 = temp; } if (t_1[i] > tNear) tNear = t_1[i]; if (t_2[i] < tFar) tFar = t_2[i]; if ((tNear > tFar) || (tFar < 0)) { intersectionDistance = float.MinValue; return false; } } } intersectionDistance = tNear; return true; }
public static bool RayIntersectsTriangle(FRay ray, Vector3 v1, Vector3 v2, Vector3 v3, bool oneSided, out float intersectionDistance) { intersectionDistance = float.MinValue; // Find Vectors for two edges sharing v1 Vector3 e1 = v2 - v1; Vector3 e2 = v3 - v1; // Begin calculating determinant - also used to calculate 'u' parameter Vector3 p; Vector3.Cross(ref ray.Direction, ref e2, out p); // If determinant is near zero, ray lies in plane of triangle/ray is parallel to plane of triangle. float det = Vector3.Dot(e1, p); if (oneSided) { Vector3 n; Vector3.Cross(ref e2, ref e1, out n); n.NormalizeFast(); float dirToTri; Vector3.Dot(ref ray.Direction, ref n, out dirToTri); // Back-facing surface, early out. if (dirToTri > 0) return false; } // No Collision if (det > -float.Epsilon && det < float.Epsilon) return false; float inv_det = 1f / det; // Calculate distance from V1 to ray origin Vector3 t; Vector3.Subtract(ref ray.Origin, ref v1, out t); // Calculate 'u' parameter and test bound float u; Vector3.Dot(ref t, ref p, out u); u *= inv_det; // No Collision - Intersection lies outside of the triangle. if (u < 0f || u > 1f) return false; // Prepare to test v parameter Vector3 q; Vector3.Cross(ref t, ref e1, out q); // Calculate 'v' parameter and test bound float v; Vector3.Dot(ref ray.Direction, ref q, out v); v *= inv_det; // No Collision - Intersection lies outside of the triangle. if (v < 0f || u + v > 1f) return false; float dist; Vector3.Dot(ref e2, ref q, out dist); dist *= inv_det; if (dist > float.Epsilon) { intersectionDistance = dist; return true; } // No hit, no win. return false; }