This commit is contained in:
corn
2026-06-07 22:59:32 +08:00
commit a62ff7379b
7171 changed files with 10015624 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c26df68fb3fd33a488bf74c9d5ae1166
folderAsset: yes
timeCreated: 1495463786
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,224 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text.RegularExpressions;
namespace Dreamteck.Splines.IO
{
public class CSV : SplineParser
{
public enum ColumnType { Position, Tangent, Tangent2, Normal, Size, Color }
public List<ColumnType> columns = new List<ColumnType>();
public CSV(SplineComputer computer)
{
Spline spline = new Spline(computer.type, computer.sampleRate);
spline.points = computer.GetPoints();
if (spline.type != Spline.Type.Bezier && spline.type != Spline.Type.Linear) spline.HermiteToBezierTangents();
if (computer.isClosed) spline.Close();
buffer = new SplineDefinition(computer.name, spline);
fileName = computer.name;
columns.Add(ColumnType.Position);
columns.Add(ColumnType.Tangent);
columns.Add(ColumnType.Tangent2);
}
public CSV(string filePath, List<ColumnType> customColumns = null)
{
if (File.Exists(filePath))
{
string ext = Path.GetExtension(filePath).ToLower();
fileName = Path.GetFileNameWithoutExtension(filePath);
if (ext != ".csv")
{
Debug.LogError("CSV Parsing ERROR: Wrong format. Please use SVG or XML");
return;
}
string[] lines = File.ReadAllLines(filePath);
if (customColumns == null)
{
columns.Add(ColumnType.Position);
columns.Add(ColumnType.Tangent);
columns.Add(ColumnType.Tangent2);
columns.Add(ColumnType.Normal);
columns.Add(ColumnType.Size);
columns.Add(ColumnType.Color);
} else columns = new List<ColumnType>(customColumns);
buffer = new SplineDefinition(fileName, Spline.Type.Hermite);
Read(lines);
}
}
void Read(string[] lines)
{
int expectedElementCount = 0;
foreach (ColumnType col in columns)
{
switch (col)
{
case ColumnType.Position: expectedElementCount +=3; break;
case ColumnType.Tangent: expectedElementCount += 3; break;
case ColumnType.Tangent2: expectedElementCount += 3; break;
case ColumnType.Normal: expectedElementCount += 3; break;
case ColumnType.Size: expectedElementCount ++; break;
case ColumnType.Color: expectedElementCount += 4; break;
}
}
for (int i = 1; i < lines.Length; i++)
{
lines[i] = Regex.Replace(lines[i], @"\s+", "");
string[] elements = lines[i].Split(',');
if(elements.Length != expectedElementCount)
{
Debug.LogError("Unexpected element count on row " + i + ". Expected " + expectedElementCount + " found " + elements.Length + " Please make sure that all values exist and the column order is correct.");
continue;
}
float[] values = new float[elements.Length];
for (int j = 0; j < elements.Length; j++)
{
float.TryParse(elements[j], out values[j]);
}
int currentValue = 0;
foreach (ColumnType col in columns)
{
switch (col)
{
case ColumnType.Position: buffer.position = new Vector3(values[currentValue++], values[currentValue++], values[currentValue++]); break;
case ColumnType.Tangent: buffer.tangent = new Vector3(values[currentValue++], values[currentValue++], values[currentValue++]); break;
case ColumnType.Tangent2: buffer.tangent2 = new Vector3(values[currentValue++], values[currentValue++], values[currentValue++]); break;
case ColumnType.Normal: buffer.normal = new Vector3(values[currentValue++], values[currentValue++], values[currentValue++]); break;
case ColumnType.Size: buffer.size = values[currentValue++]; break;
case ColumnType.Color: buffer.color = new Color(values[currentValue++], values[currentValue++], values[currentValue++], values[currentValue++]); break;
}
}
buffer.CreateSmooth();
}
}
public SplineComputer CreateSplineComputer(Vector3 position, Quaternion rotation)
{
return buffer.CreateSplineComputer(position, rotation);
}
public Spline CreateSpline()
{
return buffer.CreateSpline();
}
public void FlatX()
{
for (int i = 0; i < buffer.pointCount; i++)
{
SplinePoint p = buffer.points[i];
p.position.x = 0f;
p.tangent.x = 0f;
p.tangent2.x = 0f;
p.normal = Vector3.right;
buffer.points[i] = p;
}
}
public void FlatY()
{
for (int i = 0; i < buffer.pointCount; i++)
{
SplinePoint p = buffer.points[i];
p.position.y = 0f;
p.tangent.y = 0f;
p.tangent2.y = 0f;
p.normal = Vector3.up;
buffer.points[i] = p;
}
}
public void FlatZ()
{
for (int i = 0; i < buffer.pointCount; i++)
{
SplinePoint p = buffer.points[i];
p.position.z = 0f;
p.tangent.z = 0f;
p.tangent2.z = 0f;
p.normal = Vector3.back;
buffer.points[i] = p;
}
}
void AddTitle(ref string[] content, string title)
{
if (!string.IsNullOrEmpty(content[0])) content[0] += ",";
content[0] += title;
}
void AddVector3Title(ref string[] content, string prefix)
{
AddTitle(ref content, prefix + "X," + prefix + "Y," + prefix + "Z");
}
void AddColorTitle(ref string[] content, string prefix)
{
AddTitle(ref content, prefix + "R," + prefix + "G," + prefix + "B" + prefix + "A");
}
void AddVector3(ref string[] content, int index, Vector3 vector)
{
AddFloat(ref content, index, vector.x);
AddFloat(ref content, index, vector.y);
AddFloat(ref content, index, vector.z);
}
void AddColor(ref string[] content, int index, Color color)
{
AddFloat(ref content, index, color.r);
AddFloat(ref content, index, color.g);
AddFloat(ref content, index, color.b);
AddFloat(ref content, index, color.a);
}
void AddFloat(ref string[] content, int index, float value)
{
if (!string.IsNullOrEmpty(content[index])) content[index] += ",";
content[index] += value.ToString();
}
public void Write(string filePath)
{
if (!Directory.Exists(Path.GetDirectoryName(filePath))) throw new DirectoryNotFoundException("The file is being saved to a non-existing directory.");
List<SplinePoint> csvPoints = buffer.points;
string[] content = new string[csvPoints.Count+1];
//Add the column titles
foreach(ColumnType col in columns)
{
switch (col)
{
case ColumnType.Position: AddVector3Title(ref content, "Position"); break;
case ColumnType.Tangent: AddVector3Title(ref content, "Tangent"); break;
case ColumnType.Tangent2: AddVector3Title(ref content, "Tangent2"); break;
case ColumnType.Normal: AddVector3Title(ref content, "Normal"); break;
case ColumnType.Size: AddTitle(ref content, "Size"); break;
case ColumnType.Color: AddColorTitle(ref content, "Color"); break;
}
}
//Add the content for each column
foreach (ColumnType col in columns)
{
for (int i = 1; i <= csvPoints.Count; i++)
{
int index = i - 1;
switch (col)
{
case ColumnType.Position: AddVector3(ref content, i, csvPoints[index].position); break;
case ColumnType.Tangent: AddVector3(ref content, i, csvPoints[index].tangent); break;
case ColumnType.Tangent2: AddVector3(ref content, i, csvPoints[index].tangent2); break;
case ColumnType.Normal: AddVector3(ref content, i, csvPoints[index].normal); break;
case ColumnType.Size: AddFloat(ref content, i, csvPoints[index].size); break;
case ColumnType.Color: AddColor(ref content, i, csvPoints[index].color); break;
}
}
}
File.WriteAllLines(filePath, content);
}
}
}

View 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:

View File

