diff --git a/Runtime/SplineImporter.cs b/Runtime/SplineImporter.cs index feb63b0..7e1d91a 100644 --- a/Runtime/SplineImporter.cs +++ b/Runtime/SplineImporter.cs @@ -6,6 +6,9 @@ using System.IO; using System.Text; using Unity.Mathematics; using System.Collections.Generic; +using System.Linq; +using Unity.AI.Navigation; +using UnityEngine.AI; namespace FrameJosh.SplineImporter { @@ -22,91 +25,97 @@ namespace FrameJosh.SplineImporter if (GUILayout.Button("Import Spline")) { - SplineImporter splineImporter = target as SplineImporter; - + var splineImporter = (SplineImporter)target; splineImporter.name = splineImporter.splineData.name; - - SplineData splineData = JsonUtility.FromJson(splineImporter.splineData.text); - - SplineContainer splineContainer = splineImporter.GetComponent(); - - foreach (UnityEngine.Splines.Spline thisSpline in splineContainer.Splines) - splineContainer.RemoveSpline(thisSpline); - - foreach (SplineData.Spline thisDataSpline in splineData.splines) + var splineData = JsonUtility.FromJson(splineImporter.splineData.text); + var splineContainer = splineImporter.GetComponent(); + foreach (var thisSpline in splineContainer.Splines) splineContainer.RemoveSpline(thisSpline); +#if COM_UNITY_AI_NAVIGATION + Undo.IncrementCurrentGroup(); + var children = (from Transform child in splineImporter.transform select child.gameObject).ToList(); + children.ForEach(Undo.DestroyObjectImmediate); +#endif + foreach (var thisDataSpline in splineData.splines) { - UnityEngine.Splines.Spline thisSpline = splineContainer.AddSpline(); - + var thisSpline = splineContainer.AddSpline(); thisSpline.Closed = thisDataSpline.closed; - - foreach (ControlPoint thisControlPoint in thisDataSpline.controlPoints) + foreach (var thisControlPoint in thisDataSpline.controlPoints) { - Vector3 position = PositionToVector(thisControlPoint.position); + var position = thisControlPoint.position; + var handleL = thisControlPoint.handleL; + var handleR = thisControlPoint.handleR; + var rotation = Quaternion.LookRotation((float3)handleR - position, Vector3.up) * + Quaternion.AngleAxis(-thisControlPoint.tilt, Vector3.forward); - Vector3 handleL = PositionToVector(thisControlPoint.handleL); + float3x3 rotationMatrix = new(rotation); - Vector3 handleR = PositionToVector(thisControlPoint.handleR); - - Quaternion rotation = Quaternion.LookRotation(handleR - position, Vector3.up) * Quaternion.AngleAxis(-thisControlPoint.tilt, Vector3.forward); - - float3x3 rotationMatrix = new float3x3(rotation); - - thisSpline.Add(new() - { - Position = position * splineImporter.scale, - Rotation = rotation, - TangentIn = ((Vector3)math.mul(handleL - position, rotationMatrix)) * splineImporter.scale, - TangentOut = ((Vector3)math.mul(handleR - position, rotationMatrix)) * splineImporter.scale - }, - TangentMode.Broken); + thisSpline.Add(new BezierKnot + { + Position = position * splineImporter.scale, + Rotation = rotation, + TangentIn = math.mul((float3)handleL - position, rotationMatrix) * splineImporter.scale, + TangentOut = math.mul((float3)handleR - position, rotationMatrix) * splineImporter.scale + }, + TangentMode.Broken); } +#if COM_UNITY_AI_NAVIGATION + if (thisDataSpline.closed) continue; + var go = new GameObject + { + transform = + { + parent = splineImporter.transform + } + }; + var link = go.AddComponent(); + var startPoint = thisDataSpline.controlPoints[0].position * splineImporter.scale; + if (NavMesh.SamplePosition(startPoint, out var startHit, 2f, NavMesh.AllAreas)) + startPoint = startHit.position; + var endPoint = thisDataSpline.controlPoints[^1].position * splineImporter.scale; + if (NavMesh.SamplePosition(endPoint, out var endHit, 2f, NavMesh.AllAreas)) + endPoint = endHit.position; + link.startPoint = startPoint; + link.endPoint = endPoint; + Undo.RegisterCreatedObjectUndo(go, $"Create link from {startPoint} to {endPoint}"); +#endif } } if (GUILayout.Button("Export Spline")) { - SplineImporter splineImporter = target as SplineImporter; - + var splineImporter = (SplineImporter)target; if (!splineImporter.splineData) { - string path = "Assets" + EditorUtility.SaveFilePanel("Save .JSON", "", "New Spline.json", "json").Substring(Application.dataPath.Length); - + var path = "Assets" + + EditorUtility.SaveFilePanel("Save .JSON", "", "New Spline.json", "json")[ + Application.dataPath.Length..]; if (path.Length > 0) { File.WriteAllBytes(path, Encoding.ASCII.GetBytes("")); - AssetDatabase.Refresh(); - - TextAsset textAsset = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset; - + var textAsset = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset; splineImporter.splineData = textAsset; } else return; } - SplineContainer splineContainer = splineImporter.GetComponent(); - - SplineData splineData = new(); - - splineData.splines = new SplineData.Spline[splineContainer.Splines.Count]; - - List dataSplines = new(); - - foreach (UnityEngine.Splines.Spline thisSpline in splineContainer.Splines) + var splineContainer = splineImporter.GetComponent(); + SplineData splineData = new() + { + splines = new Spline[splineContainer.Splines.Count] + }; + List dataSplines = new(); + foreach (var thisSpline in splineContainer.Splines) { List controlPoints = new(); - - foreach (BezierKnot thisBezierKnot in thisSpline.Knots) + foreach (var thisBezierKnot in thisSpline.Knots) { - Position position = VectorToPosition(thisBezierKnot.Position); - - float3x3 rotationMatrix = new float3x3(Quaternion.Inverse(thisBezierKnot.Rotation)); - - Position handleL = VectorToPosition(math.mul(thisBezierKnot.TangentIn, rotationMatrix) + thisBezierKnot.Position); - - Position handleR = VectorToPosition(math.mul(thisBezierKnot.TangentOut, rotationMatrix) + thisBezierKnot.Position); - - controlPoints.Add(new() + Position position = thisBezierKnot.Position; + float3x3 rotationMatrix = new(Quaternion.Inverse(thisBezierKnot.Rotation)); + Position handleL = math.mul(thisBezierKnot.TangentIn, rotationMatrix) + thisBezierKnot.Position; + Position handleR = math.mul(thisBezierKnot.TangentOut, rotationMatrix) + + thisBezierKnot.Position; + controlPoints.Add(new ControlPoint { position = position, handleL = handleL, @@ -114,7 +123,7 @@ namespace FrameJosh.SplineImporter }); } - dataSplines.Add(new() + dataSplines.Add(new Spline { controlPoints = controlPoints.ToArray(), closed = thisSpline.Closed @@ -123,12 +132,14 @@ namespace FrameJosh.SplineImporter splineData.splines = dataSplines.ToArray(); - File.WriteAllText(AssetDatabase.GetAssetPath(splineImporter.splineData), JsonUtility.ToJson(splineData, true)); + File.WriteAllText(AssetDatabase.GetAssetPath(splineImporter.splineData), + JsonUtility.ToJson(splineData, true)); AssetDatabase.Refresh(); } } } + [RequireComponent(typeof(SplineContainer))] #endif public class SplineImporter : MonoBehaviour @@ -136,22 +147,7 @@ namespace FrameJosh.SplineImporter #if UNITY_EDITOR public TextAsset splineData; - public float3 scale = new (1f, 1f, 1f); - - public static Vector3 PositionToVector(Position position) - { - return new(position.x, position.z, position.y); - } - - public static Position VectorToPosition(Vector3 vector) - { - return new() - { - x = vector.x, - y = vector.z, - z = vector.y - }; - } + public float3 scale = new(1f, 1f, 1f); #endif } #if UNITY_EDITOR @@ -162,21 +158,31 @@ namespace FrameJosh.SplineImporter public struct Position { public float x; - public float y; - public float z; + + public static implicit operator Position(float3 vector) + { + return new Position + { + x = vector.x, + y = vector.z, + z = vector.y + }; + } + + public static implicit operator float3(Position vector) + { + return new float3(vector.x, vector.z, vector.y); + } } [Serializable] public struct ControlPoint { public Position position; - public Position handleL; - public Position handleR; - public float tilt; } @@ -188,7 +194,7 @@ namespace FrameJosh.SplineImporter public bool closed; } - public Spline[] splines = new Spline[0]; + public Spline[] splines = Array.Empty(); } #endif } \ No newline at end of file diff --git a/Runtime/josh.spline-importer.asmdef b/Runtime/josh.spline-importer.asmdef index bf74153..fe4baa9 100644 --- a/Runtime/josh.spline-importer.asmdef +++ b/Runtime/josh.spline-importer.asmdef @@ -3,7 +3,8 @@ "rootNamespace": "", "references": [ "Unity.Splines", - "Unity.Mathematics" + "Unity.Mathematics", + "Unity.AI.Navigation" ], "includePlatforms": [], "excludePlatforms": [], @@ -12,6 +13,12 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.ai.navigation", + "expression": "", + "define": "COM_UNITY_AI_NAVIGATION" + } + ], "noEngineReferences": false } \ No newline at end of file diff --git a/package.json b/package.json index ca0e3a6..628bec1 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,14 @@ { "name": "ru.shazbot.spline-importer", - "version": "2.0.2", + "version": "3.0.0", "displayName": "Spline Importer", "description": "Import and export splines between Blender and Unity.", "unity": "2021.3", "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", "com.unity.mathematics": "1.2.6", "com.unity.splines": "2.5.1" - }, + }, "author": { "name": "Alexander Filippov", "email": "alexander@shazbot.ru",