Initial commit
This commit is contained in:
commit
23aa1fb180
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
72
.gitignore
vendored
Normal file
72
.gitignore
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# This .gitignore file should be placed at the root of your Unity project directory
|
||||||
|
#
|
||||||
|
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
|
||||||
|
#
|
||||||
|
/[Ll]ibrary/
|
||||||
|
/[Tt]emp/
|
||||||
|
/[Oo]bj/
|
||||||
|
/[Bb]uild/
|
||||||
|
/[Bb]uilds/
|
||||||
|
/[Ll]ogs/
|
||||||
|
/[Uu]ser[Ss]ettings/
|
||||||
|
|
||||||
|
# MemoryCaptures can get excessive in size.
|
||||||
|
# They also could contain extremely sensitive data
|
||||||
|
/[Mm]emoryCaptures/
|
||||||
|
|
||||||
|
# Recordings can get excessive in size
|
||||||
|
/[Rr]ecordings/
|
||||||
|
|
||||||
|
# Uncomment this line if you wish to ignore the asset store tools plugin
|
||||||
|
# /[Aa]ssets/AssetStoreTools*
|
||||||
|
|
||||||
|
# Autogenerated Jetbrains Rider plugin
|
||||||
|
/[Aa]ssets/Plugins/Editor/JetBrains*
|
||||||
|
|
||||||
|
# Visual Studio cache directory
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Gradle cache directory
|
||||||
|
.gradle/
|
||||||
|
|
||||||
|
# Autogenerated VS/MD/Consulo solution and project files
|
||||||
|
ExportedObj/
|
||||||
|
.consulo/
|
||||||
|
*.csproj
|
||||||
|
*.unityproj
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.tmp
|
||||||
|
*.user
|
||||||
|
*.userprefs
|
||||||
|
*.pidb
|
||||||
|
*.booproj
|
||||||
|
*.svd
|
||||||
|
*.pdb
|
||||||
|
*.mdb
|
||||||
|
*.opendb
|
||||||
|
*.VC.db
|
||||||
|
|
||||||
|
# Unity3D generated meta files
|
||||||
|
*.pidb.meta
|
||||||
|
*.pdb.meta
|
||||||
|
*.mdb.meta
|
||||||
|
|
||||||
|
# Unity3D generated file on crash reports
|
||||||
|
sysinfo.txt
|
||||||
|
|
||||||
|
# Builds
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
*.unitypackage
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# Crashlytics generated file
|
||||||
|
crashlytics-build.properties
|
||||||
|
|
||||||
|
# Packed Addressables
|
||||||
|
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
|
||||||
|
|
||||||
|
# Temporary auto-generated Android Assets
|
||||||
|
/[Aa]ssets/[Ss]treamingAssets/aa.meta
|
||||||
|
/[Aa]ssets/[Ss]treamingAssets/aa/*
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Josh4359
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
7
LICENSE.meta
Normal file
7
LICENSE.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: def6304472883004fa1dc20959d55415
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
2
README.md
Normal file
2
README.md
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Spline-Importer
|
||||||
|
Import and export splines between Blender and Unity
|
7
README.md.meta
Normal file
7
README.md.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8e3449f6187d6b64dbc1ec8bdf5151d1
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Runtime.meta
Normal file
8
Runtime.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5d99f03d3767d06409c6524aa84af941
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
190
Runtime/SplineImporter.cs
Normal file
190
Runtime/SplineImporter.cs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Splines;
|
||||||
|
using static SplineImporter;
|
||||||
|
using static SplineData;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(SplineImporter))]
|
||||||
|
public class SplineImporterEditor : Editor
|
||||||
|
{
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
DrawDefaultInspector();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Import Spline"))
|
||||||
|
{
|
||||||
|
SplineImporter splineImporter = target as SplineImporter;
|
||||||
|
|
||||||
|
splineImporter.name = splineImporter.splineData.name;
|
||||||
|
|
||||||
|
SplineData splineData = JsonUtility.FromJson<SplineData>(splineImporter.splineData.text);
|
||||||
|
|
||||||
|
SplineContainer splineContainer = splineImporter.GetComponent<SplineContainer>();
|
||||||
|
|
||||||
|
foreach (UnityEngine.Splines.Spline thisSpline in splineContainer.Splines)
|
||||||
|
splineContainer.RemoveSpline(thisSpline);
|
||||||
|
|
||||||
|
foreach (SplineData.Spline thisDataSpline in splineData.splines)
|
||||||
|
{
|
||||||
|
UnityEngine.Splines.Spline thisSpline = splineContainer.AddSpline();
|
||||||
|
|
||||||
|
thisSpline.Closed = thisDataSpline.closed;
|
||||||
|
|
||||||
|
foreach (ControlPoint thisControlPoint in thisDataSpline.controlPoints)
|
||||||
|
{
|
||||||
|
Vector3 position = PositionToVector(thisControlPoint.position);
|
||||||
|
|
||||||
|
Vector3 handleL = PositionToVector(thisControlPoint.handleL);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Export Spline"))
|
||||||
|
{
|
||||||
|
SplineImporter splineImporter = target as SplineImporter;
|
||||||
|
|
||||||
|
if (!splineImporter.splineData)
|
||||||
|
{
|
||||||
|
string path = "Assets" + EditorUtility.SaveFilePanel("Save .JSON", "", "New Spline.json", "json").Substring(Application.dataPath.Length);
|
||||||
|
|
||||||
|
if (path.Length > 0)
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(path, Encoding.ASCII.GetBytes(""));
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
TextAsset textAsset = AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset)) as TextAsset;
|
||||||
|
|
||||||
|
splineImporter.splineData = textAsset;
|
||||||
|
}
|
||||||
|
else return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SplineContainer splineContainer = splineImporter.GetComponent<SplineContainer>();
|
||||||
|
|
||||||
|
SplineData splineData = new();
|
||||||
|
|
||||||
|
splineData.splines = new SplineData.Spline[splineContainer.Splines.Count];
|
||||||
|
|
||||||
|
List<SplineData.Spline> dataSplines = new();
|
||||||
|
|
||||||
|
foreach (UnityEngine.Splines.Spline thisSpline in splineContainer.Splines)
|
||||||
|
{
|
||||||
|
List<ControlPoint> controlPoints = new();
|
||||||
|
|
||||||
|
foreach (BezierKnot 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,
|
||||||
|
handleL = handleL,
|
||||||
|
handleR = handleR
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSplines.Add(new()
|
||||||
|
{
|
||||||
|
controlPoints = controlPoints.ToArray(),
|
||||||
|
closed = thisSpline.Closed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
splineData.splines = dataSplines.ToArray();
|
||||||
|
|
||||||
|
File.WriteAllText(AssetDatabase.GetAssetPath(splineImporter.splineData), JsonUtility.ToJson(splineData, true));
|
||||||
|
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RequireComponent(typeof(SplineContainer))]
|
||||||
|
public class SplineImporter : MonoBehaviour
|
||||||
|
{
|
||||||
|
public TextAsset splineData;
|
||||||
|
|
||||||
|
public float scale = 1;
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class SplineData
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public struct Position
|
||||||
|
{
|
||||||
|
public float x;
|
||||||
|
|
||||||
|
public float y;
|
||||||
|
|
||||||
|
public float z;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public struct ControlPoint
|
||||||
|
{
|
||||||
|
public Position position;
|
||||||
|
|
||||||
|
public Position handleL;
|
||||||
|
|
||||||
|
public Position handleR;
|
||||||
|
|
||||||
|
public float tilt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public struct Spline
|
||||||
|
{
|
||||||
|
public ControlPoint[] controlPoints;
|
||||||
|
|
||||||
|
public bool closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spline[] splines = new Spline[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
11
Runtime/SplineImporter.cs.meta
Normal file
11
Runtime/SplineImporter.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d91b840f8503aaf409d992f7c89e93ed
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
195
Runtime/SplinePlus.cs
Normal file
195
Runtime/SplinePlus.cs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
using Unity.Mathematics;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Splines;
|
||||||
|
|
||||||
|
public class SplinePlus : MonoBehaviour
|
||||||
|
{
|
||||||
|
public SplineContainer splineContainer;
|
||||||
|
|
||||||
|
public SplineContainer deformContainer;
|
||||||
|
|
||||||
|
public int resolution;
|
||||||
|
|
||||||
|
public void Evaluate(int splineIndex, float anchor, float distance, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
float t = anchor + (distance / splineContainer.Spline.GetLength());
|
||||||
|
|
||||||
|
if (deformContainer)
|
||||||
|
DeformSpline(splineIndex, t, out position, out rotation);
|
||||||
|
else
|
||||||
|
EvaluateSpline(splineIndex, t, out position, out rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Evaluate(float anchor, float distance, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
float t = anchor + (distance / splineContainer.Spline.GetLength());
|
||||||
|
|
||||||
|
if (deformContainer)
|
||||||
|
DeformSpline(t, out position, out rotation);
|
||||||
|
else
|
||||||
|
EvaluateSpline(t, out position, out rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetNearestPoint(Vector3 point, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
position = Vector3.zero;
|
||||||
|
|
||||||
|
rotation = Quaternion.identity;
|
||||||
|
|
||||||
|
float nearestDistance = Mathf.Infinity;
|
||||||
|
|
||||||
|
for (int i = 0; i < splineContainer.Splines.Count; i++)
|
||||||
|
{
|
||||||
|
int resolutionScale = Mathf.CeilToInt(splineContainer.Splines[i].GetLength()) * resolution;
|
||||||
|
|
||||||
|
for (float j = 0; j <= resolutionScale; j++)
|
||||||
|
{
|
||||||
|
Evaluate(i, j / resolutionScale, 0, out Vector3 thisPosition, out Quaternion thisRotation);
|
||||||
|
|
||||||
|
float thisDistance = Vector3.Distance(point, thisPosition);
|
||||||
|
|
||||||
|
if (thisDistance < nearestDistance)
|
||||||
|
{
|
||||||
|
position = thisPosition;
|
||||||
|
|
||||||
|
rotation = thisRotation;
|
||||||
|
|
||||||
|
nearestDistance = thisDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvaluateSpline(int splineIndex, float t, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
ScaledEvaluate(splineContainer, splineIndex, t, out float3 position1, out float3 tangent, out float3 upVector);
|
||||||
|
|
||||||
|
position = position1;
|
||||||
|
|
||||||
|
rotation = Quaternion.LookRotation(tangent, upVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EvaluateSpline(float t, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
splineContainer.Evaluate(t, out float3 position1, out float3 tangent, out float3 upVector);
|
||||||
|
|
||||||
|
position = position1;
|
||||||
|
|
||||||
|
rotation = Quaternion.LookRotation(tangent, upVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeformSpline(int splineIndex, float t, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
int resolutionScale = Mathf.CeilToInt(splineContainer.Splines[splineIndex].GetLength()) * resolution;
|
||||||
|
|
||||||
|
position = EvaluatePoint(splineIndex, t, out float3 upVector);
|
||||||
|
|
||||||
|
float t1 = Mathf.Clamp(t, 0, 1 - (1 / (float)resolutionScale));
|
||||||
|
|
||||||
|
Vector3 position0 = EvaluatePoint(splineIndex, t1, out _);
|
||||||
|
|
||||||
|
Vector3 position1 = EvaluatePoint(splineIndex, t1 + (1 / (float)resolutionScale), out _);
|
||||||
|
|
||||||
|
Vector3 difference = position1 - position0;
|
||||||
|
|
||||||
|
rotation = Vector3.Dot(difference, upVector) > 0
|
||||||
|
? Quaternion.LookRotation(difference, upVector)
|
||||||
|
: Quaternion.FromToRotation(Vector3.forward, difference);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeformSpline(float t, out Vector3 position, out Quaternion rotation)
|
||||||
|
{
|
||||||
|
int resolutionScale = Mathf.CeilToInt(splineContainer.CalculateLength()) * resolution;
|
||||||
|
|
||||||
|
position = EvaluatePoint(t, out float3 upVector);
|
||||||
|
|
||||||
|
float t1 = Mathf.Clamp(t, 0, 1 - (1 / (float)resolutionScale));
|
||||||
|
|
||||||
|
Vector3 position0 = EvaluatePoint(t1, out _);
|
||||||
|
|
||||||
|
Vector3 position1 = EvaluatePoint(t1 + (1 / (float)resolutionScale), out _);
|
||||||
|
|
||||||
|
Vector3 difference = position1 - position0;
|
||||||
|
|
||||||
|
rotation = Vector3.Dot(difference, upVector) > 0
|
||||||
|
? Quaternion.LookRotation(difference, upVector)
|
||||||
|
: Quaternion.FromToRotation(Vector3.forward, difference);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 EvaluatePoint(int splineIndex, float t, out float3 upVector)
|
||||||
|
{
|
||||||
|
ScaledEvaluate(splineContainer, splineIndex, t, out float3 position, out _, out upVector);
|
||||||
|
|
||||||
|
ScaledEvaluate(deformContainer, 0, position.x / deformContainer.Spline.GetLength(), out float3 deformPosition, out float3 deformTangent, out float3 deformUpVector);
|
||||||
|
|
||||||
|
float3x3 deformMatrix = new()
|
||||||
|
{
|
||||||
|
c0 = (float3)Vector3.Normalize(Vector3.Cross(deformTangent, deformUpVector)),
|
||||||
|
c1 = (float3)Vector3.Normalize(deformUpVector),
|
||||||
|
c2 = (float3)Vector3.Normalize(deformTangent)
|
||||||
|
};
|
||||||
|
|
||||||
|
return deformPosition + (deformMatrix.c0 * position.z) + (deformMatrix.c1 * position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 EvaluatePoint(float t, out float3 upVector)
|
||||||
|
{
|
||||||
|
splineContainer.Evaluate(t, out float3 position, out _, out upVector);
|
||||||
|
|
||||||
|
ScaledEvaluate(deformContainer, 0, position.x / deformContainer.Spline.GetLength(), out float3 deformPosition, out float3 deformTangent, out float3 deformUpVector);
|
||||||
|
|
||||||
|
float3x3 deformMatrix = new()
|
||||||
|
{
|
||||||
|
c0 = (float3)Vector3.Normalize(Vector3.Cross(deformTangent, deformUpVector)),
|
||||||
|
c1 = (float3)Vector3.Normalize(deformUpVector),
|
||||||
|
c2 = (float3)Vector3.Normalize(deformTangent)
|
||||||
|
};
|
||||||
|
|
||||||
|
return deformPosition + (deformMatrix.c0 * position.z) + (deformMatrix.c1 * position.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScaledEvaluate(SplineContainer splineContainer, int splineIndex, float t, out float3 position, out float3 tangent, out float3 upVector)
|
||||||
|
{
|
||||||
|
Spline spline = splineContainer.Splines[splineIndex];
|
||||||
|
|
||||||
|
if (spline == null)
|
||||||
|
{
|
||||||
|
splineContainer.Evaluate(t, out position, out tangent, out upVector);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SplineUtility.Evaluate(splineContainer.Splines[splineIndex], t, out position, out tangent, out upVector);
|
||||||
|
|
||||||
|
position = splineContainer.transform.TransformPoint(position);
|
||||||
|
|
||||||
|
tangent = splineContainer.transform.TransformVector(tangent);
|
||||||
|
|
||||||
|
upVector = splineContainer.transform.TransformDirection(upVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
if (!splineContainer || !deformContainer) return;
|
||||||
|
|
||||||
|
Gizmos.color = Color.green;
|
||||||
|
|
||||||
|
for (int i = 0; i < splineContainer.Splines.Count; i++)
|
||||||
|
{
|
||||||
|
Evaluate(i, 0, 0, out Vector3 position, out _);
|
||||||
|
|
||||||
|
Vector3 oldPosition = position;
|
||||||
|
|
||||||
|
int gizmoResolution = Mathf.CeilToInt(splineContainer.Splines[i].GetLength());
|
||||||
|
|
||||||
|
for (float j = 1; j <= gizmoResolution; j++)
|
||||||
|
{
|
||||||
|
Evaluate(i, j / gizmoResolution, 0, out position, out _);
|
||||||
|
|
||||||
|
Gizmos.DrawLine(oldPosition, position);
|
||||||
|
|
||||||
|
oldPosition = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Runtime/SplinePlus.cs.meta
Normal file
11
Runtime/SplinePlus.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 41eeb6f8f41efdb4eb29d6db19d12b6c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
17
Runtime/josh.spline-importer.asmdef
Normal file
17
Runtime/josh.spline-importer.asmdef
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "josh.spline-importer",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"Unity.Splines",
|
||||||
|
"Unity.Mathematics"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
7
Runtime/josh.spline-importer.asmdef.meta
Normal file
7
Runtime/josh.spline-importer.asmdef.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: aa8af8f66b6c2db4fa61f07958e484b9
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Samples.meta
Normal file
8
Samples.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dad66e5ce81d3da46a6f24be534d3c90
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
21
Samples/README.txt
Normal file
21
Samples/README.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
In the Spline Debug scene, there are 6 important objects:
|
||||||
|
|
||||||
|
1. Spline
|
||||||
|
- A curved spline going from (0, 0, 0) to (10, 0, -5)
|
||||||
|
|
||||||
|
2. Deform
|
||||||
|
- A curved spline used to deform Spline
|
||||||
|
|
||||||
|
3. Spline Plus
|
||||||
|
- An object with the SplinePlus component
|
||||||
|
- This object is used to deform spline Spline along spline Deform
|
||||||
|
- The resulting spline is rendered in green with Gizmos enabled
|
||||||
|
|
||||||
|
4. Evaluate
|
||||||
|
- Renders a cube gizmo along each of the above splines at a given distance from a given anchor point
|
||||||
|
|
||||||
|
5. Nearest Point
|
||||||
|
- Renders a cube gizmo at the nearest point along the deformed spline from the Spline Plus object
|
||||||
|
|
||||||
|
6. Spline Debug
|
||||||
|
- An instantiated Blender file including a tube warped around the deformed spline using Blender's Curve modifier
|
7
Samples/README.txt.meta
Normal file
7
Samples/README.txt.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4f23fad103e2d8548be04a5c255ce66b
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Samples/Spline Debug.blend
Normal file
BIN
Samples/Spline Debug.blend
Normal file
Binary file not shown.
109
Samples/Spline Debug.blend.meta
Normal file
109
Samples/Spline Debug.blend.meta
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b93c3e7374b43344ca6fa176d935f2bd
|
||||||
|
ModelImporter:
|
||||||
|
serializedVersion: 22200
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
materials:
|
||||||
|
materialImportMode: 2
|
||||||
|
materialName: 0
|
||||||
|
materialSearch: 1
|
||||||
|
materialLocation: 1
|
||||||
|
animations:
|
||||||
|
legacyGenerateAnimations: 4
|
||||||
|
bakeSimulation: 0
|
||||||
|
resampleCurves: 1
|
||||||
|
optimizeGameObjects: 0
|
||||||
|
removeConstantScaleCurves: 0
|
||||||
|
motionNodeName:
|
||||||
|
rigImportErrors:
|
||||||
|
rigImportWarnings:
|
||||||
|
animationImportErrors:
|
||||||
|
animationImportWarnings:
|
||||||
|
animationRetargetingWarnings:
|
||||||
|
animationDoRetargetingWarnings: 0
|
||||||
|
importAnimatedCustomProperties: 0
|
||||||
|
importConstraints: 0
|
||||||
|
animationCompression: 1
|
||||||
|
animationRotationError: 0.5
|
||||||
|
animationPositionError: 0.5
|
||||||
|
animationScaleError: 0.5
|
||||||
|
animationWrapMode: 0
|
||||||
|
extraExposedTransformPaths: []
|
||||||
|
extraUserProperties: []
|
||||||
|
clipAnimations: []
|
||||||
|
isReadable: 0
|
||||||
|
meshes:
|
||||||
|
lODScreenPercentages: []
|
||||||
|
globalScale: 1
|
||||||
|
meshCompression: 0
|
||||||
|
addColliders: 0
|
||||||
|
useSRGBMaterialColor: 1
|
||||||
|
sortHierarchyByName: 1
|
||||||
|
importPhysicalCameras: 1
|
||||||
|
importVisibility: 1
|
||||||
|
importBlendShapes: 1
|
||||||
|
importCameras: 1
|
||||||
|
importLights: 1
|
||||||
|
nodeNameCollisionStrategy: 1
|
||||||
|
fileIdsGeneration: 2
|
||||||
|
swapUVChannels: 0
|
||||||
|
generateSecondaryUV: 0
|
||||||
|
useFileUnits: 1
|
||||||
|
keepQuads: 0
|
||||||
|
weldVertices: 1
|
||||||
|
bakeAxisConversion: 1
|
||||||
|
preserveHierarchy: 0
|
||||||
|
skinWeightsMode: 0
|
||||||
|
maxBonesPerVertex: 4
|
||||||
|
minBoneWeight: 0.001
|
||||||
|
optimizeBones: 1
|
||||||
|
meshOptimizationFlags: -1
|
||||||
|
indexFormat: 0
|
||||||
|
secondaryUVAngleDistortion: 8
|
||||||
|
secondaryUVAreaDistortion: 15.000001
|
||||||
|
secondaryUVHardAngle: 88
|
||||||
|
secondaryUVMarginMethod: 1
|
||||||
|
secondaryUVMinLightmapResolution: 40
|
||||||
|
secondaryUVMinObjectScale: 1
|
||||||
|
secondaryUVPackMargin: 4
|
||||||
|
useFileScale: 1
|
||||||
|
strictVertexDataChecks: 0
|
||||||
|
tangentSpace:
|
||||||
|
normalSmoothAngle: 60
|
||||||
|
normalImportMode: 0
|
||||||
|
tangentImportMode: 3
|
||||||
|
normalCalculationMode: 4
|
||||||
|
legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
|
||||||
|
blendShapeNormalImportMode: 1
|
||||||
|
normalSmoothingSource: 0
|
||||||
|
referencedClips: []
|
||||||
|
importAnimation: 1
|
||||||
|
humanDescription:
|
||||||
|
serializedVersion: 3
|
||||||
|
human: []
|
||||||
|
skeleton: []
|
||||||
|
armTwist: 0.5
|
||||||
|
foreArmTwist: 0.5
|
||||||
|
upperLegTwist: 0.5
|
||||||
|
legTwist: 0.5
|
||||||
|
armStretch: 0.05
|
||||||
|
legStretch: 0.05
|
||||||
|
feetSpacing: 0
|
||||||
|
globalScale: 1
|
||||||
|
rootMotionBoneName:
|
||||||
|
hasTranslationDoF: 0
|
||||||
|
hasExtraRoot: 0
|
||||||
|
skeletonHasParents: 1
|
||||||
|
lastHumanDescriptionAvatarSource: {instanceID: 0}
|
||||||
|
autoGenerateAvatarMappingIfUnspecified: 1
|
||||||
|
animationType: 2
|
||||||
|
humanoidOversampling: 1
|
||||||
|
avatarSetup: 0
|
||||||
|
addHumanoidExtraRootOnlyWhenUsingAvatar: 1
|
||||||
|
importBlendShapeDeformPercent: 1
|
||||||
|
remapMaterialsIfMaterialImportModeIsNone: 0
|
||||||
|
additionalBone: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
BIN
Samples/Spline Debug.blend1
Normal file
BIN
Samples/Spline Debug.blend1
Normal file
Binary file not shown.
7
Samples/Spline Debug.blend1.meta
Normal file
7
Samples/Spline Debug.blend1.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d3d151e577ee0984bbf49dac54834606
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
1020
Samples/Spline Debug.unity
Normal file
1020
Samples/Spline Debug.unity
Normal file
File diff suppressed because it is too large
Load Diff
7
Samples/Spline Debug.unity.meta
Normal file
7
Samples/Spline Debug.unity.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44ea138cdbdb30745b195f3459bdf8c9
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
48
Samples/SplineEvaluateDebug.cs
Normal file
48
Samples/SplineEvaluateDebug.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using Unity.Mathematics;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class SplineEvaluateDebug : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] SplinePlus splinePlus;
|
||||||
|
|
||||||
|
[SerializeField] float anchor;
|
||||||
|
|
||||||
|
[SerializeField] float distance;
|
||||||
|
|
||||||
|
[SerializeField] float cubeSize;
|
||||||
|
|
||||||
|
[SerializeField] float matrixSize;
|
||||||
|
|
||||||
|
void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
if (!splinePlus) return;
|
||||||
|
|
||||||
|
splinePlus.Evaluate(anchor, distance, out Vector3 position, out Quaternion rotation);
|
||||||
|
|
||||||
|
transform.position = position;
|
||||||
|
|
||||||
|
transform.rotation = rotation;
|
||||||
|
|
||||||
|
Gizmos.DrawCube(position, Vector3.one * cubeSize);
|
||||||
|
|
||||||
|
splinePlus.splineContainer.Evaluate(anchor + (distance / splinePlus.splineContainer.Spline.GetLength()), out float3 position1, out _, out _);
|
||||||
|
|
||||||
|
Gizmos.DrawCube(position1, Vector3.one * cubeSize);
|
||||||
|
|
||||||
|
splinePlus.deformContainer.Evaluate(position1.x / splinePlus.deformContainer.Spline.GetLength(), out float3 deformPosition, out _, out _);
|
||||||
|
|
||||||
|
Gizmos.DrawCube(deformPosition, Vector3.one * cubeSize);
|
||||||
|
|
||||||
|
Gizmos.color = Color.green;
|
||||||
|
|
||||||
|
Gizmos.DrawRay(position, transform.up * matrixSize);
|
||||||
|
|
||||||
|
Gizmos.color = Color.red;
|
||||||
|
|
||||||
|
Gizmos.DrawRay(position, transform.right * matrixSize);
|
||||||
|
|
||||||
|
Gizmos.color = Color.blue;
|
||||||
|
|
||||||
|
Gizmos.DrawRay(position, transform.forward * matrixSize);
|
||||||
|
}
|
||||||
|
}
|
11
Samples/SplineEvaluateDebug.cs.meta
Normal file
11
Samples/SplineEvaluateDebug.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b96504a617d74094faa656840a73e7a9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
17
Samples/SplineNearestPointDebug.cs
Normal file
17
Samples/SplineNearestPointDebug.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class SplineNearestPointDebug : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] SplinePlus splinePlus;
|
||||||
|
|
||||||
|
[SerializeField] float cubeSize;
|
||||||
|
|
||||||
|
void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
if (!splinePlus) return;
|
||||||
|
|
||||||
|
splinePlus.GetNearestPoint(transform.position, out Vector3 position, out _);
|
||||||
|
|
||||||
|
Gizmos.DrawCube(position, Vector3.one * cubeSize);
|
||||||
|
}
|
||||||
|
}
|
11
Samples/SplineNearestPointDebug.cs.meta
Normal file
11
Samples/SplineNearestPointDebug.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0760258fbb4f0b5418d9a88014403d0e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Samples/Splines.meta
Normal file
8
Samples/Splines.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b9cd3cbddbe4e44abf819fdd6da81ee
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
45
Samples/Splines/Deform.json
Normal file
45
Samples/Splines/Deform.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"splines": [
|
||||||
|
{
|
||||||
|
"controlPoints": [
|
||||||
|
{
|
||||||
|
"position": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleL": {
|
||||||
|
"x": -9.999999046325684,
|
||||||
|
"y": -1.5099578831723193e-06,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleR": {
|
||||||
|
"x": 9.999999046325684,
|
||||||
|
"y": 1.5099578831723193e-06,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"tilt": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"position": {
|
||||||
|
"x": 10.000001907348633,
|
||||||
|
"y": -9.999998092651367,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleL": {
|
||||||
|
"x": 1.9073486328125e-06,
|
||||||
|
"y": -10.000001907348633,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleR": {
|
||||||
|
"x": 20.000001907348633,
|
||||||
|
"y": -9.999994277954102,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"tilt": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closed": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
Samples/Splines/Deform.json.meta
Normal file
7
Samples/Splines/Deform.json.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 22decb6838ce8a34a883a7ad6c7c3335
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
45
Samples/Splines/Spline.json
Normal file
45
Samples/Splines/Spline.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"splines": [
|
||||||
|
{
|
||||||
|
"controlPoints": [
|
||||||
|
{
|
||||||
|
"position": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleL": {
|
||||||
|
"x": -1.0,
|
||||||
|
"y": -8.742277657347586e-08,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleR": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 8.742277657347586e-08,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"tilt": -0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"position": {
|
||||||
|
"x": 10.0,
|
||||||
|
"y": -5.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleL": {
|
||||||
|
"x": 9.0,
|
||||||
|
"y": -5.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"handleR": {
|
||||||
|
"x": 11.0,
|
||||||
|
"y": -5.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"tilt": -0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"closed": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
Samples/Splines/Spline.json.meta
Normal file
7
Samples/Splines/Spline.json.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f3719dd04e0fe40438bb9578e9d53804
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
203
Spline Exporter.py
Normal file
203
Spline Exporter.py
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
bl_info = {
|
||||||
|
"name": "Spline Exporter",
|
||||||
|
"author": "Josh",
|
||||||
|
"version": (1, 0),
|
||||||
|
"blender": (2, 80, 0),
|
||||||
|
"location": "File > Import-Export",
|
||||||
|
"description": "Export spline control point data as .JSON",
|
||||||
|
"category": "Import-Export"
|
||||||
|
}
|
||||||
|
|
||||||
|
import json
|
||||||
|
import bpy
|
||||||
|
import math
|
||||||
|
from mathutils import *
|
||||||
|
from bpy_extras.io_utils import ExportHelper
|
||||||
|
from bpy_extras.io_utils import ImportHelper
|
||||||
|
from bpy.props import StringProperty
|
||||||
|
|
||||||
|
def ExportSpline():
|
||||||
|
curve = bpy.context.active_object
|
||||||
|
|
||||||
|
splines = {"splines": []}
|
||||||
|
|
||||||
|
for thisSpline in curve.data.splines:
|
||||||
|
points = {
|
||||||
|
"controlPoints": [],
|
||||||
|
"closed": thisSpline.use_cyclic_u
|
||||||
|
}
|
||||||
|
|
||||||
|
for thisPoint in thisSpline.bezier_points:
|
||||||
|
co = thisPoint.co
|
||||||
|
|
||||||
|
hl = thisPoint.handle_left
|
||||||
|
|
||||||
|
hr = thisPoint.handle_right
|
||||||
|
|
||||||
|
points["controlPoints"].append(
|
||||||
|
{
|
||||||
|
"position": {
|
||||||
|
"x": co.x,
|
||||||
|
"y": co.y,
|
||||||
|
"z": co.z
|
||||||
|
},
|
||||||
|
"handleL": {
|
||||||
|
"x": hl.x,
|
||||||
|
"y": hl.y,
|
||||||
|
"z": hl.z
|
||||||
|
},
|
||||||
|
"handleR": {
|
||||||
|
"x": hr.x,
|
||||||
|
"y": hr.y,
|
||||||
|
"z": hr.z
|
||||||
|
},
|
||||||
|
"tilt": thisPoint.tilt * (180 / math.pi)
|
||||||
|
})
|
||||||
|
|
||||||
|
splines["splines"].append(points)
|
||||||
|
|
||||||
|
class SaveJSON(bpy.types.Operator, ExportHelper):
|
||||||
|
bl_idname = "object.save_json"
|
||||||
|
|
||||||
|
bl_label = "Save JSON"
|
||||||
|
|
||||||
|
filename_ext = ".json"
|
||||||
|
|
||||||
|
filepath: StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
with open(self.filepath, "w") as f:
|
||||||
|
json.dump(splines, f, indent = 4)
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
|
||||||
|
return {"RUNNING_MODAL"}
|
||||||
|
|
||||||
|
bpy.utils.register_class(SaveJSON)
|
||||||
|
|
||||||
|
bpy.ops.object.save_json("INVOKE_DEFAULT")
|
||||||
|
|
||||||
|
def ImportSpline():
|
||||||
|
class LoadJSON(bpy.types.Operator, ImportHelper):
|
||||||
|
bl_idname = "object.load_json"
|
||||||
|
|
||||||
|
bl_label = "Load JSON"
|
||||||
|
|
||||||
|
filename_ext = ".json"
|
||||||
|
|
||||||
|
filter_glob: bpy.props.StringProperty(
|
||||||
|
default = "*.json",
|
||||||
|
options = {"HIDDEN"},
|
||||||
|
maxlen = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
with open(self.filepath, "r") as f:
|
||||||
|
splines = json.load(f)
|
||||||
|
|
||||||
|
curve = bpy.data.curves.new("BezierCurve", "CURVE")
|
||||||
|
|
||||||
|
curve.dimensions = "3D"
|
||||||
|
|
||||||
|
curve.twist_mode = "Z_UP"
|
||||||
|
|
||||||
|
for thisSpline in splines["splines"]:
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
spline = curve.splines.new("BEZIER")
|
||||||
|
|
||||||
|
for thisPoint in thisSpline["controlPoints"]:
|
||||||
|
if i > 0:
|
||||||
|
spline.bezier_points.add(1)
|
||||||
|
|
||||||
|
bezierPoint = spline.bezier_points[i]
|
||||||
|
|
||||||
|
position = thisPoint["position"]
|
||||||
|
|
||||||
|
handleL = thisPoint["handleL"]
|
||||||
|
|
||||||
|
handleR = thisPoint["handleR"]
|
||||||
|
|
||||||
|
bezierPoint.co = Vector((position["x"], position["y"], position["z"]))
|
||||||
|
|
||||||
|
bezierPoint.handle_left = Vector((handleL["x"], handleL["y"], handleL["z"]))
|
||||||
|
|
||||||
|
bezierPoint.handle_right = Vector((handleR["x"], handleR["y"], handleR["z"]))
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
spline.use_cyclic_u = thisSpline["closed"]
|
||||||
|
|
||||||
|
bpy.context.scene.collection.objects.link(bpy.data.objects.new("BezierCurve", curve))
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
bpy.utils.register_class(LoadJSON)
|
||||||
|
|
||||||
|
bpy.ops.object.load_json("INVOKE_DEFAULT")
|
||||||
|
|
||||||
|
class SplineExportOperator(bpy.types.Operator):
|
||||||
|
bl_idname = "object.spline_export_operator"
|
||||||
|
|
||||||
|
bl_label = "Export Spline"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ExportSpline()
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class SplineImportOperator(bpy.types.Operator):
|
||||||
|
bl_idname = "object.spline_import_operator"
|
||||||
|
|
||||||
|
bl_label = "Import Spline"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
ImportSpline()
|
||||||
|
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
class SplineExporterPanel(bpy.types.Panel):
|
||||||
|
bl_label = "Spline Exporter"
|
||||||
|
|
||||||
|
bl_idname = "SCENE_PT_SPLINE_EXPORTER"
|
||||||
|
|
||||||
|
bl_space_type = "PROPERTIES"
|
||||||
|
|
||||||
|
bl_region_type = "WINDOW"
|
||||||
|
|
||||||
|
bl_context = "scene"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
|
||||||
|
layout.label(text="Export Spline")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
|
||||||
|
row.scale_y = 1
|
||||||
|
|
||||||
|
row.operator("object.spline_export_operator")
|
||||||
|
|
||||||
|
row.operator("object.spline_import_operator")
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(SplineImportOperator)
|
||||||
|
|
||||||
|
bpy.utils.register_class(SplineExportOperator)
|
||||||
|
|
||||||
|
bpy.utils.register_class(SplineExporterPanel)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(SplineImportOperator)
|
||||||
|
|
||||||
|
bpy.utils.unregister_class(SplineExportOperator)
|
||||||
|
|
||||||
|
bpy.utils.unregister_class(SplineExporterPanel)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
register()
|
7
Spline Exporter.py.meta
Normal file
7
Spline Exporter.py.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cdf15d49993c2d14f936f40c16815acc
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
15
package.json
Normal file
15
package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "com.josh.spline-importer",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"displayName": "Spline Importer",
|
||||||
|
"description": "Import and export splines between Blender and Unity",
|
||||||
|
"unity": "2022.1",
|
||||||
|
"dependencies": {
|
||||||
|
"com.unity.mathematics": "1.0.0",
|
||||||
|
"com.unity.splines": "1.0.0"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Josh",
|
||||||
|
"url": "https://github.com/Josh4359"
|
||||||
|
}
|
||||||
|
}
|
7
package.json.meta
Normal file
7
package.json.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 11ef2ad10fe215940a918a2a580788a0
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
x
Reference in New Issue
Block a user