@@ -0,0 +1,659 @@
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;
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 }
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.HermiteToBezierTangents();
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;
}
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;
}
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, out x);
attribute = GetAttributeContent(rectNode, "y");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, out y);
attribute = GetAttributeContent(rectNode, "width");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, out w);
attribute = GetAttributeContent(rectNode, "height");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, out h);
attribute = GetAttributeContent(rectNode, "rx");
if (attribute != "ERROR") float.TryParse(attribute, out rx);
attribute = GetAttributeContent(rectNode, "ry");
if (attribute != "ERROR") float.TryParse(attribute, 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;
}
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, out startX);
attribute = GetAttributeContent(lineNode, "y1");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, out startY);
attribute = GetAttributeContent(lineNode, "x2");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, out endX);
attribute = GetAttributeContent(lineNode, "y2");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, 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;
}
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, out x);
attribute = GetAttributeContent(ellipseNode, "cy");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, 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, out rx);
attribute = GetAttributeContent(ellipseNode, "ry");
if (attribute == "ERROR") return 0;
} else //Nope, it's a circle
{
float.TryParse(attribute, out rx);
ry = rx;
}
float.TryParse(attribute, 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;
}
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;
}
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;
}
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;
}
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));
foreach (string token in tokens)
{
char cmd = token.Substring(0, 1).Single();
switch (cmd)
{
case 'M':
PathStart(elementName, token, false);
break;
case 'm':
PathStart(elementName, token, true);
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;
}
}
int addedTransforms = ParseTransformation(pathNode);
if (buffer != null) WriteBufferTo(paths);
return addedTransforms;
}
void PathStart(string name, string coords, bool relative)
{
if (buffer != null) WriteBufferTo(paths);
buffer = new SplineDefinition(name, Spline.Type.Bezier);
Vector2[] vectors = ParseVector2(coords);
foreach (Vector3 vector in vectors)
{
if (relative) buffer.position += vector;
else buffer.position = vector;
buffer.CreateLinear();
}
}
void PathClose()
{
buffer.closed = true;
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
void WriteBufferTo(List<SplineDefinition> list)
{
buffer.Transform(transformBuffer);
list.Add(buffer);
buffer = null;
}
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;
}
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;
}
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;
}
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";
}
}
}

View 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:

View File

@@ -0,0 +1,332 @@
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; }
}
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))
{
if (!IsWHiteSpace(accumulated))
{
float parsed = 0f;
float.TryParse(accumulated, out parsed);
list.Add(parsed);
accumulated = "";
if (c == '-') accumulated = "-";
continue;
}
}
if (!char.IsWhiteSpace(c)) accumulated += c;
}
if (!IsWHiteSpace(accumulated))
{
float p = 0f;
float.TryParse(accumulated, 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;
}
}
}

View 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:

View 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];
}
}
}
}

View 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:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 893f3cde2d97b93489384ee7ba89475b
folderAsset: yes
timeCreated: 1495473179
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,37 @@
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(7, SplinePoint.Type.SmoothMirrored);
points[0].position = Vector3.right / 2f * radius + Vector3.forward * height * 0.5f;
points[0].SetTangentPosition(points[0].position + Vector3.back * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[1].position = Vector3.forward / 2f * radius + Vector3.forward * 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.forward * height * 0.5f;
points[2].SetTangentPosition(points[2].position + Vector3.forward * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[3].position = Vector3.left / 2f * radius + Vector3.back * height * 0.5f;
points[3].SetTangentPosition(points[3].position + Vector3.forward * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[4].position = Vector3.back / 2f * radius + Vector3.back * 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.back * height * 0.5f;
points[5].SetTangentPosition(points[5].position + Vector3.back * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[6] = points[0];
}
}
}

View 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:

View File

@@ -0,0 +1,33 @@
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(5, SplinePoint.Type.SmoothMirrored);
points[0].position = Vector3.forward * 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.forward * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
points[2].position = Vector3.back * 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.back * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
points[4] = points[0];
}
}
}

View 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:

View 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.forward * length * 0.5f;
for (int i = 0; i < points.Length; i++)
{
points[i].position = origin + Vector3.forward * length * ((float)i / (points.Length - 1));
}
}
}
}

View 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:

View File

@@ -0,0 +1,31 @@
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 + 1, SplinePoint.Type.SmoothMirrored);
for (int i = 0; i < sides; i++)
{
float percent = (float)i / sides;
Vector3 pos = Quaternion.AngleAxis(360f * percent, Vector3.up) * Vector3.forward * radius;
points[i].SetPosition(pos);
}
points[points.Length - 1] = points[0];
}
}
}

View 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:

View File

@@ -0,0 +1,28 @@
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(5, SplinePoint.Type.SmoothMirrored);
points[0].position = points[0].tangent = Vector3.forward / 2f * size.y + Vector3.left / 2f * size.x;
points[1].position = points[1].tangent = Vector3.forward / 2f * size.y + Vector3.right / 2f * size.x;
points[2].position = points[2].tangent = Vector3.back / 2f * size.y + Vector3.right / 2f * size.x;
points[3].position = points[3].tangent = Vector3.back / 2f * size.y + Vector3.left / 2f * size.x;
points[4] = points[0];
}
}
}

View 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:

View File

@@ -0,0 +1,49 @@
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(9, SplinePoint.Type.Broken);
Vector2 edgeSize = size - new Vector2(xRadius, yRadius) * 2f;
points[0].SetPosition(Vector3.forward / 2f * edgeSize.y + Vector3.left / 2f * size.x);
points[1].SetPosition(Vector3.forward / 2f * size.y + Vector3.left / 2f * edgeSize.x);
points[2].SetPosition(Vector3.forward / 2f * size.y + Vector3.right / 2f * edgeSize.x);
points[3].SetPosition(Vector3.forward / 2f * edgeSize.y + Vector3.right / 2f * size.x);
points[4].SetPosition(Vector3.back / 2f * edgeSize.y + Vector3.right / 2f * size.x);
points[5].SetPosition(Vector3.back / 2f * size.y + Vector3.right / 2f * edgeSize.x);
points[6].SetPosition(Vector3.back / 2f * size.y + Vector3.left / 2f * edgeSize.x);
points[7].SetPosition(Vector3.back / 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.forward * 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.forward * yRad);
points[4].SetTangent2Position(points[4].position + Vector3.back * 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.back * yRad);
points[8] = points[0];
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a6ad8207cf520e94aa15dea4fae0027d
timeCreated: 1495474369
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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;
}
}
}
}

View 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:

View 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.Hermite;
}
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;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: acb225539fdaf0d479f2ca1a7694afcc
timeCreated: 1495473185
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
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 + 1, 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.up) * Vector3.forward * ((float)i % 2f == 0 ? radius : innerRadius);
points[i].SetPosition(pos);
}
points[points.Length - 1] = points[0];
}
}
}

View 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:

View File

