void SetupInputPlane(Vector2 mousePosition) { plane = EditorHandleUtility.FindBestPlane(mousePosition); var planeNormal = plane.normal; var planeCenter = plane.normal * -plane.distance; // if hit point on plane is cardinal axis and on grid, snap to grid. if (Math.IsCardinalAxis(planeNormal)) { const float epsilon = .00001f; bool offGrid = false; Vector3 snapVal = EditorSnapping.activeMoveSnapValue; Vector3 center = Vector3.Scale(ProBuilderSnapping.GetSnappingMaskBasedOnNormalVector(planeNormal), planeCenter); for (int i = 0; i < 3; i++) { offGrid |= Mathf.Abs(snapVal[i] % center[i]) > epsilon; } polygon.isOnGrid = !offGrid; } else { polygon.isOnGrid = false; } }
/// <summary> /// The Editing handles are used to manipulate and resize ProBuilderShapes /// These handles are used in 2 tools : EditShapeTool and DrawShapeTool. In this second tool, /// these handles allow to modified the last created shape. /// </summary> /// <param name="proBuilderShape">The Shape on which to attach the handles</param> /// <param name="updatePrefs">Parameter used to update the DrawShapeTool when needed</param> internal static void DoEditingHandles(ProBuilderShape proBuilderShape, bool updatePrefs = false) { if (proBuilderShape == null) { return; } var scale = proBuilderShape.transform.lossyScale; var position = proBuilderShape.transform.position + Vector3.Scale(proBuilderShape.transform.TransformDirection(proBuilderShape.shapeBox.center), scale); var matrix = Matrix4x4.TRS(position, proBuilderShape.transform.rotation, Vector3.one); using (new Handles.DrawingScope(matrix)) { EditorShapeUtility.UpdateFaces(proBuilderShape.editionBounds, scale, faces); for (int i = 0; i < 4; ++i) { k_OrientationControlIDs[i] = GUIUtility.GetControlID(FocusType.Passive); } for (int i = 0; i < faces.Length; ++i) { s_FaceControlIDs[i] = GUIUtility.GetControlID(FocusType.Passive); } var absSize = Math.Abs(proBuilderShape.editionBounds.size); if (absSize.x > Mathf.Epsilon && absSize.y > Mathf.Epsilon && absSize.z > Mathf.Epsilon) { DoOrientationHandles(proBuilderShape, updatePrefs); } DoSizeHandles(proBuilderShape, updatePrefs); } }
public override void OnGUI() { s_Radius.value = EditorGUILayout.FloatField("Radius", s_Radius); s_Radius.value = Mathf.Clamp(s_Radius, k_MinShapeDimensions.x, k_MinShapeDimensions.x); s_AxisSegments.value = EditorGUILayout.IntField("Number of Sides", s_AxisSegments); s_AxisSegments.value = PMath.Clamp(s_AxisSegments, 4, 128); s_Height.value = EditorGUILayout.FloatField("Height", s_Height); s_Height.value = Mathf.Clamp(s_Height.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_HeightSegments.value = EditorGUILayout.IntField("Height Segments", s_HeightSegments); s_HeightSegments.value = UnityEngine.ProBuilder.Math.Clamp(s_HeightSegments, 0, 48); s_HeightSegments.value = Mathf.Clamp(s_HeightSegments.value, 0, 128); s_Smooth.value = EditorGUILayout.Toggle("Smooth", s_Smooth); if (s_AxisSegments % 2 != 0) { s_AxisSegments.value++; } if (s_HeightSegments < 0) { s_HeightSegments.value = 0; } }
/// <summary> /// Create a shape with the last set <see cref="ShapeEditor"/> parameters. /// </summary> /// <returns>A reference to the <see cref="ProBuilderMesh"/> of the newly created GameObject.</returns> public static ProBuilderMesh CreateActiveShape() { var res = s_ShapeBuilders[PMath.Clamp(s_CurrentIndex, 0, s_ShapeBuilders.Length - 1)].Build(); Undo.RegisterCreatedObjectUndo(res.gameObject, "Create Shape"); EditorUtility.InitObject(res); return(res); }
float GetDragDistance() { Ray constraint = m_DragState.constraint; Ray mouse = m_SceneCamera.ScreenPointToRay(Input.mousePosition); Vector3 nearestPoint = PMath.GetNearestPointRayRay(constraint, mouse); float sign = System.Math.Sign(Vector3.Dot(nearestPoint - constraint.origin, constraint.direction)); return(Vector3.Distance(constraint.origin, nearestPoint) * sign); }
public override void OnGUI() { s_Radius.value = EditorGUILayout.FloatField("Radius", s_Radius); s_Height.value = EditorGUILayout.FloatField("Height", s_Height); s_AxisSegments.value = EditorGUILayout.IntField("Number of Sides", s_AxisSegments); s_Radius.value = Mathf.Clamp(s_Radius.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_Height.value = Mathf.Clamp(s_Height.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_AxisSegments.value = PMath.Clamp(s_AxisSegments, 3, 64); }
void SetPolyEditMode(PolyShape.PolyEditMode mode) { if (polygon == null) { return; } PolyShape.PolyEditMode old = polygon.polyEditMode; if (mode != old) { GUIUtility.hotControl = 0; // Entering edit mode after the shape has been finalized once before, which means // possibly reverting manual changes. Store undo state so that if this was // not intentional user can revert. if (old == PolyShape.PolyEditMode.None && polygon.m_Points.Count > 2) { if (ProBuilderEditor.instance != null) { ProBuilderEditor.instance.ClearElementSelection(); } UndoUtility.RecordComponents <ProBuilderMesh, PolyShape>(polygon.GetComponents(typeof(Component)), "Edit Polygon Shape"); } polygon.polyEditMode = mode; // If coming from Path -> Height set the mouse / origin offset if (old == PolyShape.PolyEditMode.Path && mode == PolyShape.PolyEditMode.Height && Event.current != null) { Vector3 up = polygon.transform.up; Vector3 origin = polygon.transform.TransformPoint(Math.Average(polygon.m_Points)); Ray r = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition); Vector3 p = Math.GetNearestPointRayRay(origin, up, r.origin, r.direction); float extrude = Vector3.Distance(origin, p) * Mathf.Sign(Vector3.Dot(p - origin, up)); s_HeightMouseOffset = polygon.extrude - EditorSnapping.MoveSnap(extrude); } else if (old == PolyShape.PolyEditMode.Path && mode == PolyShape.PolyEditMode.None) { var go = polygon.gameObject; EditorApplication.delayCall += () => DestroyImmediate(go); return; } RebuildPolyShapeMesh(polygon); //Dirty the polygon for serialization (fix for transition between prefab and scene mode) if (polygon != null) { UnityEditor.EditorUtility.SetDirty(polygon); } } }
public override void OnGUI() { s_Axis.value = (Axis)EditorGUILayout.EnumPopup("Axis", s_Axis); s_Width.value = EditorGUILayout.FloatField("Width", s_Width); s_Height.value = EditorGUILayout.FloatField("Length", s_Height); s_Width.value = Mathf.Clamp(s_Width.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_Height.value = Mathf.Clamp(s_Height.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_WidthSegments.value = EditorGUILayout.IntField("Width Segments", s_WidthSegments); s_HeightSegments.value = EditorGUILayout.IntField("Length Segments", s_HeightSegments); s_WidthSegments.value = PMath.Clamp(s_WidthSegments.value, 0, 512); s_HeightSegments.value = PMath.Clamp(s_HeightSegments.value, 0, 512); }
public override void OnGUI() { s_Radius.value = EditorGUILayout.FloatField("Radius", s_Radius); s_Height.value = EditorGUILayout.FloatField("Height", s_Height); s_Thickness.value = EditorGUILayout.FloatField("Thickness", s_Thickness); s_AxisSegments.value = EditorGUILayout.IntField("Number of Sides", s_AxisSegments); s_HeightSegments.value = EditorGUILayout.IntField("Height Segments", s_HeightSegments); s_Radius.value = Mathf.Clamp(s_Radius.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_Height.value = Mathf.Clamp(s_Height.value, k_MinShapeDimensions.x, k_MaxShapeDimensions.x); s_HeightSegments.value = (int)Mathf.Clamp(s_HeightSegments, 0f, 32f); s_Thickness.value = Mathf.Clamp(s_Thickness, .01f, s_Radius - .01f); s_AxisSegments.value = PMath.Clamp(s_AxisSegments, 3, 64); s_HeightSegments.value = PMath.Clamp(s_HeightSegments.value, 0, 128); }
void BeginDrag() { if (m_DragState.active) { return; } m_DragState.active = true; var trs = m_Selection.mesh.transform; // The constraint ray is stored in world space var origin = trs.TransformPoint(PMath.Average(m_Selection.mesh.positions, m_Selection.face.indexes)); var direction = trs.TransformDirection(PMath.Normal(m_Selection.mesh, m_Selection.face)); m_DragState.constraint = new Ray(origin, direction); m_DragState.meshState = new MeshState(m_Selection.mesh, m_Selection.face.distinctIndexes); m_DragState.offset = GetDragDistance(); }
public override void OnGUI() { s_Steps.value = (int)Mathf.Max(UI.EditorGUIUtility.FreeSlider("Steps", s_Steps, 2, 64), 2); s_Steps.value = PMath.Clamp(s_Steps, 2, 512); s_Sides.value = EditorGUILayout.Toggle("Build Sides", s_Sides); s_Circumference.value = EditorGUILayout.Slider("Curvature", s_Circumference, 0f, 360f); Vector3 size = s_Size.value; if (s_Circumference > 0f) { s_Mirror.value = EditorGUILayout.Toggle("Mirror", s_Mirror); size.x = Mathf.Max( UI.EditorGUIUtility.FreeSlider( new GUIContent("Stair Width", "The width of an individual stair step."), size.x, .01f, 10f), .01f); size.y = Mathf.Max( UI.EditorGUIUtility.FreeSlider( new GUIContent("Stair Height", "The total height of this staircase. You may enter any value in the float field."), size.y, .01f, 10f), .01f); size.z = Mathf.Max( UI.EditorGUIUtility.FreeSlider( new GUIContent("Inner Radius", "The distance from the center that stairs begin."), size.z, 0f, 10f), 0f); s_Size.value = size; } else { size.x = UI.EditorGUIUtility.FreeSlider("Width", size.x, 0.01f, 10f); size.y = UI.EditorGUIUtility.FreeSlider("Height", size.y, 0.01f, 10f); size.z = UI.EditorGUIUtility.FreeSlider("Depth", size.z, 0.01f, 10f); } s_Size.value = PMath.Clamp(size, k_MinShapeDimensions, k_MaxShapeDimensions); }
/// <summary> /// Resize any collider components on this mesh to match the size of the mesh bounds. /// </summary> /// <param name="mesh">The mesh target to rebuild collider volumes for.</param> public static void RebuildColliders(this ProBuilderMesh mesh) { mesh.mesh.RecalculateBounds(); var bounds = mesh.mesh.bounds; foreach (var collider in mesh.GetComponents <Collider>()) { Type t = collider.GetType(); if (t == typeof(BoxCollider)) { ((BoxCollider)collider).center = bounds.center; ((BoxCollider)collider).size = bounds.size; } else if (t == typeof(SphereCollider)) { ((SphereCollider)collider).center = bounds.center; ((SphereCollider)collider).radius = Math.LargestValue(bounds.extents); } else if (t == typeof(CapsuleCollider)) { ((CapsuleCollider)collider).center = bounds.center; Vector2 xy = new Vector2(bounds.extents.x, bounds.extents.z); ((CapsuleCollider)collider).radius = Math.LargestValue(xy); ((CapsuleCollider)collider).height = bounds.size.y; } else if (t == typeof(WheelCollider)) { ((WheelCollider)collider).center = bounds.center; ((WheelCollider)collider).radius = Math.LargestValue(bounds.extents); } else if (t == typeof(MeshCollider)) { ((MeshCollider)collider).sharedMesh = null; ((MeshCollider)collider).sharedMesh = mesh.mesh; } } }
bool CheckFaceVertexAttributes(ProBuilderMesh mesh, Face face, out InvalidFaceInfo info) { info = default; var positions = mesh.positionsInternal; var normals = mesh.normalsInternal; var tangents = mesh.tangentsInternal; var textures = mesh.texturesInternal; var indices = face.indexesInternal; var distinct = face.distinctIndexesInternal; for (int i = 0, c = indices.Length; i < c; i += 3) { if (Math.TriangleArea(positions[indices[i]], positions[indices[i + 1]], positions[indices[i + 2]]) < float.Epsilon) { info.problems += $"Degenerate Triangle {indices[i]} {indices[i+1]} {indices[i+2]}"; } } for (int i = 0, c = distinct.Length; i < c; ++i) { if (normals != null && !Math.IsNumber(normals[i])) { info.problems += $"normals [{i}] is NaN"; } if (textures != null && !Math.IsNumber(textures[i])) { info.problems += $"uv0 [{i}] is NaN"; } if (tangents != null && !Math.IsNumber(tangents[i])) { info.problems += $"tangents [{i}] is NaN"; } } info.position = Math.Average(positions, distinct); return(!string.IsNullOrEmpty(info.problems)); }
static void DoSizeHandles(ProBuilderShape proBuilderShape, bool updatePrefs) { int faceCount = s_Faces.Length; var evt = Event.current; var is2D = proBuilderShape.shape is Plane || proBuilderShape.shape is Sprite; for (int i = 0; i < faceCount; i++) { var face = faces[i]; if (is2D && !face.IsValid) { continue; } if (Event.current.type == EventType.Repaint) { Color color = k_BoundsHandleColor; color.a *= face.IsVisible ? 1f : 0.5f; using (new Handles.DrawingScope(color)) { int pointsCount = face.Points.Length; for (int k = 0; k < pointsCount; k++) { Handles.DrawLine(face.Points[k], face.Points[(k + 1) % pointsCount]); } } } if (DoFaceSizeHandle(face, s_FaceControlIDs[i])) { if (!s_SizeManipulationInit) { s_StartCenter = proBuilderShape.transform.position + proBuilderShape.transform.TransformVector(proBuilderShape.shapeBox.center); s_StartScale = proBuilderShape.transform.lossyScale; s_StartScaleInverse = new Vector3(1f / Mathf.Abs(s_StartScale.x), 1f / Mathf.Abs(s_StartScale.y), 1f / Mathf.Abs(s_StartScale.z)); s_StartPositionLocal = face.CenterPosition; s_StartPositionGlobal = proBuilderShape.transform.TransformPoint(Vector3.Scale(face.CenterPosition, s_StartScale)); s_StartSize = proBuilderShape.size; s_SizeManipulationInit = true; s_Scaling = Vector3.Scale(face.Normal, Math.Sign(s_StartSize)); } var targetSize = s_StartSize; if (Math.IsCardinalAxis(proBuilderShape.transform.up) && EditorSnapSettings.gridSnapEnabled && !EditorSnapSettings.incrementalSnapActive && !evt.alt) { var faceDelta = (s_SizeDelta * s_Faces[i].Normal); var facePosition = s_StartPositionGlobal + faceDelta; facePosition = ProBuilderSnapping.Snap(facePosition, EditorSnapping.activeMoveSnapValue); targetSize += Vector3.Scale((facePosition - s_StartPositionGlobal), s_Scaling); } else { //Should we expand on the 2 sides? var modifier = evt.alt ? 2f : 1f; var delta = modifier * (s_SizeDelta * s_Faces[i].Normal); delta.Scale(s_Scaling); delta.Scale(s_StartScaleInverse); targetSize += delta; var snap = EditorSnapSettings.incrementalSnapActive ? Vector3.Scale(EditorSnapping.activeMoveSnapValue, Math.Abs(face.Normal)) : Vector3.zero; targetSize = ProBuilderSnapping.Snap(targetSize, snap); } var center = Vector3.zero; if (!evt.alt) { center = Vector3.Scale((targetSize - s_StartSize) / 2f, s_Scaling); center = Vector3.Scale(center, Math.Sign(s_StartScale)); center = proBuilderShape.transform.TransformVector(center); } ApplyProperties(proBuilderShape, s_StartCenter + center, targetSize); if (updatePrefs) { DrawShapeTool.SaveShapeParams(proBuilderShape); } } } }
void DoExistingPointsGUI() { Transform trs = polygon.transform; int len = polygon.m_Points.Count; Vector3 up = trs.up; Vector3 right = trs.right; Vector3 forward = trs.forward; Vector3 center = Vector3.zero; Event evt = Event.current; bool used = evt.type == EventType.Used; if (!used && (evt.type == EventType.MouseDown && evt.button == 0 && !EditorHandleUtility.IsAppendModifier(evt.modifiers))) { m_SelectedIndex = -1; } if (evt.type == EventType.Repaint && polygon.polyEditMode == PolyShape.PolyEditMode.Path) { Vector3 currentPos = polygon.transform.TransformPoint(m_CurrentPosition); Handles.color = k_HandleColor; Handles.DotHandleCap(-1, currentPos, Quaternion.identity, HandleUtility.GetHandleSize(currentPos) * k_HandleSize, evt.type); Handles.color = Color.white; } if (polygon.polyEditMode == PolyShape.PolyEditMode.Height) { if (!used && evt.type == EventType.MouseUp && evt.button == 0 && !EditorHandleUtility.IsAppendModifier(evt.modifiers)) { evt.Use(); SetPolyEditMode(PolyShape.PolyEditMode.Edit); } bool sceneInUse = EditorHandleUtility.SceneViewInUse(evt); Ray r = HandleUtility.GUIPointToWorldRay(evt.mousePosition); Vector3 origin = polygon.transform.TransformPoint(Math.Average(polygon.m_Points)); float extrude = polygon.extrude; if (evt.type == EventType.MouseMove && !sceneInUse) { Vector3 p = Math.GetNearestPointRayRay(origin, up, r.origin, r.direction); extrude = EditorSnapping.MoveSnap(s_HeightMouseOffset + Vector3.Distance(origin, p) * Mathf.Sign(Vector3.Dot(p - origin, up))); } Vector3 extrudePoint = origin + (extrude * up); if (m_DrawHeightHandles) { Handles.color = k_HandleColor; Handles.DotHandleCap(-1, origin, Quaternion.identity, HandleUtility.GetHandleSize(origin) * k_HandleSize, evt.type); Handles.color = k_HandleColorGreen; Handles.DrawLine(origin, extrudePoint); Handles.DotHandleCap(-1, extrudePoint, Quaternion.identity, HandleUtility.GetHandleSize(extrudePoint) * k_HandleSize, evt.type); Handles.color = Color.white; } if (!sceneInUse && polygon.extrude != extrude) { OnBeginVertexMovement(); polygon.extrude = extrude; RebuildPolyShapeMesh(false); } } else { // vertex dots for (int ii = 0; ii < len; ii++) { Vector3 point = trs.TransformPoint(polygon.m_Points[ii]); center.x += point.x; center.y += point.y; center.z += point.z; float size = HandleUtility.GetHandleSize(point) * k_HandleSize; Handles.color = ii == m_SelectedIndex ? k_HandleSelectedColor : k_HandleColor; EditorGUI.BeginChangeCheck(); point = Handles.Slider2D(point, up, right, forward, size, Handles.DotHandleCap, Vector2.zero, true); if (EditorGUI.EndChangeCheck()) { UndoUtility.RecordObject(polygon, "Move Polygon Shape Point"); polygon.m_Points[ii] = GetPointInLocalSpace(point); OnBeginVertexMovement(); RebuildPolyShapeMesh(false); } // "clicked" a button if (!used && evt.type == EventType.Used) { if (ii == 0 && polygon.m_Points.Count > 2 && polygon.polyEditMode == PolyShape.PolyEditMode.Path) { m_NextMouseUpAdvancesMode = true; return; } else { used = true; m_SelectedIndex = ii; } } } Handles.color = Color.white; // height setting if (polygon.polyEditMode != PolyShape.PolyEditMode.Path && polygon.m_Points.Count > 2) { center.x /= (float)len; center.y /= (float)len; center.z /= (float)len; Vector3 extrude = center + (up * polygon.extrude); m_DistanceFromHeightHandle = Vector2.Distance(HandleUtility.WorldToGUIPoint(extrude), evt.mousePosition); EditorGUI.BeginChangeCheck(); if (m_DrawHeightHandles) { Handles.color = k_HandleColor; Handles.DotHandleCap(-1, center, Quaternion.identity, HandleUtility.GetHandleSize(center) * k_HandleSize, evt.type); Handles.DrawLine(center, extrude); Handles.color = k_HandleColorGreen; extrude = Handles.Slider(extrude, up, HandleUtility.GetHandleSize(extrude) * k_HandleSize, Handles.DotHandleCap, 0f); Handles.color = Color.white; } if (EditorGUI.EndChangeCheck()) { UndoUtility.RecordObject(polygon, "Set Polygon Shape Height"); polygon.extrude = EditorSnapping.MoveSnap(Vector3.Distance(extrude, center) * Mathf.Sign(Vector3.Dot(up, extrude - center))); OnBeginVertexMovement(); RebuildPolyShapeMesh(false); } } } }
void DoPointPlacement() { Event evt = Event.current; EventType eventType = evt.type; if (m_PlacingPoint) { Ray ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition); if (eventType == EventType.MouseDrag) { float hitDistance = Mathf.Infinity; if (plane.Raycast(ray, out hitDistance)) { evt.Use(); polygon.m_Points[m_SelectedIndex] = GetPointInLocalSpace(ray.GetPoint(hitDistance)); RebuildPolyShapeMesh(false); SceneView.RepaintAll(); } } if (eventType == EventType.MouseUp || eventType == EventType.Ignore || eventType == EventType.KeyDown || eventType == EventType.KeyUp) { evt.Use(); m_PlacingPoint = false; m_SelectedIndex = -1; SceneView.RepaintAll(); } } else if (polygon.polyEditMode == PolyShape.PolyEditMode.Path) { if (eventType == EventType.MouseDown && HandleUtility.nearestControl == m_ControlId) { if (polygon.m_Points.Count < 1) { SetupInputPlane(evt.mousePosition); } float hitDistance = Mathf.Infinity; Ray ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition); if (plane.Raycast(ray, out hitDistance)) { UndoUtility.RecordObject(polygon, "Add Polygon Shape Point"); Vector3 hit = ray.GetPoint(hitDistance); if (polygon.m_Points.Count < 1) { // this monstrosity exists so that grid and incremental snap work when possible, and // incremental is enabled when grid is not available. polygon.transform.position = m_Polygon.isOnGrid ? EditorSnapping.MoveSnap(hit) : EditorSnapping.snapMode == SnapMode.Relative ? ProBuilderSnapping.Snap(hit, EditorSnapping.incrementalSnapMoveValue) : hit; Vector3 cameraFacingPlaneNormal = plane.normal; if (Vector3.Dot(cameraFacingPlaneNormal, SceneView.lastActiveSceneView.camera.transform.forward) > 0f) { cameraFacingPlaneNormal *= -1; } polygon.transform.rotation = Quaternion.LookRotation(cameraFacingPlaneNormal) * Quaternion.Euler(new Vector3(90f, 0f, 0f)); } Vector3 point = GetPointInLocalSpace(hit); if (polygon.m_Points.Count > 2 && Math.Approx3(polygon.m_Points[0], point)) { m_NextMouseUpAdvancesMode = true; return; } polygon.m_Points.Add(point); m_PlacingPoint = true; m_SelectedIndex = polygon.m_Points.Count - 1; RebuildPolyShapeMesh(polygon); evt.Use(); } } else { float hitDistance = Mathf.Infinity; Ray ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition); if (plane.Raycast(ray, out hitDistance)) { Vector3 hit = ray.GetPoint(hitDistance); m_CurrentPosition = GetPointInLocalSpace(hit); } } } else if (polygon.polyEditMode == PolyShape.PolyEditMode.Edit) { if (polygon.m_Points.Count < 3) { SetPolyEditMode(PolyShape.PolyEditMode.Path); return; } if (m_DistanceFromHeightHandle > PreferenceKeys.k_MaxPointDistanceFromControl) { // point insertion Vector2 mouse = evt.mousePosition; Ray ray = HandleUtility.GUIPointToWorldRay(mouse); float hitDistance = Mathf.Infinity; if (plane.Raycast(ray, out hitDistance)) { Vector3 hit = ray.GetPoint(hitDistance); Vector3 point = GetPointInLocalSpace(hit); int polyCount = polygon.m_Points.Count; float distToLineInGUI; int index; Vector3 pInGUI = EditorHandleUtility.ClosestPointToPolyLine(polygon.m_Points, out index, out distToLineInGUI, true, polygon.transform); Vector3 aToPoint = point - polygon.m_Points[index - 1]; Vector3 aToB = polygon.m_Points[index % polyCount] - polygon.m_Points[index - 1]; float ratio = Vector3.Dot(aToPoint, aToB.normalized) / aToB.magnitude; Vector3 wp = Vector3.Lerp(polygon.m_Points[index - 1], polygon.m_Points[index % polyCount], ratio); wp = polygon.transform.TransformPoint(wp); Vector2 aInGUI = HandleUtility.WorldToGUIPoint(polygon.transform.TransformPoint(polygon.m_Points[index - 1])); Vector2 bInGUI = HandleUtility.WorldToGUIPoint(polygon.transform.TransformPoint(polygon.m_Points[index % polyCount])); float distanceToVertex = Mathf.Min(Vector2.Distance(mouse, aInGUI), Vector2.Distance(mouse, bInGUI)); if (distanceToVertex > PreferenceKeys.k_MaxPointDistanceFromControl && distToLineInGUI < PreferenceKeys.k_MaxPointDistanceFromControl) { m_MouseCursor = MouseCursor.ArrowPlus; if (evt.type == EventType.Repaint) { Handles.color = Color.green; Handles.DotHandleCap(-1, wp, Quaternion.identity, HandleUtility.GetHandleSize(wp) * k_HandleSize, evt.type); } if (evt.type == EventType.MouseDown && HandleUtility.nearestControl == m_ControlId) { evt.Use(); UndoUtility.RecordObject(polygon, "Insert Point"); polygon.m_Points.Insert(index, point); m_SelectedIndex = index; m_PlacingPoint = true; RebuildPolyShapeMesh(true); OnBeginVertexMovement(); } Handles.color = Color.white; } if (evt.type != EventType.Repaint) { SceneView.RepaintAll(); } } } } }
static bool DoOrientationHandle(FaceData face, ProBuilderShape proBuilderShape) { Event evt = Event.current; bool hasRotated = false; switch (evt.type) { case EventType.MouseDown: if (k_OrientationControlIDs.Contains(HandleUtility.nearestControl) && evt.button == 0) { s_CurrentId = HandleUtility.nearestControl; GUIUtility.hotControl = s_CurrentId; evt.Use(); } break; case EventType.MouseUp: if (k_OrientationControlIDs.Contains(HandleUtility.nearestControl) && evt.button == 0) { GUIUtility.hotControl = 0; evt.Use(); if (s_CurrentId == HandleUtility.nearestControl) { //Execute rotation Vector3 targetedNormal = Vector3.zero; for (int i = 0; i < k_OrientationControlIDs.Length; i++) { if (k_OrientationControlIDs[i] == s_CurrentId) { targetedNormal = (s_ArrowsLines[i][1] - face.CenterPosition).normalized; break; } } var currentNormal = face.Normal; currentNormal.Scale(Math.Sign(proBuilderShape.size)); targetedNormal.Scale(Math.Sign(proBuilderShape.size)); Vector3 rotationAxis = Vector3.Cross(currentNormal, targetedNormal); var angle = Vector3.SignedAngle(currentNormal, targetedNormal, rotationAxis); s_ShapeRotation = Quaternion.AngleAxis(angle, rotationAxis); s_CurrentAngle = (s_CurrentAngle + angle) % 360; hasRotated = true; } s_CurrentId = -1; } break; case EventType.Layout: for (int i = 0; i < 4; i++) { var rectPos = 0.8f * s_ArrowsLines[i][1] + 0.2f * face.CenterPosition; float dist = HandleUtility.DistanceToRectangle(rectPos, Quaternion.LookRotation(face.Normal), HandleUtility.GetHandleSize(face.CenterPosition) * s_DefaultMidpointSquareSize / 2f); HandleUtility.AddControl(k_OrientationControlIDs[i], dist); } break; case EventType.Repaint: if (s_CurrentArrowHovered != HandleUtility.nearestControl) { s_CurrentAngle = 0f; } int pointsCount = face.Points.Length; s_CurrentArrowHovered = -1; for (int i = 0; i < pointsCount; i++) { var rectHandleSize = HandleUtility.GetHandleSize(face.CenterPosition) * s_DefaultMidpointSquareSize; var sideDirection = (face.Points[(i + 1) % pointsCount] - face.Points[i]).normalized; var arrowDirection = Vector3.Cross(face.Normal.normalized, sideDirection).normalized; var topDirection = 2.5f * rectHandleSize * arrowDirection; var top = face.CenterPosition + topDirection; var A = topDirection.magnitude; var a = 0.33f * Mathf.Sqrt(2f * A * A); var h = 0.5f * Mathf.Sqrt(2f * a * a); s_ArrowsLines[i][0] = top - (h * arrowDirection + h * sideDirection); s_ArrowsLines[i][1] = top; s_ArrowsLines[i][2] = top - (h * arrowDirection - h * sideDirection); bool selected = HandleUtility.nearestControl == k_OrientationControlIDs[i]; Color color = selected ? EditorHandleDrawing.edgeSelectedColor : k_BoundsHandleColor; color.a = 1.0f; using (new Handles.DrawingScope(color)) { Handles.DrawAAPolyLine(5f, s_ArrowsLines[i]); if (selected) { EditorGUIUtility.AddCursorRect(new Rect(0, 0, Screen.width, Screen.height), MouseCursor.RotateArrow); s_CurrentArrowHovered = HandleUtility.nearestControl; Handles.DrawAAPolyLine(3f, new Vector3[] { Vector3.Scale(proBuilderShape.rotation * Vector3.up, proBuilderShape.size / 2f), Vector3.zero, Vector3.Scale(proBuilderShape.rotation * Vector3.forward, proBuilderShape.size / 2f) }); } } } break; case EventType.MouseDrag: if (k_OrientationControlIDs.Contains(s_CurrentId) && HandleUtility.nearestControl != s_CurrentId) { GUIUtility.hotControl = 0; s_CurrentId = -1; } break; } return(hasRotated); }
public override ShapeState DoState(Event evt) { if (evt.type == EventType.KeyDown) { switch (evt.keyCode) { case KeyCode.Escape: ToolManager.RestorePreviousTool(); break; } } if (tool.m_LastShapeCreated != null) { EditShapeTool.DoEditingHandles(tool.m_LastShapeCreated, true); } if (evt.isMouse && HandleUtility.nearestControl == tool.controlID) { var res = EditorHandleUtility.FindBestPlaneAndBitangent(evt.mousePosition); Ray ray = HandleUtility.GUIPointToWorldRay(evt.mousePosition); float hit; if (res.item1.Raycast(ray, out hit)) { //Plane init tool.m_Plane = res.item1; tool.m_PlaneForward = res.item2; tool.m_PlaneRight = Vector3.Cross(tool.m_Plane.normal, tool.m_PlaneForward); var planeNormal = tool.m_Plane.normal; var planeCenter = tool.m_Plane.normal * -tool.m_Plane.distance; // if hit point on plane is cardinal axis and on grid, snap to grid. if (Math.IsCardinalAxis(planeNormal)) { const float epsilon = .00001f; bool offGrid = false; Vector3 snapVal = EditorSnapping.activeMoveSnapValue; Vector3 center = Vector3.Scale(ProBuilderSnapping.GetSnappingMaskBasedOnNormalVector(planeNormal), planeCenter); for (int i = 0; i < 3; i++) { offGrid |= Mathf.Abs(snapVal[i] % center[i]) > epsilon; } tool.m_IsOnGrid = !offGrid; } else { tool.m_IsOnGrid = false; } m_HitPosition = tool.GetPoint(ray.GetPoint(hit)); //Click has been done => Define a plane for the tool if (evt.type == EventType.MouseDown) { //BB init tool.m_BB_Origin = m_HitPosition; tool.m_BB_HeightCorner = tool.m_BB_Origin; tool.m_BB_OppositeCorner = tool.m_BB_Origin; return(NextState()); } } else { m_HitPosition = Vector3.negativeInfinity; } } if (GUIUtility.hotControl == 0 && evt.shift && !(evt.control || evt.command)) { tool.DuplicatePreview(m_HitPosition); } else if (tool.m_DuplicateGO != null) { Object.DestroyImmediate(tool.m_DuplicateGO); } // Repaint to visualize the placement preview dot if (evt.type == EventType.MouseMove && HandleUtility.nearestControl == tool.controlID) { HandleUtility.Repaint(); } if (evt.type == EventType.Repaint) { if (GUIUtility.hotControl == 0 && HandleUtility.nearestControl == tool.controlID) { using (new Handles.DrawingScope(EditorHandleDrawing.vertexSelectedColor)) { Handles.DotHandleCap(-1, m_HitPosition, Quaternion.identity, HandleUtility.GetHandleSize(m_HitPosition) * 0.05f, EventType.Repaint); } } if (GUIUtility.hotControl == 0 && evt.shift && !(evt.control || evt.command)) { tool.DrawBoundingBox(false); } } return(this); }
static ShapeBuilder GetActiveShapeBuilder() { return(s_ShapeBuilders[PMath.Clamp(s_CurrentIndex, 0, s_ShapeBuilders.Length - 1)]); }
static void DoSizeHandles(ProBuilderShape proBuilderShape, bool updatePrefs) { int faceCount = s_Faces.Length; var evt = Event.current; var is2D = proBuilderShape.shape is Plane || proBuilderShape.shape is Sprite; for (int i = 0; i < faceCount; i++) { var face = faces[i]; if (is2D && !face.IsValid) { continue; } if (Event.current.type == EventType.Repaint) { Color color = k_BoundsHandleColor; color.a *= face.IsVisible ? 1f : 0.5f; using (new Handles.DrawingScope(color)) { int pointsCount = face.Points.Length; for (int k = 0; k < pointsCount; k++) { Handles.DrawLine(face.Points[k], face.Points[(k + 1) % pointsCount]); } } } if (DoFaceSizeHandle(face)) { float modifier = 1f; if (evt.alt) { modifier = 2f; } if (!s_SizeManipulationInit) { s_StartCenter = proBuilderShape.transform.position + proBuilderShape.transform.TransformVector(proBuilderShape.shapeBox.center); s_StartPosition = face.CenterPosition; s_StartSize = proBuilderShape.size; s_SizeManipulationInit = true; s_Scaling = Vector3.Scale(face.Normal, Math.Sign(s_StartSize)); } var targetDelta = modifier * (s_TargetSize - s_StartPosition); targetDelta.Scale(s_Scaling); var targetSize = s_StartSize + targetDelta; var snap = Math.IsCardinalAxis(proBuilderShape.transform.up) && EditorSnapSettings.gridSnapEnabled ? EditorSnapping.activeMoveSnapValue : Vector3.zero; targetSize = ProBuilderSnapping.Snap(targetSize, snap); var center = Vector3.zero; if (!evt.alt) { center = Vector3.Scale((targetSize - s_StartSize) / 2f, s_Scaling); center = Vector3.Scale(center, Math.Sign(proBuilderShape.transform.lossyScale)); center = proBuilderShape.transform.TransformVector(center); } ApplyProperties(proBuilderShape, s_StartCenter + center, targetSize); if (updatePrefs) { DrawShapeTool.SaveShapeParams(proBuilderShape); } } } }