1
1
using System ;
2
2
using UnityEngine ;
3
3
using UnityEditor ;
4
- using Object = UnityEngine . Object ;
5
4
using System . Reflection ;
5
+ using Object = UnityEngine . Object ;
6
6
7
7
namespace BezierSolution . Extras
8
8
{
9
+ public enum QuickEditModePointPlacement { SceneGeometry = 0 , CameraPlane = 1 , XY = 2 , XZ = 3 , YZ = 4 } ;
10
+
9
11
public static class BezierUtils
10
12
{
11
13
private const string PRECEDING_CONTROL_POINT_LABEL = " <--" ;
@@ -30,7 +32,8 @@ public static class BezierUtils
30
32
private static readonly GUIContent AUTO_CONSTRUCT_SPLINE_2_TEXT = new GUIContent ( "Auto Construct Spline 2" , "Constructs a smooth path (another algorithm)" ) ;
31
33
private static readonly GUIContent AUTO_CALCULATE_NORMALS_TEXT = new GUIContent ( "Auto Calculate Normals" , "Attempts to automatically calculate the end points' normal vectors" ) ;
32
34
private static readonly GUIContent AUTO_CONSTRUCT_ALWAYS_TEXT = new GUIContent ( "Always" , "Applies this method automatically as spline's points change" ) ;
33
- private static readonly GUIContent QUICK_EDIT_MODE_TEXT = new GUIContent ( "Quick Edit Mode" , "Quickly add new points to the spline or snap existing points to the scene geometry" ) ;
35
+ private static readonly GUIContent QUICK_EDIT_MODE_TEXT = new GUIContent ( "Quick Edit Mode" , "Quickly add new points to the spline or move the existing points" ) ;
36
+ private static readonly GUIContent QUICK_EDIT_POINT_PLACEMENT_MODE_TEXT = new GUIContent ( "Point Placement Mode" , "Determines where the dragged or newly created points will be placed:\n - Scene Geometry: the scene geometry under the cursor\n - Camera Plane: Scene camera's local XY plane\n - XY: XY plane\n - XZ: XZ plane\n - YZ: YZ plane" ) ;
34
37
private static readonly GUIContent QUICK_EDIT_MODIFY_NORMALS_TEXT = new GUIContent ( "Use Raycast Normals" , "While dragging a point or adding a new point, the point's Normal vector will be set to the normal of the scene geometry under the cursor" ) ;
35
38
private static readonly GUIContent QUICK_EDIT_PRESERVE_SPLINE_SHAPE_TEXT = new GUIContent ( "Preserve Spline Shape" , "While inserting new points along the spline, the spline's shape will be preserved but the neighboring end points' 'Handle Mode' will no longer be 'Mirrored'" ) ;
36
39
@@ -515,21 +518,23 @@ public static void DrawSplineInspectorGUI( BezierSpline[] splines )
515
518
516
519
if ( QuickEditSplineMode )
517
520
{
518
- EditorGUILayout . HelpBox ( "- Dragging a point: snaps the dragged point to the scene geometry under the cursor \n - CTRL+Left Click: adds a new point to the end of the spline\n - CTRL+Shift+Left Click: inserts a new point along the spline\n - Shift+Left Click: deletes clicked point" , MessageType . Info ) ;
521
+ EditorGUILayout . HelpBox ( "- Dragging a point: moves the dragged point\n - CTRL+Left Click: adds a new point to the end of the spline\n - CTRL+Shift+Left Click: inserts a new point along the spline\n - Shift+Left Click: deletes clicked point" , MessageType . Info ) ;
519
522
520
- if ( Array . Find ( splines , ( s ) => ! s . autoCalculateNormals ) )
523
+ EditorGUI . indentLevel ++ ;
524
+
525
+ BezierSettings . QuickEditPointPlacement = ( QuickEditModePointPlacement ) EditorGUILayout . EnumPopup ( QUICK_EDIT_POINT_PLACEMENT_MODE_TEXT , BezierSettings . QuickEditPointPlacement ) ;
526
+
527
+ if ( BezierSettings . QuickEditPointPlacement == QuickEditModePointPlacement . SceneGeometry && Array . Find ( splines , ( s ) => ! s . autoCalculateNormals ) )
521
528
{
522
529
EditorGUI . indentLevel ++ ;
523
530
BezierSettings . QuickEditSplineModifyNormals = EditorGUILayout . Toggle ( QUICK_EDIT_MODIFY_NORMALS_TEXT , BezierSettings . QuickEditSplineModifyNormals ) ;
524
531
EditorGUI . indentLevel -- ;
525
532
}
526
533
527
534
if ( Array . Find ( splines , ( s ) => s . autoConstructMode == SplineAutoConstructMode . None ) )
528
- {
529
- EditorGUI . indentLevel ++ ;
530
535
BezierSettings . QuickEditSplinePreserveShape = EditorGUILayout . Toggle ( QUICK_EDIT_PRESERVE_SPLINE_SHAPE_TEXT , BezierSettings . QuickEditSplinePreserveShape ) ;
531
- EditorGUI . indentLevel -- ;
532
- }
536
+
537
+ EditorGUI . indentLevel -- ;
533
538
}
534
539
}
535
540
@@ -559,13 +564,14 @@ public static void DrawBezierPoint( BezierPoint point, int pointIndex, bool isSe
559
564
{
560
565
// Point is dragged, snap it to the scene geometry
561
566
Vector3 sceneHitPoint , sceneHitNormal ;
562
- RaycastAgainstScene ( point . spline [ point . spline . Count - 1 ] , out sceneHitPoint , out sceneHitNormal ) ;
563
-
564
- Undo . RecordObject ( point . transform , "Move point" ) ;
565
- point . transform . position = sceneHitPoint ;
567
+ if ( RaycastAgainstScene ( point . spline [ point . spline . Count - 1 ] , out sceneHitPoint , out sceneHitNormal ) )
568
+ {
569
+ Undo . RecordObject ( point . transform , "Move point" ) ;
570
+ point . transform . position = sceneHitPoint ;
566
571
567
- if ( BezierSettings . QuickEditSplineModifyNormals && ! point . spline . autoCalculateNormals )
568
- point . SetNormalAndResetIntermediateNormals ( sceneHitNormal , "Move point" ) ;
572
+ if ( BezierSettings . QuickEditPointPlacement == QuickEditModePointPlacement . SceneGeometry && BezierSettings . QuickEditSplineModifyNormals && ! point . spline . autoCalculateNormals )
573
+ point . SetNormalAndResetIntermediateNormals ( sceneHitNormal , "Move point" ) ;
574
+ }
569
575
}
570
576
}
571
577
else
@@ -745,34 +751,35 @@ public static void QuickEditModeSceneGUI( BezierSpline[] splines )
745
751
if ( closestEndPoint )
746
752
{
747
753
Vector3 sceneHitPoint , sceneHitNormal ;
748
- RaycastAgainstScene ( closestEndPoint , out sceneHitPoint , out sceneHitNormal ) ;
749
-
750
- // Draw a line from the closest end point to the raycast hit point
751
- Color c = Handles . color ;
752
- Handles . color = BezierSettings . QuickEditModeNewEndPointColor ;
753
- Handles . DotHandleCap ( 0 , sceneHitPoint , Quaternion . identity , HandleUtility . GetHandleSize ( sceneHitPoint ) * BezierSettings . QuickEditModeNewEndPointSize , EventType . Repaint ) ;
754
- Handles . DrawLine ( sceneHitPoint , closestEndPoint . position ) ;
755
- Handles . color = c ;
756
-
757
- // When left clicked, insert a point at the highlighted position
758
- if ( e . type == EventType . MouseDown && e . button == 0 )
754
+ if ( RaycastAgainstScene ( closestEndPoint , out sceneHitPoint , out sceneHitNormal ) )
759
755
{
760
- BezierPoint newPoint = closestEndPoint . spline . InsertNewPointAt ( closestEndPoint . spline . Count ) ;
761
- newPoint . position = sceneHitPoint ;
762
- if ( BezierSettings . QuickEditSplineModifyNormals && ! closestEndPoint . spline . autoCalculateNormals )
763
- newPoint . SetNormalAndResetIntermediateNormals ( sceneHitNormal , null ) ;
764
-
765
- // Rotate the previous point's followingControlPointPosition in the direction of the new point and assign the resulting vector
766
- // to the new point's followingControlPointPosition
767
- Vector3 directionToNewPoint = sceneHitPoint - closestEndPoint . position ;
768
- Quaternion controlPointDeltaRotation = Quaternion . FromToRotation ( closestEndPoint . followingControlPointPosition - closestEndPoint . position , directionToNewPoint ) ;
769
- newPoint . followingControlPointPosition = sceneHitPoint + controlPointDeltaRotation * ( directionToNewPoint * 0.35f ) ;
770
-
771
- Undo . RegisterCreatedObjectUndo ( newPoint . gameObject , "Insert Point" ) ;
772
- if ( newPoint . transform . parent )
773
- Undo . RegisterCompleteObjectUndo ( newPoint . transform . parent , "Insert Point" ) ;
774
-
775
- e . Use ( ) ;
756
+ // Draw a line from the closest end point to the raycast hit point
757
+ Color c = Handles . color ;
758
+ Handles . color = BezierSettings . QuickEditModeNewEndPointColor ;
759
+ Handles . DotHandleCap ( 0 , sceneHitPoint , Quaternion . identity , HandleUtility . GetHandleSize ( sceneHitPoint ) * BezierSettings . QuickEditModeNewEndPointSize , EventType . Repaint ) ;
760
+ Handles . DrawLine ( sceneHitPoint , closestEndPoint . position ) ;
761
+ Handles . color = c ;
762
+
763
+ // When left clicked, insert a point at the highlighted position
764
+ if ( e . type == EventType . MouseDown && e . button == 0 )
765
+ {
766
+ BezierPoint newPoint = closestEndPoint . spline . InsertNewPointAt ( closestEndPoint . spline . Count ) ;
767
+ newPoint . position = sceneHitPoint ;
768
+ if ( BezierSettings . QuickEditPointPlacement == QuickEditModePointPlacement . SceneGeometry && BezierSettings . QuickEditSplineModifyNormals && ! closestEndPoint . spline . autoCalculateNormals )
769
+ newPoint . SetNormalAndResetIntermediateNormals ( sceneHitNormal , null ) ;
770
+
771
+ // Rotate the previous point's followingControlPointPosition in the direction of the new point and assign the resulting vector
772
+ // to the new point's followingControlPointPosition
773
+ Vector3 directionToNewPoint = sceneHitPoint - closestEndPoint . position ;
774
+ Quaternion controlPointDeltaRotation = Quaternion . FromToRotation ( closestEndPoint . followingControlPointPosition - closestEndPoint . position , directionToNewPoint ) ;
775
+ newPoint . followingControlPointPosition = sceneHitPoint + controlPointDeltaRotation * ( directionToNewPoint * 0.35f ) ;
776
+
777
+ Undo . RegisterCreatedObjectUndo ( newPoint . gameObject , "Insert Point" ) ;
778
+ if ( newPoint . transform . parent )
779
+ Undo . RegisterCompleteObjectUndo ( newPoint . transform . parent , "Insert Point" ) ;
780
+
781
+ e . Use ( ) ;
782
+ }
776
783
}
777
784
}
778
785
}
@@ -906,71 +913,87 @@ internal static void SetSplineDirtyWithUndo( BezierSpline spline, string undo, I
906
913
spline . CheckDirty ( ) ;
907
914
}
908
915
909
- private static void RaycastAgainstScene ( BezierPoint referencePoint , out Vector3 position , out Vector3 normal )
916
+ private static bool RaycastAgainstScene ( BezierPoint referencePoint , out Vector3 position , out Vector3 normal )
910
917
{
911
918
EventType eventType = Event . current . type ;
912
919
Ray ray = HandleUtility . GUIPointToWorldRay ( Event . current . mousePosition ) ;
913
920
914
- // First, try raycasting against scene geometry with or without colliders (it doesn't matter)
915
- // Credit: https://forum.unity.com/threads/editor-raycast-against-scene-meshes-without-collider-editor-select-object-using-gui-coordinate.485502
916
- if ( intersectRayMeshMethod != null && eventType != EventType . Layout && eventType != EventType . Repaint ) // HandleUtility.PickGameObject doesn't work with Layout and Repaint events in OnSceneGUI
921
+ if ( BezierSettings . QuickEditPointPlacement == QuickEditModePointPlacement . SceneGeometry )
917
922
{
918
- GameObject gameObjectUnderCursor = HandleUtility . PickGameObject ( Event . current . mousePosition , false ) ;
919
- if ( gameObjectUnderCursor )
923
+ // First, try raycasting against scene geometry with or without colliders (it doesn't matter)
924
+ // Credit: https://forum.unity.com/threads/editor-raycast-against-scene-meshes-without-collider-editor-select-object-using-gui-coordinate.485502
925
+ if ( intersectRayMeshMethod != null && eventType != EventType . Layout && eventType != EventType . Repaint ) // HandleUtility.PickGameObject doesn't work with Layout and Repaint events in OnSceneGUI
920
926
{
921
- Mesh meshUnderCursor = null ;
922
- MeshFilter meshFilter = gameObjectUnderCursor . GetComponent < MeshFilter > ( ) ;
923
- if ( meshFilter )
924
- meshUnderCursor = meshFilter . sharedMesh ;
925
-
926
- if ( ! meshUnderCursor )
927
+ GameObject gameObjectUnderCursor = HandleUtility . PickGameObject ( Event . current . mousePosition , false ) ;
928
+ if ( gameObjectUnderCursor )
927
929
{
928
- SkinnedMeshRenderer skinnedMeshRenderer = gameObjectUnderCursor . GetComponent < SkinnedMeshRenderer > ( ) ;
929
- if ( skinnedMeshRenderer )
930
- meshUnderCursor = skinnedMeshRenderer . sharedMesh ;
931
- }
930
+ Mesh meshUnderCursor = null ;
931
+ MeshFilter meshFilter = gameObjectUnderCursor . GetComponent < MeshFilter > ( ) ;
932
+ if ( meshFilter )
933
+ meshUnderCursor = meshFilter . sharedMesh ;
932
934
933
- if ( meshUnderCursor )
934
- {
935
- object [ ] rayMeshParameters = new object [ ] { ray , meshUnderCursor , gameObjectUnderCursor . transform . localToWorldMatrix , null } ;
936
- if ( ( bool ) intersectRayMeshMethod . Invoke ( null , rayMeshParameters ) )
935
+ if ( ! meshUnderCursor )
936
+ {
937
+ SkinnedMeshRenderer skinnedMeshRenderer = gameObjectUnderCursor . GetComponent < SkinnedMeshRenderer > ( ) ;
938
+ if ( skinnedMeshRenderer )
939
+ meshUnderCursor = skinnedMeshRenderer . sharedMesh ;
940
+ }
941
+
942
+ if ( meshUnderCursor )
937
943
{
938
- RaycastHit hit = ( RaycastHit ) rayMeshParameters [ 3 ] ;
939
- position = hit . point ;
940
- normal = hit . normal . normalized ;
944
+ object [ ] rayMeshParameters = new object [ ] { ray , meshUnderCursor , gameObjectUnderCursor . transform . localToWorldMatrix , null } ;
945
+ if ( ( bool ) intersectRayMeshMethod . Invoke ( null , rayMeshParameters ) )
946
+ {
947
+ RaycastHit hit = ( RaycastHit ) rayMeshParameters [ 3 ] ;
948
+ position = hit . point ;
949
+ normal = hit . normal . normalized ;
941
950
942
- return ;
951
+ return true ;
952
+ }
943
953
}
944
954
}
945
955
}
946
- }
947
956
948
- // Raycast against scene geometry with colliders
949
- object raycastResult = HandleUtility . RaySnap ( ray ) ;
950
- if ( raycastResult != null && raycastResult is RaycastHit )
951
- {
952
- position = ( ( RaycastHit ) raycastResult ) . point ;
953
- normal = ( ( RaycastHit ) raycastResult ) . normal . normalized ;
957
+ // Raycast against scene geometry with colliders
958
+ object raycastResult = HandleUtility . RaySnap ( ray ) ;
959
+ if ( raycastResult != null && raycastResult is RaycastHit )
960
+ {
961
+ position = ( ( RaycastHit ) raycastResult ) . point ;
962
+ normal = ( ( RaycastHit ) raycastResult ) . normal . normalized ;
954
963
955
- return ;
964
+ return true ;
965
+ }
956
966
}
957
967
958
968
// Raycast against a plane that goes through referencePoint
959
969
if ( referencePoint )
960
970
{
961
- Plane plane = new Plane ( referencePoint . normal , referencePoint . position ) ;
971
+ Vector3 planeNormal ;
972
+ switch ( BezierSettings . QuickEditPointPlacement )
973
+ {
974
+ case QuickEditModePointPlacement . SceneGeometry : planeNormal = ( referencePoint . normal != Vector3 . zero ) ? referencePoint . normal : Vector3 . up ; break ;
975
+ case QuickEditModePointPlacement . CameraPlane : planeNormal = SceneView . lastActiveSceneView . camera . transform . forward ; break ;
976
+ case QuickEditModePointPlacement . XY : planeNormal = Vector3 . forward ; break ;
977
+ case QuickEditModePointPlacement . XZ : planeNormal = Vector3 . up ; break ;
978
+ case QuickEditModePointPlacement . YZ : planeNormal = Vector3 . right ; break ;
979
+ default : planeNormal = Vector3 . up ; break ;
980
+ }
981
+
982
+ Plane plane = new Plane ( planeNormal , referencePoint . position ) ;
962
983
float enter ;
963
984
if ( plane . Raycast ( ray , out enter ) )
964
985
{
965
986
position = ray . GetPoint ( enter ) ;
966
987
normal = referencePoint . normal ;
967
988
968
- return ;
989
+ return true ;
969
990
}
970
991
}
971
992
972
993
position = ray . GetPoint ( 5f ) ;
973
994
normal = Vector3 . up ;
995
+
996
+ return false ;
974
997
}
975
998
976
999
public static void DrawSeparator ( )
0 commit comments