@@ -0,0 +1,517 @@
namespace Dreamteck.Splines
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SampleCollection
{
[HideInInspector]
public SplineSample[] samples = new SplineSample[0];
public int Count
{
get { return samples.Length; }
}
public int[] optimizedIndices = new int[0];
bool hasSamples
{
get { return Count > 0; }
}
public SplineComputer.SampleMode sampleMode = SplineComputer.SampleMode.Default;
public double clipFrom = 0.0, clipTo = 1.0;
public bool loopSamples = false;
public bool samplesAreLooped
{
get
{
return loopSamples && clipFrom >= clipTo;
}
}
public double span
{
get
{
if (samplesAreLooped) return (1.0 - clipFrom) + clipTo;
return clipTo - clipFrom;
}
}
public SampleCollection()
{
}
public SampleCollection(SampleCollection input)
{
samples = input.samples;
optimizedIndices = input.optimizedIndices;
sampleMode = input.sampleMode;
clipFrom = input.clipFrom;
clipTo = input.clipTo;
}
public int GetClippedSampleCount(out int startIndex, out int endIndex)
{
startIndex = endIndex = 0;
if (sampleMode == SplineComputer.SampleMode.Default)
{
startIndex = DMath.FloorInt((Count - 1) * clipFrom);
endIndex = DMath.CeilInt((Count - 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 < Count - 1) endIndex++;
}
if (samplesAreLooped) //Handle looping segments
{
int toSamples = endIndex + 1;
int fromSamples = Count - startIndex;
return toSamples + fromSamples;
}
return endIndex - startIndex + 1;
}
/// <summary>
/// Takes a regular 0-1 percent mapped to the start and end of the spline and maps it to the clipFrom and clipTo valies. Useful for working with clipped samples
/// </summary>
/// <param name="percent"></param>
/// <returns></returns>
public double ClipPercent(double percent)
{
ClipPercent(ref percent);
return percent;
}
/// <summary>
/// Takes a regular 0-1 percent mapped to the start and end of the spline and maps it to the clipFrom and clipTo valies. Useful for working with clipped samples
/// </summary>
/// <param name="percent"></param>
/// <returns></returns>
public void ClipPercent(ref double percent)
{
if (Count == 0)
{
percent = 0.0;
return;
}
if (samplesAreLooped)
{
if (percent >= clipFrom && percent <= 1.0) { percent = DMath.InverseLerp(clipFrom, clipFrom + span, percent); }//If in the range clipFrom - 1.0
else if (percent <= clipTo) { percent = DMath.InverseLerp(clipTo - span, clipTo, percent); } //if in the range 0.0 - clipTo
else
{
//Find the nearest clip start
if (DMath.InverseLerp(clipTo, clipFrom, percent) < 0.5) percent = 1.0;
else percent = 0.0;
}
}
else percent = DMath.InverseLerp(clipFrom, clipTo, percent);
}
public double UnclipPercent(double percent)
{
UnclipPercent(ref percent);
return percent;
}
public void UnclipPercent(ref double percent)
{
if(percent == 0.0)
{
percent = clipFrom;
return;
} else if(percent == 1.0)
{
percent = clipTo;
return;
}
if (samplesAreLooped)
{
double fromLength = (1.0 - clipFrom) / span;
if (fromLength == 0.0)
{
percent = 0.0;
return;
}
if (percent < fromLength) percent = DMath.Lerp(clipFrom, 1.0, percent / fromLength);
else if (clipTo == 0.0)
{
percent = 0.0;
return;
}
else percent = DMath.Lerp(0.0, clipTo, (percent - fromLength) / (clipTo / span));
}
else percent = DMath.Lerp(clipFrom, clipTo, percent);
percent = DMath.Clamp01(percent);
}
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);
}
//Debug.Log(percent + " sample index " + index + " -> " + sampleIndex);
if (sampleIndex < Count - 1) lerp = DMath.InverseLerp(samples[sampleIndex].percent, samples[sampleIndex + 1].percent, lerpPercent);
return;
}
sampleIndex = DMath.FloorInt(percent * (Count - 1));
lerp = (Count - 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;
UnclipPercent(ref percent);
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);
else 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, result);
return result;
}
/// <summary>
/// Same as Spline.Evaluate but the result is transformed by the computer's transform
/// </summary>
/// <param name="result"></param>
/// <param name="percent"></param>
public void Evaluate(double percent, SplineSample result)
{
if (!hasSamples)
{
result = new SplineSample();
return;
}
UnclipPercent(ref percent);
int index;
double lerp;
GetSamplingValues(percent, out index, out lerp);
if (lerp > 0.0) SplineSample.Lerp(samples[index], samples[index + 1], lerp, result);
else result.CopyFrom(samples[index]);
}
/// <summary>
/// Same as Spline.Evaluate 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 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 < Count - 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] = 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 < Count - 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)
{
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 = UnclipPercent(DMath.Clamp01(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 = Count - 1;
if (samplesAreLooped)
{
GetSamplingValues(clipFrom, out minIndex, out lerp);
GetSamplingValues(clipTo, out maxIndex, out lerp);
if (lerp > 0.0) maxIndex++;
}
while (moved < distance)
{
lastDistance = Vector3.Distance(samples[sampleIndex].position, lastPos);
moved += lastDistance;
if (moved >= distance) break;
lastPos = samples[sampleIndex].position;
lastPercent = samples[sampleIndex].percent;
if (direction == Spline.Direction.Forward)
{
if (sampleIndex == Count - 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[Count-1].position;
lastPercent = samples[Count - 1].percent;
sampleIndex = Count - 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);
}
/// <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, SplineSample result, double from = 0.0, double to = 1.0)
{
if (!hasSamples) return;
if (Count == 1)
{
if (result == null) result = new SplineSample(samples[0]);
else result.CopyFrom(samples[0]);
return;
}
Spline.FormatFromTo(ref from, ref to);
//First make a very rough sample of the from-to region
int steps = (controlPointCount - 1) * 6; //Sampling six points per segment is enough to find the closest point range
int step = Count / steps;
if (step < 1) step = 1;
float minDist = (position - samples[0].position).sqrMagnitude;
int fromIndex = 0;
int toIndex = Count - 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 < Count - 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;
float dist = (position - samples[i].position).sqrMagnitude;
if (dist < minDist)
{
minDist = dist;
checkFrom = Mathf.Max(i - step, 0);
checkTo = Mathf.Min(i + step, Count - 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 - samples[i].position).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 > Count - 1) frontIndex = Count - 1;
Vector3 back = LinearAlgebraUtility.ProjectOnLine(samples[backIndex].position, samples[index].position, position);
Vector3 front = LinearAlgebraUtility.ProjectOnLine(samples[index].position, samples[frontIndex].position, position);
float backLength = (samples[index].position - samples[backIndex].position).magnitude;
float frontLength = (samples[index].position - samples[frontIndex].position).magnitude;
float backProjectDist = (back - samples[backIndex].position).magnitude;
float frontProjectDist = (front - samples[frontIndex].position).magnitude;
if (backIndex < index && index < frontIndex)
{
if ((position - back).sqrMagnitude < (position - front).sqrMagnitude)
{
SplineSample.Lerp(samples[backIndex], samples[index], backProjectDist / backLength, result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
}
else
{
SplineSample.Lerp(samples[frontIndex], samples[index], frontProjectDist / frontLength, result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
}
}
else if (backIndex < index)
{
SplineSample.Lerp(samples[backIndex], samples[index], backProjectDist / backLength, result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
}
else
{
SplineSample.Lerp(samples[frontIndex], samples[index], frontProjectDist / frontLength, result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
}
if (Count > 1 && from == 0.0 && to == 1.0 && result.percent < samples[1].percent) //Handle looped splines
{
Vector3 projected = LinearAlgebraUtility.ProjectOnLine(samples[Count - 1].position, samples[Count - 2].position, position);
if ((position - projected).sqrMagnitude < (position - result.position).sqrMagnitude)
{
double l = LinearAlgebraUtility.InverseLerp(samples[Count - 1].position, samples[Count - 2].position, projected);
SplineSample.Lerp(samples[Count - 1], samples[Count - 2], l, result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(Count - 1), GetSamplePercent(Count - 2), l);
}
}
}
double GetSamplePercent(int sampleIndex)
{
if (sampleMode == SplineComputer.SampleMode.Optimized)
{
return samples[optimizedIndices[sampleIndex]].percent;
}
return (double)sampleIndex / (Count - 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)
{
if (!hasSamples) return 0f;
Spline.FormatFromTo(ref from, ref to);
float length = 0f;
Vector3 pos = EvaluatePosition(from);
int fromIndex, toIndex;
double lerp;
GetSamplingValues(from, out fromIndex, out lerp);
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < Count - 1) toIndex++;
for (int i = fromIndex+1; i < toIndex; i++)
{
length += Vector3.Distance(samples[i].position, pos);
pos = samples[i].position;
}
length += Vector3.Distance(EvaluatePosition(to), pos);
return length;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2ab72bc0d28ecbe4c8a42a5e1c8e2da5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,824 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Dreamteck;
namespace Dreamteck.Splines {
//The Spline class defines a spline with world coordinates. It comes with various sampling methods
[System.Serializable]
public class Spline {
public enum Direction { Forward = 1, Backward = -1 }
public enum Type { Hermite, BSpline, Bezier, Linear };
public SplinePoint[] points = new SplinePoint[0];
[SerializeField]
private bool closed = false;
public Type type = Type.Bezier;
public bool linearAverageDirection = true;
public AnimationCurve customValueInterpolation = null;
public AnimationCurve customNormalInterpolation = null;
public int sampleRate = 10;
private static Vector3[] hermitePoints = new Vector3[4];
/// <summary>
/// Returns true if the spline is closed
/// </summary>
public bool isClosed
{
get
{
return closed && points.Length >= 4;
}
set { }
}
/// <summary>
/// The step size of the percent incrementation when evaluating a spline (based on percision)
/// </summary>
public double moveStep
{
get {
if (type == Type.Linear) return 1f / (points.Length-1);
return 1f / (iterations-1);
}
set { }
}
/// <summary>
/// The total count of samples for the spline (based on the sample rate)
/// </summary>
public int iterations
{
get {
if (type == Type.Linear) return points.Length;
return sampleRate * (points.Length - 1) - (points.Length-1) + 1;
}
}
public Spline(Type type){
this.type = type;
points = new SplinePoint[0];
}
public Spline(Type type, int sampleRate)
{
this.type = type;
this.sampleRate = sampleRate;
points = new SplinePoint[0];
}
/// <summary>
/// Calculate the length of the spline
/// </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 multiplier for precision [0-1] default: 1f</param>
/// <returns></returns>
public float CalculateLength(double from = 0.0, double to = 1.0, double resolution = 1.0)
{
if (points.Length == 0) return 0f;
resolution = DMath.Clamp01(resolution);
if (resolution == 0.0) return 0f;
from = DMath.Clamp01(from);
to = DMath.Clamp01(to);
if (to < from) to = from;
double percent = from;
Vector3 lastPos = EvaluatePosition(percent);
float sum = 0f;
while (true)
{
percent = DMath.Move(percent, to, moveStep / resolution);
Vector3 pos = EvaluatePosition(percent);
sum += (pos - lastPos).magnitude;
lastPos = pos;
if (percent == to) break;
}
return sum;
}
/// <summary>
/// Project point on the spline. Returns evaluation percent.
/// </summary>
/// <param name="position">3D Point</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>
/// <returns></returns>
public double Project(Vector3 position, int subdivide = 4, double from = 0.0, double to = 1.0)
{
if (points.Length == 0) return 0.0;
if (closed && from == 0.0 && to == 1.0) //Handle looped splines
{
double closest = GetClosestPoint(subdivide, position, from, to, Mathf.RoundToInt(Mathf.Max(iterations / points.Length, 10)) * 5);
if (closest < moveStep)
{
double nextClosest = GetClosestPoint(subdivide, position, 0.5, to, Mathf.RoundToInt(Mathf.Max(iterations / points.Length, 10)) * 5);
if (Vector3.Distance(position, EvaluatePosition(nextClosest)) < Vector3.Distance(position, EvaluatePosition(closest))) return nextClosest;
}
return closest;
}
return GetClosestPoint(subdivide, position, from, to, Mathf.RoundToInt(Mathf.Max(iterations / points.Length, 10)) * 5);
}
/// <summary>
/// Casts rays along the spline against all colliders in the scene
/// </summary>
/// <param name="hit">Hit information</param>
/// <param name="hitPercent">The percent of evaluation where the hit occured</param>
/// <param name="layerMask">Layer mask for the raycast</param>
/// <param name="resolution">Resolution multiplier for precision [0-1] default: 1f</param>
/// <param name="from">Raycast from [0-1] default: 0f</param>
/// <param name="to">Raycast to [0-1] default: 1f</param>
/// <param name="hitTriggers">Should hit triggers? (not supported in 5.1)</param>
/// <returns></returns>
public bool Raycast(out RaycastHit hit, out double hitPercent, LayerMask layerMask, double resolution = 1.0, double from = 0.0, double to = 1.0, QueryTriggerInteraction hitTriggers = QueryTriggerInteraction.UseGlobal
)
{
resolution = DMath.Clamp01(resolution);
from = DMath.Clamp01(from);
to = DMath.Clamp01(to);
double percent = from;
Vector3 fromPos = EvaluatePosition(percent);
hitPercent = 0f;
if (resolution == 0f)
{
hit = new RaycastHit();
hitPercent = 0f;
return false;
}
while (true)
{
double prevPercent = percent;
percent = DMath.Move(percent, to, moveStep / resolution);
Vector3 toPos = EvaluatePosition(percent);
if (Physics.Linecast(fromPos, toPos, out hit, layerMask, hitTriggers))
{
double segmentPercent = (hit.point - fromPos).sqrMagnitude / (toPos - fromPos).sqrMagnitude;
hitPercent = DMath.Lerp(prevPercent, percent, segmentPercent);
return true;
}
fromPos = toPos;
if (percent == to) break;
}
return false;
}
/// <summary>
/// Casts rays along the spline against all colliders in the scene and returns all hits. Order is not guaranteed.
/// </summary>
/// <param name="hits">Hit information</param>
/// <param name="hitPercents">The percents of evaluation where each hit occured</param>
/// <param name="layerMask">Layer mask for the raycast</param>
/// <param name="resolution">Resolution multiplier for precision [0-1] default: 1f</param>
/// <param name="from">Raycast from [0-1] default: 0f</param>
/// <param name="to">Raycast to [0-1] default: 1f</param>
/// <param name="hitTriggers">Should hit triggers? (not supported in 5.1)</param>
/// <returns></returns>
public bool RaycastAll(out RaycastHit[] hits, out double[] hitPercents, LayerMask layerMask, double resolution = 1.0, double from = 0.0, double to = 1.0, QueryTriggerInteraction hitTriggers = QueryTriggerInteraction.UseGlobal
)
{
resolution = DMath.Clamp01(resolution);
from = DMath.Clamp01(from);
to = DMath.Clamp01(to);
double percent = from;
Vector3 fromPos = EvaluatePosition(percent);
List<RaycastHit> hitList = new List<RaycastHit>();
List<double> percentList = new List<double>();
if (resolution == 0f)
{
hits = new RaycastHit[0];
hitPercents = new double[0];
return false;
}
bool hasHit = false;
while (true)
{
double prevPercent = percent;
percent = DMath.Move(percent, to, moveStep / resolution);
Vector3 toPos = EvaluatePosition(percent);
RaycastHit[] h = Physics.RaycastAll(fromPos, toPos - fromPos, Vector3.Distance(fromPos, toPos), layerMask, hitTriggers);
for (int i = 0; i < h.Length; i++)
{
hasHit = true;
double segmentPercent = (h[i].point - fromPos).sqrMagnitude / (toPos - fromPos).sqrMagnitude;
percentList.Add(DMath.Lerp(prevPercent, percent, segmentPercent));
hitList.Add(h[i]);
}
fromPos = toPos;
if (percent == to) break;
}
hits = hitList.ToArray();
hitPercents = percentList.ToArray();
return hasHit;
}
/// <summary>
/// Converts a point index to spline percent
/// </summary>
/// <param name="pointIndex">The point index</param>
/// <returns></returns>
public double GetPointPercent(int pointIndex)
{
return DMath.Clamp01((double)pointIndex / (points.Length - 1));
}
/// <summary>
/// Evaluate the spline and return position. This is simpler and faster than Evaluate.
/// </summary>
/// <param name="percent">Percent of evaluation [0-1]</param>
public Vector3 EvaluatePosition(double percent)
{
if (points.Length == 0) return Vector3.zero;
Vector3 point = new Vector3();
EvaluatePosition(ref point, percent);
return point;
}
/// <summary>
/// Evaluate the spline at the given time and return a SplineResult
/// </summary>
/// <param name="percent">Percent of evaluation [0-1]</param>
public SplineSample Evaluate(double percent)
{
SplineSample result = new SplineSample();
Evaluate(result, percent);
return result;
}
/// <summary>
/// Evaluate the spline at the position of a given point and return a SplineResult
/// </summary>
/// <param name="pointIndex">Point index</param>
public SplineSample Evaluate(int pointIndex)
{
SplineSample result = new SplineSample();
Evaluate(result, GetPointPercent(pointIndex));
return result;
}
/// <summary>
/// Evaluate the splien at the given point and write the result to the "result" object
/// </summary>
/// <param name="result">The result output</param>
/// <param name="pointIndex">Point index</param>
public void Evaluate(SplineSample result, int pointIndex)
{
Evaluate(result, GetPointPercent(pointIndex));
}
/// <summary>
/// Evaluate the splien at the given time and write the result to the "result" object
/// </summary>
/// <param name="result">The result output</param>
/// <param name="percent">Percent of evaluation [0-1]</param>
public void Evaluate(SplineSample result, double percent)
{
if (points.Length == 0)
{
result = new SplineSample();
return;
}
percent = DMath.Clamp01(percent);
if (closed && points.Length <= 2) closed = false;
if (points.Length == 1)
{
result.position = points[0].position;
result.up = points[0].normal;
result.forward = Vector3.forward;
result.size = points[0].size;
result.color = points[0].color;
result.percent = percent;
return;
}
double doubleIndex = (points.Length - 1) * percent;
int pointIndex = Mathf.Clamp(DMath.FloorInt(doubleIndex), 0, points.Length - 2);
double getPercent = doubleIndex - pointIndex;
Vector3 point = EvaluatePosition(percent);
result.position = point;
result.percent = percent;
if (pointIndex <= points.Length - 2)
{
SplinePoint nextPoint = points[pointIndex + 1];
if (pointIndex == points.Length - 2 && closed) nextPoint = points[0];
float valueInterpolation = (float)getPercent;
if (customValueInterpolation != null)
{
if (customValueInterpolation.length > 0) valueInterpolation = customValueInterpolation.Evaluate(valueInterpolation);
}
float normalInterpolation = (float)getPercent;
if (customNormalInterpolation != null)
{
if (customNormalInterpolation.length > 0) normalInterpolation = customNormalInterpolation.Evaluate(normalInterpolation);
}
result.size = Mathf.Lerp(points[pointIndex].size, nextPoint.size, valueInterpolation);
result.color = Color.Lerp(points[pointIndex].color, nextPoint.color, valueInterpolation);
result.up = Vector3.Slerp(points[pointIndex].normal, nextPoint.normal, normalInterpolation);
}
else
{
if (closed)
{
result.size = points[0].size;
result.color = points[0].color;
result.up = points[0].normal;
}
else
{
result.size = points[pointIndex].size;
result.color = points[pointIndex].color;
result.up = points[pointIndex].normal;
}
}
if (type == Type.BSpline)
{
double step = 1.0 / (iterations-1);
if (percent <= 1.0 - step && percent >= step) result.forward = EvaluatePosition(percent + step) - EvaluatePosition(percent - step);
else
{
Vector3 back = Vector3.zero, front = Vector3.zero;
if (closed)
{
if (percent < step) back = EvaluatePosition(1.0 - (step - percent));
else back = EvaluatePosition(percent - step);
if (percent > 1.0 - step) front = EvaluatePosition(step - (1.0 - percent));
else front = EvaluatePosition(percent + step);
result.forward = front - back;
}
else
{
back = result.position - EvaluatePosition(percent - step);
front = EvaluatePosition(percent + step) - result.position;
result.forward = Vector3.Slerp(front, back, back.magnitude / front.magnitude);
}
}
} else EvaluateTangent(ref result.forward, percent);
result.forward.Normalize();
}
/// <summary>
/// Evaluates the spline segment and writes the results to the array
/// </summary>
/// <param name="from">Start position [0-1]</param>
/// <param name="to">Target position [from-1]</param>
/// <returns></returns>
public void Evaluate(ref SplineSample[] samples, double from = 0.0, double to = 1.0)
{
if (points.Length == 0) {
samples = new SplineSample[0];
return;
}
from = DMath.Clamp01(from);
to = DMath.Clamp(to, from, 1.0);
double fromValue = from * (iterations - 1);
double toValue = to * (iterations - 1);
int clippedIterations = DMath.CeilInt(toValue) - DMath.FloorInt(fromValue) + 1;
if (samples == null) samples = new SplineSample[clippedIterations];
else if (samples.Length != clippedIterations) samples = new SplineSample[clippedIterations];
double percent = from;
double ms = moveStep;
int index = 0;
while (true)
{
samples[index] = Evaluate(percent);
index++;
if (index >= samples.Length) break;
percent = DMath.Move(percent, to, ms);
}
}
/// <summary>
/// Evaluates the spline segment and writes uniformly spaced results to the array
/// </summary>
/// <param name="from">Start position [0-1]</param>
/// <param name="to">Target position [from-1]</param>
/// <returns></returns>
public void EvaluateUniform(ref SplineSample[] samples, ref double[] originalSamplePercents, double from = 0.0, double to = 1.0)
{
if (points.Length == 0)
{
samples = new SplineSample[0];
return;
}
from = DMath.Clamp01(from);
to = DMath.Clamp(to, from, 1.0);
double fromValue = from * (iterations - 1);
double toValue = to * (iterations - 1);
int clippedIterations = DMath.CeilInt(toValue) - DMath.FloorInt(fromValue) + 1;
if (samples == null || samples.Length != clippedIterations) samples = new SplineSample[clippedIterations];
if (originalSamplePercents == null || originalSamplePercents.Length != clippedIterations) originalSamplePercents = new double[clippedIterations];
for (int i = 0; i < samples.Length; i++)
{
if (samples[i] == null) samples[i] = new SplineSample();
}
float lengthStep = CalculateLength(from, to) / (iterations - 1);
Evaluate(samples[0], from);
samples[0].percent = originalSamplePercents[0] = from;
double lastPercent = from;
float moved = 0f;
for (int i = 1; i < samples.Length - 1; i++)
{
Evaluate(samples[i], Travel(lastPercent, lengthStep, out moved, Direction.Forward));
lastPercent = samples[i].percent;
originalSamplePercents[i] = lastPercent;
samples[i].percent = DMath.Lerp(from, to, (double)i/ (samples.Length - 1));
}
Evaluate(samples[samples.Length - 1], to);
samples[samples.Length - 1].percent = originalSamplePercents[originalSamplePercents.Length - 1] = to;
}
/// <summary>
/// Evaluates the spline segment based on the spline's precision and returns only the position.
/// </summary>
/// <param name="positions">The position buffer</param>
/// <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 (points.Length == 0) {
positions = new Vector3[0];
return;
}
from = DMath.Clamp01(from);
to = DMath.Clamp(to, from, 1.0);
double fromValue = from * (iterations - 1);
double toValue = to * (iterations - 1);
int clippedIterations = DMath.CeilInt(toValue) - DMath.FloorInt(fromValue) + 1;
if (positions.Length != clippedIterations) positions = new Vector3[clippedIterations];
double percent = from;
double ms = moveStep;
int index = 0;
while (true)
{
positions[index] = EvaluatePosition(percent);
index++;
if (index >= positions.Length) break;
percent = DMath.Move(percent, to, ms);
}
}
/// <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, out float moved, Direction direction)
{
moved = 0f;
if (points.Length <= 1) return 0.0;
if (direction == Direction.Forward && start >= 1.0) return 1.0;
else if (direction == Direction.Backward && start <= 0.0) return 0.0; ;
if (distance == 0f) return DMath.Clamp01(start);
Vector3 pos = Vector3.zero;
EvaluatePosition(ref pos, start);
Vector3 lastPosition = pos;
double lastPercent = start;
int i = iterations - 1;
int nextSampleIndex = direction == Spline.Direction.Forward ? DMath.CeilInt(start * i) : DMath.FloorInt(start * i);
float lastDistance = 0f;
double percent = start;
while (true)
{
percent = (double)nextSampleIndex / i;
pos = EvaluatePosition(percent);
lastDistance = Vector3.Distance(pos, lastPosition);
lastPosition = pos;
moved += lastDistance;
if (moved >= distance) break;
lastPercent = percent;
if (direction == Spline.Direction.Forward)
{
if (nextSampleIndex == i) break;
nextSampleIndex++;
}
else
{
if (nextSampleIndex == 0) break;
nextSampleIndex--;
}
}
return DMath.Lerp(lastPercent, percent, 1f - (moved - distance) / lastDistance);
}
public double Travel(double start, float distance, Spline.Direction direction = Spline.Direction.Forward)
{
float moved;
return Travel(start, distance, out moved, direction);
}
public void EvaluatePosition(ref Vector3 point, double percent)
{
percent = DMath.Clamp01(percent);
double doubleIndex = (points.Length - 1) * percent;
int pointIndex = DMath.FloorInt(doubleIndex);
if (type == Type.Bezier) pointIndex = Mathf.Clamp(pointIndex, 0, Mathf.Max(points.Length - 2, 0));
GetPoint(ref point, doubleIndex - pointIndex, pointIndex);
}
public void EvaluateTangent(ref Vector3 tangent, double percent)
{
percent = DMath.Clamp01(percent);
double doubleIndex = (points.Length - 1) * percent;
int pointIndex = DMath.FloorInt(doubleIndex);
if (type == Type.Bezier) pointIndex = Mathf.Clamp(pointIndex, 0, Mathf.Max(points.Length - 2, 0));
GetTangent(ref tangent, doubleIndex - pointIndex, pointIndex);
}
//Get closest point in spline segment. Used for projection
private double GetClosestPoint(int iterations, Vector3 point, double start, double end, int slices)
{
if (iterations <= 0)
{
float startDist = (point - EvaluatePosition(start)).sqrMagnitude;
float endDist = (point - EvaluatePosition(end)).sqrMagnitude;
if (startDist < endDist) return start;
else if (endDist < startDist) return end;
else return (start + end) / 2;
}
double closestPercent = 0.0;
float closestDistance = Mathf.Infinity;
double tick = (end - start) / slices;
double t = start;
Vector3 pos = Vector3.zero;
while (true)
{
EvaluatePosition(ref pos, t);
float dist = (point - pos).sqrMagnitude;
if (dist < closestDistance)
{
closestDistance = dist;
closestPercent = t;
}
if (t == end) break;
t = DMath.Move(t, end, tick);
}
double newStart = closestPercent - tick;
if (newStart < start) newStart = start;
double newEnd = closestPercent + tick;
if (newEnd > end) newEnd = end;
return GetClosestPoint(--iterations, point, newStart, newEnd, slices);
}
/// <summary>
/// Break the closed spline
/// </summary>
public void Break()
{
Break(0);
}
/// <summary>
/// Break the closed spline at given point
/// </summary>
/// <param name="at"></param>
public void Break(int at)
{
if (!closed) return;
if (at >= points.Length) return;
SplinePoint[] prev = new SplinePoint[at];
for (int i = 0; i < prev.Length; i++) prev[i] = points[i];
for (int i = at; i < points.Length - 1; i++) points[i - at] = points[i];
for (int i = 0; i < prev.Length; i++) points[points.Length - at + i - 1] = prev[i];
points[points.Length - 1] = points[0];
closed = false;
}
/// <summary>
/// Close the spline. This will cause the first and last points of the spline to merge
/// </summary>
public void Close()
{
if (points.Length < 4)
{
Debug.LogError("Points need to be at least 4 to close the spline");
return;
}
closed = true;
}
/// <summary>
/// Convert the spline to a Bezier path
/// </summary>
public void HermiteToBezierTangents()
{
switch (type)
{
case Type.Linear:
for (int i = 0; i < points.Length; i++)
{
points[i].type = SplinePoint.Type.Broken;
points[i].SetTangentPosition(points[i].position);
points[i].SetTangent2Position(points[i].position);
}
break;
case Type.Hermite:
for (int i = 0; i < points.Length; i++)
{
GetHermitePoints(i);
points[i].type = SplinePoint.Type.SmoothMirrored;
if (i == 0)
{
Vector3 direction = hermitePoints[1] - hermitePoints[2];
if (closed)
{
direction = points[points.Length - 2].position - points[i + 1].position;
points[i].SetTangentPosition(points[i].position + direction / 6f);
} else points[i].SetTangentPosition(points[i].position + direction / 3f);
}
else if (i == points.Length - 1)
{
Vector3 direction = hermitePoints[2] - hermitePoints[3];
points[i].SetTangentPosition(points[i].position + direction / 3f);
}
else
{
Vector3 direction = hermitePoints[0] - hermitePoints[2];
points[i].SetTangentPosition(points[i].position + direction / 6f);
}
}
break;
case Type.BSpline:
//No BSPline support yet
break;
}
type = Type.Bezier;
}
private void GetPoint(ref Vector3 point, double percent, int pointIndex)
{
//Handle closed paths
if (closed && points.Length > 3)
{
if (pointIndex == points.Length - 2)
{
points[0].SetTangentPosition(points[points.Length - 1].tangent);
points[points.Length - 1] = points[0];
}
} else closed = false;
switch (type)
{
case Type.Hermite: HermiteGetPoint(ref point, percent, pointIndex); break;
case Type.Bezier: BezierGetPoint(ref point, percent, pointIndex); break;
case Type.BSpline: BSPGetPoint(ref point, percent, pointIndex); break;
case Type.Linear: LinearGetPoint(ref point, percent, pointIndex); break;
}
}
private void GetTangent(ref Vector3 tangent, double percent, int pointIndex)
{
switch (type)
{
case Type.Hermite: GetHermiteTangent(ref tangent, percent, pointIndex); break;
case Type.Bezier: BezierGetTangent(ref tangent, percent, pointIndex); break;
//case Type.BSpline: BSPGetTangent(ref tangent, percent, pointIndex); break;
case Type.Linear: LinearGetTangent(ref tangent, percent, pointIndex); break;
}
}
private void LinearGetPoint(ref Vector3 point, double t, int i)
{
if (points.Length == 0)
{
point = Vector3.zero;
return;
}
if (i < points.Length - 1)
{
t = DMath.Clamp01(t);
i = Mathf.Clamp(i, 0, points.Length - 2);
point = Vector3.Lerp(points[i].position, points[i + 1].position, (float)t);
} else point = points[i].position;
}
private void LinearGetTangent(ref Vector3 tangent, double t, int i)
{
if (points.Length == 0)
{
tangent = Vector3.forward;
return;
}
GetHermitePoints(i);
if (linearAverageDirection) tangent = Vector3.Slerp(hermitePoints[1] - hermitePoints[0], hermitePoints[2] - hermitePoints[1], 0.5f);
else tangent = hermitePoints[2] - hermitePoints[1];
}
private void BSPGetPoint(ref Vector3 point, double time, int i)
{
//Used for getting a point on a B-spline
if (points.Length > 0) point = points[0].position;
if (points.Length > 1)
{
float t1 = (float)DMath.Clamp01(time);
GetHermitePoints(i);
point = ((-hermitePoints[0] + hermitePoints[2]) / 2f
+ t1 * ((hermitePoints[0] - 2f * hermitePoints[1] + hermitePoints[2]) / 2f
+ t1 * (-hermitePoints[0] + 3f * hermitePoints[1] - 3f * hermitePoints[2] + hermitePoints[3]) / 6f)) * t1
+ (hermitePoints[0] + 4f * hermitePoints[1] + hermitePoints[2]) / 6f;
}
}
private void BezierGetPoint(ref Vector3 point, double t, int i)
{
//Used for getting a point on a Bezier spline
if (points.Length > 0) point = points[0].position;
else return;
if (points.Length == 1) return;
if (i < points.Length - 1)
{
t = DMath.Clamp01(t);
i = Mathf.Clamp(i, 0, points.Length - 2);
float ft = (float)t;
float nt = 1f - ft;
point = nt * nt * nt * points[i].position +
3f * nt * nt * ft * points[i].tangent2 +
3f * nt * ft * ft * points[i + 1].tangent +
ft * ft * ft * points[i + 1].position;
}
}
private void BezierGetTangent(ref Vector3 tangent, double t, int i)
{
if (points.Length > 0) tangent = points[0].tangent2;
else return;
if (points.Length == 1) return;
if (i < points.Length - 1)
{
t = DMath.Clamp01(t);
i = Mathf.Clamp(i, 0, points.Length - 2);
float ft = (float)t;
float nt = 1f - ft;
tangent = -3 * nt * nt * points[i].position +
3 * nt * nt * points[i].tangent2 -
6 * ft * nt * points[i].tangent2 -
3 * ft * ft * points[i + 1].tangent +
6 * ft * nt * points[i + 1].tangent +
3 * ft * ft * points[i + 1].position;
}
}
private void HermiteGetPoint(ref Vector3 point, double t, int i)
{
float t1 = (float)t;
float t2 = t1 * t1;
float t3 = t2 * t1;
if (points.Length > 0) point = points[0].position;
if (i >= points.Length) return;
if (points.Length > 1)
{
GetHermitePoints(i);
point = 0.5f * ((2f * hermitePoints[1]) + (-hermitePoints[0] + hermitePoints[2]) * t1
+ (2f * hermitePoints[0] - 5f * hermitePoints[1] + 4f * hermitePoints[2] - hermitePoints[3]) * t2
+ (-hermitePoints[0] + 3f * hermitePoints[1] - 3f * hermitePoints[2] + hermitePoints[3]) * t3);
}
}
private void GetHermiteTangent(ref Vector3 direction, double t, int i)
{
float t1 = (float)t;
float t2 = t1 * t1;
if (points.Length > 0) direction = Vector3.forward;
if (i >= points.Length) return;
if (points.Length > 1)
{
GetHermitePoints(i);
direction = (6 * t2 - 6 * t1) * hermitePoints[1]
+ (3 * t2 - 4 * t1 + 1) * (hermitePoints[2] - hermitePoints[0]) * 0.5f
+ (-6 * t2 + 6 * t1) * hermitePoints[2]
+ (3 * t2 - 2 * t1) * (hermitePoints[3] - hermitePoints[1]) * 0.5f;
}
}
private void GetHermitePoints(int i)
{
//Fills the array with the current point, the previous one, the next one and the one after that. Used for Hermite and Bspline
if (i > 0) hermitePoints[0] = points[i - 1].position;
else if (closed && points.Length - 2 > i) hermitePoints[0] = points[points.Length - 2].position;
else if (i + 1 < points.Length) hermitePoints[0] = points[i].position + (points[i].position - points[i + 1].position); //Extrapolate
else hermitePoints[0] = points[i].position;
hermitePoints[1] = points[i].position;
if (i + 1 < points.Length) hermitePoints[2] = points[i + 1].position;
else if (closed && (i + 2) - points.Length != i) hermitePoints[2] = points[(i + 2) - points.Length].position;
else hermitePoints[2] = hermitePoints[1] + (hermitePoints[1] - hermitePoints[0]); //Extrapolate
if (i + 2 < points.Length) hermitePoints[3] = points[i + 2].position;
else if (closed && (i + 3) - points.Length != i) hermitePoints[3] = points[(i + 3) - points.Length].position;
else hermitePoints[3] = hermitePoints[2] + (hermitePoints[2] - hermitePoints[1]); //Extrapolate
}
public static void FormatFromTo(ref double from, ref double to, bool preventInvert = true)
{
from = DMath.Clamp01(from);
to = DMath.Clamp01(to);
if (preventInvert && from > to)
{
double tmp = from;
from = to;
to = tmp;
} else to = DMath.Clamp(to, 0.0, 1.0);
}
}
}

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d8bb4e3c138e5124b9ea58db9d93e1f4
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
using UnityEngine;
using UnityEngine.Serialization;
using System.Collections;
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
{
_type = value;
if(value == Type.SmoothMirrored) SmoothMirrorTangent2();
}
}
[FormerlySerializedAs("type")]
[SerializeField]
[HideInInspector]
private Type _type;
public Vector3 position;
public Color color;
public Vector3 normal;
public float size;
public Vector3 tangent;
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, out result.tangent, out result.tangent2);
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;
}
static void GetInterpolatedTangents(SplinePoint a, SplinePoint b, float t, out Vector3 t1, out Vector3 t2)
{
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;
t1 = P01_12;
t2 = P12_23;
}
public static bool AreDifferent(ref SplinePoint a, ref SplinePoint b)
{
if (a.position != b.position) return true;
if (a.tangent != b.tangent) return true;
if (a.tangent2 != b.tangent2) return true;
if (a.normal != b.normal) return true;
if (a.color != b.color) return true;
if (a.size != b.size) return true;
if (a.type != b.type) return true;
return false;
}
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;
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;
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;
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;
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;
switch (_type)
{
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
case Type.SmoothFree: SmoothFreeTangent2(); break;
}
}
private void SmoothMirrorTangent2()
{
tangent2 = position + (position - tangent);
}
private void SmoothMirrorTangent()
{
tangent = position + (position - tangent2);
}
private void SmoothFreeTangent2()
{
tangent2 = position + (position - tangent).normalized * (tangent2 - position).magnitude;
}
private void SmoothFreeTangent()
{
tangent = position + (position - tangent2).normalized * (tangent - position).magnitude;
}
}
}

View 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:

View File

@@ -0,0 +1,146 @@
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace Dreamteck.Splines
{
public static class SplinePrefs
{
public enum DuplicationDirection { Forward, Backward }
private static bool loaded = false;
public static DuplicationDirection duplicationDirection = DuplicationDirection.Forward;
public static bool defaultAlwaysDraw = false;
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 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.Hermite;
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);
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);
highlightColor = EditorGUILayout.ColorField("Highlight color", highlightColor);
highlightContentColor = EditorGUILayout.ColorField("Highlight content color", highlightContentColor);
duplicationDirection = (DuplicationDirection)EditorGUILayout.EnumPopup("Duplicate Direction", duplicationDirection);
showPointNumbers = EditorGUILayout.Toggle("Show point numbers", showPointNumbers);
if (GUILayout.Button("Use Defaults", GUILayout.Width(120)))
{
duplicationDirection = DuplicationDirection.Forward;
defaultAlwaysDraw = false;
defaultShowThickness = false;
default2D = false;
startInCreationMode = true;
defaultColor = Color.white;
highlightColor = new Color(0f, 0.564f, 1f, 1f);
highlightContentColor = new Color(1f, 1f, 1f, 0.95f);
showPointNumbers = false;
defaultComputerSpace = SplineComputer.Space.Local;
defaultType = Spline.Type.Hermite;
createPointSize = 1f;
createPointColor = Color.white;
SavePrefs();
}
if (GUI.changed) SavePrefs();
}
public static void LoadPrefs()
{
defaultAlwaysDraw = EditorPrefs.GetBool("Dreamteck.Splines.defaultAlwaysDraw", false);
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));
highlightContentColor = LoadColor("Dreamteck.Splines.highlightContentColor", new Color(1f, 1f, 1f, 0.95f));
defaultComputerSpace = (SplineComputer.Space)EditorPrefs.GetInt("Dreamteck.Splines.defaultComputerSpace", 0);
defaultType = (Spline.Type)EditorPrefs.GetInt("Dreamteck.Splines.defaultType", 0);
duplicationDirection = (DuplicationDirection)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.defaultAlwaysDraw", defaultAlwaysDraw);
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.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

