private void DrawSelectionBox(Vector3 mousePosition, bool bAutoSelectPoints) { // First draw the selection box from drag start to current mouse position. // Calculating the bounding box in screenspace then converting to world seems to // produce the best lines in the Scene view regardless of camera orientation. Vector3 xVec = new Vector3((mousePosition.x - _dragMouseStart.x), 0, 0); Vector3 yVec = new Vector3(0, (mousePosition.y - _dragMouseStart.y), 0); Vector3 s00 = _dragMouseStart; Vector3 s01 = _dragMouseStart + xVec; Vector3 s10 = _dragMouseStart + yVec; Vector3 s11 = _dragMouseStart + xVec + yVec; Vector3 camFwd = _currentCamera.transform.forward; float depth = Mathf.Abs((_currentCamera.transform.position + camFwd * 2f).z); Vector3 w00 = _currentCamera.ScreenToWorldPoint(new Vector3(s00.x, s00.y, depth)); Vector3 w01 = _currentCamera.ScreenToWorldPoint(new Vector3(s01.x, s01.y, depth)); Vector3 w10 = _currentCamera.ScreenToWorldPoint(new Vector3(s10.x, s10.y, depth)); Vector3 w11 = _currentCamera.ScreenToWorldPoint(new Vector3(s11.x, s11.y, depth)); Color defaultColor = Handles.color; Handles.color = Color.white; Vector3[] lines = new Vector3[] { w00, w01, w11, w10, w00 }; Handles.DrawSolidRectangleWithOutline(lines, _selectionBoxFillColor, _selectionBoxOutlineColor); Handles.color = defaultColor; if (bAutoSelectPoints) { // Now we select points withing the selection box DeselectAllSelectedPoints(); // We'll use a rect to test against each point Rect selectionRect = new Rect(_dragMouseStart.x, _dragMouseStart.y, (mousePosition.x - _dragMouseStart.x), (mousePosition.y - _dragMouseStart.y)); Transform outputTransform =_selectedAttributesStore.OutputTransform; Vector3[] positionArray = new Vector3[0]; _selectedAttributesStore.GetPositionAttributeValues(out positionArray); int numPoints = positionArray.Length; for (int i = 0; i < numPoints; ++i) { // Convert vertices to screenspace // _editPointBoxMeshIndices contains the indices of the drawn points (which could be a subset of all mesh points) Vector3 pointPosition = outputTransform.localToWorldMatrix * positionArray[i]; Vector3 pointScreenPosition = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPosition, _currentCamera); if (selectionRect.Contains(pointScreenPosition, true)) { _editPointsSelectedIndices.Add(i); } } } }
/// <summary> /// Select points under the given mouse position. Multiple points can be selected. /// </summary> /// <param name="mousePosition"></param> private void SelectPointsWithMouseClick(Vector3 mousePosition) { Transform outputTransform = _selectedAttributesStore.OutputTransform; Vector3[] positionArray = new Vector3[0]; _selectedAttributesStore.GetPositionAttributeValues(out positionArray); mousePosition.z = 0f; float closestDistance = _mouseSelectDistanceThreshold; _editPointsSelectedIndices.Clear(); int numPoints = positionArray.Length; for(int i = 0; i < numPoints; ++i) { // Convert vertices to screenspace // _editPointBoxMeshIndices contains the indices of the drawn points (which could be a subset of all mesh points) Vector3 pointPosition = outputTransform.localToWorldMatrix * positionArray[i]; Vector3 pointScreenPosition = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPosition, _currentCamera); pointScreenPosition.z = 0f; float deltaMag = Vector3.Distance(pointScreenPosition, mousePosition); if(deltaMag < closestDistance) { closestDistance = deltaMag; _editPointsSelectedIndices.Clear(); _editPointsSelectedIndices.Add(i); } else if(deltaMag == closestDistance) { // Could have multiple points at this location _editPointsSelectedIndices.Add(i); } } }
private void UpdateAddMode(HEU_HoudiniAsset asset, int controlID, EventType eventType, Vector3 mousePosition, List<SerializedObject> updatedCurves) { Event currentEvent = Event.current; Color defaultHandleColor = Handles.color; switch (eventType) { case EventType.MouseDown: { if (!currentEvent.alt && currentEvent.button == 0 && _closestCurveName != null && _closestPointIndex >= 0) { AddPoint(_closestCurveName, _closestPointIndex, _newPointPosition, updatedCurves); _closestCurveName = null; currentEvent.Use(); } break; } case EventType.MouseUp: { break; } case EventType.MouseMove: { // Use the mouse move event will force a repaint allowing for much more responsive UI currentEvent.Use(); break; } case EventType.KeyUp: { if (currentEvent.keyCode == KeyCode.Space && !currentEvent.alt) { // Toggle modes SwitchToMode(HEU_Curve.Interaction.EDIT); } else if (currentEvent.keyCode == KeyCode.Escape || currentEvent.keyCode == KeyCode.Return || currentEvent.keyCode == KeyCode.KeypadEnter) { SwitchToMode(HEU_Curve.Interaction.VIEW); currentEvent.Use(); } break; } case EventType.KeyDown: { if (currentEvent.keyCode == KeyCode.Backspace || currentEvent.keyCode == KeyCode.Delete) { // Delete last added point if (_latestPointAddedCurve != null) { HEU_Curve latestAddCurve = GetCurve(_latestPointAddedCurve); if (latestAddCurve != null && _latestPointsAdded.Count > 0) { SelectSinglePoint(latestAddCurve, _latestPointsAdded.Pop()); DeleteSelectedPoints(updatedCurves); } } currentEvent.Use(); } else if (currentEvent.keyCode == KeyCode.A) { int mode = (int)_newPointMode + 1; if (mode > (int)CurveNewPointMode.END) { mode = (int)CurveNewPointMode.START; } _newPointMode = (CurveNewPointMode)mode; } else if (currentEvent.keyCode == KeyCode.F1) { _showInfo = !_showInfo; } break; } case EventType.Layout: { // This disables deselection on asset while in Add mode HandleUtility.AddDefaultControl(GUIUtility.GetControlID(FocusType.Passive)); break; } case EventType.Repaint: { bool bMouseInDrawArea = HEU_GeneralUtility.IsMouseWithinSceneView(_currentCamera, mousePosition) && !HEU_GeneralUtility.IsMouseOverRect(_currentCamera, mousePosition, ref _curveEditorUIRect) && (!_showInfoRepaint || !HEU_GeneralUtility.IsMouseOverRect(_currentCamera, mousePosition, ref _infoRect)); // Plane for default collider Plane collisionPlane = new Plane(Vector3.up, Vector3.zero); //Ray mouseRay = _currentCamera.ScreenPointToRay(mousePosition); //Vector3 planePosition = mouseRay.origin + mouseRay.direction * 100f; //Plane collisionPlane = new Plane(-_currentCamera.transform.forward, planePosition); HEU_Curve.CurveDrawCollision drawCollision = asset.CurveDrawCollision; List<Collider> drawColliders = null; LayerMask drawLayerMask = Physics.DefaultRaycastLayers; if (drawCollision == HEU_Curve.CurveDrawCollision.LAYERMASK) { drawLayerMask = asset.GetCurveDrawLayerMask(); } else if (drawCollision == HEU_Curve.CurveDrawCollision.COLLIDERS) { drawColliders = asset.GetCurveDrawColliders(); } // Adding new point between line segments _closestPointIndex = -1; _closestCurveName = null; _newPointPosition = Vector3.zero; float closestDistance = float.MaxValue; foreach (HEU_Curve curve in _curves) { // Draw the cooked curve using its vertices DrawCurveUsingVertices(curve, _selectedCurveColor); DrawPointCaps(curve, _addModeDefaultPointColor); List<Vector3> points = curve.GetAllPoints(); int numPoints = points.Count; if (_currentCamera != null && bMouseInDrawArea) { Ray ray = _currentCamera.ScreenPointToRay(mousePosition); RaycastHit[] results = null; if (numPoints > 0 && (_newPointMode == CurveNewPointMode.INSIDE)) { // Control -> add point between closest line segment for (int i = 0; i < numPoints - 1; ++i) { Vector3 pointPos0 = curve.GetTransformedPosition(points[i]); Vector3 pointPos1 = curve.GetTransformedPosition(points[i + 1]); Vector3 screenPos0 = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPos0, _currentCamera); Vector3 screenPos1 = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPos1, _currentCamera); float distance = HandleUtility.DistancePointToLineSegment(mousePosition, screenPos0, screenPos1); if (distance < closestDistance) { closestDistance = distance; _closestPointIndex = i + 1; _closestCurveName = curve.CurveName; } } } else { // Show new point from either end of curve, whichever is closest. // Use collision to find new point. Vector3 hitPoint = Vector3.zero; bool bHit = false; if (drawCollision == HEU_Curve.CurveDrawCollision.LAYERMASK) { // Using layermask RaycastHit hitInfo; if (Physics.Raycast(ray, out hitInfo, _rayCastMaxDistance, drawLayerMask)) { hitPoint = hitInfo.point; bHit = true; } } else if (drawColliders != null && drawColliders.Count > 0) { // Using colliders results = Physics.RaycastAll(ray, _rayCastMaxDistance, drawLayerMask); foreach (RaycastHit hit in results) { foreach (Collider drawCollider in drawColliders) { if (hit.collider == drawCollider) { hitPoint = hit.point; bHit = true; break; } } } } else { // Using identity plane float collisionEnter = 0f; if (collisionPlane.Raycast(ray, out collisionEnter)) { collisionEnter = Mathf.Clamp(collisionEnter, _currentCamera.nearClipPlane, _currentCamera.farClipPlane); hitPoint = ray.origin + ray.direction * collisionEnter; bHit = true; } } if (bHit) { Vector3 hitPointScreenPosition = HEU_EditorUI.GetHandleWorldToScreenPosition(hitPoint, _currentCamera); // Find the closest point to add from (either first or last point) // Empty curve: // If its just a single curve, we can use the hit point as closest point. // For multiple curves, it gets trickier since we don't have an existing point // to check for closest point. So we'll just use the parent's transform position // as our anchor point. Vector3 checkPoint = Vector3.zero; int curveClosestPointIndex = 0; if (numPoints == 0) { if (_curves.Count > 1) { // Multiple curves -> use position of asset checkPoint = curve._targetGameObject.transform.position; } else { // Single curve -> use hit point as closest checkPoint = hitPoint; } } else if (_newPointMode == CurveNewPointMode.START) { // Curve with at least 1 point + shift held -> use first point checkPoint = HEU_EditorUI.GetHandleWorldToScreenPosition(curve.GetTransformedPoint(0), _currentCamera); curveClosestPointIndex = 0; } else { // Curve with at least 1 point -> use last point checkPoint = HEU_EditorUI.GetHandleWorldToScreenPosition(curve.GetTransformedPoint(numPoints - 1), _currentCamera); curveClosestPointIndex = numPoints; } float curveClosestPointDistance = Vector3.Distance(checkPoint, hitPointScreenPosition); if (curveClosestPointDistance < closestDistance) { closestDistance = curveClosestPointDistance; _closestPointIndex = curveClosestPointIndex; _closestCurveName = curve.CurveName; _newPointPosition = hitPoint; } // Snap to grid _newPointPosition = HEU_EditorUI.GetSnapPosition(_newPointPosition); } } } } // Note that curve name can be empty for valid empty curves if (_closestCurveName != null && _closestPointIndex >= 0) { HEU_Curve closestCurve = GetCurve(_closestCurveName); if (closestCurve != null) { int numPoints = closestCurve.GetNumPoints(); if ((_newPointMode == CurveNewPointMode.INSIDE) && !currentEvent.alt && numPoints >= 2) { // Handle adding new point at projected mouse cursor between points // First draw the curve line segments DrawCurveUsingPoints(closestCurve, Color.yellow); // Draw the caps again to hid the ends of line segments above (visually pleasing) DrawPointCaps(closestCurve, _addModeDefaultPointColor); Vector3 pointPos0 = closestCurve.GetTransformedPoint(_closestPointIndex - 1); Vector3 pointPos1 = closestCurve.GetTransformedPoint(_closestPointIndex); Vector3 screenPos0 = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPos0, _currentCamera); Vector3 screenPos1 = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPos1, _currentCamera); Vector3 curveNewPointPosition = HandleUtility.ProjectPointLine(mousePosition, screenPos0, screenPos1); Vector2 deltaNew = curveNewPointPosition - screenPos0; Vector2 deltaLine = screenPos1 - screenPos0; float ratio = Mathf.Clamp01(deltaNew.magnitude / deltaLine.magnitude); Vector3 newDir = (pointPos1 - pointPos0); curveNewPointPosition = pointPos0 + (newDir.normalized * newDir.magnitude * ratio); Handles.color = _selectedPointColor; HEU_EditorUI.DrawSphereCap(GUIUtility.GetControlID(FocusType.Passive), curveNewPointPosition, Quaternion.identity, HEU_EditorUI.GetHandleSize(curveNewPointPosition)); Handles.color = Color.yellow; HEU_EditorUI.DrawCircleCap(0, pointPos0, Quaternion.LookRotation(_currentCamera.transform.forward), HEU_EditorUI.GetHandleSize(pointPos0)); HEU_EditorUI.DrawCircleCap(0, pointPos1, Quaternion.LookRotation(_currentCamera.transform.forward), HEU_EditorUI.GetHandleSize(pointPos1)); Handles.color = defaultHandleColor; _newPointPosition = curveNewPointPosition; SceneView.RepaintAll(); } else if (!currentEvent.alt) { // Handle adding new point at closest curve's end points if (closestCurve.GetNumPoints() > 0) { // Draw dotted line from last point to newPointPosition int connectionPoint = (_closestPointIndex > 0) ? _closestPointIndex - 1 : 0; Vector3 pointPos0 = closestCurve.GetTransformedPoint(connectionPoint); Vector3[] dottedLineSegments = new Vector3[] { pointPos0, _newPointPosition }; Handles.color = _dottedLineColor; Handles.DrawDottedLines(dottedLineSegments, 4f); } Handles.color = _selectedPointColor; HEU_EditorUI.DrawSphereCap(GUIUtility.GetControlID(FocusType.Passive), _newPointPosition, Quaternion.identity, HEU_EditorUI.GetHandleSize(_newPointPosition)); Handles.color = defaultHandleColor; SceneView.RepaintAll(); } } } break; } } }
private static Vector3 DoDragHandleAxis(DragAxisInfo axisInfo, Vector3 position, ref DragHandleResult result) { // Must request a control ID for each interactible control in the GUI that can respond to events int id = GUIUtility.GetControlID(axisInfo._handleHash, FocusType.Passive); float handleSize = HandleUtility.GetHandleSize(position); Camera camera = Camera.current; Event currentEvent = Event.current; Vector2 mousePos = HEU_EditorUI.GetMousePosition(ref currentEvent, camera); Vector3 handlePosition = Handles.matrix.MultiplyPoint(position); Matrix4x4 cachedHandleMatrix = Handles.matrix; int mouseButtonID = Event.current.button; // Process events (using GetTypeForControl to filter events relevant to this control) switch (currentEvent.GetTypeForControl(id)) { case EventType.MouseDown: { if (HandleUtility.nearestControl == id && (mouseButtonID == 0 || mouseButtonID == 1)) { GUIUtility.hotControl = id; axisInfo._dragMouseCurrent = axisInfo._dragMouseStart = mousePos; axisInfo._dragWorldStart = position; axisInfo._handleHasMoved = false; currentEvent.Use(); EditorGUIUtility.SetWantsMouseJumping(1); if (mouseButtonID == 0) { result = DragHandleResult.LMB_PRESS; } else if (mouseButtonID == 1) { result = DragHandleResult.RMB_PRESS; } } break; } case EventType.MouseUp: { if (GUIUtility.hotControl == id && (mouseButtonID == 0 || mouseButtonID == 1)) { GUIUtility.hotControl = 0; currentEvent.Use(); EditorGUIUtility.SetWantsMouseJumping(0); if (mouseButtonID == 0) { result = DragHandleResult.LMB_RELEASE; } else if (mouseButtonID == 1) { result = DragHandleResult.RMB_RELEASE; } // Double-click if (mousePos == axisInfo._dragMouseStart) { bool doubleClick = (axisInfo._handleClickID == id) && (Time.realtimeSinceStartup - axisInfo._handleClickTime < _handleDoubleClikcInterval); axisInfo._handleClickID = id; axisInfo._handleClickTime = Time.realtimeSinceStartup; if (mouseButtonID == 0) { result = doubleClick ? DragHandleResult.LMB_DOUBLECLICK : DragHandleResult.LMB_CLICK; } else if (mouseButtonID == 1) { result = doubleClick ? DragHandleResult.RMB_DOUBLECLICK : DragHandleResult.RMB_CLICK; } } } break; } case EventType.MouseDrag: { if (GUIUtility.hotControl == id) { if (axisInfo._dragAxis == DragAxis.ALL_AXIS) { // Free movement - (all axis) // Flip y because Unity is inverted axisInfo._dragMouseCurrent += new Vector2(currentEvent.delta.x, -currentEvent.delta.y); Vector3 position2 = camera.WorldToScreenPoint(Handles.matrix.MultiplyPoint(axisInfo._dragWorldStart)) + (Vector3)(axisInfo._dragMouseCurrent - axisInfo._dragMouseStart); position = Handles.matrix.inverse.MultiplyPoint(camera.ScreenToWorldPoint(position2)); } else { // Linear movement (constraint to current axis) // Get the delta between the original handle position and the original mouse down position // This is used to substract from relative mouse movement to counter the handle jump when dragging Vector3 originalDragStartSS = HEU_EditorUI.GetHandleWorldToScreenPosition(axisInfo._dragWorldStart, camera); Vector3 deltaOriginalDragOffset = axisInfo._dragMouseStart - new Vector2(originalDragStartSS.x, originalDragStartSS.y); Vector3 startPosSS = HEU_EditorUI.GetHandleWorldToScreenPosition(position, camera); Vector3 constraintSS = HEU_EditorUI.GetHandleWorldToScreenPosition(position + axisInfo._direction, camera) - startPosSS; Vector3 mousePosV3 = mousePos; Vector3 relativeMousePos = mousePosV3 - startPosSS - deltaOriginalDragOffset; float projection = Vector3.Dot(relativeMousePos, constraintSS.normalized); float normalization = 1 / constraintSS.magnitude; float scale = projection * normalization; position += axisInfo._direction * scale; } if (mouseButtonID == 0) { result = DragHandleResult.LMB_DRAG; } else if (mouseButtonID == 1) { result = DragHandleResult.RMB_DRAG; } axisInfo._handleHasMoved = true; GUI.changed = true; currentEvent.Use(); } break; } case EventType.MouseMove: case EventType.Repaint: { Color handleColor = Handles.color; if ((GUIUtility.hotControl == id && axisInfo._handleHasMoved) || (HandleUtility.nearestControl == id)) { Handles.color = Color.yellow; } else { Handles.color = axisInfo._axisColor; } Handles.matrix = Matrix4x4.identity; if (axisInfo._dragAxis == DragAxis.ALL_AXIS) { HEU_EditorUI.DrawCubeCap(id, handlePosition, Quaternion.identity, handleSize * 0.25f); } else { HEU_EditorUI.DrawArrowCap(id, handlePosition, Quaternion.LookRotation(axisInfo._direction), handleSize); } Handles.matrix = cachedHandleMatrix; Handles.color = handleColor; // This forces a Repaint. We want this when we change the axis color due to being cursor being nearest. if (currentEvent.type == EventType.MouseMove && HandleUtility.nearestControl == id) { SceneView.RepaintAll(); } break; } case EventType.Layout: { // AddControl tells Unity where each Handle is relative to the current mouse position Handles.matrix = Matrix4x4.identity; if (axisInfo._dragAxis == DragAxis.ALL_AXIS) { float distance = handleSize * 0.3f; HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(handlePosition, distance)); } else { HandleUtility.AddControl(id, HandleUtility.DistanceToLine(handlePosition, handlePosition + axisInfo._direction * handleSize) * 0.4f); } Handles.matrix = cachedHandleMatrix; break; } } return(position); }
private void DrawSelectionBox(Vector3 mousePosition, bool bAutoSelectPoints) { // First draw the selection box from drag start to current mouse position. // Calculating the bounding box in screenspace then converting to world seems to // produce the best lines in the Scene view regardless of camera orientation. Vector3 xVec = new Vector3((mousePosition.x - _dragMouseStart.x), 0, 0); Vector3 yVec = new Vector3(0, (mousePosition.y - _dragMouseStart.y), 0); Vector3 s00 = _dragMouseStart; Vector3 s01 = _dragMouseStart + xVec; Vector3 s10 = _dragMouseStart + yVec; Vector3 s11 = _dragMouseStart + xVec + yVec; Vector3 camFwd = _currentCamera.transform.forward; float depth = Mathf.Abs((_currentCamera.transform.position + camFwd * 2f).z); Vector3 w00 = _currentCamera.ScreenToWorldPoint(new Vector3(s00.x, s00.y, depth)); Vector3 w01 = _currentCamera.ScreenToWorldPoint(new Vector3(s01.x, s01.y, depth)); Vector3 w10 = _currentCamera.ScreenToWorldPoint(new Vector3(s10.x, s10.y, depth)); Vector3 w11 = _currentCamera.ScreenToWorldPoint(new Vector3(s11.x, s11.y, depth)); Color defaultColor = Handles.color; Handles.color = Color.white; Vector3[] lines = new Vector3[] { w00, w01, w11, w10, w00 }; Handles.DrawSolidRectangleWithOutline(lines, _selectionBoxFillColor, _selectionBoxOutlineColor); Handles.color = defaultColor; if (bAutoSelectPoints) { // Now we select points withing the selection box DeselectAllPoints(); // We'll use a rect to test against each curve point Rect selectionRect = new Rect(_dragMouseStart.x, _dragMouseStart.y, (mousePosition.x - _dragMouseStart.x), (mousePosition.y - _dragMouseStart.y)); foreach (HEU_Curve curve in _curves) { int numPoints = curve.GetNumPoints(); for (int i = 0; i < numPoints; ++i) { Vector3 pointPosition = curve.GetTransformedPoint(i); Vector3 pointScreenPosition = HEU_EditorUI.GetHandleWorldToScreenPosition(pointPosition, _currentCamera); if (selectionRect.Contains(pointScreenPosition, true)) { SelectAddPoint(curve, i); Handles.color = _selectedPointColor; } else { Handles.color = _unselectedPointColor; } HEU_EditorUI.DrawSphereCap(i, pointPosition, Quaternion.identity, HEU_EditorUI.GetHandleSize(pointPosition)); Handles.color = defaultColor; } } } }