1
This commit is contained in:
9
Assets/Dreamteck/Splines/Core/IO.meta
Normal file
9
Assets/Dreamteck/Splines/Core/IO.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c26df68fb3fd33a488bf74c9d5ae1166
|
||||
folderAsset: yes
|
||||
timeCreated: 1495463786
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1
Assets/Dreamteck/Splines/Core/IO/CSV.cs
Normal file
1
Assets/Dreamteck/Splines/Core/IO/CSV.cs
Normal file
File diff suppressed because one or more lines are too long
12
Assets/Dreamteck/Splines/Core/IO/CSV.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/IO/CSV.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2313af4c5f59614393edd640d06f36c
|
||||
timeCreated: 1495699453
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
889
Assets/Dreamteck/Splines/Core/IO/SVG.cs
Normal file
889
Assets/Dreamteck/Splines/Core/IO/SVG.cs
Normal file
@@ -0,0 +1,889 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Linq;
|
||||
using Dreamteck.Splines.Primitives;
|
||||
using System;
|
||||
|
||||
namespace Dreamteck.Splines.IO
|
||||
{
|
||||
public class SVG : SplineParser
|
||||
{
|
||||
public enum Axis { X, Y, Z }
|
||||
internal class PathSegment
|
||||
{
|
||||
internal Vector3 startTangent = Vector3.zero;
|
||||
internal Vector3 endTangent = Vector3.zero;
|
||||
internal Vector3 endPoint = Vector3.zero;
|
||||
internal enum Type { Cubic, CubicShort, Quadratic, QuadraticShort }
|
||||
|
||||
internal PathSegment(Vector2 s, Vector2 e, Vector2 c)
|
||||
{
|
||||
startTangent = s;
|
||||
endTangent = e;
|
||||
endPoint = c;
|
||||
}
|
||||
|
||||
internal PathSegment()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum Element { All, Path, Polygon, Ellipse, Rectangle, Line }
|
||||
private System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-US");
|
||||
private System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Any;
|
||||
List<SplineDefinition> paths = new List<SplineDefinition>();
|
||||
List<SplineDefinition> polygons = new List<SplineDefinition>();
|
||||
List<SplineDefinition> ellipses = new List<SplineDefinition>();
|
||||
List<SplineDefinition> rectangles = new List<SplineDefinition>();
|
||||
List<SplineDefinition> lines = new List<SplineDefinition>();
|
||||
|
||||
List<Transformation> transformBuffer = new List<Transformation>();
|
||||
|
||||
public SVG(string filePath)
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
string ext = Path.GetExtension(filePath).ToLower();
|
||||
fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
if (ext != ".svg" && ext != ".xml")
|
||||
{
|
||||
Debug.LogError("SVG Parsing ERROR: Wrong format. Please use SVG or XML");
|
||||
return;
|
||||
}
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.XmlResolver = null;
|
||||
try
|
||||
{
|
||||
doc.Load(filePath);
|
||||
}
|
||||
catch (XmlException ex)
|
||||
{
|
||||
Debug.LogError(ex.Message);
|
||||
return;
|
||||
}
|
||||
Read(doc);
|
||||
}
|
||||
}
|
||||
|
||||
public SVG(List<SplineComputer> computers)
|
||||
{
|
||||
paths = new List<SplineDefinition>(computers.Count);
|
||||
for (int i = 0; i < computers.Count; i++)
|
||||
{
|
||||
if (computers[i] == null) continue;
|
||||
Spline spline = new Spline(computers[i].type, computers[i].sampleRate);
|
||||
spline.points = computers[i].GetPoints();
|
||||
if (spline.type != Spline.Type.Bezier && spline.type != Spline.Type.Linear) spline.CatToBezierTangents();
|
||||
if (computers[i].isClosed) spline.Close();
|
||||
paths.Add(new SplineDefinition(computers[i].name, spline));
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(string filePath, Axis ax = Axis.Z)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
XmlElement svg = doc.CreateElement("svg");
|
||||
foreach (SplineDefinition path in paths)
|
||||
{
|
||||
string elementName = "path";
|
||||
string attributeName = "d";
|
||||
if (path.type == Spline.Type.Linear)
|
||||
{
|
||||
attributeName = "points";
|
||||
if (path.closed) elementName = "polygon";
|
||||
else elementName = "polyline";
|
||||
}
|
||||
XmlElement splineNode = doc.CreateElement(elementName);
|
||||
XmlAttribute splineAttribute = doc.CreateAttribute("id");
|
||||
splineAttribute.Value = path.name;
|
||||
splineNode.Attributes.Append(splineAttribute);
|
||||
splineAttribute = doc.CreateAttribute(attributeName);
|
||||
if (path.type == Spline.Type.Linear) splineAttribute.Value = EncodePolygon(path, ax);
|
||||
else splineAttribute.Value = EncodePath(path, ax);
|
||||
splineNode.Attributes.Append(splineAttribute);
|
||||
splineAttribute = doc.CreateAttribute("stroke");
|
||||
splineAttribute.Value = "black";
|
||||
splineNode.Attributes.Append(splineAttribute);
|
||||
splineAttribute = doc.CreateAttribute("stroke-width");
|
||||
splineAttribute.Value = "3";
|
||||
splineNode.Attributes.Append(splineAttribute);
|
||||
splineAttribute = doc.CreateAttribute("fill");
|
||||
splineAttribute.Value = "none";
|
||||
splineNode.Attributes.Append(splineAttribute);
|
||||
svg.AppendChild(splineNode);
|
||||
}
|
||||
XmlAttribute svgAttribute = doc.CreateAttribute("version");
|
||||
svgAttribute.Value = "1.1";
|
||||
svg.Attributes.Append(svgAttribute);
|
||||
svgAttribute = doc.CreateAttribute("xmlns");
|
||||
svgAttribute.Value = "http://www.w3.org/2000/svg";
|
||||
svg.Attributes.Append(svgAttribute);
|
||||
doc.AppendChild(svg);
|
||||
doc.Save(filePath);
|
||||
}
|
||||
|
||||
Vector2 MapPoint(Vector3 original, Axis ax)
|
||||
{
|
||||
switch (ax)
|
||||
{
|
||||
case Axis.X: return new Vector2(original.z, -original.y);
|
||||
case Axis.Y: return new Vector2(original.x, -original.z);
|
||||
case Axis.Z: return new Vector2(original.x, -original.y);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
private void Read(XmlDocument doc)
|
||||
{
|
||||
transformBuffer.Clear();
|
||||
Traverse(doc.ChildNodes);
|
||||
}
|
||||
|
||||
private void Traverse(XmlNodeList nodes)
|
||||
{
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
int addedTransforms = 0;
|
||||
switch (node.Name)
|
||||
{
|
||||
case "g": addedTransforms = ParseTransformation(node); break;
|
||||
case "path": addedTransforms = ReadPath(node); break;
|
||||
case "polygon": addedTransforms = ReadPolygon(node, true); break;
|
||||
case "polyline": addedTransforms = ReadPolygon(node, false); break;
|
||||
case "ellipse": addedTransforms = ReadEllipse(node); break;
|
||||
case "circle": addedTransforms = ReadEllipse(node); break;
|
||||
case "line": addedTransforms = ReadLine(node); break;
|
||||
case "rect": addedTransforms = ReadRectangle(node); break;
|
||||
}
|
||||
Traverse(node.ChildNodes);
|
||||
if (addedTransforms > 0) transformBuffer.RemoveRange(transformBuffer.Count - addedTransforms, addedTransforms);
|
||||
}
|
||||
}
|
||||
|
||||
public List<SplineComputer> CreateSplineComputers(Vector3 position, Quaternion rotation, Element elements = Element.All)
|
||||
{
|
||||
List<SplineComputer> computers = new List<SplineComputer>();
|
||||
if (elements == Element.All || elements == Element.Path)
|
||||
{
|
||||
foreach (SplineDefinition definition in paths) computers.Add(definition.CreateSplineComputer(position, rotation));
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Polygon)
|
||||
{
|
||||
foreach (SplineDefinition definition in polygons) computers.Add(definition.CreateSplineComputer(position, rotation));
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Ellipse)
|
||||
{
|
||||
foreach (SplineDefinition definition in ellipses) computers.Add(definition.CreateSplineComputer(position, rotation));
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Rectangle)
|
||||
{
|
||||
foreach (SplineDefinition definition in rectangles) computers.Add(definition.CreateSplineComputer(position, rotation));
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Line)
|
||||
{
|
||||
foreach (SplineDefinition definition in lines) computers.Add(definition.CreateSplineComputer(position, rotation));
|
||||
}
|
||||
return computers;
|
||||
}
|
||||
|
||||
public List<Spline> CreateSplines(Element elements = Element.All)
|
||||
{
|
||||
List<Spline> splines = new List<Spline>();
|
||||
if (elements == Element.All || elements == Element.Path)
|
||||
{
|
||||
foreach (SplineDefinition definition in paths) splines.Add(definition.CreateSpline());
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Polygon)
|
||||
{
|
||||
foreach (SplineDefinition definition in polygons) splines.Add(definition.CreateSpline());
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Ellipse)
|
||||
{
|
||||
foreach (SplineDefinition definition in ellipses) splines.Add(definition.CreateSpline());
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Rectangle)
|
||||
{
|
||||
foreach (SplineDefinition definition in rectangles) splines.Add(definition.CreateSpline());
|
||||
}
|
||||
if (elements == Element.All || elements == Element.Line)
|
||||
{
|
||||
foreach (SplineDefinition definition in lines) splines.Add(definition.CreateSpline());
|
||||
}
|
||||
return splines;
|
||||
}
|
||||
|
||||
private int ReadRectangle(XmlNode rectNode)
|
||||
{
|
||||
float x = 0f, y = 0f, w = 0f, h = 0f, rx = -1f, ry = -1f;
|
||||
string attribute = GetAttributeContent(rectNode, "x");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out x);
|
||||
attribute = GetAttributeContent(rectNode, "y");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out y);
|
||||
attribute = GetAttributeContent(rectNode, "width");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out w);
|
||||
attribute = GetAttributeContent(rectNode, "height");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out h);
|
||||
attribute = GetAttributeContent(rectNode, "rx");
|
||||
if (attribute != "ERROR") float.TryParse(attribute, style, culture, out rx);
|
||||
attribute = GetAttributeContent(rectNode, "ry");
|
||||
if (attribute != "ERROR") float.TryParse(attribute, style, culture, out ry);
|
||||
else ry = rx;
|
||||
string elementName = GetAttributeContent(rectNode, "id");
|
||||
|
||||
if (rx == -1f && ry == -1f)
|
||||
{
|
||||
Rectangle rect = new Rectangle();
|
||||
rect.offset = new Vector2(x + w / 2f, -y - h / 2f);
|
||||
rect.size = new Vector2(w, h);
|
||||
if (elementName == "ERROR") elementName = fileName + "_rectangle" + (rectangles.Count + 1);
|
||||
buffer = new SplineDefinition(elementName, rect.CreateSpline());
|
||||
}
|
||||
else
|
||||
{
|
||||
RoundedRectangle rect = new RoundedRectangle();
|
||||
rect.offset = new Vector2(x + w / 2f, -y - h / 2f);
|
||||
rect.size = new Vector2(w, h);
|
||||
rect.xRadius = rx;
|
||||
rect.yRadius = ry;
|
||||
if (elementName == "ERROR") elementName = fileName + "_roundedRectangle" + (rectangles.Count + 1);
|
||||
buffer = new SplineDefinition(elementName, rect.CreateSpline());
|
||||
}
|
||||
int addedTransforms = ParseTransformation(rectNode);
|
||||
WriteBufferTo(rectangles);
|
||||
return addedTransforms;
|
||||
}
|
||||
|
||||
private int ReadLine(XmlNode lineNode)
|
||||
{
|
||||
float startX = 0f, startY = 0f, endX = 0f, endY = 0f;
|
||||
string attribute = GetAttributeContent(lineNode, "x1");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out startX);
|
||||
attribute = GetAttributeContent(lineNode, "y1");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out startY);
|
||||
attribute = GetAttributeContent(lineNode, "x2");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out endX);
|
||||
attribute = GetAttributeContent(lineNode, "y2");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out endY);
|
||||
string elementName = GetAttributeContent(lineNode, "id");
|
||||
if (elementName == "ERROR") elementName = fileName + "_line" + (ellipses.Count + 1);
|
||||
buffer = new SplineDefinition(elementName, Spline.Type.Linear);
|
||||
buffer.position = new Vector2(startX, -startY);
|
||||
buffer.CreateLinear();
|
||||
buffer.position = new Vector2(endX, -endY);
|
||||
buffer.CreateLinear();
|
||||
int addedTransforms = ParseTransformation(lineNode);
|
||||
WriteBufferTo(lines);
|
||||
return addedTransforms;
|
||||
}
|
||||
|
||||
private int ReadEllipse(XmlNode ellipseNode)
|
||||
{
|
||||
float x = 0f, y = 0f, rx = 0f, ry = 0f;
|
||||
string attribute = GetAttributeContent(ellipseNode, "cx");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out x);
|
||||
attribute = GetAttributeContent(ellipseNode, "cy");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out y);
|
||||
attribute = GetAttributeContent(ellipseNode, "r");
|
||||
string shapeName = "circle";
|
||||
if (attribute == "ERROR") //It might be an ellipse
|
||||
{
|
||||
shapeName = "ellipse";
|
||||
attribute = GetAttributeContent(ellipseNode, "rx");
|
||||
if (attribute == "ERROR") return 0;
|
||||
float.TryParse(attribute, style, culture, out rx);
|
||||
attribute = GetAttributeContent(ellipseNode, "ry");
|
||||
if (attribute == "ERROR") return 0;
|
||||
}
|
||||
else //Nope, it's a circle
|
||||
{
|
||||
float.TryParse(attribute, style, culture, out rx);
|
||||
ry = rx;
|
||||
}
|
||||
float.TryParse(attribute, style, culture, out ry);
|
||||
Ellipse ellipse = new Ellipse();
|
||||
ellipse.offset = new Vector2(x, -y);
|
||||
ellipse.xRadius = rx;
|
||||
ellipse.yRadius = ry;
|
||||
|
||||
string elementName = GetAttributeContent(ellipseNode, "id");
|
||||
if (elementName == "ERROR") elementName = fileName + "_" + shapeName + (ellipses.Count + 1);
|
||||
buffer = new SplineDefinition(elementName, ellipse.CreateSpline());
|
||||
int addedTransforms = ParseTransformation(ellipseNode);
|
||||
WriteBufferTo(ellipses);
|
||||
return addedTransforms;
|
||||
}
|
||||
|
||||
private int ReadPolygon(XmlNode polyNode, bool closed)
|
||||
{
|
||||
string contents = GetAttributeContent(polyNode, "points");
|
||||
if (contents == "ERROR") return 0;
|
||||
List<float> coords = ParseFloatArray(contents);
|
||||
if (coords.Count % 2 != 0)
|
||||
{
|
||||
Debug.LogWarning("There is an error with one of the polygon shapes.");
|
||||
return 0;
|
||||
}
|
||||
string elementName = GetAttributeContent(polyNode, "id");
|
||||
if (elementName == "ERROR") elementName = fileName + (closed ? "_polygon " : "_polyline") + (polygons.Count + 1);
|
||||
buffer = new SplineDefinition(elementName, Spline.Type.Linear);
|
||||
int count = coords.Count / 2;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
buffer.position = new Vector2(coords[0 + 2 * i], -coords[1 + 2 * i]);
|
||||
buffer.CreateLinear();
|
||||
}
|
||||
if (closed)
|
||||
{
|
||||
buffer.CreateClosingPoint();
|
||||
buffer.closed = true;
|
||||
}
|
||||
int addedTransforms = ParseTransformation(polyNode);
|
||||
WriteBufferTo(polygons);
|
||||
return addedTransforms;
|
||||
}
|
||||
|
||||
private int ParseTransformation(XmlNode node)
|
||||
{
|
||||
string transformAttribute = GetAttributeContent(node, "transform");
|
||||
if (transformAttribute == "ERROR") return 0;
|
||||
List<Transformation> trs = ParseTransformations(transformAttribute);
|
||||
transformBuffer.AddRange(trs);
|
||||
return trs.Count;
|
||||
}
|
||||
|
||||
private List<Transformation> ParseTransformations(string transformContent)
|
||||
{
|
||||
List<Transformation> trs = new List<Transformation>();
|
||||
MatchCollection matches = Regex.Matches(transformContent.ToLower(), @"(?<function>translate|rotate|scale|skewx|skewy|matrix)\s*\((\s*(?<param>-?\s*\d+(\.\d+)?)\s*\,*\s*)+\)");
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
if (match.Groups["function"].Success)
|
||||
{
|
||||
CaptureCollection parameters = match.Groups["param"].Captures;
|
||||
switch (match.Groups["function"].Value)
|
||||
{
|
||||
case "translate":
|
||||
if (parameters.Count < 2) break;
|
||||
trs.Add(new Translate(new Vector2(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value))));
|
||||
break;
|
||||
case "rotate":
|
||||
if (parameters.Count < 1) break;
|
||||
trs.Add(new Rotate(float.Parse(parameters[0].Value)));
|
||||
break;
|
||||
case "scale":
|
||||
if (parameters.Count < 2) break;
|
||||
trs.Add(new Scale(new Vector2(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value))));
|
||||
break;
|
||||
case "skewx":
|
||||
if (parameters.Count < 1) break;
|
||||
trs.Add(new SkewX(float.Parse(parameters[0].Value)));
|
||||
break;
|
||||
case "skewy":
|
||||
if (parameters.Count < 1) break;
|
||||
trs.Add(new SkewY(float.Parse(parameters[0].Value)));
|
||||
break;
|
||||
case "matrix":
|
||||
if (parameters.Count < 6) break;
|
||||
trs.Add(new MatrixTransform(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value), float.Parse(parameters[2].Value), float.Parse(parameters[3].Value), float.Parse(parameters[4].Value), float.Parse(parameters[5].Value)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return trs;
|
||||
}
|
||||
|
||||
private int ReadPath(XmlNode pathNode)
|
||||
{
|
||||
string contents = GetAttributeContent(pathNode, "d");
|
||||
if (contents == "ERROR") return 0;
|
||||
string elementName = GetAttributeContent(pathNode, "id");
|
||||
if (elementName == "ERROR") elementName = fileName + "_path " + (paths.Count + 1);
|
||||
IEnumerable<string> tokens = Regex.Split(contents, @"(?=[A-Za-z])").Where(t => !string.IsNullOrEmpty(t));
|
||||
int numSplines = 0;
|
||||
foreach (string token in tokens)
|
||||
{
|
||||
char cmd = token.Substring(0, 1).Single();
|
||||
switch (cmd)
|
||||
{
|
||||
case 'M':
|
||||
PathStart(elementName, token, false);
|
||||
++numSplines;
|
||||
break;
|
||||
case 'm':
|
||||
PathStart(elementName, token, true);
|
||||
++numSplines;
|
||||
break;
|
||||
case 'Z':
|
||||
PathClose();
|
||||
break;
|
||||
case 'z':
|
||||
PathClose();
|
||||
break;
|
||||
case 'L':
|
||||
PathLineTo(token, false);
|
||||
break;
|
||||
case 'l':
|
||||
PathLineTo(token, true);
|
||||
break;
|
||||
case 'H':
|
||||
PathHorizontalLineTo(token, false);
|
||||
break;
|
||||
case 'h':
|
||||
PathHorizontalLineTo(token, true);
|
||||
break;
|
||||
case 'V':
|
||||
PathVerticalLineTo(token, false);
|
||||
break;
|
||||
case 'v':
|
||||
PathVerticalLineTo(token, true);
|
||||
break;
|
||||
case 'C':
|
||||
PathCurveTo(token, PathSegment.Type.Cubic, false);
|
||||
break;
|
||||
case 'c':
|
||||
PathCurveTo(token, PathSegment.Type.Cubic, true);
|
||||
break;
|
||||
case 'S':
|
||||
PathCurveTo(token, PathSegment.Type.CubicShort, false);
|
||||
break;
|
||||
case 's':
|
||||
PathCurveTo(token, PathSegment.Type.CubicShort, true);
|
||||
break;
|
||||
case 'Q':
|
||||
PathCurveTo(token, PathSegment.Type.Quadratic, false);
|
||||
break;
|
||||
case 'q':
|
||||
PathCurveTo(token, PathSegment.Type.Quadratic, true);
|
||||
break;
|
||||
case 'T':
|
||||
PathCurveTo(token, PathSegment.Type.QuadraticShort, false);
|
||||
break;
|
||||
case 't':
|
||||
PathCurveTo(token, PathSegment.Type.QuadraticShort, true);
|
||||
break;
|
||||
case 'A':
|
||||
PathArcTo(token, false);
|
||||
break;
|
||||
case 'a':
|
||||
PathArcTo(token, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buffer != null) WriteBufferTo(paths);
|
||||
int addedTransforms = ParseTransformation(pathNode);
|
||||
for (int i = paths.Count - 1; i >= paths.Count - numSplines; --i) paths[i].Transform(transformBuffer);
|
||||
return addedTransforms;
|
||||
}
|
||||
|
||||
private void PathStart(string name, string coords, bool relative)
|
||||
{
|
||||
if (buffer != null) WriteBufferTo(paths);
|
||||
buffer = new SplineDefinition(name, Spline.Type.Bezier);
|
||||
if (relative) buffer.position = paths.Last().GetLastPoint().position;
|
||||
Vector2[] vectors = ParseVector2(coords);
|
||||
foreach (Vector3 vector in vectors)
|
||||
{
|
||||
if (relative) buffer.position += vector;
|
||||
else buffer.position = vector;
|
||||
buffer.CreateLinear();
|
||||
}
|
||||
}
|
||||
|
||||
private void PathClose()
|
||||
{
|
||||
buffer.CreateClosingPoint();
|
||||
buffer.closed = true;
|
||||
}
|
||||
|
||||
private void PathLineTo(string coords, bool relative)
|
||||
{
|
||||
Vector2[] vectors = ParseVector2(coords);
|
||||
foreach (Vector3 vector in vectors)
|
||||
{
|
||||
if (relative) buffer.position += vector;
|
||||
else buffer.position = vector;
|
||||
buffer.CreateLinear();
|
||||
}
|
||||
}
|
||||
|
||||
private void PathHorizontalLineTo(string coords, bool relative)
|
||||
{
|
||||
float[] floats = ParseFloat(coords);
|
||||
foreach (float f in floats)
|
||||
{
|
||||
if (relative) buffer.position.x += f;
|
||||
else buffer.position.x = f;
|
||||
buffer.CreateLinear();
|
||||
}
|
||||
}
|
||||
|
||||
private void PathVerticalLineTo(string coords, bool relative)
|
||||
{
|
||||
float[] floats = ParseFloat(coords);
|
||||
foreach (float f in floats)
|
||||
{
|
||||
if (relative) buffer.position.y -= f;
|
||||
else buffer.position.y = -f;
|
||||
buffer.CreateLinear();
|
||||
}
|
||||
}
|
||||
|
||||
private void PathCurveTo(string coords, PathSegment.Type type, bool relative)
|
||||
{
|
||||
PathSegment[] segment = ParsePathSegment(coords, type);
|
||||
for (int i = 0; i < segment.Length; i++)
|
||||
{
|
||||
SplinePoint p = buffer.GetLastPoint();
|
||||
p.type = SplinePoint.Type.Broken;
|
||||
|
||||
//Get the control points
|
||||
Vector3 startPoint = p.position;
|
||||
Vector3 endPoint = segment[i].endPoint;
|
||||
Vector3 startTangent = segment[i].startTangent;
|
||||
Vector3 endTangent = segment[i].endTangent;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PathSegment.Type.CubicShort: startTangent = startPoint - p.tangent; break;
|
||||
case PathSegment.Type.Quadratic:
|
||||
buffer.tangent = segment[i].startTangent;
|
||||
startTangent = startPoint + 2f / 3f * (buffer.tangent - startPoint);
|
||||
endTangent = endPoint + 2f / 3f * (buffer.tangent - endPoint);
|
||||
break;
|
||||
case PathSegment.Type.QuadraticShort:
|
||||
Vector3 reflection = startPoint + (startPoint - buffer.tangent);
|
||||
startTangent = startPoint + 2f / 3f * (reflection - startPoint);
|
||||
endTangent = endPoint + 2f / 3f * (reflection - endPoint);
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == PathSegment.Type.CubicShort || type == PathSegment.Type.QuadraticShort) p.type = SplinePoint.Type.SmoothMirrored; //Smooth the previous point
|
||||
else
|
||||
{
|
||||
if (relative) p.SetTangent2Position(startPoint + startTangent);
|
||||
else p.SetTangent2Position(startTangent);
|
||||
}
|
||||
buffer.SetLastPoint(p);
|
||||
if (relative)
|
||||
{
|
||||
buffer.position += endPoint;
|
||||
buffer.tangent = startPoint + endTangent;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.position = endPoint;
|
||||
buffer.tangent = endTangent;
|
||||
}
|
||||
buffer.CreateBroken();
|
||||
}
|
||||
}
|
||||
|
||||
private void PathArcTo(string coords, bool relative)
|
||||
{
|
||||
// Get Arc Arguments
|
||||
float[] floats = ParseFloat(coords);
|
||||
float rx = floats[0];
|
||||
float ry = floats[1];
|
||||
float rotation = floats[2] * Mathf.Deg2Rad;
|
||||
bool largeArc = floats[3] > 0.5f;
|
||||
bool sweep = floats[4] > 0.5f;
|
||||
float x = floats[5];
|
||||
float y = floats[6];
|
||||
|
||||
// Set the last buffer point to broken
|
||||
SplinePoint p = buffer.GetLastPoint();
|
||||
p.type = SplinePoint.Type.Broken;
|
||||
|
||||
// Get the start and end point. Note: Flip Y, as the Ellipse Parameters calculation assumes positive Y values
|
||||
Vector3 sp = p.position;
|
||||
sp.y *= -1;
|
||||
Vector3 ep = new Vector3(x, y, 0);
|
||||
if (relative) ep += sp;
|
||||
|
||||
Vector2 c;
|
||||
float theta1;
|
||||
float sweepTheta;
|
||||
float adjustedRx;
|
||||
float adjustedRy;
|
||||
|
||||
// Get the Ellipse Parameters
|
||||
CalculateEllipseParams(sp, ep, rotation, rx, ry, largeArc, sweep,
|
||||
out c, out theta1, out sweepTheta, out adjustedRx, out adjustedRy);
|
||||
|
||||
// Flip the center Y back
|
||||
c.y *= -1;
|
||||
|
||||
// Generate the ellipse primitive. Note: Rotated by -90 degrees and flipped, so first point starts at +X and goes clockwise
|
||||
Ellipse ellipse = new Ellipse();
|
||||
ellipse.offset = c;
|
||||
ellipse.rotation = new Vector3(0, 0, -90 - rotation * Mathf.Rad2Deg);
|
||||
ellipse.xRadius = adjustedRx;
|
||||
ellipse.yRadius = adjustedRy;
|
||||
var ellipseSpline = ellipse.CreateSpline();
|
||||
var esp = ellipseSpline.points;
|
||||
|
||||
|
||||
var tmpp = esp[1];
|
||||
esp[1] = esp[3];
|
||||
esp[3] = tmpp;
|
||||
for (int i = 0; i < esp.Length; i++)
|
||||
{
|
||||
FlipTangents(ref esp[i]);
|
||||
}
|
||||
|
||||
// Find the percentages to sample the ellipse at
|
||||
var startP = theta1 / (Mathf.PI * 2);
|
||||
startP = ModP(startP, 1f);
|
||||
var sweepP = sweepTheta / (Mathf.PI * 2);
|
||||
var endP = startP + sweepP;
|
||||
|
||||
var percentages = GetArcSegmentPercentages(startP, endP);
|
||||
|
||||
// Sample the ellipse and add points to buffer
|
||||
for (int i = 1; i < percentages.Length; ++i)
|
||||
{
|
||||
double pp = percentages[i - 1];
|
||||
double pc = percentages[i];
|
||||
double pr = pc - pp;
|
||||
int sgn = Math.Sign(pr); // This sign indicates a reversed range if < 0; The sampled tangents need to be flipped if this is the case
|
||||
pr *= sgn;
|
||||
if (pr < 0.0001d) continue; // Sample error margin
|
||||
double d = 0.75d / pr;
|
||||
pc = ModP(pc, 1d);
|
||||
pp = ModP(pp, 1d);
|
||||
|
||||
// New point in buffer
|
||||
Vector3 posc = Vector3.zero, tanc = Vector3.zero, tanp = Vector3.zero;
|
||||
ellipseSpline.EvaluatePosition(pc, ref posc);
|
||||
ellipseSpline.EvaluateTangent(pc, ref tanc);
|
||||
tanc *= sgn;
|
||||
tanc /= (float)d;
|
||||
buffer.position = posc;
|
||||
buffer.tangent = posc - tanc;
|
||||
|
||||
// Modify tangent2 of last point in buffer
|
||||
ellipseSpline.EvaluateTangent(pp, ref tanp);
|
||||
tanp *= sgn;
|
||||
tanp /= (float)d;
|
||||
p = buffer.GetLastPoint();
|
||||
p.type = SplinePoint.Type.Broken;
|
||||
p.SetTangent2Position(p.position + tanp);
|
||||
buffer.SetLastPoint(p);
|
||||
|
||||
buffer.CreateBroken();
|
||||
}
|
||||
}
|
||||
|
||||
private void FlipTangents(ref SplinePoint point)
|
||||
{
|
||||
var tmpt = point.tangent;
|
||||
point.tangent = point.tangent2;
|
||||
point.tangent2 = tmpt;
|
||||
}
|
||||
|
||||
private void CalculateEllipseParams(Vector2 p0, Vector2 p1, float phi, float rx, float ry, bool fa, bool fs, out Vector2 c, out float theta1, out float sweepTheta, out float adjustedRx, out float adjustedRy)
|
||||
{
|
||||
// From https://observablehq.com/@toja/ellipse-and-elliptical-arc-conversion
|
||||
float sinPhi = Mathf.Sin(phi);
|
||||
float cosPhi = Mathf.Cos(phi);
|
||||
|
||||
float x = cosPhi * (p0.x - p1.x) / 2 + sinPhi * (p0.y - p1.y) / 2;
|
||||
float y = -sinPhi * (p0.x - p1.x) / 2 + cosPhi * (p0.y - p1.y) / 2;
|
||||
|
||||
float px = x * x, py = y * y, prx = rx * rx, pry = ry * ry;
|
||||
|
||||
rx = Mathf.Abs(rx);
|
||||
ry = Mathf.Abs(ry);
|
||||
float l = px / prx + py / pry;
|
||||
if (l > 1)
|
||||
{
|
||||
float sqrtl = Mathf.Sqrt(l);
|
||||
rx = sqrtl * rx;
|
||||
ry = sqrtl * ry;
|
||||
prx = rx * rx;
|
||||
pry = ry * ry;
|
||||
}
|
||||
adjustedRx = rx;
|
||||
adjustedRy = ry;
|
||||
|
||||
float sign = fa == fs ? -1 : 1;
|
||||
float m = Mathf.Sqrt((prx * pry - prx * py - pry * px) / (prx * py + pry * px)) * sign;
|
||||
|
||||
float ccx = m * (rx * y) / ry;
|
||||
float ccy = m * (-ry * x) / rx;
|
||||
|
||||
c = new Vector2(
|
||||
cosPhi * ccx - sinPhi * ccy + (p0.x + p1.x) / 2,
|
||||
sinPhi * ccx + cosPhi * ccy + (p0.y + p1.y) / 2
|
||||
);
|
||||
|
||||
theta1 = VectorAngle(new Vector2(1, 0), new Vector2((x - ccx) / rx, (y - ccy) / ry));
|
||||
sweepTheta = VectorAngle(new Vector2((x - ccx) / rx, (y - ccy) / ry), new Vector2((-x - ccx) / rx, (-y - ccy) / ry));
|
||||
sweepTheta *= Mathf.Rad2Deg;
|
||||
sweepTheta %= 360;
|
||||
|
||||
if (!fs && sweepTheta > 0) sweepTheta -= 360;
|
||||
if (fs && sweepTheta < 0) sweepTheta += 360;
|
||||
sweepTheta *= Mathf.Deg2Rad;
|
||||
}
|
||||
|
||||
private double[] GetArcSegmentPercentages(double start, double end)
|
||||
{
|
||||
List<double> percentages = new List<double>();
|
||||
bool swap = start > end;
|
||||
if (swap) { double tmp = start; start = end; end = tmp; }
|
||||
|
||||
percentages.Add(start);
|
||||
double rsM = Math.Ceiling(start * 4d) * 0.25d;
|
||||
if (rsM > end)
|
||||
{
|
||||
percentages.Add(end);
|
||||
return ReturnPercentage(swap, percentages);
|
||||
}
|
||||
else if (start < rsM)
|
||||
{
|
||||
percentages.Add(rsM);
|
||||
}
|
||||
|
||||
double rem = rsM + 0.25d;
|
||||
for (; rem <= end; rem += 0.25d)
|
||||
{
|
||||
percentages.Add(rem);
|
||||
}
|
||||
rem -= 0.25d;
|
||||
if (rem < end)
|
||||
{
|
||||
percentages.Add(end);
|
||||
}
|
||||
|
||||
return ReturnPercentage(swap, percentages);
|
||||
}
|
||||
|
||||
private double[] ReturnPercentage(bool swap, List<double> percentages)
|
||||
{
|
||||
var ret = new double[percentages.Count];
|
||||
for (int i = 0; i < percentages.Count; ++i)
|
||||
{
|
||||
var r = swap ? percentages.Count - 1 - i : i;
|
||||
var p = percentages[r];
|
||||
ret[i] = p;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private float VectorAngle(Vector2 u, Vector2 v)
|
||||
{
|
||||
float sign = u.x * v.y - u.y * v.x < 0 ? -1 : 1;
|
||||
float ua = Mathf.Sqrt(u.x * u.x + u.y * u.y);
|
||||
float va = Mathf.Sqrt(v.x * v.x + v.y * v.y);
|
||||
float dot = u.x * v.x + u.y * v.y;
|
||||
return sign * Mathf.Acos(dot / (ua * va));
|
||||
}
|
||||
|
||||
private float ModP(float f, float div)
|
||||
{
|
||||
return ((f % div) + div) % div;
|
||||
}
|
||||
|
||||
private double ModP(double d, double div)
|
||||
{
|
||||
return ((d % div) + div) % div;
|
||||
}
|
||||
|
||||
private void WriteBufferTo(List<SplineDefinition> list)
|
||||
{
|
||||
buffer.Transform(transformBuffer);
|
||||
list.Add(buffer);
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
private PathSegment[] ParsePathSegment(string coord, PathSegment.Type type)
|
||||
{
|
||||
List<float> list = ParseFloatArray(coord.Substring(1));
|
||||
int count = 0;
|
||||
switch (type)
|
||||
{
|
||||
case PathSegment.Type.Cubic: count = list.Count / 6; break;
|
||||
case PathSegment.Type.Quadratic: count = list.Count / 4; break;
|
||||
case PathSegment.Type.CubicShort: count = list.Count / 4; break;
|
||||
case PathSegment.Type.QuadraticShort: count = list.Count / 2; break;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
Debug.Log("Error in " + coord + " " + type);
|
||||
return new PathSegment[] { new PathSegment() };
|
||||
}
|
||||
PathSegment[] data = new PathSegment[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PathSegment.Type.Cubic: data[i] = new PathSegment(new Vector2(list[0 + 6 * i], -list[1 + 6 * i]), new Vector2(list[2 + 6 * i], -list[3 + 6 * i]), new Vector2(list[4 + 6 * i], -list[5 + 6 * i])); break;
|
||||
case PathSegment.Type.Quadratic: data[i] = new PathSegment(new Vector2(list[0 + 4 * i], -list[1 + 4 * i]), Vector2.zero, new Vector2(list[2 + 4 * i], -list[3 + 4 * i])); break;
|
||||
case PathSegment.Type.CubicShort: data[i] = new PathSegment(Vector2.zero, new Vector2(list[0 + 4 * i], -list[1 + 4 * i]), new Vector2(list[2 + 4 * i], -list[3 + 4 * i])); break;
|
||||
case PathSegment.Type.QuadraticShort: data[i] = new PathSegment(Vector2.zero, Vector2.zero, new Vector2(list[0 + 4 * i], -list[1 + 4 * i])); break;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private string EncodePath(SplineDefinition definition, Axis ax)
|
||||
{
|
||||
string text = "M";
|
||||
for (int i = 0; i < definition.pointCount; i++)
|
||||
{
|
||||
SplinePoint p = definition.points[i];
|
||||
Vector3 tangent = MapPoint(p.tangent, ax);
|
||||
Vector3 position = MapPoint(p.position, ax);
|
||||
if (i == 0) text += position.x + "," + position.y;
|
||||
else
|
||||
{
|
||||
SplinePoint lp = definition.points[i - 1];
|
||||
Vector3 tangent2 = MapPoint(lp.tangent2, ax);
|
||||
text += "C" + tangent2.x + "," + tangent2.y + "," + tangent.x + "," + tangent.y + "," + position.x + "," + position.y;
|
||||
}
|
||||
}
|
||||
if (definition.closed) text += "z";
|
||||
return text;
|
||||
}
|
||||
|
||||
private string EncodePolygon(SplineDefinition definition, Axis ax)
|
||||
{
|
||||
string text = "";
|
||||
for (int i = 0; i < definition.pointCount; i++)
|
||||
{
|
||||
Vector3 position = MapPoint(definition.points[i].position, ax);
|
||||
if (text != "") text += ",";
|
||||
text += position.x + "," + position.y;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
private string GetAttributeContent(XmlNode node, string attributeName)
|
||||
{
|
||||
for (int j = 0; j < node.Attributes.Count; j++)
|
||||
{
|
||||
if (node.Attributes[j].Name == attributeName) return node.Attributes[j].InnerText;
|
||||
}
|
||||
return "ERROR";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/IO/SVG.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/IO/SVG.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b2394d649216f5479fddeb2b08a6628
|
||||
timeCreated: 1495463805
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
337
Assets/Dreamteck/Splines/Core/IO/SplineParser.cs
Normal file
337
Assets/Dreamteck/Splines/Core/IO/SplineParser.cs
Normal file
@@ -0,0 +1,337 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.IO
|
||||
{
|
||||
public class SplineParser
|
||||
{
|
||||
protected string fileName = "";
|
||||
public string name
|
||||
{
|
||||
get { return fileName; }
|
||||
}
|
||||
|
||||
private System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-US");
|
||||
private System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Any;
|
||||
|
||||
internal class Transformation
|
||||
{
|
||||
protected static Matrix4x4 matrix = new Matrix4x4();
|
||||
|
||||
internal static void ResetMatrix()
|
||||
{
|
||||
matrix.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one);
|
||||
}
|
||||
|
||||
internal virtual void Push()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
internal static void Apply(SplinePoint[] points)
|
||||
{
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
SplinePoint p = points[i];
|
||||
p.position = matrix.MultiplyPoint(p.position);
|
||||
p.tangent = matrix.MultiplyPoint(p.tangent);
|
||||
p.tangent2 = matrix.MultiplyPoint(p.tangent2);
|
||||
points[i] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Translate : Transformation
|
||||
{
|
||||
private Vector2 offset = Vector2.zero;
|
||||
public Translate(Vector2 o)
|
||||
{
|
||||
offset = o;
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
Matrix4x4 translate = new Matrix4x4();
|
||||
translate.SetTRS(new Vector2(offset.x, -offset.y), Quaternion.identity, Vector3.one);
|
||||
matrix = matrix * translate;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Rotate : Transformation
|
||||
{
|
||||
private float angle = 0f;
|
||||
public Rotate(float a)
|
||||
{
|
||||
angle = a;
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
Matrix4x4 rotate = new Matrix4x4();
|
||||
rotate.SetTRS(Vector3.zero, Quaternion.AngleAxis(angle, Vector3.back), Vector3.one);
|
||||
matrix = matrix * rotate;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Scale : Transformation
|
||||
{
|
||||
private Vector2 multiplier = Vector2.one;
|
||||
public Scale(Vector2 s)
|
||||
{
|
||||
multiplier = s;
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
Matrix4x4 scale = new Matrix4x4();
|
||||
scale.SetTRS(Vector3.zero, Quaternion.identity, multiplier);
|
||||
matrix = matrix * scale;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SkewX : Transformation
|
||||
{
|
||||
private float amount = 0f;
|
||||
public SkewX(float a)
|
||||
{
|
||||
amount = a;
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
Matrix4x4 skew = new Matrix4x4();
|
||||
skew[0, 0] = 1.0f;
|
||||
skew[1, 1] = 1.0f;
|
||||
skew[2, 2] = 1.0f;
|
||||
skew[3, 3] = 1.0f;
|
||||
skew[0, 1] = Mathf.Tan(-amount * Mathf.Deg2Rad);
|
||||
matrix = matrix * skew;
|
||||
}
|
||||
}
|
||||
|
||||
internal class SkewY : Transformation
|
||||
{
|
||||
private float amount = 0f;
|
||||
public SkewY(float a)
|
||||
{
|
||||
amount = a;
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
Matrix4x4 skew = new Matrix4x4();
|
||||
skew[0, 0] = 1.0f;
|
||||
skew[1, 1] = 1.0f;
|
||||
skew[2, 2] = 1.0f;
|
||||
skew[3, 3] = 1.0f;
|
||||
skew[1, 0] = Mathf.Tan(-amount * Mathf.Deg2Rad);
|
||||
matrix = matrix *skew;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MatrixTransform : Transformation
|
||||
{
|
||||
private Matrix4x4 transformMatrix = new Matrix4x4();
|
||||
|
||||
public MatrixTransform(float a, float b, float c, float d, float e, float f)
|
||||
{
|
||||
transformMatrix.SetRow(0, new Vector4(a, c, 0f, e));
|
||||
transformMatrix.SetRow(1, new Vector4(b, d, 0f, -f));
|
||||
transformMatrix.SetRow(2, new Vector4(0f, 0f, 1f, 0f));
|
||||
transformMatrix.SetRow(3, new Vector4(0f, 0f, 0f, 1f));
|
||||
}
|
||||
|
||||
internal override void Push()
|
||||
{
|
||||
matrix = matrix * transformMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal class SplineDefinition
|
||||
{
|
||||
internal string name = "";
|
||||
internal Spline.Type type = Spline.Type.Linear;
|
||||
internal List<SplinePoint> points = new List<SplinePoint>();
|
||||
internal bool closed = false;
|
||||
|
||||
internal int pointCount
|
||||
{
|
||||
get { return points.Count; }
|
||||
}
|
||||
|
||||
internal Vector3 position = Vector3.zero;
|
||||
internal Vector3 tangent = Vector3.zero;
|
||||
internal Vector3 tangent2 = Vector3.zero;
|
||||
internal Vector3 normal = Vector3.back;
|
||||
internal float size = 1f;
|
||||
internal Color color = Color.white;
|
||||
|
||||
internal SplineDefinition(string n, Spline.Type t)
|
||||
{
|
||||
name = n;
|
||||
type = t;
|
||||
}
|
||||
|
||||
internal SplineDefinition(string n, Spline spline)
|
||||
{
|
||||
name = n;
|
||||
type = spline.type;
|
||||
closed = spline.isClosed;
|
||||
points = new List<SplinePoint>(spline.points);
|
||||
}
|
||||
|
||||
internal SplinePoint GetLastPoint()
|
||||
{
|
||||
if (points.Count == 0) return new SplinePoint();
|
||||
return points[points.Count - 1];
|
||||
}
|
||||
|
||||
internal void SetLastPoint(SplinePoint point)
|
||||
{
|
||||
if (points.Count == 0) return;
|
||||
points[points.Count - 1] = point;
|
||||
}
|
||||
|
||||
internal void CreateClosingPoint()
|
||||
{
|
||||
SplinePoint p = new SplinePoint(points[0]);
|
||||
points.Add(p);
|
||||
}
|
||||
|
||||
internal void CreateSmooth()
|
||||
{
|
||||
points.Add(new SplinePoint(position, tangent, normal, size, color));
|
||||
}
|
||||
|
||||
internal void CreateBroken()
|
||||
{
|
||||
SplinePoint point = new SplinePoint(new SplinePoint(position, tangent, normal, size, color));
|
||||
point.type = SplinePoint.Type.Broken;
|
||||
point.SetTangent2Position(point.position);
|
||||
point.normal = normal;
|
||||
point.color = color;
|
||||
point.size = size;
|
||||
points.Add(point);
|
||||
}
|
||||
|
||||
internal void CreateLinear()
|
||||
{
|
||||
tangent = position;
|
||||
CreateSmooth();
|
||||
}
|
||||
|
||||
internal SplineComputer CreateSplineComputer(Vector3 position, Quaternion rotation)
|
||||
{
|
||||
GameObject go = new GameObject(name);
|
||||
go.transform.position = position;
|
||||
go.transform.rotation = rotation;
|
||||
SplineComputer computer = go.AddComponent<SplineComputer>();
|
||||
#if UNITY_EDITOR
|
||||
if(Application.isPlaying) computer.ResampleTransform();
|
||||
#endif
|
||||
computer.type = type;
|
||||
if(closed)
|
||||
{
|
||||
if (points[0].type == SplinePoint.Type.Broken) points[0].SetTangentPosition(GetLastPoint().tangent2);
|
||||
}
|
||||
computer.SetPoints(points.ToArray(), SplineComputer.Space.Local);
|
||||
if (closed) computer.Close();
|
||||
return computer;
|
||||
}
|
||||
|
||||
internal Spline CreateSpline()
|
||||
{
|
||||
Spline spline = new Spline(type);
|
||||
spline.points = points.ToArray();
|
||||
if (closed) spline.Close();
|
||||
return spline;
|
||||
}
|
||||
|
||||
internal void Transform(List<Transformation> trs)
|
||||
{
|
||||
SplinePoint[] p = points.ToArray();
|
||||
Transformation.ResetMatrix();
|
||||
foreach(Transformation t in trs) t.Push();
|
||||
Transformation.Apply(p);
|
||||
for (int i = 0; i < p.Length; i++) points[i] = p[i];
|
||||
SplinePoint[] debugPoints = new SplinePoint[1];
|
||||
debugPoints[0] = new SplinePoint();
|
||||
Transformation.Apply(debugPoints);
|
||||
}
|
||||
}
|
||||
|
||||
internal SplineDefinition buffer = null;
|
||||
|
||||
internal Vector2[] ParseVector2(string coord)
|
||||
{
|
||||
List<float> list = ParseFloatArray(coord.Substring(1));
|
||||
int count = list.Count / 2;
|
||||
if (count == 0)
|
||||
{
|
||||
Debug.Log("Error in " + coord);
|
||||
return new Vector2[] { Vector2.zero };
|
||||
}
|
||||
Vector2[] vectors = new Vector2[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
vectors[i] = new Vector2(list[0 + i * 2], -list[1 + i * 2]);
|
||||
}
|
||||
return vectors;
|
||||
}
|
||||
|
||||
internal float[] ParseFloat(string coord)
|
||||
{
|
||||
List<float> list = ParseFloatArray(coord.Substring(1));
|
||||
if (list.Count < 1)
|
||||
{
|
||||
Debug.Log("Error in " + coord);
|
||||
return new float[] { 0f };
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
internal List<float> ParseFloatArray(string content)
|
||||
{
|
||||
string accumulated = "";
|
||||
List<float> list = new List<float>();
|
||||
foreach (char c in content)
|
||||
{
|
||||
if (c == ',' || c == '-' || char.IsWhiteSpace(c) || (accumulated.Contains(".") && c == '.'))
|
||||
{
|
||||
if (!IsWHiteSpace(accumulated))
|
||||
{
|
||||
float parsed = 0f;
|
||||
float.TryParse(accumulated, style, culture, out parsed);
|
||||
list.Add(parsed);
|
||||
accumulated = "";
|
||||
if (c == '-') accumulated = "-";
|
||||
if (c == '.') accumulated = "0.";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!char.IsWhiteSpace(c)) accumulated += c;
|
||||
}
|
||||
if (!IsWHiteSpace(accumulated))
|
||||
{
|
||||
float p = 0f;
|
||||
float.TryParse(accumulated, style, culture, out p);
|
||||
list.Add(p);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public bool IsWHiteSpace(string s)
|
||||
{
|
||||
foreach (char c in s)
|
||||
{
|
||||
if (!char.IsWhiteSpace(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/IO/SplineParser.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/IO/SplineParser.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62e998669a36f6946b5d107a292616b0
|
||||
timeCreated: 1495464643
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/Dreamteck/Splines/Core/ObjectSequence.cs
Normal file
66
Assets/Dreamteck/Splines/Core/ObjectSequence.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
[System.Serializable]
|
||||
public class ObjectSequence<T>
|
||||
{
|
||||
public T startObject;
|
||||
public T endObject;
|
||||
public T[] objects;
|
||||
public enum Iteration { Ordered, Random }
|
||||
public Iteration iteration = Iteration.Ordered;
|
||||
public int randomSeed
|
||||
{
|
||||
get { return _randomSeed; }
|
||||
set
|
||||
{
|
||||
if (value != _randomSeed)
|
||||
{
|
||||
_randomSeed = value;
|
||||
randomizer = new System.Random(_randomSeed);
|
||||
}
|
||||
}
|
||||
}
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private int _randomSeed = 1;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private int index = 0;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
System.Random randomizer;
|
||||
|
||||
public ObjectSequence(){
|
||||
randomizer = new System.Random(_randomSeed);
|
||||
}
|
||||
|
||||
public T GetFirst()
|
||||
{
|
||||
if (startObject != null) return startObject;
|
||||
else return Next();
|
||||
}
|
||||
|
||||
public T GetLast()
|
||||
{
|
||||
if (endObject != null) return endObject;
|
||||
else return Next();
|
||||
}
|
||||
|
||||
public T Next()
|
||||
{
|
||||
if (iteration == Iteration.Ordered)
|
||||
{
|
||||
if (index >= objects.Length) index = 0;
|
||||
return objects[index++];
|
||||
} else
|
||||
{
|
||||
int randomIndex = randomizer.Next(objects.Length-1);
|
||||
return objects[randomIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/ObjectSequence.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/ObjectSequence.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 810a671b1d2fb20408afff3c04f7be70
|
||||
timeCreated: 1483463982
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Dreamteck/Splines/Core/Primitives.meta
Normal file
9
Assets/Dreamteck/Splines/Core/Primitives.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 893f3cde2d97b93489384ee7ba89475b
|
||||
folderAsset: yes
|
||||
timeCreated: 1495473179
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/Dreamteck/Splines/Core/Primitives/Capsule.cs
Normal file
36
Assets/Dreamteck/Splines/Core/Primitives/Capsule.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Capsule : SplinePrimitive
|
||||
{
|
||||
public float radius = 1f;
|
||||
public float height = 2f;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Bezier;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(6, SplinePoint.Type.SmoothMirrored);
|
||||
points[0].position = Vector3.right / 2f * radius + Vector3.up * height * 0.5f;
|
||||
points[0].SetTangentPosition(points[0].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
points[1].position = Vector3.up / 2f * radius + Vector3.up * height * 0.5f;
|
||||
points[1].SetTangentPosition(points[1].position + Vector3.right * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
points[2].position = Vector3.left / 2f * radius + Vector3.up * height * 0.5f;
|
||||
points[2].SetTangentPosition(points[2].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
points[3].position = Vector3.left / 2f * radius + Vector3.down * height * 0.5f;
|
||||
points[3].SetTangentPosition(points[3].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
points[4].position = Vector3.down / 2f * radius + Vector3.down * height * 0.5f;
|
||||
points[4].SetTangentPosition(points[4].position + Vector3.left * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
points[5].position = Vector3.right / 2f * radius + Vector3.down * height * 0.5f;
|
||||
points[5].SetTangentPosition(points[5].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Capsule.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Capsule.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e39006c982d03b3409b070eab24d0843
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/Dreamteck/Splines/Core/Primitives/Ellipse.cs
Normal file
32
Assets/Dreamteck/Splines/Core/Primitives/Ellipse.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Ellipse : SplinePrimitive
|
||||
{
|
||||
public float xRadius = 1f;
|
||||
public float yRadius = 1f;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Bezier;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(4, SplinePoint.Type.SmoothMirrored);
|
||||
points[0].position = Vector3.up * yRadius;
|
||||
points[0].SetTangentPosition(points[0].position + Vector3.right * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * xRadius);
|
||||
points[1].position = Vector3.left * xRadius;
|
||||
points[1].SetTangentPosition(points[1].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
|
||||
points[2].position = Vector3.down * yRadius;
|
||||
points[2].SetTangentPosition(points[2].position + Vector3.left * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * xRadius);
|
||||
points[3].position = Vector3.right * xRadius;
|
||||
points[3].SetTangentPosition(points[3].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Ellipse.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Ellipse.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4ff5eb5b93412154ea3ec2e48ca9ca4d
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/Dreamteck/Splines/Core/Primitives/Line.cs
Normal file
31
Assets/Dreamteck/Splines/Core/Primitives/Line.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Line : SplinePrimitive
|
||||
{
|
||||
public bool mirror = true;
|
||||
public float length = 1f;
|
||||
public int segments = 1;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Linear;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = false;
|
||||
CreatePoints(segments + 1, SplinePoint.Type.SmoothMirrored);
|
||||
Vector3 origin = Vector3.zero;
|
||||
if (mirror) origin = -Vector3.up * length * 0.5f;
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
points[i].position = origin + Vector3.up * length * ((float)i / (points.Length - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Line.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Line.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c7d365eea2f73048be2306c775e3df4
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Assets/Dreamteck/Splines/Core/Primitives/Ngon.cs
Normal file
30
Assets/Dreamteck/Splines/Core/Primitives/Ngon.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Ngon : SplinePrimitive
|
||||
{
|
||||
public float radius = 1f;
|
||||
public int sides = 3;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Linear;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(sides, SplinePoint.Type.SmoothMirrored);
|
||||
for (int i = 0; i < sides; i++)
|
||||
{
|
||||
float percent = (float)i / sides;
|
||||
Vector3 pos = Quaternion.AngleAxis(360f * percent, Vector3.forward) * Vector3.up * radius;
|
||||
points[i].SetPosition(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Ngon.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Ngon.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79792b94d3f3fc2489022f36d48a41d4
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
27
Assets/Dreamteck/Splines/Core/Primitives/Rectangle.cs
Normal file
27
Assets/Dreamteck/Splines/Core/Primitives/Rectangle.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Rectangle : SplinePrimitive
|
||||
{
|
||||
public Vector2 size = Vector2.one;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Linear;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(4, SplinePoint.Type.SmoothMirrored);
|
||||
points[0].position = points[0].tangent = Vector3.up / 2f * size.y + Vector3.left / 2f * size.x;
|
||||
points[1].position = points[1].tangent = Vector3.up / 2f * size.y + Vector3.right / 2f * size.x;
|
||||
points[2].position = points[2].tangent = Vector3.down / 2f * size.y + Vector3.right / 2f * size.x;
|
||||
points[3].position = points[3].tangent = Vector3.down / 2f * size.y + Vector3.left / 2f * size.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Rectangle.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Rectangle.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e39c41817ed55504cbef2a470c95599d
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/Dreamteck/Splines/Core/Primitives/RoundedRectangle.cs
Normal file
45
Assets/Dreamteck/Splines/Core/Primitives/RoundedRectangle.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class RoundedRectangle : SplinePrimitive
|
||||
{
|
||||
public Vector2 size = Vector2.one;
|
||||
public float xRadius = 0.25f;
|
||||
public float yRadius = 0.25f;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Bezier;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(8, SplinePoint.Type.Broken);
|
||||
Vector2 edgeSize = size - new Vector2(xRadius, yRadius) * 2f;
|
||||
points[0].SetPosition(Vector3.up / 2f * edgeSize.y + Vector3.left / 2f * size.x);
|
||||
points[1].SetPosition(Vector3.up / 2f * size.y + Vector3.left / 2f * edgeSize.x);
|
||||
points[2].SetPosition(Vector3.up / 2f * size.y + Vector3.right / 2f * edgeSize.x);
|
||||
points[3].SetPosition(Vector3.up / 2f * edgeSize.y + Vector3.right / 2f * size.x);
|
||||
points[4].SetPosition(Vector3.down / 2f * edgeSize.y + Vector3.right / 2f * size.x);
|
||||
points[5].SetPosition(Vector3.down / 2f * size.y + Vector3.right / 2f * edgeSize.x);
|
||||
points[6].SetPosition(Vector3.down / 2f * size.y + Vector3.left / 2f * edgeSize.x);
|
||||
points[7].SetPosition(Vector3.down / 2f * edgeSize.y + Vector3.left / 2f * size.x);
|
||||
|
||||
float xRad = 2f * (Mathf.Sqrt(2f) - 1f) / 3f * xRadius * 2f;
|
||||
float yRad = 2f * (Mathf.Sqrt(2f) - 1f) / 3f * yRadius * 2f;
|
||||
points[0].SetTangent2Position(points[0].position + Vector3.up * yRad);
|
||||
points[1].SetTangentPosition(points[1].position + Vector3.left * xRad);
|
||||
points[2].SetTangent2Position(points[2].position + Vector3.right * xRad);
|
||||
points[3].SetTangentPosition(points[3].position + Vector3.up * yRad);
|
||||
points[4].SetTangent2Position(points[4].position + Vector3.down * yRad);
|
||||
points[5].SetTangentPosition(points[5].position + Vector3.right * xRad);
|
||||
points[6].SetTangent2Position(points[6].position + Vector3.left * xRad);
|
||||
points[7].SetTangentPosition(points[7].position + Vector3.down * yRad);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6ad8207cf520e94aa15dea4fae0027d
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
50
Assets/Dreamteck/Splines/Core/Primitives/Spiral.cs
Normal file
50
Assets/Dreamteck/Splines/Core/Primitives/Spiral.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Spiral : SplinePrimitive
|
||||
{
|
||||
public float startRadius = 1f;
|
||||
public float endRadius = 1f;
|
||||
public float stretch = 1f;
|
||||
public int iterations = 3;
|
||||
public bool clockwise = true;
|
||||
public AnimationCurve curve = new AnimationCurve();
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Bezier;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = false;
|
||||
CreatePoints(iterations * 4 + 1, SplinePoint.Type.SmoothMirrored);
|
||||
float radiusDelta = Mathf.Abs(endRadius - startRadius);
|
||||
float radiusDeltaPercent = radiusDelta / Mathf.Max(Mathf.Abs(endRadius), Mathf.Abs(startRadius));
|
||||
float multiplier = 1f;
|
||||
if (endRadius > startRadius) multiplier = -1;
|
||||
float angle = 0f;
|
||||
float str = 0f;
|
||||
float angleDirection = clockwise ? 1f : -1f;
|
||||
for (int i = 0; i <= iterations * 4; i++)
|
||||
{
|
||||
float percent = curve.Evaluate((float)i / (iterations * 4));
|
||||
float radius = Mathf.Lerp(startRadius, endRadius, percent);
|
||||
Quaternion rot = Quaternion.AngleAxis(angle, Vector3.up);
|
||||
points[i].position = rot * Vector3.forward / 2f * radius + Vector3.up * str;
|
||||
Quaternion tangentRot = Quaternion.identity;
|
||||
if (multiplier > 0) tangentRot = Quaternion.AngleAxis(Mathf.Lerp(0f, 90f * 0.16f * angleDirection, radiusDeltaPercent * percent), Vector3.up);
|
||||
else tangentRot = Quaternion.AngleAxis(Mathf.Lerp(0f, -90f * 0.16f * angleDirection, (1f - percent) * radiusDeltaPercent), Vector3.up);
|
||||
if (clockwise) points[i].tangent = points[i].position - (tangentRot * rot * Vector3.right * radius + Vector3.up * stretch / 4f) * 2 * (Mathf.Sqrt(2f) - 1f) / 3f;
|
||||
else points[i].tangent = points[i].position + (tangentRot * rot * Vector3.right * radius - Vector3.up * stretch / 4f) * 2 * (Mathf.Sqrt(2f) - 1f) / 3f;
|
||||
points[i].tangent2 = points[i].position - (points[i].tangent - points[i].position);
|
||||
str += stretch / 4f;
|
||||
angle += 90f * angleDirection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Spiral.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Spiral.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4c765265077e6a4d9bc4e5d2cc75f3c
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
110
Assets/Dreamteck/Splines/Core/Primitives/SplinePrimitive.cs
Normal file
110
Assets/Dreamteck/Splines/Core/Primitives/SplinePrimitive.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives {
|
||||
public class SplinePrimitive
|
||||
{
|
||||
protected bool closed = false;
|
||||
protected SplinePoint[] points = new SplinePoint[0];
|
||||
|
||||
public Vector3 offset = Vector3.zero;
|
||||
public Vector3 rotation = Vector3.zero;
|
||||
public bool is2D = false;
|
||||
|
||||
public virtual void Calculate()
|
||||
{
|
||||
Generate();
|
||||
ApplyOffset();
|
||||
}
|
||||
|
||||
protected virtual void Generate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Spline CreateSpline()
|
||||
{
|
||||
Generate();
|
||||
ApplyOffset();
|
||||
Spline spline = new Spline(GetSplineType());
|
||||
spline.points = points;
|
||||
if (closed) spline.Close();
|
||||
return spline;
|
||||
}
|
||||
|
||||
public void UpdateSpline(Spline spline)
|
||||
{
|
||||
Generate();
|
||||
ApplyOffset();
|
||||
spline.type = GetSplineType();
|
||||
spline.points = points;
|
||||
if (closed) spline.Close();
|
||||
else if (spline.isClosed) spline.Break();
|
||||
}
|
||||
|
||||
public SplineComputer CreateSplineComputer(string name, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
Generate();
|
||||
ApplyOffset();
|
||||
GameObject go = new GameObject(name);
|
||||
SplineComputer comp = go.AddComponent<SplineComputer>();
|
||||
comp.SetPoints(points, SplineComputer.Space.Local);
|
||||
if (closed) comp.Close();
|
||||
comp.transform.position = position;
|
||||
comp.transform.rotation = rotation;
|
||||
return comp;
|
||||
}
|
||||
|
||||
public void UpdateSplineComputer(SplineComputer comp)
|
||||
{
|
||||
Generate();
|
||||
ApplyOffset();
|
||||
comp.type = GetSplineType();
|
||||
comp.SetPoints(points, SplineComputer.Space.Local);
|
||||
if (closed) comp.Close();
|
||||
else if (comp.isClosed) comp.Break();
|
||||
}
|
||||
|
||||
public SplinePoint[] GetPoints()
|
||||
{
|
||||
return points;
|
||||
}
|
||||
|
||||
public virtual Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.CatmullRom;
|
||||
}
|
||||
|
||||
public bool GetIsClosed()
|
||||
{
|
||||
return closed;
|
||||
}
|
||||
|
||||
void ApplyOffset()
|
||||
{
|
||||
Quaternion freeRot = Quaternion.Euler(rotation);
|
||||
if (is2D) freeRot = Quaternion.AngleAxis(-rotation.z, Vector3.forward) * Quaternion.AngleAxis(90f, Vector3.right);
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
points[i].position = freeRot * points[i].position;
|
||||
points[i].tangent = freeRot * points[i].tangent;
|
||||
points[i].tangent2 = freeRot * points[i].tangent2;
|
||||
points[i].normal = freeRot * points[i].normal;
|
||||
}
|
||||
for (int i = 0; i < points.Length; i++) points[i].SetPosition(points[i].position + offset);
|
||||
}
|
||||
|
||||
protected void CreatePoints(int count, SplinePoint.Type type)
|
||||
{
|
||||
if (points.Length != count) points = new SplinePoint[count];
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
points[i].type = type;
|
||||
points[i].normal = Vector3.up;
|
||||
points[i].color = Color.white;
|
||||
points[i].size = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acb225539fdaf0d479f2ca1a7694afcc
|
||||
timeCreated: 1495473185
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/Dreamteck/Splines/Core/Primitives/Star.cs
Normal file
32
Assets/Dreamteck/Splines/Core/Primitives/Star.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Dreamteck.Splines.Primitives
|
||||
{
|
||||
public class Star : SplinePrimitive
|
||||
{
|
||||
public float radius = 1f;
|
||||
public float depth = 0.5f;
|
||||
public int sides = 5;
|
||||
|
||||
public override Spline.Type GetSplineType()
|
||||
{
|
||||
return Spline.Type.Linear;
|
||||
}
|
||||
|
||||
protected override void Generate()
|
||||
{
|
||||
base.Generate();
|
||||
closed = true;
|
||||
CreatePoints(sides * 2, SplinePoint.Type.SmoothMirrored);
|
||||
float innerRadius = radius * depth;
|
||||
for (int i = 0; i < sides * 2; i++)
|
||||
{
|
||||
float percent = (float)i / (float)(sides * 2);
|
||||
Vector3 pos = Quaternion.AngleAxis(180 + 360f * percent, Vector3.forward) * Vector3.up * ((float)i % 2f == 0 ? radius : innerRadius);
|
||||
points[i].SetPosition(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/Primitives/Star.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/Primitives/Star.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30808d4ccaa42844698780f3f2af4308
|
||||
timeCreated: 1495474369
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
643
Assets/Dreamteck/Splines/Core/SampleCollection.cs
Normal file
643
Assets/Dreamteck/Splines/Core/SampleCollection.cs
Normal file
@@ -0,0 +1,643 @@
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
[System.Serializable]
|
||||
public class SampleCollection
|
||||
{
|
||||
[HideInInspector]
|
||||
[UnityEngine.Serialization.FormerlySerializedAs("samples")]
|
||||
public SplineSample[] samples = new SplineSample[0];
|
||||
private static bool __useModifier = false;
|
||||
private static ISampleModifier __modifier = null;
|
||||
|
||||
public int length
|
||||
{
|
||||
get { return samples.Length; }
|
||||
}
|
||||
public int[] optimizedIndices = new int[0];
|
||||
bool hasSamples
|
||||
{
|
||||
get { return samples.Length > 0; }
|
||||
}
|
||||
public SplineComputer.SampleMode sampleMode = SplineComputer.SampleMode.Default;
|
||||
private SplineSample _workSample = new SplineSample();
|
||||
|
||||
public SampleCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public SampleCollection(SampleCollection input)
|
||||
{
|
||||
samples = input.samples;
|
||||
optimizedIndices = input.optimizedIndices;
|
||||
sampleMode = input.sampleMode;
|
||||
}
|
||||
|
||||
public int GetClippedSampleCount(double clipFrom, double clipTo, out int startIndex, out int endIndex)
|
||||
{
|
||||
startIndex = endIndex = 0;
|
||||
if (sampleMode == SplineComputer.SampleMode.Default)
|
||||
{
|
||||
startIndex = DMath.FloorInt((samples.Length - 1) * clipFrom);
|
||||
endIndex = DMath.CeilInt((samples.Length - 1) * clipTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
double clipFromLerp = 0.0, clipToLerp = 0.0;
|
||||
GetSamplingValues(clipFrom, out startIndex, out clipFromLerp);
|
||||
GetSamplingValues(clipTo, out endIndex, out clipToLerp);
|
||||
if (clipToLerp > 0.0 && endIndex < samples.Length - 1) endIndex++;
|
||||
}
|
||||
|
||||
if (clipTo < clipFrom) //Handle looping segments
|
||||
{
|
||||
int toSamples = endIndex + 1;
|
||||
int fromSamples = samples.Length - startIndex;
|
||||
return toSamples + fromSamples;
|
||||
}
|
||||
return endIndex - startIndex + 1;
|
||||
}
|
||||
|
||||
public void GetSamplingValues(double percent, out int sampleIndex, out double lerp)
|
||||
{
|
||||
lerp = 0.0;
|
||||
if (sampleMode == SplineComputer.SampleMode.Optimized)
|
||||
{
|
||||
double indexValue = percent * (optimizedIndices.Length - 1);
|
||||
int index = DMath.FloorInt(indexValue);
|
||||
sampleIndex = optimizedIndices[index];
|
||||
double lerpPercent = 0.0;
|
||||
if (index < optimizedIndices.Length - 1)
|
||||
{
|
||||
//Percent 0-1 between the sampleIndex and the next sampleIndex
|
||||
double indexLerp = indexValue - index;
|
||||
double sampleIndexPercent = (double)index / (optimizedIndices.Length - 1);
|
||||
double nextSampleIndexPercent = (double)(index + 1) / (optimizedIndices.Length - 1);
|
||||
//Percent 0-1 of the sample between the sampleIndices' percents
|
||||
lerpPercent = DMath.Lerp(sampleIndexPercent, nextSampleIndexPercent, indexLerp);
|
||||
}
|
||||
if (sampleIndex < samples.Length - 1)
|
||||
{
|
||||
lerp = DMath.InverseLerp(samples[sampleIndex].percent, samples[sampleIndex + 1].percent, lerpPercent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sampleIndex = DMath.FloorInt(percent * (samples.Length - 1));
|
||||
lerp = (samples.Length - 1) * percent - sampleIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Spline.EvaluatePosition but the result is transformed by the computer's transform
|
||||
/// </summary>
|
||||
/// <param name="percent">Evaluation percent</param>
|
||||
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
|
||||
/// <returns></returns>
|
||||
public Vector3 EvaluatePosition(double percent)
|
||||
{
|
||||
if (!hasSamples) return Vector3.zero;
|
||||
int index;
|
||||
double lerp;
|
||||
GetSamplingValues(percent, out index, out lerp);
|
||||
if (lerp > 0.0)
|
||||
{
|
||||
return Vector3.Lerp(samples[index].position, samples[index + 1].position, (float)lerp);
|
||||
}
|
||||
return samples[index].position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Spline.Evaluate but the result is transformed by the computer's transform
|
||||
/// </summary>
|
||||
/// <param name="percent">Evaluation percent</param>
|
||||
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
|
||||
/// <returns></returns>
|
||||
public SplineSample Evaluate(double percent)
|
||||
{
|
||||
SplineSample result = new SplineSample();
|
||||
Evaluate(percent, ref result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the sample collection and transforms the result by the <see cref="localToWorldMatrix"/>
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <param name="percent"></param>
|
||||
public void Evaluate(double percent, ref SplineSample result)
|
||||
{
|
||||
if (!hasSamples)
|
||||
{
|
||||
result = new SplineSample();
|
||||
return;
|
||||
}
|
||||
int index;
|
||||
double lerp;
|
||||
GetSamplingValues(percent, out index, out lerp);
|
||||
if (lerp > 0.0)
|
||||
{
|
||||
SplineSample.Lerp(ref samples[index], ref samples[index + 1], lerp, ref result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.FastCopy(ref samples[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the sample collection and transforms the results by the <see cref="localToWorldMatrix"/>
|
||||
/// </summary>
|
||||
/// <param name="from">Start position [0-1]</param>
|
||||
/// <param name="to">Target position [from-1]</param>
|
||||
/// <returns></returns>
|
||||
public void Evaluate(ref SplineSample[] results, double from = 0.0, double to = 1.0)
|
||||
{
|
||||
if (!hasSamples)
|
||||
{
|
||||
results = new SplineSample[0];
|
||||
return;
|
||||
}
|
||||
Spline.FormatFromTo(ref from, ref to);
|
||||
int fromIndex, toIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(from, out fromIndex, out lerp);
|
||||
GetSamplingValues(to, out toIndex, out lerp);
|
||||
if (lerp > 0.0 && toIndex < samples.Length - 1)
|
||||
{
|
||||
toIndex++;
|
||||
}
|
||||
int clippedIterations = toIndex - fromIndex + 1;
|
||||
if (results == null)
|
||||
{
|
||||
results = new SplineSample[clippedIterations];
|
||||
}
|
||||
else if (results.Length != clippedIterations)
|
||||
{
|
||||
results = new SplineSample[clippedIterations];
|
||||
}
|
||||
|
||||
results[0] = Evaluate(from);
|
||||
results[results.Length - 1] = Evaluate(to);
|
||||
for (int i = 1; i < results.Length - 1; i++)
|
||||
{
|
||||
results[i].FastCopy(ref samples[i + fromIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Spline.EvaluatePositions but the results are transformed by the computer's transform
|
||||
/// </summary>
|
||||
/// <param name="from">Start position [0-1]</param>
|
||||
/// <param name="to">Target position [from-1]</param>
|
||||
/// <returns></returns>
|
||||
public void EvaluatePositions(ref Vector3[] positions, double from = 0.0, double to = 1.0)
|
||||
{
|
||||
if (!hasSamples)
|
||||
{
|
||||
positions = new Vector3[0];
|
||||
return;
|
||||
}
|
||||
|
||||
Spline.FormatFromTo(ref from, ref to);
|
||||
int fromIndex, toIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(from, out fromIndex, out lerp);
|
||||
GetSamplingValues(to, out toIndex, out lerp);
|
||||
if (lerp > 0.0 && toIndex < samples.Length - 1)
|
||||
{
|
||||
toIndex++;
|
||||
}
|
||||
int clippedIterations = toIndex - fromIndex + 1;
|
||||
|
||||
if (positions == null)
|
||||
{
|
||||
positions = new Vector3[clippedIterations];
|
||||
}
|
||||
else if (positions.Length != clippedIterations)
|
||||
{
|
||||
positions = new Vector3[clippedIterations];
|
||||
}
|
||||
|
||||
positions[0] = EvaluatePosition(from);
|
||||
positions[positions.Length - 1] = EvaluatePosition(to);
|
||||
for (int i = 1; i < positions.Length - 1; i++)
|
||||
{
|
||||
positions[i] = samples[i + fromIndex].position;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the percent from the spline at a given distance from the start point
|
||||
/// </summary>
|
||||
/// <param name="start">The start point</param>
|
||||
/// <param name="distance">The distance to travel</param>
|
||||
/// <param name="direction">The direction towards which to move</param>
|
||||
/// <returns></returns>
|
||||
public double Travel(double start, float distance, Spline.Direction direction, out float moved, double clipFrom = 0.0, double clipTo = 1.0)
|
||||
{
|
||||
moved = 0f;
|
||||
if (!hasSamples) return 0.0;
|
||||
if (direction == Spline.Direction.Forward && start >= 1.0) return clipTo;
|
||||
else if (direction == Spline.Direction.Backward && start <= 0.0) return clipFrom;
|
||||
|
||||
double lastPercent = start;
|
||||
if (distance == 0f) return lastPercent;
|
||||
Vector3 lastPos = EvaluatePosition(start);
|
||||
int sampleIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(lastPercent, out sampleIndex, out lerp);
|
||||
if (direction == Spline.Direction.Forward && lerp > 0.0) sampleIndex++;
|
||||
float lastDistance = 0f;
|
||||
int minIndex = 0;
|
||||
int maxIndex = samples.Length - 1;
|
||||
|
||||
bool samplesAreLooped = clipTo < clipFrom;
|
||||
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
GetSamplingValues(clipFrom, out minIndex, out lerp);
|
||||
GetSamplingValues(clipTo, out maxIndex, out lerp);
|
||||
if (lerp > 0.0) maxIndex++;
|
||||
}
|
||||
|
||||
while (moved < distance)
|
||||
{
|
||||
Vector3 transformedPos = samples[sampleIndex].position;
|
||||
lastDistance = Vector3.Distance(transformedPos, lastPos);
|
||||
moved += lastDistance;
|
||||
if (moved >= distance) break;
|
||||
lastPos = transformedPos;
|
||||
lastPercent = samples[sampleIndex].percent;
|
||||
if (direction == Spline.Direction.Forward)
|
||||
{
|
||||
if (sampleIndex == samples.Length - 1)
|
||||
{
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
lastPos = samples[0].position;
|
||||
lastPercent = samples[0].percent;
|
||||
sampleIndex = 1;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if (samplesAreLooped && sampleIndex == maxIndex) break;
|
||||
sampleIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sampleIndex == 0)
|
||||
{
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
lastPos = samples[samples.Length - 1].position;
|
||||
lastPercent = samples[samples.Length - 1].percent;
|
||||
sampleIndex = samples.Length - 2;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if (samplesAreLooped && sampleIndex == minIndex) break;
|
||||
sampleIndex--;
|
||||
}
|
||||
}
|
||||
float moveExcess = 0f;
|
||||
if (moved > distance)
|
||||
{
|
||||
moveExcess = moved - distance;
|
||||
}
|
||||
|
||||
|
||||
double lerpPercent = 0.0;
|
||||
if(lastDistance > 0.0)
|
||||
{
|
||||
lerpPercent = moveExcess / lastDistance;
|
||||
}
|
||||
double p = DMath.Lerp(lastPercent, samples[sampleIndex].percent, 1f - lerpPercent);
|
||||
moved -= moveExcess;
|
||||
return p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the percent from the spline at a given distance from the start point while applying a local <paramref name="offset"/> to each sample
|
||||
/// The offset is multiplied by the sample sizes
|
||||
/// </summary>
|
||||
/// <param name="start">The start point</param>
|
||||
/// /// <param name="distance">The distance to travel</param>
|
||||
/// <param name="direction">The direction towards which to move</param>
|
||||
/// <returns></returns>
|
||||
public double TravelWithOffset(double start, float distance, Spline.Direction direction, Vector3 offset, out float moved, double clipFrom = 0.0, double clipTo = 1.0)
|
||||
{
|
||||
moved = 0f;
|
||||
if (!hasSamples) return 0.0;
|
||||
if (direction == Spline.Direction.Forward && start >= 1.0) return clipTo;
|
||||
else if (direction == Spline.Direction.Backward && start <= 0.0) return clipFrom;
|
||||
|
||||
double lastPercent = start;
|
||||
if (distance == 0f) return lastPercent;
|
||||
|
||||
Evaluate(start, ref _workSample);
|
||||
Vector3 lastPos = _workSample.position + _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
|
||||
|
||||
int sampleIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(lastPercent, out sampleIndex, out lerp);
|
||||
if (direction == Spline.Direction.Forward && lerp > 0.0) sampleIndex++;
|
||||
float lastDistance = 0f;
|
||||
int minIndex = 0;
|
||||
int maxIndex = length - 1;
|
||||
|
||||
bool samplesAreLooped = clipTo < clipFrom;
|
||||
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
GetSamplingValues(clipFrom, out minIndex, out lerp);
|
||||
GetSamplingValues(clipTo, out maxIndex, out lerp);
|
||||
if (lerp > 0.0) maxIndex++;
|
||||
}
|
||||
|
||||
while (moved < distance)
|
||||
{
|
||||
Vector3 newPos = samples[sampleIndex].position +
|
||||
samples[sampleIndex].up * (offset.y * samples[sampleIndex].size) +
|
||||
samples[sampleIndex].right * (offset.x * samples[sampleIndex].size) +
|
||||
samples[sampleIndex].forward * (offset.z * samples[sampleIndex].size);
|
||||
lastDistance = Vector3.Distance(newPos, lastPos);
|
||||
moved += lastDistance;
|
||||
if (moved >= distance)
|
||||
{
|
||||
break;
|
||||
}
|
||||
lastPos = newPos;
|
||||
lastPercent = samples[sampleIndex].percent;
|
||||
if (direction == Spline.Direction.Forward)
|
||||
{
|
||||
if (sampleIndex == length - 1)
|
||||
{
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
lastPos = samples[0].position +
|
||||
samples[0].up * (offset.y * samples[0].size) +
|
||||
samples[0].right * (offset.x * samples[0].size) +
|
||||
samples[0].forward * (offset.z * samples[0].size);
|
||||
lastPercent = samples[0].percent;
|
||||
sampleIndex = 1;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if (samplesAreLooped && sampleIndex == maxIndex) break;
|
||||
sampleIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sampleIndex == 0)
|
||||
{
|
||||
if (samplesAreLooped)
|
||||
{
|
||||
int lastIndex = samples.Length - 1;
|
||||
lastPos = samples[lastIndex].position +
|
||||
samples[lastIndex].up * (offset.y * samples[lastIndex].size) +
|
||||
samples[lastIndex].right * (offset.x * samples[lastIndex].size) +
|
||||
samples[lastIndex].forward * (offset.z * samples[lastIndex].size);
|
||||
lastPercent = samples[lastIndex].percent;
|
||||
sampleIndex = samples.Length - 2;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if (samplesAreLooped && sampleIndex == minIndex) break;
|
||||
sampleIndex--;
|
||||
}
|
||||
}
|
||||
float moveExcess = 0f;
|
||||
if (moved > distance)
|
||||
{
|
||||
moveExcess = moved - distance;
|
||||
}
|
||||
|
||||
double p = DMath.Lerp(lastPercent, samples[sampleIndex].percent, 1f - moveExcess / lastDistance);
|
||||
moved -= moveExcess;
|
||||
return p;
|
||||
}
|
||||
|
||||
public double Travel(double start, float distance, Spline.Direction direction = Spline.Direction.Forward)
|
||||
{
|
||||
float moved;
|
||||
return Travel(start, distance, direction, out moved);
|
||||
}
|
||||
|
||||
private Vector3 GetModifiedPosition(ref SplineSample sample)
|
||||
{
|
||||
if (!__useModifier) return sample.position;
|
||||
return __modifier.GetModifiedSamplePosition(ref sample);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Spline.Project but the point is transformed by the computer's transform.
|
||||
/// </summary>
|
||||
/// <param name="position">Point in space</param>
|
||||
/// <param name="subdivide">Subdivisions default: 4</param>
|
||||
/// <param name="from">Sample from [0-1] default: 0f</param>
|
||||
/// <param name="to">Sample to [0-1] default: 1f</param>
|
||||
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
|
||||
/// <param name="subdivisions">Subdivisions for the Calculate mode. Don't assign if not using Calculated mode.</param>
|
||||
/// <returns></returns>
|
||||
public void Project(Vector3 position, int controlPointCount, ref SplineSample result, double from = 0.0, double to = 1.0, ISampleModifier modifier = null)
|
||||
{
|
||||
if (!hasSamples) return;
|
||||
if (samples.Length == 1)
|
||||
{
|
||||
result.FastCopy(ref samples[0]);
|
||||
return;
|
||||
}
|
||||
__useModifier = modifier != null;
|
||||
__modifier = modifier;
|
||||
Spline.FormatFromTo(ref from, ref to);
|
||||
//First make a very rough sample of the from-to region
|
||||
int steps = (controlPointCount - 1) * 4; //Sampling four points per segment is enough to find the closest point range
|
||||
int step = samples.Length / steps;
|
||||
if (step < 1) step = 1;
|
||||
|
||||
float minDist = (position - GetModifiedPosition(ref samples[0])).sqrMagnitude;
|
||||
int fromIndex = 0;
|
||||
int toIndex = samples.Length - 1;
|
||||
double lerp;
|
||||
if (from != 0.0) GetSamplingValues(from, out fromIndex, out lerp);
|
||||
if (to != 1.0)
|
||||
{
|
||||
GetSamplingValues(to, out toIndex, out lerp);
|
||||
if (lerp > 0.0 && toIndex < samples.Length - 1) toIndex++;
|
||||
}
|
||||
int checkFrom = fromIndex;
|
||||
int checkTo = toIndex;
|
||||
|
||||
//Find the closest point range which will be checked in detail later
|
||||
for (int i = fromIndex; i < toIndex; i += step)
|
||||
{
|
||||
if (i >= toIndex) i = toIndex-1;
|
||||
Vector3 pos1 = GetModifiedPosition(ref samples[i]);
|
||||
Vector3 pos2 = GetModifiedPosition(ref samples[Mathf.Min(i + step, toIndex)]);
|
||||
Vector3 projected = LinearAlgebraUtility.ProjectOnLine(pos1, pos2, position);
|
||||
float dist = (position - projected).sqrMagnitude;
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
checkFrom = Mathf.Max(i - step, 0);
|
||||
checkTo = Mathf.Min(i + step, samples.Length - 1);
|
||||
}
|
||||
if (i == toIndex) break;
|
||||
}
|
||||
minDist = (position - samples[checkFrom].position).sqrMagnitude;
|
||||
|
||||
int index = checkFrom;
|
||||
//Find the closest result within the range
|
||||
for (int i = checkFrom + 1; i <= checkTo; i++)
|
||||
{
|
||||
float dist = (position - GetModifiedPosition(ref samples[i])).sqrMagnitude;
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
//Project the point on the line between the two closest samples
|
||||
int backIndex = index - 1;
|
||||
if (backIndex < 0) backIndex = 0;
|
||||
int frontIndex = index + 1;
|
||||
if (frontIndex > samples.Length - 1) frontIndex = samples.Length - 1;
|
||||
|
||||
Vector3 backPos = GetModifiedPosition(ref samples[backIndex]);
|
||||
Vector3 currentPos = GetModifiedPosition(ref samples[index]);
|
||||
Vector3 frontPos = GetModifiedPosition(ref samples[frontIndex]);
|
||||
|
||||
Vector3 back = LinearAlgebraUtility.ProjectOnLine(backPos, currentPos, position);
|
||||
Vector3 front = LinearAlgebraUtility.ProjectOnLine(currentPos, frontPos, position);
|
||||
float backLength = (currentPos - backPos).magnitude;
|
||||
float frontLength = (currentPos - frontPos).magnitude;
|
||||
float backProjectDist = (back - backPos).magnitude;
|
||||
float frontProjectDist = (front - frontPos).magnitude;
|
||||
if (backIndex < index && index < frontIndex)
|
||||
{
|
||||
if ((position - back).sqrMagnitude < (position - front).sqrMagnitude)
|
||||
{
|
||||
SplineSample.Lerp(ref samples[backIndex], ref samples[index], backProjectDist / backLength, ref result);
|
||||
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
SplineSample.Lerp(ref samples[frontIndex], ref samples[index], frontProjectDist / frontLength, ref result);
|
||||
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
|
||||
}
|
||||
}
|
||||
else if (backIndex < index)
|
||||
{
|
||||
SplineSample.Lerp(ref samples[backIndex], ref samples[index], backProjectDist / backLength, ref result);
|
||||
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
SplineSample.Lerp(ref samples[frontIndex], ref samples[index], frontProjectDist / frontLength, ref result);
|
||||
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
|
||||
}
|
||||
|
||||
if (from == 0.0 && to == 1.0 && result.percent < samples[1].percent) //Handle looped splines
|
||||
{
|
||||
Vector3 pos1 = GetModifiedPosition(ref samples[samples.Length - 1]);
|
||||
Vector3 pos2 = GetModifiedPosition(ref samples[samples.Length - 2]);
|
||||
|
||||
Vector3 projected = LinearAlgebraUtility.ProjectOnLine(pos1, pos2, position);
|
||||
if ((position - projected).sqrMagnitude < (position - result.position).sqrMagnitude)
|
||||
{
|
||||
double l = LinearAlgebraUtility.InverseLerp(pos1, pos2, projected);
|
||||
SplineSample.Lerp(ref samples[samples.Length - 1], ref samples[samples.Length - 2], l, ref result);
|
||||
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(samples.Length - 1), GetSamplePercent(samples.Length - 2), l);
|
||||
}
|
||||
}
|
||||
|
||||
if (__useModifier)
|
||||
{
|
||||
modifier.ApplySampleModifiers(ref result);
|
||||
}
|
||||
}
|
||||
|
||||
private double GetSamplePercent(int sampleIndex)
|
||||
{
|
||||
if (sampleMode == SplineComputer.SampleMode.Optimized)
|
||||
{
|
||||
return samples[optimizedIndices[sampleIndex]].percent;
|
||||
}
|
||||
return (double)sampleIndex / (samples.Length - 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as Spline.CalculateLength but this takes the computer's transform into account when calculating the length.
|
||||
/// </summary>
|
||||
/// <param name="from">Calculate from [0-1] default: 0f</param>
|
||||
/// <param name="to">Calculate to [0-1] default: 1f</param>
|
||||
/// <param name="resolution">Resolution [0-1] default: 1f</param>
|
||||
/// <param name="address">Node address of junctions</param>
|
||||
/// <returns></returns>
|
||||
public float CalculateLength(double from = 0.0, double to = 1.0, bool preventInvert = true)
|
||||
{
|
||||
if (!hasSamples) return 0f;
|
||||
Spline.FormatFromTo(ref from, ref to, preventInvert);
|
||||
float length = 0f;
|
||||
Vector3 lastPos = EvaluatePosition(from);
|
||||
int fromIndex, toIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(from, out fromIndex, out lerp);
|
||||
GetSamplingValues(to, out toIndex, out lerp);
|
||||
|
||||
if (lerp > 0.0 && toIndex < this.length - 1)
|
||||
{
|
||||
toIndex++;
|
||||
}
|
||||
|
||||
for (int i = fromIndex + 1; i < toIndex; i++)
|
||||
{
|
||||
Vector3 currentPos = samples[i].position;
|
||||
length += Vector3.Distance(currentPos, lastPos);
|
||||
lastPos = currentPos;
|
||||
}
|
||||
length += Vector3.Distance(EvaluatePosition(to), lastPos);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the length between <paramref name="from"/> and <paramref name="to"/> with applied local offset to to the samples
|
||||
/// The offset is multiplied by the sample sizes
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
public float CalculateLengthWithOffset(Vector3 offset, double from = 0.0, double to = 1.0)
|
||||
{
|
||||
if (!hasSamples) return 0f;
|
||||
Spline.FormatFromTo(ref from, ref to);
|
||||
float length = 0f;
|
||||
Evaluate(from, ref _workSample);
|
||||
Vector3 lastPos = _workSample.position + _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
|
||||
int fromIndex, toIndex;
|
||||
double lerp;
|
||||
GetSamplingValues(from, out fromIndex, out lerp);
|
||||
GetSamplingValues(to, out toIndex, out lerp);
|
||||
|
||||
if (lerp > 0.0 && toIndex < this.length - 1)
|
||||
{
|
||||
toIndex++;
|
||||
}
|
||||
|
||||
for (int i = fromIndex + 1; i < toIndex; i++)
|
||||
{
|
||||
Vector3 newPos = samples[i].position + samples[i].up * (offset.y * samples[i].size) + samples[i].right * (offset.x * samples[i].size) + samples[i].forward * (offset.z * samples[i].size);
|
||||
length += Vector3.Distance(newPos, lastPos);
|
||||
lastPos = newPos;
|
||||
}
|
||||
|
||||
Evaluate(to, ref _workSample);
|
||||
_workSample.position += _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
|
||||
length += Vector3.Distance(_workSample.position, lastPos);
|
||||
return length;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Dreamteck/Splines/Core/SampleCollection.cs.meta
Normal file
11
Assets/Dreamteck/Splines/Core/SampleCollection.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ab72bc0d28ecbe4c8a42a5e1c8e2da5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1099
Assets/Dreamteck/Splines/Core/Spline.cs
Normal file
1099
Assets/Dreamteck/Splines/Core/Spline.cs
Normal file
File diff suppressed because it is too large
Load Diff
10
Assets/Dreamteck/Splines/Core/Spline.cs.meta
Normal file
10
Assets/Dreamteck/Splines/Core/Spline.cs.meta
Normal file
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8bb4e3c138e5124b9ea58db9d93e1f4
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
412
Assets/Dreamteck/Splines/Core/SplinePoint.cs
Normal file
412
Assets/Dreamteck/Splines/Core/SplinePoint.cs
Normal file
@@ -0,0 +1,412 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
using System;
|
||||
|
||||
namespace Dreamteck.Splines{
|
||||
[System.Serializable]
|
||||
//A control point used by the SplineClass
|
||||
public struct SplinePoint {
|
||||
public enum Type {SmoothMirrored, Broken, SmoothFree};
|
||||
public Type type
|
||||
{
|
||||
get { return _type; }
|
||||
set
|
||||
{
|
||||
isDirty = _type != value;
|
||||
_type = value;
|
||||
if (value == Type.SmoothMirrored)
|
||||
{
|
||||
SmoothMirrorTangent2();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting the value of isDirty will set the point not dirty
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
public bool isDirty;
|
||||
|
||||
[FormerlySerializedAs("type")]
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Type _type;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_position")]
|
||||
public Vector3 position;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_color")]
|
||||
public Color color;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_normal")]
|
||||
public Vector3 normal;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_size")]
|
||||
public float size;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_tangent")]
|
||||
public Vector3 tangent;
|
||||
|
||||
[HideInInspector]
|
||||
[FormerlySerializedAs("_tangent2")]
|
||||
public Vector3 tangent2;
|
||||
|
||||
public static SplinePoint Lerp(SplinePoint a, SplinePoint b, float t)
|
||||
{
|
||||
SplinePoint result = a;
|
||||
if (a.type == Type.Broken || b.type == Type.Broken) result.type = Type.Broken;
|
||||
else if (a.type == Type.SmoothFree || b.type == Type.SmoothFree) result.type = Type.SmoothFree;
|
||||
else result.type = Type.SmoothMirrored;
|
||||
result.position = Vector3.Lerp(a.position, b.position, t);
|
||||
GetInterpolatedTangents(a, b, t, ref result);
|
||||
result.color = Color.Lerp(a.color, b.color, t);
|
||||
result.size = Mathf.Lerp(a.size, b.size, t);
|
||||
result.normal = Vector3.Slerp(a.normal, b.normal, t);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void GetInterpolatedTangents(SplinePoint a, SplinePoint b, float t, ref SplinePoint target)
|
||||
{
|
||||
Vector3 P0_1 = (1f - t) * a.position + t * a.tangent2;
|
||||
Vector3 P1_2 = (1f - t) * a.tangent2 + t * b.tangent;
|
||||
Vector3 P2_3 = (1f - t) * b.tangent + t * b.position;
|
||||
Vector3 P01_12 = (1 - t) * P0_1 + t * P1_2;
|
||||
Vector3 P12_23 = (1 - t) * P1_2 + t * P2_3;
|
||||
target.tangent = P01_12;
|
||||
target.tangent2 = P12_23;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if(obj is SplinePoint)
|
||||
{
|
||||
return EqualsFast((SplinePoint)obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool EqualsFast(SplinePoint obj)
|
||||
{
|
||||
SplinePoint other = (SplinePoint)obj;
|
||||
if (position != other.position) return false;
|
||||
if (tangent != other.tangent) return false;
|
||||
if (tangent2 != other.tangent2) return false;
|
||||
if (normal != other.normal) return false;
|
||||
if (_type != other._type) return false;
|
||||
if (size != other.size) return false;
|
||||
if (color != other.color) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static bool operator == (SplinePoint p1, SplinePoint p2)
|
||||
{
|
||||
return p1.EqualsFast(p2);
|
||||
}
|
||||
|
||||
public static bool operator != (SplinePoint p1, SplinePoint p2)
|
||||
{
|
||||
return !p1.EqualsFast(p2);
|
||||
}
|
||||
|
||||
public void SetPosition(Vector3 pos)
|
||||
{
|
||||
tangent -= position - pos;
|
||||
tangent2 -= position - pos;
|
||||
position = pos;
|
||||
}
|
||||
|
||||
public void SetTangentPosition(Vector3 pos)
|
||||
{
|
||||
tangent = pos;
|
||||
switch (_type)
|
||||
{
|
||||
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
|
||||
case Type.SmoothFree: SmoothFreeTangent2(); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTangent2Position(Vector3 pos)
|
||||
{
|
||||
tangent2 = pos;
|
||||
switch (_type)
|
||||
{
|
||||
case Type.SmoothMirrored: SmoothMirrorTangent(); break;
|
||||
case Type.SmoothFree: SmoothFreeTangent(); break;
|
||||
}
|
||||
}
|
||||
|
||||
public SplinePoint(Vector3 p)
|
||||
{
|
||||
position = p;
|
||||
tangent = p;
|
||||
tangent2 = p;
|
||||
color = Color.white;
|
||||
normal = Vector3.up;
|
||||
size = 1f;
|
||||
_type = Type.SmoothMirrored;
|
||||
isDirty = false;
|
||||
SmoothMirrorTangent2();
|
||||
}
|
||||
|
||||
public SplinePoint(Vector3 p, Vector3 t){
|
||||
position = p;
|
||||
tangent = t;
|
||||
tangent2 = p + (p - t);
|
||||
color = Color.white;
|
||||
normal = Vector3.up;
|
||||
size = 1f;
|
||||
_type = Type.SmoothMirrored;
|
||||
isDirty = false;
|
||||
SmoothMirrorTangent2();
|
||||
}
|
||||
|
||||
public SplinePoint(Vector3 pos, Vector3 tan, Vector3 nor, float s, Color col){
|
||||
position = pos;
|
||||
tangent = tan;
|
||||
tangent2 = pos + (pos - tan);
|
||||
normal = nor;
|
||||
size = s;
|
||||
color = col;
|
||||
_type = Type.SmoothMirrored;
|
||||
isDirty = false;
|
||||
SmoothMirrorTangent2();
|
||||
}
|
||||
|
||||
public SplinePoint(Vector3 pos, Vector3 tan, Vector3 tan2, Vector3 nor, float s, Color col)
|
||||
{
|
||||
position = pos;
|
||||
tangent = tan;
|
||||
tangent2 = tan2;
|
||||
normal = nor;
|
||||
size = s;
|
||||
color = col;
|
||||
_type = Type.Broken;
|
||||
isDirty = false;
|
||||
switch (_type)
|
||||
{
|
||||
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
|
||||
case Type.SmoothFree: SmoothFreeTangent2(); break;
|
||||
}
|
||||
}
|
||||
|
||||
public SplinePoint(SplinePoint source)
|
||||
{
|
||||
position = source.position;
|
||||
tangent = source.tangent;
|
||||
tangent2 = source.tangent2;
|
||||
color = source.color;
|
||||
normal = source.normal;
|
||||
size = source.size;
|
||||
_type = source.type;
|
||||
isDirty = false;
|
||||
switch (_type)
|
||||
{
|
||||
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
|
||||
case Type.SmoothFree: SmoothFreeTangent2(); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Flatten(LinearAlgebraUtility.Axis axis, float flatValue = 0f)
|
||||
{
|
||||
position = LinearAlgebraUtility.FlattenVector(position, axis, flatValue);
|
||||
tangent = LinearAlgebraUtility.FlattenVector(tangent, axis, flatValue);
|
||||
tangent2 = LinearAlgebraUtility.FlattenVector(tangent2, axis, flatValue);
|
||||
switch (axis)
|
||||
{
|
||||
case LinearAlgebraUtility.Axis.X: normal = Vector3.right; break;
|
||||
case LinearAlgebraUtility.Axis.Y: normal = Vector3.up; break;
|
||||
case LinearAlgebraUtility.Axis.Z: normal = Vector3.forward; break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPositionX(float value)
|
||||
{
|
||||
if(position.x != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
position.x = value;
|
||||
}
|
||||
|
||||
public void SetPositionY(float value)
|
||||
{
|
||||
if(position.y != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
position.y = value;
|
||||
}
|
||||
|
||||
public void SetPositionZ(float value)
|
||||
{
|
||||
if(position.z != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
position.z = value;
|
||||
}
|
||||
|
||||
public void SetTangentX(float value)
|
||||
{
|
||||
if(tangent.x != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent.x = value;
|
||||
}
|
||||
|
||||
public void SetTangentY(float value)
|
||||
{
|
||||
if (tangent.y != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent.y = value;
|
||||
}
|
||||
|
||||
public void SetTangentZ(float value)
|
||||
{
|
||||
if(tangent.z != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent.z = value;
|
||||
}
|
||||
|
||||
public void SetTangent2X(float value)
|
||||
{
|
||||
if (tangent2.x != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent2.x = value;
|
||||
}
|
||||
|
||||
public void SetTangent2Y(float value)
|
||||
{
|
||||
if (tangent2.y != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent2.y = value;
|
||||
}
|
||||
|
||||
public void SetTangent2Z(float value)
|
||||
{
|
||||
if (tangent2.z != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
tangent2.z = value;
|
||||
}
|
||||
|
||||
public void SetNormalX(float value)
|
||||
{
|
||||
if (normal.x != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
normal.x = value;
|
||||
}
|
||||
|
||||
public void SetNormalY(float value)
|
||||
{
|
||||
if (normal.y != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
normal.y = value;
|
||||
}
|
||||
|
||||
public void SetNormalZ(float value)
|
||||
{
|
||||
if(normal.z != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
normal.z = value;
|
||||
}
|
||||
|
||||
public void SetColorR(float value)
|
||||
{
|
||||
if (color.r != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
color.r = value;
|
||||
}
|
||||
|
||||
public void SetColorG(float value)
|
||||
{
|
||||
if (color.g != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
color.g = value;
|
||||
}
|
||||
|
||||
public void SetColorB(float value)
|
||||
{
|
||||
if(color.b != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
color.b = value;
|
||||
}
|
||||
|
||||
public void SetColorA(float value)
|
||||
{
|
||||
if (color.a != value)
|
||||
{
|
||||
isDirty = true;
|
||||
}
|
||||
color.a = value;
|
||||
}
|
||||
|
||||
private void SmoothMirrorTangent2()
|
||||
{
|
||||
tangent2 = position + (position - tangent);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
private void SmoothMirrorTangent()
|
||||
{
|
||||
tangent = position + (position - tangent2);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
private void SmoothFreeTangent2()
|
||||
{
|
||||
tangent2 = position + (position - tangent).normalized * (tangent2 - position).magnitude;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
private void SmoothFreeTangent()
|
||||
{
|
||||
tangent = position + (position - tangent2).normalized * (tangent - position).magnitude;
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash *= hash * 23 + _type.GetHashCode();
|
||||
hash = hash * 23 + position.GetHashCode();
|
||||
hash = hash * 23 + normal.GetHashCode();
|
||||
hash = hash * 23 + tangent.GetHashCode();
|
||||
hash = hash * 23 + tangent2.GetHashCode();
|
||||
hash = hash * 23 + color.GetHashCode();
|
||||
hash = hash * 23 + size.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/SplinePoint.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/SplinePoint.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 373843b4b8b230a4d83d3257cb163ae6
|
||||
timeCreated: 1434316645
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
156
Assets/Dreamteck/Splines/Core/SplinePrefs.cs
Normal file
156
Assets/Dreamteck/Splines/Core/SplinePrefs.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
public static class SplinePrefs
|
||||
{
|
||||
private static bool loaded = false;
|
||||
public static Spline.Direction duplicationDirection = Spline.Direction.Forward;
|
||||
public static bool defaultAlwaysDraw = false;
|
||||
public static SplineComputer.EditorUpdateMode defaultEditorUpdateMode = SplineComputer.EditorUpdateMode.Default;
|
||||
public static bool defaultShowThickness = false;
|
||||
public static bool default2D = false;
|
||||
public static bool startInCreationMode = false;
|
||||
public static SplineComputer.Space pointEditSpace = SplineComputer.Space.Local;
|
||||
public static Color defaultColor = Color.white;
|
||||
public static Color highlightColor = Color.white;
|
||||
public static Color outlineColor = Color.black;
|
||||
public static Color highlightContentColor = new Color(1f, 1f, 1f, 0.95f);
|
||||
public static bool showPointNumbers = false;
|
||||
public static SplineComputer.Space defaultComputerSpace = SplineComputer.Space.Local;
|
||||
public static Spline.Type defaultType = Spline.Type.CatmullRom;
|
||||
public static float createPointSize = 1f;
|
||||
public static Color createPointColor = Color.white;
|
||||
|
||||
static SplinePrefs()
|
||||
{
|
||||
LoadPrefs();
|
||||
}
|
||||
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider SplinesSettingsProvider()
|
||||
{
|
||||
SettingsProvider provider = new SettingsProvider("Dreamteck/Splines", SettingsScope.User)
|
||||
{
|
||||
label = "Splines",
|
||||
guiHandler = (searchContext) =>
|
||||
{
|
||||
OnGUI();
|
||||
},
|
||||
keywords = new HashSet<string>(new[] { "Dreamteck", "Splines", "Path", "Curve"})
|
||||
};
|
||||
|
||||
return provider;
|
||||
}
|
||||
#else
|
||||
[PreferenceItem("DTK Splines")]
|
||||
#endif
|
||||
public static void OnGUI()
|
||||
{
|
||||
if (!loaded) LoadPrefs();
|
||||
EditorGUILayout.LabelField("Newly created splines:", EditorStyles.boldLabel);
|
||||
startInCreationMode = EditorGUILayout.Toggle("Start in Creation Mode", startInCreationMode);
|
||||
defaultComputerSpace = (SplineComputer.Space)EditorGUILayout.EnumPopup("Space", defaultComputerSpace);
|
||||
defaultType = (Spline.Type)EditorGUILayout.EnumPopup("Type", defaultType);
|
||||
defaultAlwaysDraw = EditorGUILayout.Toggle("Always draw", defaultAlwaysDraw);
|
||||
defaultEditorUpdateMode = (SplineComputer.EditorUpdateMode)EditorGUILayout.EnumPopup("Default Editor Update Mode", defaultEditorUpdateMode);
|
||||
defaultShowThickness = EditorGUILayout.Toggle("Show thickness", defaultShowThickness);
|
||||
default2D = EditorGUILayout.Toggle("2D Mode", default2D);
|
||||
defaultColor = EditorGUILayout.ColorField("Spline color", defaultColor);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Newly created points:", EditorStyles.boldLabel);
|
||||
createPointSize = EditorGUILayout.FloatField("Default Size", createPointSize);
|
||||
createPointColor = EditorGUILayout.ColorField("Default Color", createPointColor);
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Editor", EditorStyles.boldLabel);
|
||||
outlineColor = EditorGUILayout.ColorField("Outline color", outlineColor);
|
||||
highlightColor = EditorGUILayout.ColorField("Highlight color", highlightColor);
|
||||
highlightContentColor = EditorGUILayout.ColorField("Highlight content color", highlightContentColor);
|
||||
duplicationDirection = (Spline.Direction)EditorGUILayout.EnumPopup("Duplicate Direction", duplicationDirection);
|
||||
showPointNumbers = EditorGUILayout.Toggle("Show point numbers", showPointNumbers);
|
||||
|
||||
if (GUILayout.Button("Use Defaults", GUILayout.Width(120)))
|
||||
{
|
||||
duplicationDirection = Spline.Direction.Forward;
|
||||
defaultAlwaysDraw = false;
|
||||
defaultEditorUpdateMode = SplineComputer.EditorUpdateMode.Default;
|
||||
defaultShowThickness = false;
|
||||
default2D = false;
|
||||
startInCreationMode = true;
|
||||
defaultColor = Color.white;
|
||||
highlightColor = new Color(0.0313f, 0.737f, 0.796f, 1f);
|
||||
outlineColor = Color.Lerp(defaultColor, Color.black, 0.65f);
|
||||
highlightContentColor = new Color(1f, 1f, 1f, 0.95f);
|
||||
showPointNumbers = false;
|
||||
defaultComputerSpace = SplineComputer.Space.Local;
|
||||
defaultType = Spline.Type.CatmullRom;
|
||||
createPointSize = 1f;
|
||||
createPointColor = Color.white;
|
||||
SavePrefs();
|
||||
}
|
||||
if (GUI.changed) SavePrefs();
|
||||
}
|
||||
|
||||
public static void LoadPrefs()
|
||||
{
|
||||
defaultAlwaysDraw = EditorPrefs.GetBool("Dreamteck.Splines.defaultAlwaysDraw", false);
|
||||
defaultEditorUpdateMode = (SplineComputer.EditorUpdateMode) EditorPrefs.GetInt("Dreamteck.Splines.defaultEditorUpdateMode", 0);
|
||||
defaultShowThickness = EditorPrefs.GetBool("Dreamteck.Splines.defaultShowThickness", false);
|
||||
default2D = EditorPrefs.GetBool("Dreamteck.Splines.default2D", false);
|
||||
startInCreationMode = EditorPrefs.GetBool("Dreamteck.Splines.startInCreationMode", true);
|
||||
showPointNumbers = EditorPrefs.GetBool("Dreamteck.Splines.showPointNumbers", false);
|
||||
pointEditSpace = (SplineComputer.Space)EditorPrefs.GetInt("Dreamteck.Splines.pointEditSpace", 1);
|
||||
defaultColor = LoadColor("Dreamteck.Splines.defaultColor", Color.white);
|
||||
highlightColor = LoadColor("Dreamteck.Splines.highlightColor", new Color(0f, 0.564f, 1f, 1f));
|
||||
outlineColor = LoadColor("Dreamteck.Splines.outlineColor", Color.Lerp(defaultColor, Color.black, 0.65f));
|
||||
highlightContentColor = LoadColor("Dreamteck.Splines.highlightContentColor", new Color(1f, 1f, 1f, 0.95f));
|
||||
defaultComputerSpace = (SplineComputer.Space)EditorPrefs.GetInt("Dreamteck.Splines.defaultComputerSpace", 1);
|
||||
defaultType = (Spline.Type)EditorPrefs.GetInt("Dreamteck.Splines.defaultType", 0);
|
||||
duplicationDirection = (Spline.Direction)EditorPrefs.GetInt("Dreamteck.Splines.duplicationDirection", 0);
|
||||
createPointSize = EditorPrefs.GetFloat("Dreamteck.Splines.createPointSize", 1f);
|
||||
createPointColor = LoadColor("Dreamteck.Splines.createPointColor", Color.white);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
private static Color LoadColor(string name, Color defaultValue)
|
||||
{
|
||||
Color col = Color.white;
|
||||
string colorString = EditorPrefs.GetString(name, defaultValue.r+":"+defaultValue.g+ ":" + defaultValue.b+ ":" + defaultValue.a);
|
||||
string[] elements = colorString.Split(':');
|
||||
if (elements.Length < 4) return col;
|
||||
float r = 0f, g = 0f, b = 0f, a = 0f;
|
||||
float.TryParse(elements[0], out r);
|
||||
float.TryParse(elements[1], out g);
|
||||
float.TryParse(elements[2], out b);
|
||||
float.TryParse(elements[3], out a);
|
||||
col = new Color(r, g, b, a);
|
||||
return col;
|
||||
}
|
||||
|
||||
public static void SavePrefs()
|
||||
{
|
||||
EditorPrefs.SetBool("Dreamteck.Splines.startInCreationMode", startInCreationMode);
|
||||
EditorPrefs.SetBool("Dreamteck.Splines.defaultAlwaysDraw", defaultAlwaysDraw);
|
||||
EditorPrefs.SetInt("Dreamteck.Splines.defaultEditorUpdateMode", (int)defaultEditorUpdateMode);
|
||||
EditorPrefs.SetBool("Dreamteck.Splines.defaultShowThickness", defaultShowThickness);
|
||||
EditorPrefs.SetBool("Dreamteck.Splines.default2D", default2D);
|
||||
EditorPrefs.SetBool("Dreamteck.Splines.showPointNumbers", showPointNumbers);
|
||||
EditorPrefs.SetInt("Dreamteck.Splines.pointEditSpace", (int)pointEditSpace);
|
||||
EditorPrefs.SetString("Dreamteck.Splines.defaultColor", defaultColor.r+ ":" + defaultColor.g+ ":" + defaultColor.b+ ":" + defaultColor.a);
|
||||
EditorPrefs.SetString("Dreamteck.Splines.highlightColor", highlightColor.r + ":" + highlightColor.g + ":" + highlightColor.b + ":" + highlightColor.a);
|
||||
EditorPrefs.SetString("Dreamteck.Splines.outlineColor", outlineColor.r + ":" + outlineColor.g + ":" + outlineColor.b + ":" + outlineColor.a);
|
||||
EditorPrefs.SetString("Dreamteck.Splines.highlightContentColor", highlightContentColor.r + ":" + highlightContentColor.g + ":" + highlightContentColor.b + ":" + highlightContentColor.a);
|
||||
EditorPrefs.SetInt("Dreamteck.Splines.defaultComputerSpace", (int)defaultComputerSpace);
|
||||
EditorPrefs.SetInt("Dreamteck.Splines.defaultType", (int)defaultType);
|
||||
EditorPrefs.SetInt("Dreamteck.Splines.duplicationDirection", (int)duplicationDirection);
|
||||
EditorPrefs.SetFloat("Dreamteck.Splines.createPointSize", createPointSize);
|
||||
EditorPrefs.SetString("Dreamteck.Splines.createPointColor", createPointColor.r + ":" + createPointColor.g + ":" + createPointColor.b + ":" + createPointColor.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
12
Assets/Dreamteck/Splines/Core/SplinePrefs.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/SplinePrefs.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7baa0d1cc3b5f744aa07790170949167
|
||||
timeCreated: 1496225937
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
103
Assets/Dreamteck/Splines/Core/SplineSample.cs
Normal file
103
Assets/Dreamteck/Splines/Core/SplineSample.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using UnityEngine;
|
||||
using Dreamteck;
|
||||
|
||||
namespace Dreamteck.Splines{
|
||||
[System.Serializable]
|
||||
public struct SplineSample {
|
||||
public Vector3 position;
|
||||
public Vector3 up;
|
||||
public Vector3 forward;
|
||||
public Color color;
|
||||
public float size;
|
||||
public double percent;
|
||||
|
||||
public void FastCopy(ref SplineSample sample)
|
||||
{
|
||||
position = sample.position;
|
||||
up = sample.up;
|
||||
forward = sample.forward;
|
||||
color = sample.color;
|
||||
size = sample.size;
|
||||
percent = sample.percent;
|
||||
}
|
||||
|
||||
|
||||
public Quaternion rotation
|
||||
{
|
||||
get {
|
||||
if (up == forward)
|
||||
{
|
||||
if (up == Vector3.up) return Quaternion.LookRotation(Vector3.up, Vector3.back);
|
||||
else return Quaternion.LookRotation(forward, Vector3.up);
|
||||
}
|
||||
return Quaternion.LookRotation(forward, up); }
|
||||
}
|
||||
|
||||
public Vector3 right
|
||||
{
|
||||
get {
|
||||
if(up == forward)
|
||||
{
|
||||
if (up == Vector3.up) return Vector3.right;
|
||||
else return Vector3.Cross(Vector3.up, forward).normalized;
|
||||
}
|
||||
return Vector3.Cross(up, forward).normalized; }
|
||||
}
|
||||
|
||||
|
||||
public static SplineSample Lerp(ref SplineSample a, ref SplineSample b, float t)
|
||||
{
|
||||
SplineSample result = new SplineSample();
|
||||
Lerp(ref a, ref b, t, ref result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SplineSample Lerp(ref SplineSample a, ref SplineSample b, double t)
|
||||
{
|
||||
SplineSample result = new SplineSample();
|
||||
Lerp(ref a, ref b, t, ref result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void Lerp(ref SplineSample a, ref SplineSample b, double t, ref SplineSample target)
|
||||
{
|
||||
float ft = (float)t;
|
||||
DMath.LerpVector3NonAlloc(a.position, b.position, t, ref target.position);
|
||||
target.forward = Vector3.Slerp(a.forward, b.forward, ft);
|
||||
target.up = Vector3.Slerp(a.up, b.up, ft);
|
||||
target.color = Color.Lerp(a.color, b.color, ft);
|
||||
target.size = Mathf.Lerp(a.size, b.size, ft);
|
||||
target.percent = DMath.Lerp(a.percent, b.percent, t);
|
||||
}
|
||||
|
||||
public static void Lerp(ref SplineSample a, ref SplineSample b, float t, ref SplineSample target)
|
||||
{
|
||||
DMath.LerpVector3NonAlloc(a.position, b.position, t, ref target.position);
|
||||
target.forward = Vector3.Slerp(a.forward, b.forward, t);
|
||||
target.up = Vector3.Slerp(a.up, b.up, t);
|
||||
target.color = Color.Lerp(a.color, b.color, t);
|
||||
target.size = Mathf.Lerp(a.size, b.size, t);
|
||||
target.percent = DMath.Lerp(a.percent, b.percent, t);
|
||||
}
|
||||
|
||||
public void Lerp(ref SplineSample b, double t)
|
||||
{
|
||||
Lerp(ref this, ref b, t, ref this);
|
||||
}
|
||||
|
||||
public void Lerp(ref SplineSample b, float t)
|
||||
{
|
||||
Lerp(ref this, ref b, t, ref this);
|
||||
}
|
||||
|
||||
public SplineSample(Vector3 position, Vector3 up, Vector3 forward, Color color, float size, double percent)
|
||||
{
|
||||
this.position = position;
|
||||
this.up = up;
|
||||
this.forward = forward;
|
||||
this.color = color;
|
||||
this.size = size;
|
||||
this.percent = percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/SplineSample.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/SplineSample.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4dd959ac300a1db4da9527c1e10ac8e2
|
||||
timeCreated: 1434316665
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
208
Assets/Dreamteck/Splines/Core/SplineThreading.cs
Normal file
208
Assets/Dreamteck/Splines/Core/SplineThreading.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
#if !UNITY_WSA
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
public static class SplineThreading
|
||||
{
|
||||
public delegate void EmptyHandler();
|
||||
public static int threadCount
|
||||
{
|
||||
get {
|
||||
#if UNITY_WSA
|
||||
return 0;
|
||||
#else
|
||||
return threads.Length;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if !UNITY_WSA
|
||||
if(value > threads.Length)
|
||||
{
|
||||
while (threads.Length < value)
|
||||
{
|
||||
ThreadDef thread = new ThreadDef();
|
||||
#if UNITY_EDITOR
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
thread.Restart();
|
||||
}
|
||||
#else
|
||||
thread.Restart();
|
||||
#endif
|
||||
ArrayUtility.Add(ref threads, thread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if !UNITY_WSA
|
||||
internal class ThreadDef
|
||||
{
|
||||
internal class Worker
|
||||
{
|
||||
internal bool computing = false;
|
||||
internal Queue<EmptyHandler> instructions = new Queue<EmptyHandler>();
|
||||
}
|
||||
internal delegate void BoolHandler(bool flag);
|
||||
private ParameterizedThreadStart start = null;
|
||||
internal Thread thread = null;
|
||||
private Worker worker = new Worker();
|
||||
internal bool isAlive
|
||||
{
|
||||
get { return thread != null && thread.IsAlive; }
|
||||
}
|
||||
internal bool computing
|
||||
{
|
||||
get
|
||||
{
|
||||
return worker.computing;
|
||||
}
|
||||
}
|
||||
|
||||
internal ThreadDef()
|
||||
{
|
||||
start = new ParameterizedThreadStart(RunThread);
|
||||
}
|
||||
|
||||
internal void Queue(EmptyHandler handler)
|
||||
{
|
||||
worker.instructions.Enqueue(handler);
|
||||
}
|
||||
|
||||
internal void Interrupt()
|
||||
{
|
||||
thread.Interrupt();
|
||||
}
|
||||
|
||||
internal void Restart()
|
||||
{
|
||||
thread = new Thread(start);
|
||||
thread.Start(worker);
|
||||
}
|
||||
|
||||
internal void Abort()
|
||||
{
|
||||
if (isAlive)
|
||||
{
|
||||
thread.Abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
internal static ThreadDef[] threads = new ThreadDef[2];
|
||||
internal static readonly object locker = new object();
|
||||
static SplineThreading()
|
||||
{
|
||||
Application.quitting += Quitting;
|
||||
for (int i = 0; i < threads.Length; i++)
|
||||
{
|
||||
threads[i] = new ThreadDef();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
PrewarmThreads();
|
||||
UnityEditor.EditorApplication.playModeStateChanged += OnPlayStateChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static void OnPlayStateChanged(UnityEditor.PlayModeStateChange state)
|
||||
{
|
||||
if (state == UnityEditor.PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
Quitting();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Quitting()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
static void RunThread(object o)
|
||||
{
|
||||
ThreadDef.Worker work = (ThreadDef.Worker)o;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
work.computing = false;
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
work.computing = true;
|
||||
lock (locker)
|
||||
{
|
||||
while (work.instructions.Count > 0)
|
||||
{
|
||||
EmptyHandler h = work.instructions.Dequeue();
|
||||
if (h != null) h();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
if(ex.Message != "") Debug.LogError("THREAD EXCEPTION " + ex.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Debug.Log("Thread stopped");
|
||||
work.computing = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
public static void Run(EmptyHandler handler)
|
||||
{
|
||||
#if !UNITY_WSA
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
handler();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < threads.Length; i++)
|
||||
{
|
||||
if (!threads[i].isAlive) threads[i].Restart();
|
||||
if (!threads[i].computing || i == threads.Length - 1)
|
||||
{
|
||||
threads[i].Queue(handler);
|
||||
if(!threads[i].computing)threads[i].Interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void PrewarmThreads()
|
||||
{
|
||||
#if !UNITY_WSA
|
||||
for (int i = 0; i < threads.Length; i++)
|
||||
{
|
||||
if (!threads[i].isAlive)
|
||||
{
|
||||
threads[i].Restart();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
#if !UNITY_WSA
|
||||
for (int i = 0; i < threads.Length; i++)
|
||||
{
|
||||
threads[i].Abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Dreamteck/Splines/Core/SplineThreading.cs.meta
Normal file
11
Assets/Dreamteck/Splines/Core/SplineThreading.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3accda9a749b24c459cb983529284d01
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
173
Assets/Dreamteck/Splines/Core/SplineTrigger.cs
Normal file
173
Assets/Dreamteck/Splines/Core/SplineTrigger.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
[System.Serializable]
|
||||
public class TriggerGroup{
|
||||
#if UNITY_EDITOR
|
||||
public bool open = false;
|
||||
#endif
|
||||
|
||||
public bool enabled = true;
|
||||
public string name = "";
|
||||
public Color color = Color.white;
|
||||
public SplineTrigger[] triggers = new SplineTrigger[0];
|
||||
|
||||
public void Check(double start, double end, SplineUser user = null)
|
||||
{
|
||||
for (int i = 0; i < triggers.Length; i++)
|
||||
{
|
||||
if (triggers[i] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (triggers[i].Check(start, end))
|
||||
{
|
||||
triggers[i].Invoke(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
for (int i = 0; i < triggers.Length; i++) triggers[i].Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all triggers within the specified range
|
||||
/// </summary>
|
||||
public List<SplineTrigger> GetTriggers(double from, double to)
|
||||
{
|
||||
List<SplineTrigger> triggerList = new List<SplineTrigger>();
|
||||
for (int i = 0; i < triggers.Length; i++)
|
||||
{
|
||||
if (triggers[i] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(triggers[i].position >= from && triggers[i].position <= to)
|
||||
{
|
||||
triggerList.Add(triggers[i]);
|
||||
}
|
||||
}
|
||||
return triggerList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new trigger inside the group
|
||||
/// </summary>
|
||||
public SplineTrigger AddTrigger(double position, SplineTrigger.Type type)
|
||||
{
|
||||
return AddTrigger(position, type, "Trigger " + (triggers.Length + 1), Color.white);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new trigger inside the group
|
||||
/// </summary>
|
||||
public SplineTrigger AddTrigger(double position, SplineTrigger.Type type, string name, Color color)
|
||||
{
|
||||
SplineTrigger newTrigger = new SplineTrigger(type);
|
||||
newTrigger.position = position;
|
||||
newTrigger.color = color;
|
||||
newTrigger.name = name;
|
||||
ArrayUtility.Add(ref triggers, newTrigger);
|
||||
return newTrigger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the trigger at the given index from the group
|
||||
/// </summary>
|
||||
public void RemoveTrigger(int index)
|
||||
{
|
||||
ArrayUtility.RemoveAt(ref triggers, index);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class SplineTrigger
|
||||
{
|
||||
public string name = "Trigger";
|
||||
public enum Type { Double, Forward, Backward}
|
||||
[SerializeField]
|
||||
public Type type = Type.Double;
|
||||
public bool workOnce = false;
|
||||
private bool worked = false;
|
||||
[Range(0f, 1f)]
|
||||
public double position = 0.5;
|
||||
[SerializeField]
|
||||
public bool enabled = true;
|
||||
[SerializeField]
|
||||
public Color color = Color.white;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
public TriggerEvent onCross = new TriggerEvent();
|
||||
|
||||
public SplineTrigger(Type t)
|
||||
{
|
||||
type = t;
|
||||
enabled = true;
|
||||
onCross = new TriggerEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a new UnityAction to the trigger
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void AddListener(UnityAction<SplineUser> action)
|
||||
{
|
||||
onCross.AddListener(action);
|
||||
}
|
||||
|
||||
public void AddListener(UnityAction action)
|
||||
{
|
||||
UnityAction<SplineUser> addAction = new UnityAction<SplineUser>((user) => { action.Invoke(); });
|
||||
onCross.AddListener(addAction);
|
||||
}
|
||||
|
||||
public void RemoveListener(UnityAction<SplineUser> action)
|
||||
{
|
||||
onCross.RemoveListener(action);
|
||||
}
|
||||
|
||||
public void RemoveAllListeners()
|
||||
{
|
||||
onCross.RemoveAllListeners();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
worked = false;
|
||||
}
|
||||
|
||||
public bool Check(double previousPercent, double currentPercent)
|
||||
{
|
||||
if (!enabled) return false;
|
||||
if (workOnce && worked) return false;
|
||||
bool passed = false;
|
||||
switch (type)
|
||||
{
|
||||
case Type.Double: passed = (previousPercent <= position && currentPercent >= position) || (currentPercent <= position && previousPercent >= position); break;
|
||||
case Type.Forward: passed = previousPercent <= position && currentPercent >= position; break;
|
||||
case Type.Backward: passed = currentPercent <= position && previousPercent >= position; break;
|
||||
}
|
||||
if (passed) worked = true;
|
||||
return passed;
|
||||
}
|
||||
|
||||
public void Invoke(SplineUser user = null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) return;
|
||||
#endif
|
||||
onCross.Invoke(user);
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class TriggerEvent : UnityEvent<SplineUser>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/SplineTrigger.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/SplineTrigger.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 49af47eda040af44b8003635251e89cc
|
||||
timeCreated: 1457966388
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
118
Assets/Dreamteck/Splines/Core/SplineUtility.cs
Normal file
118
Assets/Dreamteck/Splines/Core/SplineUtility.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public static class SplineUtility
|
||||
{
|
||||
public enum MergeSide { Start, End }
|
||||
|
||||
/// <summary>
|
||||
/// Merges two spline objects into one. The result will be merged into <paramref name="baseSpline"/>
|
||||
/// </summary>
|
||||
/// <param name="baseSpline">The base spline object that</param>
|
||||
/// <param name="addedSpline">The object that will be merged into the base spline</param>
|
||||
/// <param name="side">Which side of the base spline to append to - beginning or end?</param>
|
||||
/// <param name="mergeEndpoints">Should the end points of the splines be merged or should they be bridged?</param>
|
||||
/// <param name="destroyAddedSpline">If true, the added spline's game object will be destroyed after merge</param>
|
||||
public static void Merge(SplineComputer baseSpline, SplineComputer addedSpline, MergeSide side, bool mergeEndpoints = false, bool destroyAddedSpline = false)
|
||||
{
|
||||
SplinePoint[] mergedPoints = addedSpline.GetPoints();
|
||||
SplinePoint[] basePoints = baseSpline.GetPoints();
|
||||
List<SplinePoint> pointsList = new List<SplinePoint>();
|
||||
SplinePoint[] points;
|
||||
if (!mergeEndpoints) points = new SplinePoint[mergedPoints.Length + basePoints.Length];
|
||||
else points = new SplinePoint[mergedPoints.Length + basePoints.Length - 1];
|
||||
|
||||
if (side == MergeSide.End)
|
||||
{
|
||||
if (side == MergeSide.Start)
|
||||
{
|
||||
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
|
||||
for (int i = mergeEndpoints ? 1 : 0; i < mergedPoints.Length; i++) pointsList.Add(mergedPoints[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
|
||||
for (int i = 0; i < mergedPoints.Length - (mergeEndpoints ? 1 : 0); i++) pointsList.Add(mergedPoints[(mergedPoints.Length - 1) - i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (side == MergeSide.Start)
|
||||
{
|
||||
for (int i = 0; i < mergedPoints.Length - (mergeEndpoints ? 1 : 0); i++) pointsList.Add(mergedPoints[(mergedPoints.Length - 1) - i]);
|
||||
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = mergeEndpoints ? 1 : 0; i < mergedPoints.Length; i++) pointsList.Add(mergedPoints[i]);
|
||||
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
|
||||
}
|
||||
}
|
||||
points = pointsList.ToArray();
|
||||
double mergedPercent = (double)(mergedPoints.Length - 1) / (points.Length - 1);
|
||||
double from = 0.0;
|
||||
double to = 1.0;
|
||||
if (side == MergeSide.End)
|
||||
{
|
||||
from = 1.0 - mergedPercent;
|
||||
to = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
from = 0.0;
|
||||
to = mergedPercent;
|
||||
}
|
||||
|
||||
|
||||
List<Node> mergedNodes = new List<Node>();
|
||||
List<int> mergedIndices = new List<int>();
|
||||
|
||||
for (int i = 0; i < addedSpline.pointCount; i++)
|
||||
{
|
||||
Node node = addedSpline.GetNode(i);
|
||||
if (node != null)
|
||||
{
|
||||
mergedNodes.Add(node);
|
||||
mergedIndices.Add(i);
|
||||
addedSpline.DisconnectNode(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
SplineUser[] subs = addedSpline.GetSubscribers();
|
||||
for (int i = 0; i < subs.Length; i++)
|
||||
{
|
||||
addedSpline.Unsubscribe(subs[i]);
|
||||
subs[i].spline = baseSpline;
|
||||
subs[i].clipFrom = DMath.Lerp(from, to, subs[i].clipFrom);
|
||||
subs[i].clipTo = DMath.Lerp(from, to, subs[i].clipTo);
|
||||
}
|
||||
baseSpline.SetPoints(points);
|
||||
|
||||
if (side == MergeSide.Start)
|
||||
{
|
||||
baseSpline.ShiftNodes(0, baseSpline.pointCount - 1, addedSpline.pointCount);
|
||||
for (int i = 0; i < mergedNodes.Count; i++)
|
||||
{
|
||||
baseSpline.ConnectNode(mergedNodes[i], mergedIndices[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < mergedNodes.Count; i++)
|
||||
{
|
||||
int connectIndex = mergedIndices[i] + basePoints.Length;
|
||||
if (mergeEndpoints) connectIndex--;
|
||||
baseSpline.ConnectNode(mergedNodes[i], connectIndex);
|
||||
}
|
||||
}
|
||||
if (destroyAddedSpline)
|
||||
{
|
||||
Object.Destroy(addedSpline.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Dreamteck/Splines/Core/SplineUtility.cs.meta
Normal file
11
Assets/Dreamteck/Splines/Core/SplineUtility.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74914877e76bc924eaa2e97157f84f95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
363
Assets/Dreamteck/Splines/Core/TransformModule.cs
Normal file
363
Assets/Dreamteck/Splines/Core/TransformModule.cs
Normal file
@@ -0,0 +1,363 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Dreamteck.Splines
|
||||
{
|
||||
|
||||
[System.Serializable]
|
||||
public class TransformModule : ISerializationCallbackReceiver
|
||||
{
|
||||
public Vector2 offset
|
||||
{
|
||||
get { return _offset; }
|
||||
set
|
||||
{
|
||||
if (value != _offset)
|
||||
{
|
||||
_offset = value;
|
||||
_hasOffset = _offset != Vector2.zero;
|
||||
if (targetUser != null)
|
||||
{
|
||||
targetUser.Rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public Vector3 rotationOffset
|
||||
{
|
||||
get { return _rotationOffset; }
|
||||
set
|
||||
{
|
||||
if (value != _rotationOffset)
|
||||
{
|
||||
_rotationOffset = value;
|
||||
_hasRotationOffset = _rotationOffset != Vector3.zero;
|
||||
if (targetUser != null)
|
||||
{
|
||||
targetUser.Rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool hasOffset
|
||||
{
|
||||
get { return _hasOffset; }
|
||||
}
|
||||
|
||||
public bool hasRotationOffset
|
||||
{
|
||||
get { return _hasRotationOffset; }
|
||||
}
|
||||
|
||||
public Vector3 baseScale
|
||||
{
|
||||
get { return _baseScale; }
|
||||
set
|
||||
{
|
||||
if (value != _baseScale)
|
||||
{
|
||||
_baseScale = value;
|
||||
if (targetUser != null)
|
||||
{
|
||||
targetUser.Rebuild();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool is2D
|
||||
{
|
||||
get { return _2dMode; }
|
||||
set
|
||||
{
|
||||
_2dMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool _hasOffset = false;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool _hasRotationOffset = false;
|
||||
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Vector2 _offset;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Vector3 _rotationOffset = Vector3.zero;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private Vector3 _baseScale = Vector3.one;
|
||||
[SerializeField]
|
||||
[HideInInspector]
|
||||
private bool _2dMode = false;
|
||||
public enum VelocityHandleMode { Zero, Preserve, Align, AlignRealistic }
|
||||
public VelocityHandleMode velocityHandleMode = VelocityHandleMode.Zero;
|
||||
public SplineSample splineResult
|
||||
{
|
||||
get
|
||||
{
|
||||
return _splineResult;
|
||||
}
|
||||
set
|
||||
{
|
||||
_splineResult = value;
|
||||
}
|
||||
}
|
||||
private SplineSample _splineResult;
|
||||
|
||||
public bool applyPositionX = true;
|
||||
public bool applyPositionY = true;
|
||||
public bool applyPositionZ = true;
|
||||
public bool applyPosition2D = true;
|
||||
public bool retainLocalPosition = false;
|
||||
|
||||
public Spline.Direction direction = Spline.Direction.Forward;
|
||||
public bool applyPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_2dMode)
|
||||
{
|
||||
return applyPosition2D;
|
||||
}
|
||||
return applyPositionX || applyPositionY || applyPositionZ;
|
||||
}
|
||||
set
|
||||
{
|
||||
applyPositionX = applyPositionY = applyPositionZ = applyPosition2D = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool applyRotationX = true;
|
||||
public bool applyRotationY = true;
|
||||
public bool applyRotationZ = true;
|
||||
public bool applyRotation2D = true;
|
||||
public bool retainLocalRotation = false;
|
||||
public bool applyRotation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_2dMode)
|
||||
{
|
||||
return applyRotation2D;
|
||||
}
|
||||
return applyRotationX || applyRotationY || applyRotationZ;
|
||||
}
|
||||
set
|
||||
{
|
||||
applyRotationX = applyRotationY = applyRotationZ = applyRotation2D = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool applyScaleX = false;
|
||||
public bool applyScaleY = false;
|
||||
public bool applyScaleZ = false;
|
||||
public bool applyScale
|
||||
{
|
||||
get
|
||||
{
|
||||
return applyScaleX || applyScaleY || applyScaleZ;
|
||||
}
|
||||
set
|
||||
{
|
||||
applyScaleX = applyScaleY = applyScaleZ = value;
|
||||
}
|
||||
}
|
||||
[HideInInspector]
|
||||
public SplineUser targetUser = null;
|
||||
|
||||
//These are used to save allocations
|
||||
private static Vector3 position = Vector3.zero;
|
||||
private static Quaternion rotation = Quaternion.identity;
|
||||
|
||||
public void ApplyTransform(Transform input)
|
||||
{
|
||||
input.position = GetPosition(input.position);
|
||||
input.rotation = GetRotation(input.rotation);
|
||||
input.localScale = GetScale(input.localScale);
|
||||
}
|
||||
|
||||
public void ApplyRigidbody(Rigidbody input)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
ApplyTransform(input.transform);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
input.transform.localScale = GetScale(input.transform.localScale);
|
||||
input.MovePosition(GetPosition(input.position));
|
||||
if (!input.isKinematic)
|
||||
{
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
input.linearVelocity = HandleVelocity(input.linearVelocity);
|
||||
#else
|
||||
input.velocity = HandleVelocity(input.velocity);
|
||||
#endif
|
||||
}
|
||||
input.MoveRotation(GetRotation(input.rotation));
|
||||
Vector3 angularVelocity = input.angularVelocity;
|
||||
if (applyRotationX)
|
||||
{
|
||||
angularVelocity.x = 0f;
|
||||
}
|
||||
if (applyRotationY)
|
||||
{
|
||||
angularVelocity.y = 0f;
|
||||
}
|
||||
if (applyRotationZ || applyRotation2D)
|
||||
{
|
||||
angularVelocity.z = 0f;
|
||||
}
|
||||
input.angularVelocity = angularVelocity;
|
||||
}
|
||||
|
||||
public void ApplyRigidbody2D(Rigidbody2D input)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
ApplyTransform(input.transform);
|
||||
input.transform.rotation = Quaternion.AngleAxis(GetRotation(Quaternion.Euler(0f, 0f, input.rotation)).eulerAngles.z, Vector3.forward);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
input.transform.localScale = GetScale(input.transform.localScale);
|
||||
input.position = GetPosition(input.position);
|
||||
if (!input.isKinematic)
|
||||
{
|
||||
#if UNITY_6000_OR_NEWER
|
||||
input.linearVelocity = HandleVelocity(input.linearVelocity);
|
||||
#else
|
||||
input.velocity = HandleVelocity(input.velocity);
|
||||
#endif
|
||||
}
|
||||
input.rotation = GetRotation(Quaternion.Euler(0f, 0f, input.rotation)).eulerAngles.z;
|
||||
if (applyRotationX)
|
||||
{
|
||||
input.angularVelocity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 HandleVelocity(Vector3 velocity)
|
||||
{
|
||||
Vector3 idealVelocity = Vector3.zero;
|
||||
Vector3 direction = Vector3.right;
|
||||
switch (velocityHandleMode)
|
||||
{
|
||||
case VelocityHandleMode.Preserve: idealVelocity = velocity; break;
|
||||
case VelocityHandleMode.Align:
|
||||
direction = _splineResult.forward;
|
||||
if (Vector3.Dot(velocity, direction) < 0f) direction *= -1f;
|
||||
idealVelocity = direction * velocity.magnitude; break;
|
||||
case VelocityHandleMode.AlignRealistic:
|
||||
direction = _splineResult.forward;
|
||||
if (Vector3.Dot(velocity, direction) < 0f) direction *= -1f;
|
||||
idealVelocity = direction * velocity.magnitude * Vector3.Dot(velocity.normalized, direction); break;
|
||||
}
|
||||
if (applyPositionX) velocity.x = idealVelocity.x;
|
||||
if (applyPositionY) velocity.y = idealVelocity.y;
|
||||
if (applyPositionZ) velocity.z = idealVelocity.z;
|
||||
return velocity;
|
||||
}
|
||||
|
||||
private Vector3 GetPosition(Vector3 inputPosition)
|
||||
{
|
||||
position = _splineResult.position;
|
||||
Vector2 finalOffset = _offset;
|
||||
if (finalOffset != Vector2.zero)
|
||||
{
|
||||
position += _splineResult.right * finalOffset.x * _splineResult.size + _splineResult.up * finalOffset.y * _splineResult.size;
|
||||
}
|
||||
if (retainLocalPosition)
|
||||
{
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(position, _splineResult.rotation, Vector3.one);
|
||||
Vector3 splineLocalPosition = matrix.inverse.MultiplyPoint3x4(targetUser.transform.position);
|
||||
splineLocalPosition.x = applyPositionX ? 0f : splineLocalPosition.x;
|
||||
splineLocalPosition.y = applyPositionY ? 0f : splineLocalPosition.y;
|
||||
splineLocalPosition.z = applyPositionZ ? 0f : splineLocalPosition.z;
|
||||
inputPosition = matrix.MultiplyPoint3x4(splineLocalPosition);
|
||||
} else
|
||||
{
|
||||
if (applyPositionX) inputPosition.x = position.x;
|
||||
if (applyPositionY) inputPosition.y = position.y;
|
||||
if (applyPositionZ) inputPosition.z = position.z;
|
||||
}
|
||||
return inputPosition;
|
||||
}
|
||||
|
||||
private Quaternion GetRotation(Quaternion inputRotation)
|
||||
{
|
||||
rotation = Quaternion.LookRotation(_splineResult.forward * (direction == Spline.Direction.Forward ? 1f : -1f), _splineResult.up);
|
||||
if (_2dMode)
|
||||
{
|
||||
if (applyRotation2D)
|
||||
{
|
||||
rotation *= Quaternion.Euler(90, -90, 0);
|
||||
inputRotation = Quaternion.AngleAxis(rotation.eulerAngles.z + _rotationOffset.z, Vector3.forward);
|
||||
}
|
||||
return inputRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_rotationOffset != Vector3.zero)
|
||||
{
|
||||
rotation = rotation * Quaternion.Euler(_rotationOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (retainLocalRotation)
|
||||
{
|
||||
Quaternion localRotation = Quaternion.Inverse(rotation) * inputRotation;
|
||||
Vector3 targetEuler = localRotation.eulerAngles;
|
||||
targetEuler.x = applyRotationX ? 0f : targetEuler.x;
|
||||
targetEuler.y = applyRotationY ? 0f : targetEuler.y;
|
||||
targetEuler.z = applyRotationZ ? 0f : targetEuler.z;
|
||||
inputRotation = rotation * Quaternion.Euler(targetEuler);
|
||||
} else
|
||||
{
|
||||
if (!applyRotationX || !applyRotationY || !applyRotationZ)
|
||||
{
|
||||
Vector3 targetEuler = rotation.eulerAngles;
|
||||
Vector3 sourceEuler = inputRotation.eulerAngles;
|
||||
if (!applyRotationX) targetEuler.x = sourceEuler.x;
|
||||
if (!applyRotationY) targetEuler.y = sourceEuler.y;
|
||||
if (!applyRotationZ) targetEuler.z = sourceEuler.z;
|
||||
inputRotation.eulerAngles = targetEuler;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputRotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
return inputRotation;
|
||||
}
|
||||
|
||||
private Vector3 GetScale(Vector3 inputScale)
|
||||
{
|
||||
if (applyScaleX) inputScale.x = _baseScale.x * _splineResult.size;
|
||||
if (applyScaleY) inputScale.y = _baseScale.y * _splineResult.size;
|
||||
if (applyScaleZ) inputScale.z = _baseScale.z * _splineResult.size;
|
||||
return inputScale;
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
_hasRotationOffset = _rotationOffset != Vector3.zero;
|
||||
_hasOffset = _offset != Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Assets/Dreamteck/Splines/Core/TransformModule.cs.meta
Normal file
12
Assets/Dreamteck/Splines/Core/TransformModule.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4d0258b139ab2142a6d1ba90331008a
|
||||
timeCreated: 1482584035
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user