View 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:

View File

@@ -0,0 +1,116 @@
using UnityEngine;
using Dreamteck;
namespace Dreamteck.Splines{
[System.Serializable]
public class SplineSample {
public Vector3 position = Vector3.zero;
public Vector3 up = Vector3.up;
public Vector3 forward = Vector3.forward;
public Color color = Color.white;
public float size = 1f;
public double percent = 0.0;
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(SplineSample a, SplineSample b, float t)
{
SplineSample result = new SplineSample();
Lerp(a, b, t, result);
return result;
}
public static SplineSample Lerp(SplineSample a, SplineSample b, double t)
{
SplineSample result = new SplineSample();
Lerp(a, b, t, result);
return result;
}
public static void Lerp(SplineSample a, SplineSample b, double t, SplineSample target)
{
float ft = (float)t;
target.position = DMath.LerpVector3(a.position, b.position, t);
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(SplineSample a, SplineSample b, float t, SplineSample target)
{
target.position = DMath.LerpVector3(a.position, b.position, t);
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(SplineSample b, double t)
{
Lerp(this, b, t, this);
}
public void Lerp(SplineSample b, float t)
{
Lerp(this, b, t, this);
}
public void CopyFrom(SplineSample input)
{
position = input.position;
forward = input.forward;
up = input.up;
color = input.color;
size = input.size;
percent = input.percent;
}
public SplineSample()
{
}
public SplineSample(Vector3 position, Vector3 normal, Vector3 direction, Color color, float size, double percent)
{
this.position = position;
this.up = normal;
this.forward = direction;
this.color = color;
this.size = size;
this.percent = percent;
}
public SplineSample(SplineSample input)
{
position = input.position;
up = input.up;
forward = input.forward;
color = input.color;
size = input.size;
percent = input.percent;
}
}
}

View 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:

View File

@@ -0,0 +1,181 @@
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 { return threads.Length; }
set
{
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);
}
}
}
}
#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);
Debug.Log("Starting Thread");
}
internal void Abort()
{
if (isAlive) thread.Abort();
Debug.Log("Stopping Thread");
}
}
internal static ThreadDef[] threads = new ThreadDef[2];
internal static readonly object locker = new object();
static SplineThreading()
{
//Application.quitting += Quitting; Does not work in Unity 2017
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.Log("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()
{
for (int i = 0; i < threads.Length; i++)
{
if (!threads[i].isAlive) threads[i].Restart();
}
}
public static void Stop()
{
#if !UNITY_WSA
for (int i = 0; i < threads.Length; i++) threads[i].Abort();
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3accda9a749b24c459cb983529284d01
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,95 @@
namespace Dreamteck.Splines
{
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)
{
for (int i = 0; i < triggers.Length; i++)
{
if (triggers[i] == null) continue;
if (triggers[i].Check(start, end)) triggers[i].Invoke();
}
}
public void Reset()
{
for (int i = 0; i < triggers.Length; i++) triggers[i].Reset();
}
}
[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 UnityEvent onCross = new UnityEvent();
public SplineTrigger(Type t)
{
type = t;
enabled = true;
onCross = new UnityEvent();
}
/// <summary>
/// Add a new UnityAction to the trigger
/// </summary>
/// <param name="action"></param>
public void AddListener(UnityAction action)
{
onCross.AddListener(action);
}
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()
{
#if UNITY_EDITOR
if (!Application.isPlaying) return;
#endif
onCross.Invoke();
}
}
}

View 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:

View File

@@ -0,0 +1,221 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace Dreamteck.Splines
{
[System.Serializable]
public class TransformModule
{
public Vector2 offset
{
get { return _offset; }
set
{
if (value != _offset)
{
_offset = value;
if (targetUser != null) targetUser.Rebuild();
}
}
}
public Vector3 rotationOffset
{
get { return _rotationOffset; }
set
{
if (value != _rotationOffset)
{
_rotationOffset = value;
if (targetUser != null) targetUser.Rebuild();
}
}
}
public Vector3 baseScale
{
get { return _baseScale; }
set
{
if (value != _baseScale)
{
_baseScale = value;
if (targetUser != null) targetUser.Rebuild();
}
}
}
[SerializeField]
[HideInInspector]
[FormerlySerializedAs("offset")]
private Vector2 _offset;
[SerializeField]
[HideInInspector]
[FormerlySerializedAs("rotationOffset")]
private Vector3 _rotationOffset = Vector3.zero;
[SerializeField]
[HideInInspector]
[FormerlySerializedAs("baseScale")]
private Vector3 _baseScale = Vector3.one;
public enum VelocityHandleMode { Zero, Preserve, Align, AlignRealistic }
public VelocityHandleMode velocityHandleMode = VelocityHandleMode.Zero;
public SplineSample splineResult
{
get
{
if (_splineResult == null) _splineResult = new SplineSample();
return _splineResult;
}
set
{
if (_splineResult == null) _splineResult = new SplineSample(value);
else _splineResult.CopyFrom(value);
}
}
private SplineSample _splineResult;
public bool applyPositionX = true;
public bool applyPositionY = true;
public bool applyPositionZ = true;
public Spline.Direction direction = Spline.Direction.Forward;
public bool applyPosition
{
get
{
return applyPositionX || applyPositionY || applyPositionZ;
}
set
{
applyPositionX = applyPositionY = applyPositionZ = value;
}
}
public bool applyRotationX = true;
public bool applyRotationY = true;
public bool applyRotationZ = true;
public bool applyRotation
{
get
{
return applyRotationX || applyRotationY || applyRotationZ;
}
set
{
applyRotationX = applyRotationY = applyRotationZ = 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)
{
input.transform.localScale = GetScale(input.transform.localScale);
input.MovePosition(GetPosition(input.position));
input.velocity = HandleVelocity(input.velocity);
Vector3 velocity = input.velocity;
input.velocity = velocity;
input.MoveRotation(GetRotation(input.rotation));
velocity = input.angularVelocity;
if (applyRotationX) velocity.x = 0f;
if (applyRotationY) velocity.y = 0f;
if (applyRotationZ) velocity.z = 0f;
input.angularVelocity = velocity;
}
public void ApplyRigidbody2D(Rigidbody2D input)
{
input.transform.localScale = GetScale(input.transform.localScale);
input.position = GetPosition(input.position);
input.velocity = HandleVelocity(input.velocity);
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 (customOffset != null) finalOffset += customOffset.Evaluate(_splineResult.percent);
if (finalOffset != Vector2.zero) position += _splineResult.right * finalOffset.x * _splineResult.size + _splineResult.up * finalOffset.y * _splineResult.size;
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 (_rotationOffset != Vector3.zero) rotation = rotation * Quaternion.Euler(_rotationOffset);
//if (customRotation != null) rotation = customRotation.Evaluate(rotation, _splineResult.percent);
if (!applyRotationX || !applyRotationY)
{
Vector3 euler = rotation.eulerAngles;
if (!applyRotationX) euler.x = inputRotation.eulerAngles.x;
if (!applyRotationY) euler.y = inputRotation.eulerAngles.y;
if (!applyRotationZ) euler.z = inputRotation.eulerAngles.z;
inputRotation.eulerAngles = euler;
}
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;
}
}
}

View 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: