This commit is contained in:
SoulliesOfficial
2025-06-06 10:14:55 -04:00
parent d4e860fa16
commit db4d131192
1088 changed files with 45704 additions and 2260 deletions

View File

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

File diff suppressed because one or more lines are too long

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,889 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text.RegularExpressions;
using System.Linq;
using Dreamteck.Splines.Primitives;
using System;
namespace Dreamteck.Splines.IO
{
public class SVG : SplineParser
{
public enum Axis { X, Y, Z }
internal class PathSegment
{
internal Vector3 startTangent = Vector3.zero;
internal Vector3 endTangent = Vector3.zero;
internal Vector3 endPoint = Vector3.zero;
internal enum Type { Cubic, CubicShort, Quadratic, QuadraticShort }
internal PathSegment(Vector2 s, Vector2 e, Vector2 c)
{
startTangent = s;
endTangent = e;
endPoint = c;
}
internal PathSegment()
{
}
}
public enum Element { All, Path, Polygon, Ellipse, Rectangle, Line }
private System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-US");
private System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Any;
List<SplineDefinition> paths = new List<SplineDefinition>();
List<SplineDefinition> polygons = new List<SplineDefinition>();
List<SplineDefinition> ellipses = new List<SplineDefinition>();
List<SplineDefinition> rectangles = new List<SplineDefinition>();
List<SplineDefinition> lines = new List<SplineDefinition>();
List<Transformation> transformBuffer = new List<Transformation>();
public SVG(string filePath)
{
if (File.Exists(filePath))
{
string ext = Path.GetExtension(filePath).ToLower();
fileName = Path.GetFileNameWithoutExtension(filePath);
if (ext != ".svg" && ext != ".xml")
{
Debug.LogError("SVG Parsing ERROR: Wrong format. Please use SVG or XML");
return;
}
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
try
{
doc.Load(filePath);
}
catch (XmlException ex)
{
Debug.LogError(ex.Message);
return;
}
Read(doc);
}
}
public SVG(List<SplineComputer> computers)
{
paths = new List<SplineDefinition>(computers.Count);
for (int i = 0; i < computers.Count; i++)
{
if (computers[i] == null) continue;
Spline spline = new Spline(computers[i].type, computers[i].sampleRate);
spline.points = computers[i].GetPoints();
if (spline.type != Spline.Type.Bezier && spline.type != Spline.Type.Linear) spline.CatToBezierTangents();
if (computers[i].isClosed) spline.Close();
paths.Add(new SplineDefinition(computers[i].name, spline));
}
}
public void Write(string filePath, Axis ax = Axis.Z)
{
XmlDocument doc = new XmlDocument();
XmlElement svg = doc.CreateElement("svg");
foreach (SplineDefinition path in paths)
{
string elementName = "path";
string attributeName = "d";
if (path.type == Spline.Type.Linear)
{
attributeName = "points";
if (path.closed) elementName = "polygon";
else elementName = "polyline";
}
XmlElement splineNode = doc.CreateElement(elementName);
XmlAttribute splineAttribute = doc.CreateAttribute("id");
splineAttribute.Value = path.name;
splineNode.Attributes.Append(splineAttribute);
splineAttribute = doc.CreateAttribute(attributeName);
if (path.type == Spline.Type.Linear) splineAttribute.Value = EncodePolygon(path, ax);
else splineAttribute.Value = EncodePath(path, ax);
splineNode.Attributes.Append(splineAttribute);
splineAttribute = doc.CreateAttribute("stroke");
splineAttribute.Value = "black";
splineNode.Attributes.Append(splineAttribute);
splineAttribute = doc.CreateAttribute("stroke-width");
splineAttribute.Value = "3";
splineNode.Attributes.Append(splineAttribute);
splineAttribute = doc.CreateAttribute("fill");
splineAttribute.Value = "none";
splineNode.Attributes.Append(splineAttribute);
svg.AppendChild(splineNode);
}
XmlAttribute svgAttribute = doc.CreateAttribute("version");
svgAttribute.Value = "1.1";
svg.Attributes.Append(svgAttribute);
svgAttribute = doc.CreateAttribute("xmlns");
svgAttribute.Value = "http://www.w3.org/2000/svg";
svg.Attributes.Append(svgAttribute);
doc.AppendChild(svg);
doc.Save(filePath);
}
Vector2 MapPoint(Vector3 original, Axis ax)
{
switch (ax)
{
case Axis.X: return new Vector2(original.z, -original.y);
case Axis.Y: return new Vector2(original.x, -original.z);
case Axis.Z: return new Vector2(original.x, -original.y);
}
return original;
}
private void Read(XmlDocument doc)
{
transformBuffer.Clear();
Traverse(doc.ChildNodes);
}
private void Traverse(XmlNodeList nodes)
{
foreach (XmlNode node in nodes)
{
int addedTransforms = 0;
switch (node.Name)
{
case "g": addedTransforms = ParseTransformation(node); break;
case "path": addedTransforms = ReadPath(node); break;
case "polygon": addedTransforms = ReadPolygon(node, true); break;
case "polyline": addedTransforms = ReadPolygon(node, false); break;
case "ellipse": addedTransforms = ReadEllipse(node); break;
case "circle": addedTransforms = ReadEllipse(node); break;
case "line": addedTransforms = ReadLine(node); break;
case "rect": addedTransforms = ReadRectangle(node); break;
}
Traverse(node.ChildNodes);
if (addedTransforms > 0) transformBuffer.RemoveRange(transformBuffer.Count - addedTransforms, addedTransforms);
}
}
public List<SplineComputer> CreateSplineComputers(Vector3 position, Quaternion rotation, Element elements = Element.All)
{
List<SplineComputer> computers = new List<SplineComputer>();
if (elements == Element.All || elements == Element.Path)
{
foreach (SplineDefinition definition in paths) computers.Add(definition.CreateSplineComputer(position, rotation));
}
if (elements == Element.All || elements == Element.Polygon)
{
foreach (SplineDefinition definition in polygons) computers.Add(definition.CreateSplineComputer(position, rotation));
}
if (elements == Element.All || elements == Element.Ellipse)
{
foreach (SplineDefinition definition in ellipses) computers.Add(definition.CreateSplineComputer(position, rotation));
}
if (elements == Element.All || elements == Element.Rectangle)
{
foreach (SplineDefinition definition in rectangles) computers.Add(definition.CreateSplineComputer(position, rotation));
}
if (elements == Element.All || elements == Element.Line)
{
foreach (SplineDefinition definition in lines) computers.Add(definition.CreateSplineComputer(position, rotation));
}
return computers;
}
public List<Spline> CreateSplines(Element elements = Element.All)
{
List<Spline> splines = new List<Spline>();
if (elements == Element.All || elements == Element.Path)
{
foreach (SplineDefinition definition in paths) splines.Add(definition.CreateSpline());
}
if (elements == Element.All || elements == Element.Polygon)
{
foreach (SplineDefinition definition in polygons) splines.Add(definition.CreateSpline());
}
if (elements == Element.All || elements == Element.Ellipse)
{
foreach (SplineDefinition definition in ellipses) splines.Add(definition.CreateSpline());
}
if (elements == Element.All || elements == Element.Rectangle)
{
foreach (SplineDefinition definition in rectangles) splines.Add(definition.CreateSpline());
}
if (elements == Element.All || elements == Element.Line)
{
foreach (SplineDefinition definition in lines) splines.Add(definition.CreateSpline());
}
return splines;
}
private int ReadRectangle(XmlNode rectNode)
{
float x = 0f, y = 0f, w = 0f, h = 0f, rx = -1f, ry = -1f;
string attribute = GetAttributeContent(rectNode, "x");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out x);
attribute = GetAttributeContent(rectNode, "y");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out y);
attribute = GetAttributeContent(rectNode, "width");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out w);
attribute = GetAttributeContent(rectNode, "height");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out h);
attribute = GetAttributeContent(rectNode, "rx");
if (attribute != "ERROR") float.TryParse(attribute, style, culture, out rx);
attribute = GetAttributeContent(rectNode, "ry");
if (attribute != "ERROR") float.TryParse(attribute, style, culture, out ry);
else ry = rx;
string elementName = GetAttributeContent(rectNode, "id");
if (rx == -1f && ry == -1f)
{
Rectangle rect = new Rectangle();
rect.offset = new Vector2(x + w / 2f, -y - h / 2f);
rect.size = new Vector2(w, h);
if (elementName == "ERROR") elementName = fileName + "_rectangle" + (rectangles.Count + 1);
buffer = new SplineDefinition(elementName, rect.CreateSpline());
}
else
{
RoundedRectangle rect = new RoundedRectangle();
rect.offset = new Vector2(x + w / 2f, -y - h / 2f);
rect.size = new Vector2(w, h);
rect.xRadius = rx;
rect.yRadius = ry;
if (elementName == "ERROR") elementName = fileName + "_roundedRectangle" + (rectangles.Count + 1);
buffer = new SplineDefinition(elementName, rect.CreateSpline());
}
int addedTransforms = ParseTransformation(rectNode);
WriteBufferTo(rectangles);
return addedTransforms;
}
private int ReadLine(XmlNode lineNode)
{
float startX = 0f, startY = 0f, endX = 0f, endY = 0f;
string attribute = GetAttributeContent(lineNode, "x1");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out startX);
attribute = GetAttributeContent(lineNode, "y1");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out startY);
attribute = GetAttributeContent(lineNode, "x2");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out endX);
attribute = GetAttributeContent(lineNode, "y2");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out endY);
string elementName = GetAttributeContent(lineNode, "id");
if (elementName == "ERROR") elementName = fileName + "_line" + (ellipses.Count + 1);
buffer = new SplineDefinition(elementName, Spline.Type.Linear);
buffer.position = new Vector2(startX, -startY);
buffer.CreateLinear();
buffer.position = new Vector2(endX, -endY);
buffer.CreateLinear();
int addedTransforms = ParseTransformation(lineNode);
WriteBufferTo(lines);
return addedTransforms;
}
private int ReadEllipse(XmlNode ellipseNode)
{
float x = 0f, y = 0f, rx = 0f, ry = 0f;
string attribute = GetAttributeContent(ellipseNode, "cx");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out x);
attribute = GetAttributeContent(ellipseNode, "cy");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out y);
attribute = GetAttributeContent(ellipseNode, "r");
string shapeName = "circle";
if (attribute == "ERROR") //It might be an ellipse
{
shapeName = "ellipse";
attribute = GetAttributeContent(ellipseNode, "rx");
if (attribute == "ERROR") return 0;
float.TryParse(attribute, style, culture, out rx);
attribute = GetAttributeContent(ellipseNode, "ry");
if (attribute == "ERROR") return 0;
}
else //Nope, it's a circle
{
float.TryParse(attribute, style, culture, out rx);
ry = rx;
}
float.TryParse(attribute, style, culture, out ry);
Ellipse ellipse = new Ellipse();
ellipse.offset = new Vector2(x, -y);
ellipse.xRadius = rx;
ellipse.yRadius = ry;
string elementName = GetAttributeContent(ellipseNode, "id");
if (elementName == "ERROR") elementName = fileName + "_" + shapeName + (ellipses.Count + 1);
buffer = new SplineDefinition(elementName, ellipse.CreateSpline());
int addedTransforms = ParseTransformation(ellipseNode);
WriteBufferTo(ellipses);
return addedTransforms;
}
private int ReadPolygon(XmlNode polyNode, bool closed)
{
string contents = GetAttributeContent(polyNode, "points");
if (contents == "ERROR") return 0;
List<float> coords = ParseFloatArray(contents);
if (coords.Count % 2 != 0)
{
Debug.LogWarning("There is an error with one of the polygon shapes.");
return 0;
}
string elementName = GetAttributeContent(polyNode, "id");
if (elementName == "ERROR") elementName = fileName + (closed ? "_polygon " : "_polyline") + (polygons.Count + 1);
buffer = new SplineDefinition(elementName, Spline.Type.Linear);
int count = coords.Count / 2;
for (int i = 0; i < count; i++)
{
buffer.position = new Vector2(coords[0 + 2 * i], -coords[1 + 2 * i]);
buffer.CreateLinear();
}
if (closed)
{
buffer.CreateClosingPoint();
buffer.closed = true;
}
int addedTransforms = ParseTransformation(polyNode);
WriteBufferTo(polygons);
return addedTransforms;
}
private int ParseTransformation(XmlNode node)
{
string transformAttribute = GetAttributeContent(node, "transform");
if (transformAttribute == "ERROR") return 0;
List<Transformation> trs = ParseTransformations(transformAttribute);
transformBuffer.AddRange(trs);
return trs.Count;
}
private List<Transformation> ParseTransformations(string transformContent)
{
List<Transformation> trs = new List<Transformation>();
MatchCollection matches = Regex.Matches(transformContent.ToLower(), @"(?<function>translate|rotate|scale|skewx|skewy|matrix)\s*\((\s*(?<param>-?\s*\d+(\.\d+)?)\s*\,*\s*)+\)");
foreach (Match match in matches)
{
if (match.Groups["function"].Success)
{
CaptureCollection parameters = match.Groups["param"].Captures;
switch (match.Groups["function"].Value)
{
case "translate":
if (parameters.Count < 2) break;
trs.Add(new Translate(new Vector2(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value))));
break;
case "rotate":
if (parameters.Count < 1) break;
trs.Add(new Rotate(float.Parse(parameters[0].Value)));
break;
case "scale":
if (parameters.Count < 2) break;
trs.Add(new Scale(new Vector2(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value))));
break;
case "skewx":
if (parameters.Count < 1) break;
trs.Add(new SkewX(float.Parse(parameters[0].Value)));
break;
case "skewy":
if (parameters.Count < 1) break;
trs.Add(new SkewY(float.Parse(parameters[0].Value)));
break;
case "matrix":
if (parameters.Count < 6) break;
trs.Add(new MatrixTransform(float.Parse(parameters[0].Value), float.Parse(parameters[1].Value), float.Parse(parameters[2].Value), float.Parse(parameters[3].Value), float.Parse(parameters[4].Value), float.Parse(parameters[5].Value)));
break;
}
}
}
return trs;
}
private int ReadPath(XmlNode pathNode)
{
string contents = GetAttributeContent(pathNode, "d");
if (contents == "ERROR") return 0;
string elementName = GetAttributeContent(pathNode, "id");
if (elementName == "ERROR") elementName = fileName + "_path " + (paths.Count + 1);
IEnumerable<string> tokens = Regex.Split(contents, @"(?=[A-Za-z])").Where(t => !string.IsNullOrEmpty(t));
int numSplines = 0;
foreach (string token in tokens)
{
char cmd = token.Substring(0, 1).Single();
switch (cmd)
{
case 'M':
PathStart(elementName, token, false);
++numSplines;
break;
case 'm':
PathStart(elementName, token, true);
++numSplines;
break;
case 'Z':
PathClose();
break;
case 'z':
PathClose();
break;
case 'L':
PathLineTo(token, false);
break;
case 'l':
PathLineTo(token, true);
break;
case 'H':
PathHorizontalLineTo(token, false);
break;
case 'h':
PathHorizontalLineTo(token, true);
break;
case 'V':
PathVerticalLineTo(token, false);
break;
case 'v':
PathVerticalLineTo(token, true);
break;
case 'C':
PathCurveTo(token, PathSegment.Type.Cubic, false);
break;
case 'c':
PathCurveTo(token, PathSegment.Type.Cubic, true);
break;
case 'S':
PathCurveTo(token, PathSegment.Type.CubicShort, false);
break;
case 's':
PathCurveTo(token, PathSegment.Type.CubicShort, true);
break;
case 'Q':
PathCurveTo(token, PathSegment.Type.Quadratic, false);
break;
case 'q':
PathCurveTo(token, PathSegment.Type.Quadratic, true);
break;
case 'T':
PathCurveTo(token, PathSegment.Type.QuadraticShort, false);
break;
case 't':
PathCurveTo(token, PathSegment.Type.QuadraticShort, true);
break;
case 'A':
PathArcTo(token, false);
break;
case 'a':
PathArcTo(token, true);
break;
}
}
if (buffer != null) WriteBufferTo(paths);
int addedTransforms = ParseTransformation(pathNode);
for (int i = paths.Count - 1; i >= paths.Count - numSplines; --i) paths[i].Transform(transformBuffer);
return addedTransforms;
}
private void PathStart(string name, string coords, bool relative)
{
if (buffer != null) WriteBufferTo(paths);
buffer = new SplineDefinition(name, Spline.Type.Bezier);
if (relative) buffer.position = paths.Last().GetLastPoint().position;
Vector2[] vectors = ParseVector2(coords);
foreach (Vector3 vector in vectors)
{
if (relative) buffer.position += vector;
else buffer.position = vector;
buffer.CreateLinear();
}
}
private void PathClose()
{
buffer.CreateClosingPoint();
buffer.closed = true;
}
private void PathLineTo(string coords, bool relative)
{
Vector2[] vectors = ParseVector2(coords);
foreach (Vector3 vector in vectors)
{
if (relative) buffer.position += vector;
else buffer.position = vector;
buffer.CreateLinear();
}
}
private void PathHorizontalLineTo(string coords, bool relative)
{
float[] floats = ParseFloat(coords);
foreach (float f in floats)
{
if (relative) buffer.position.x += f;
else buffer.position.x = f;
buffer.CreateLinear();
}
}
private void PathVerticalLineTo(string coords, bool relative)
{
float[] floats = ParseFloat(coords);
foreach (float f in floats)
{
if (relative) buffer.position.y -= f;
else buffer.position.y = -f;
buffer.CreateLinear();
}
}
private void PathCurveTo(string coords, PathSegment.Type type, bool relative)
{
PathSegment[] segment = ParsePathSegment(coords, type);
for (int i = 0; i < segment.Length; i++)
{
SplinePoint p = buffer.GetLastPoint();
p.type = SplinePoint.Type.Broken;
//Get the control points
Vector3 startPoint = p.position;
Vector3 endPoint = segment[i].endPoint;
Vector3 startTangent = segment[i].startTangent;
Vector3 endTangent = segment[i].endTangent;
switch (type)
{
case PathSegment.Type.CubicShort: startTangent = startPoint - p.tangent; break;
case PathSegment.Type.Quadratic:
buffer.tangent = segment[i].startTangent;
startTangent = startPoint + 2f / 3f * (buffer.tangent - startPoint);
endTangent = endPoint + 2f / 3f * (buffer.tangent - endPoint);
break;
case PathSegment.Type.QuadraticShort:
Vector3 reflection = startPoint + (startPoint - buffer.tangent);
startTangent = startPoint + 2f / 3f * (reflection - startPoint);
endTangent = endPoint + 2f / 3f * (reflection - endPoint);
break;
}
if (type == PathSegment.Type.CubicShort || type == PathSegment.Type.QuadraticShort) p.type = SplinePoint.Type.SmoothMirrored; //Smooth the previous point
else
{
if (relative) p.SetTangent2Position(startPoint + startTangent);
else p.SetTangent2Position(startTangent);
}
buffer.SetLastPoint(p);
if (relative)
{
buffer.position += endPoint;
buffer.tangent = startPoint + endTangent;
}
else
{
buffer.position = endPoint;
buffer.tangent = endTangent;
}
buffer.CreateBroken();
}
}
private void PathArcTo(string coords, bool relative)
{
// Get Arc Arguments
float[] floats = ParseFloat(coords);
float rx = floats[0];
float ry = floats[1];
float rotation = floats[2] * Mathf.Deg2Rad;
bool largeArc = floats[3] > 0.5f;
bool sweep = floats[4] > 0.5f;
float x = floats[5];
float y = floats[6];
// Set the last buffer point to broken
SplinePoint p = buffer.GetLastPoint();
p.type = SplinePoint.Type.Broken;
// Get the start and end point. Note: Flip Y, as the Ellipse Parameters calculation assumes positive Y values
Vector3 sp = p.position;
sp.y *= -1;
Vector3 ep = new Vector3(x, y, 0);
if (relative) ep += sp;
Vector2 c;
float theta1;
float sweepTheta;
float adjustedRx;
float adjustedRy;
// Get the Ellipse Parameters
CalculateEllipseParams(sp, ep, rotation, rx, ry, largeArc, sweep,
out c, out theta1, out sweepTheta, out adjustedRx, out adjustedRy);
// Flip the center Y back
c.y *= -1;
// Generate the ellipse primitive. Note: Rotated by -90 degrees and flipped, so first point starts at +X and goes clockwise
Ellipse ellipse = new Ellipse();
ellipse.offset = c;
ellipse.rotation = new Vector3(0, 0, -90 - rotation * Mathf.Rad2Deg);
ellipse.xRadius = adjustedRx;
ellipse.yRadius = adjustedRy;
var ellipseSpline = ellipse.CreateSpline();
var esp = ellipseSpline.points;
var tmpp = esp[1];
esp[1] = esp[3];
esp[3] = tmpp;
for (int i = 0; i < esp.Length; i++)
{
FlipTangents(ref esp[i]);
}
// Find the percentages to sample the ellipse at
var startP = theta1 / (Mathf.PI * 2);
startP = ModP(startP, 1f);
var sweepP = sweepTheta / (Mathf.PI * 2);
var endP = startP + sweepP;
var percentages = GetArcSegmentPercentages(startP, endP);
// Sample the ellipse and add points to buffer
for (int i = 1; i < percentages.Length; ++i)
{
double pp = percentages[i - 1];
double pc = percentages[i];
double pr = pc - pp;
int sgn = Math.Sign(pr); // This sign indicates a reversed range if < 0; The sampled tangents need to be flipped if this is the case
pr *= sgn;
if (pr < 0.0001d) continue; // Sample error margin
double d = 0.75d / pr;
pc = ModP(pc, 1d);
pp = ModP(pp, 1d);
// New point in buffer
Vector3 posc = Vector3.zero, tanc = Vector3.zero, tanp = Vector3.zero;
ellipseSpline.EvaluatePosition(pc, ref posc);
ellipseSpline.EvaluateTangent(pc, ref tanc);
tanc *= sgn;
tanc /= (float)d;
buffer.position = posc;
buffer.tangent = posc - tanc;
// Modify tangent2 of last point in buffer
ellipseSpline.EvaluateTangent(pp, ref tanp);
tanp *= sgn;
tanp /= (float)d;
p = buffer.GetLastPoint();
p.type = SplinePoint.Type.Broken;
p.SetTangent2Position(p.position + tanp);
buffer.SetLastPoint(p);
buffer.CreateBroken();
}
}
private void FlipTangents(ref SplinePoint point)
{
var tmpt = point.tangent;
point.tangent = point.tangent2;
point.tangent2 = tmpt;
}
private void CalculateEllipseParams(Vector2 p0, Vector2 p1, float phi, float rx, float ry, bool fa, bool fs, out Vector2 c, out float theta1, out float sweepTheta, out float adjustedRx, out float adjustedRy)
{
// From https://observablehq.com/@toja/ellipse-and-elliptical-arc-conversion
float sinPhi = Mathf.Sin(phi);
float cosPhi = Mathf.Cos(phi);
float x = cosPhi * (p0.x - p1.x) / 2 + sinPhi * (p0.y - p1.y) / 2;
float y = -sinPhi * (p0.x - p1.x) / 2 + cosPhi * (p0.y - p1.y) / 2;
float px = x * x, py = y * y, prx = rx * rx, pry = ry * ry;
rx = Mathf.Abs(rx);
ry = Mathf.Abs(ry);
float l = px / prx + py / pry;
if (l > 1)
{
float sqrtl = Mathf.Sqrt(l);
rx = sqrtl * rx;
ry = sqrtl * ry;
prx = rx * rx;
pry = ry * ry;
}
adjustedRx = rx;
adjustedRy = ry;
float sign = fa == fs ? -1 : 1;
float m = Mathf.Sqrt((prx * pry - prx * py - pry * px) / (prx * py + pry * px)) * sign;
float ccx = m * (rx * y) / ry;
float ccy = m * (-ry * x) / rx;
c = new Vector2(
cosPhi * ccx - sinPhi * ccy + (p0.x + p1.x) / 2,
sinPhi * ccx + cosPhi * ccy + (p0.y + p1.y) / 2
);
theta1 = VectorAngle(new Vector2(1, 0), new Vector2((x - ccx) / rx, (y - ccy) / ry));
sweepTheta = VectorAngle(new Vector2((x - ccx) / rx, (y - ccy) / ry), new Vector2((-x - ccx) / rx, (-y - ccy) / ry));
sweepTheta *= Mathf.Rad2Deg;
sweepTheta %= 360;
if (!fs && sweepTheta > 0) sweepTheta -= 360;
if (fs && sweepTheta < 0) sweepTheta += 360;
sweepTheta *= Mathf.Deg2Rad;
}
private double[] GetArcSegmentPercentages(double start, double end)
{
List<double> percentages = new List<double>();
bool swap = start > end;
if (swap) { double tmp = start; start = end; end = tmp; }
percentages.Add(start);
double rsM = Math.Ceiling(start * 4d) * 0.25d;
if (rsM > end)
{
percentages.Add(end);
return ReturnPercentage(swap, percentages);
}
else if (start < rsM)
{
percentages.Add(rsM);
}
double rem = rsM + 0.25d;
for (; rem <= end; rem += 0.25d)
{
percentages.Add(rem);
}
rem -= 0.25d;
if (rem < end)
{
percentages.Add(end);
}
return ReturnPercentage(swap, percentages);
}
private double[] ReturnPercentage(bool swap, List<double> percentages)
{
var ret = new double[percentages.Count];
for (int i = 0; i < percentages.Count; ++i)
{
var r = swap ? percentages.Count - 1 - i : i;
var p = percentages[r];
ret[i] = p;
}
return ret;
}
private float VectorAngle(Vector2 u, Vector2 v)
{
float sign = u.x * v.y - u.y * v.x < 0 ? -1 : 1;
float ua = Mathf.Sqrt(u.x * u.x + u.y * u.y);
float va = Mathf.Sqrt(v.x * v.x + v.y * v.y);
float dot = u.x * v.x + u.y * v.y;
return sign * Mathf.Acos(dot / (ua * va));
}
private float ModP(float f, float div)
{
return ((f % div) + div) % div;
}
private double ModP(double d, double div)
{
return ((d % div) + div) % div;
}
private void WriteBufferTo(List<SplineDefinition> list)
{
buffer.Transform(transformBuffer);
list.Add(buffer);
buffer = null;
}
private PathSegment[] ParsePathSegment(string coord, PathSegment.Type type)
{
List<float> list = ParseFloatArray(coord.Substring(1));
int count = 0;
switch (type)
{
case PathSegment.Type.Cubic: count = list.Count / 6; break;
case PathSegment.Type.Quadratic: count = list.Count / 4; break;
case PathSegment.Type.CubicShort: count = list.Count / 4; break;
case PathSegment.Type.QuadraticShort: count = list.Count / 2; break;
}
if (count == 0)
{
Debug.Log("Error in " + coord + " " + type);
return new PathSegment[] { new PathSegment() };
}
PathSegment[] data = new PathSegment[count];
for (int i = 0; i < count; i++)
{
switch (type)
{
case PathSegment.Type.Cubic: data[i] = new PathSegment(new Vector2(list[0 + 6 * i], -list[1 + 6 * i]), new Vector2(list[2 + 6 * i], -list[3 + 6 * i]), new Vector2(list[4 + 6 * i], -list[5 + 6 * i])); break;
case PathSegment.Type.Quadratic: data[i] = new PathSegment(new Vector2(list[0 + 4 * i], -list[1 + 4 * i]), Vector2.zero, new Vector2(list[2 + 4 * i], -list[3 + 4 * i])); break;
case PathSegment.Type.CubicShort: data[i] = new PathSegment(Vector2.zero, new Vector2(list[0 + 4 * i], -list[1 + 4 * i]), new Vector2(list[2 + 4 * i], -list[3 + 4 * i])); break;
case PathSegment.Type.QuadraticShort: data[i] = new PathSegment(Vector2.zero, Vector2.zero, new Vector2(list[0 + 4 * i], -list[1 + 4 * i])); break;
}
}
return data;
}
private string EncodePath(SplineDefinition definition, Axis ax)
{
string text = "M";
for (int i = 0; i < definition.pointCount; i++)
{
SplinePoint p = definition.points[i];
Vector3 tangent = MapPoint(p.tangent, ax);
Vector3 position = MapPoint(p.position, ax);
if (i == 0) text += position.x + "," + position.y;
else
{
SplinePoint lp = definition.points[i - 1];
Vector3 tangent2 = MapPoint(lp.tangent2, ax);
text += "C" + tangent2.x + "," + tangent2.y + "," + tangent.x + "," + tangent.y + "," + position.x + "," + position.y;
}
}
if (definition.closed) text += "z";
return text;
}
private string EncodePolygon(SplineDefinition definition, Axis ax)
{
string text = "";
for (int i = 0; i < definition.pointCount; i++)
{
Vector3 position = MapPoint(definition.points[i].position, ax);
if (text != "") text += ",";
text += position.x + "," + position.y;
}
return text;
}
private string GetAttributeContent(XmlNode node, string attributeName)
{
for (int j = 0; j < node.Attributes.Count; j++)
{
if (node.Attributes[j].Name == attributeName) return node.Attributes[j].InnerText;
}
return "ERROR";
}
}
}

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,337 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.IO
{
public class SplineParser
{
protected string fileName = "";
public string name
{
get { return fileName; }
}
private System.Globalization.CultureInfo culture = new System.Globalization.CultureInfo("en-US");
private System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Any;
internal class Transformation
{
protected static Matrix4x4 matrix = new Matrix4x4();
internal static void ResetMatrix()
{
matrix.SetTRS(Vector3.zero, Quaternion.identity, Vector3.one);
}
internal virtual void Push()
{
}
internal static void Apply(SplinePoint[] points)
{
for (int i = 0; i < points.Length; i++)
{
SplinePoint p = points[i];
p.position = matrix.MultiplyPoint(p.position);
p.tangent = matrix.MultiplyPoint(p.tangent);
p.tangent2 = matrix.MultiplyPoint(p.tangent2);
points[i] = p;
}
}
}
internal class Translate : Transformation
{
private Vector2 offset = Vector2.zero;
public Translate(Vector2 o)
{
offset = o;
}
internal override void Push()
{
Matrix4x4 translate = new Matrix4x4();
translate.SetTRS(new Vector2(offset.x, -offset.y), Quaternion.identity, Vector3.one);
matrix = matrix * translate;
}
}
internal class Rotate : Transformation
{
private float angle = 0f;
public Rotate(float a)
{
angle = a;
}
internal override void Push()
{
Matrix4x4 rotate = new Matrix4x4();
rotate.SetTRS(Vector3.zero, Quaternion.AngleAxis(angle, Vector3.back), Vector3.one);
matrix = matrix * rotate;
}
}
internal class Scale : Transformation
{
private Vector2 multiplier = Vector2.one;
public Scale(Vector2 s)
{
multiplier = s;
}
internal override void Push()
{
Matrix4x4 scale = new Matrix4x4();
scale.SetTRS(Vector3.zero, Quaternion.identity, multiplier);
matrix = matrix * scale;
}
}
internal class SkewX : Transformation
{
private float amount = 0f;
public SkewX(float a)
{
amount = a;
}
internal override void Push()
{
Matrix4x4 skew = new Matrix4x4();
skew[0, 0] = 1.0f;
skew[1, 1] = 1.0f;
skew[2, 2] = 1.0f;
skew[3, 3] = 1.0f;
skew[0, 1] = Mathf.Tan(-amount * Mathf.Deg2Rad);
matrix = matrix * skew;
}
}
internal class SkewY : Transformation
{
private float amount = 0f;
public SkewY(float a)
{
amount = a;
}
internal override void Push()
{
Matrix4x4 skew = new Matrix4x4();
skew[0, 0] = 1.0f;
skew[1, 1] = 1.0f;
skew[2, 2] = 1.0f;
skew[3, 3] = 1.0f;
skew[1, 0] = Mathf.Tan(-amount * Mathf.Deg2Rad);
matrix = matrix *skew;
}
}
internal class MatrixTransform : Transformation
{
private Matrix4x4 transformMatrix = new Matrix4x4();
public MatrixTransform(float a, float b, float c, float d, float e, float f)
{
transformMatrix.SetRow(0, new Vector4(a, c, 0f, e));
transformMatrix.SetRow(1, new Vector4(b, d, 0f, -f));
transformMatrix.SetRow(2, new Vector4(0f, 0f, 1f, 0f));
transformMatrix.SetRow(3, new Vector4(0f, 0f, 0f, 1f));
}
internal override void Push()
{
matrix = matrix * transformMatrix;
}
}
internal class SplineDefinition
{
internal string name = "";
internal Spline.Type type = Spline.Type.Linear;
internal List<SplinePoint> points = new List<SplinePoint>();
internal bool closed = false;
internal int pointCount
{
get { return points.Count; }
}
internal Vector3 position = Vector3.zero;
internal Vector3 tangent = Vector3.zero;
internal Vector3 tangent2 = Vector3.zero;
internal Vector3 normal = Vector3.back;
internal float size = 1f;
internal Color color = Color.white;
internal SplineDefinition(string n, Spline.Type t)
{
name = n;
type = t;
}
internal SplineDefinition(string n, Spline spline)
{
name = n;
type = spline.type;
closed = spline.isClosed;
points = new List<SplinePoint>(spline.points);
}
internal SplinePoint GetLastPoint()
{
if (points.Count == 0) return new SplinePoint();
return points[points.Count - 1];
}
internal void SetLastPoint(SplinePoint point)
{
if (points.Count == 0) return;
points[points.Count - 1] = point;
}
internal void CreateClosingPoint()
{
SplinePoint p = new SplinePoint(points[0]);
points.Add(p);
}
internal void CreateSmooth()
{
points.Add(new SplinePoint(position, tangent, normal, size, color));
}
internal void CreateBroken()
{
SplinePoint point = new SplinePoint(new SplinePoint(position, tangent, normal, size, color));
point.type = SplinePoint.Type.Broken;
point.SetTangent2Position(point.position);
point.normal = normal;
point.color = color;
point.size = size;
points.Add(point);
}
internal void CreateLinear()
{
tangent = position;
CreateSmooth();
}
internal SplineComputer CreateSplineComputer(Vector3 position, Quaternion rotation)
{
GameObject go = new GameObject(name);
go.transform.position = position;
go.transform.rotation = rotation;
SplineComputer computer = go.AddComponent<SplineComputer>();
#if UNITY_EDITOR
if(Application.isPlaying) computer.ResampleTransform();
#endif
computer.type = type;
if(closed)
{
if (points[0].type == SplinePoint.Type.Broken) points[0].SetTangentPosition(GetLastPoint().tangent2);
}
computer.SetPoints(points.ToArray(), SplineComputer.Space.Local);
if (closed) computer.Close();
return computer;
}
internal Spline CreateSpline()
{
Spline spline = new Spline(type);
spline.points = points.ToArray();
if (closed) spline.Close();
return spline;
}
internal void Transform(List<Transformation> trs)
{
SplinePoint[] p = points.ToArray();
Transformation.ResetMatrix();
foreach(Transformation t in trs) t.Push();
Transformation.Apply(p);
for (int i = 0; i < p.Length; i++) points[i] = p[i];
SplinePoint[] debugPoints = new SplinePoint[1];
debugPoints[0] = new SplinePoint();
Transformation.Apply(debugPoints);
}
}
internal SplineDefinition buffer = null;
internal Vector2[] ParseVector2(string coord)
{
List<float> list = ParseFloatArray(coord.Substring(1));
int count = list.Count / 2;
if (count == 0)
{
Debug.Log("Error in " + coord);
return new Vector2[] { Vector2.zero };
}
Vector2[] vectors = new Vector2[count];
for (int i = 0; i < count; i++)
{
vectors[i] = new Vector2(list[0 + i * 2], -list[1 + i * 2]);
}
return vectors;
}
internal float[] ParseFloat(string coord)
{
List<float> list = ParseFloatArray(coord.Substring(1));
if (list.Count < 1)
{
Debug.Log("Error in " + coord);
return new float[] { 0f };
}
return list.ToArray();
}
internal List<float> ParseFloatArray(string content)
{
string accumulated = "";
List<float> list = new List<float>();
foreach (char c in content)
{
if (c == ',' || c == '-' || char.IsWhiteSpace(c) || (accumulated.Contains(".") && c == '.'))
{
if (!IsWHiteSpace(accumulated))
{
float parsed = 0f;
float.TryParse(accumulated, style, culture, out parsed);
list.Add(parsed);
accumulated = "";
if (c == '-') accumulated = "-";
if (c == '.') accumulated = "0.";
continue;
}
}
if (!char.IsWhiteSpace(c)) accumulated += c;
}
if (!IsWHiteSpace(accumulated))
{
float p = 0f;
float.TryParse(accumulated, style, culture, out p);
list.Add(p);
}
return list;
}
public bool IsWHiteSpace(string s)
{
foreach (char c in s)
{
if (!char.IsWhiteSpace(c))
{
return false;
}
}
return true;
}
}
}

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,36 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class Capsule : SplinePrimitive
{
public float radius = 1f;
public float height = 2f;
public override Spline.Type GetSplineType()
{
return Spline.Type.Bezier;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(6, SplinePoint.Type.SmoothMirrored);
points[0].position = Vector3.right / 2f * radius + Vector3.up * height * 0.5f;
points[0].SetTangentPosition(points[0].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[1].position = Vector3.up / 2f * radius + Vector3.up * height * 0.5f;
points[1].SetTangentPosition(points[1].position + Vector3.right * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[2].position = Vector3.left / 2f * radius + Vector3.up * height * 0.5f;
points[2].SetTangentPosition(points[2].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[3].position = Vector3.left / 2f * radius + Vector3.down * height * 0.5f;
points[3].SetTangentPosition(points[3].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[4].position = Vector3.down / 2f * radius + Vector3.down * height * 0.5f;
points[4].SetTangentPosition(points[4].position + Vector3.left * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
points[5].position = Vector3.right / 2f * radius + Vector3.down * height * 0.5f;
points[5].SetTangentPosition(points[5].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 3f * radius);
}
}
}

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,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class Ellipse : SplinePrimitive
{
public float xRadius = 1f;
public float yRadius = 1f;
public override Spline.Type GetSplineType()
{
return Spline.Type.Bezier;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(4, SplinePoint.Type.SmoothMirrored);
points[0].position = Vector3.up * yRadius;
points[0].SetTangentPosition(points[0].position + Vector3.right * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * xRadius);
points[1].position = Vector3.left * xRadius;
points[1].SetTangentPosition(points[1].position + Vector3.up * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
points[2].position = Vector3.down * yRadius;
points[2].SetTangentPosition(points[2].position + Vector3.left * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * xRadius);
points[3].position = Vector3.right * xRadius;
points[3].SetTangentPosition(points[3].position + Vector3.down * 2 * (Mathf.Sqrt(2f) - 1f) / 1.5f * yRadius);
}
}
}

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.up * length * 0.5f;
for (int i = 0; i < points.Length; i++)
{
points[i].position = origin + Vector3.up * 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,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class Ngon : SplinePrimitive
{
public float radius = 1f;
public int sides = 3;
public override Spline.Type GetSplineType()
{
return Spline.Type.Linear;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(sides, SplinePoint.Type.SmoothMirrored);
for (int i = 0; i < sides; i++)
{
float percent = (float)i / sides;
Vector3 pos = Quaternion.AngleAxis(360f * percent, Vector3.forward) * Vector3.up * radius;
points[i].SetPosition(pos);
}
}
}
}

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,27 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class Rectangle : SplinePrimitive
{
public Vector2 size = Vector2.one;
public override Spline.Type GetSplineType()
{
return Spline.Type.Linear;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(4, SplinePoint.Type.SmoothMirrored);
points[0].position = points[0].tangent = Vector3.up / 2f * size.y + Vector3.left / 2f * size.x;
points[1].position = points[1].tangent = Vector3.up / 2f * size.y + Vector3.right / 2f * size.x;
points[2].position = points[2].tangent = Vector3.down / 2f * size.y + Vector3.right / 2f * size.x;
points[3].position = points[3].tangent = Vector3.down / 2f * size.y + Vector3.left / 2f * size.x;
}
}
}

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,45 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class RoundedRectangle : SplinePrimitive
{
public Vector2 size = Vector2.one;
public float xRadius = 0.25f;
public float yRadius = 0.25f;
public override Spline.Type GetSplineType()
{
return Spline.Type.Bezier;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(8, SplinePoint.Type.Broken);
Vector2 edgeSize = size - new Vector2(xRadius, yRadius) * 2f;
points[0].SetPosition(Vector3.up / 2f * edgeSize.y + Vector3.left / 2f * size.x);
points[1].SetPosition(Vector3.up / 2f * size.y + Vector3.left / 2f * edgeSize.x);
points[2].SetPosition(Vector3.up / 2f * size.y + Vector3.right / 2f * edgeSize.x);
points[3].SetPosition(Vector3.up / 2f * edgeSize.y + Vector3.right / 2f * size.x);
points[4].SetPosition(Vector3.down / 2f * edgeSize.y + Vector3.right / 2f * size.x);
points[5].SetPosition(Vector3.down / 2f * size.y + Vector3.right / 2f * edgeSize.x);
points[6].SetPosition(Vector3.down / 2f * size.y + Vector3.left / 2f * edgeSize.x);
points[7].SetPosition(Vector3.down / 2f * edgeSize.y + Vector3.left / 2f * size.x);
float xRad = 2f * (Mathf.Sqrt(2f) - 1f) / 3f * xRadius * 2f;
float yRad = 2f * (Mathf.Sqrt(2f) - 1f) / 3f * yRadius * 2f;
points[0].SetTangent2Position(points[0].position + Vector3.up * yRad);
points[1].SetTangentPosition(points[1].position + Vector3.left * xRad);
points[2].SetTangent2Position(points[2].position + Vector3.right * xRad);
points[3].SetTangentPosition(points[3].position + Vector3.up * yRad);
points[4].SetTangent2Position(points[4].position + Vector3.down * yRad);
points[5].SetTangentPosition(points[5].position + Vector3.right * xRad);
points[6].SetTangent2Position(points[6].position + Vector3.left * xRad);
points[7].SetTangentPosition(points[7].position + Vector3.down * yRad);
}
}
}

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.CatmullRom;
}
public bool GetIsClosed()
{
return closed;
}
void ApplyOffset()
{
Quaternion freeRot = Quaternion.Euler(rotation);
if (is2D) freeRot = Quaternion.AngleAxis(-rotation.z, Vector3.forward) * Quaternion.AngleAxis(90f, Vector3.right);
for (int i = 0; i < points.Length; i++)
{
points[i].position = freeRot * points[i].position;
points[i].tangent = freeRot * points[i].tangent;
points[i].tangent2 = freeRot * points[i].tangent2;
points[i].normal = freeRot * points[i].normal;
}
for (int i = 0; i < points.Length; i++) points[i].SetPosition(points[i].position + offset);
}
protected void CreatePoints(int count, SplinePoint.Type type)
{
if (points.Length != count) points = new SplinePoint[count];
for (int i = 0; i < points.Length; i++)
{
points[i].type = type;
points[i].normal = Vector3.up;
points[i].color = Color.white;
points[i].size = 1f;
}
}
}
}

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,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Dreamteck.Splines.Primitives
{
public class Star : SplinePrimitive
{
public float radius = 1f;
public float depth = 0.5f;
public int sides = 5;
public override Spline.Type GetSplineType()
{
return Spline.Type.Linear;
}
protected override void Generate()
{
base.Generate();
closed = true;
CreatePoints(sides * 2, SplinePoint.Type.SmoothMirrored);
float innerRadius = radius * depth;
for (int i = 0; i < sides * 2; i++)
{
float percent = (float)i / (float)(sides * 2);
Vector3 pos = Quaternion.AngleAxis(180 + 360f * percent, Vector3.forward) * Vector3.up * ((float)i % 2f == 0 ? radius : innerRadius);
points[i].SetPosition(pos);
}
}
}
}

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,643 @@
namespace Dreamteck.Splines
{
using UnityEngine;
[System.Serializable]
public class SampleCollection
{
[HideInInspector]
[UnityEngine.Serialization.FormerlySerializedAs("samples")]
public SplineSample[] samples = new SplineSample[0];
private static bool __useModifier = false;
private static ISampleModifier __modifier = null;
public int length
{
get { return samples.Length; }
}
public int[] optimizedIndices = new int[0];
bool hasSamples
{
get { return samples.Length > 0; }
}
public SplineComputer.SampleMode sampleMode = SplineComputer.SampleMode.Default;
private SplineSample _workSample = new SplineSample();
public SampleCollection()
{
}
public SampleCollection(SampleCollection input)
{
samples = input.samples;
optimizedIndices = input.optimizedIndices;
sampleMode = input.sampleMode;
}
public int GetClippedSampleCount(double clipFrom, double clipTo, out int startIndex, out int endIndex)
{
startIndex = endIndex = 0;
if (sampleMode == SplineComputer.SampleMode.Default)
{
startIndex = DMath.FloorInt((samples.Length - 1) * clipFrom);
endIndex = DMath.CeilInt((samples.Length - 1) * clipTo);
}
else
{
double clipFromLerp = 0.0, clipToLerp = 0.0;
GetSamplingValues(clipFrom, out startIndex, out clipFromLerp);
GetSamplingValues(clipTo, out endIndex, out clipToLerp);
if (clipToLerp > 0.0 && endIndex < samples.Length - 1) endIndex++;
}
if (clipTo < clipFrom) //Handle looping segments
{
int toSamples = endIndex + 1;
int fromSamples = samples.Length - startIndex;
return toSamples + fromSamples;
}
return endIndex - startIndex + 1;
}
public void GetSamplingValues(double percent, out int sampleIndex, out double lerp)
{
lerp = 0.0;
if (sampleMode == SplineComputer.SampleMode.Optimized)
{
double indexValue = percent * (optimizedIndices.Length - 1);
int index = DMath.FloorInt(indexValue);
sampleIndex = optimizedIndices[index];
double lerpPercent = 0.0;
if (index < optimizedIndices.Length - 1)
{
//Percent 0-1 between the sampleIndex and the next sampleIndex
double indexLerp = indexValue - index;
double sampleIndexPercent = (double)index / (optimizedIndices.Length - 1);
double nextSampleIndexPercent = (double)(index + 1) / (optimizedIndices.Length - 1);
//Percent 0-1 of the sample between the sampleIndices' percents
lerpPercent = DMath.Lerp(sampleIndexPercent, nextSampleIndexPercent, indexLerp);
}
if (sampleIndex < samples.Length - 1)
{
lerp = DMath.InverseLerp(samples[sampleIndex].percent, samples[sampleIndex + 1].percent, lerpPercent);
}
return;
}
sampleIndex = DMath.FloorInt(percent * (samples.Length - 1));
lerp = (samples.Length - 1) * percent - sampleIndex;
}
/// <summary>
/// Same as Spline.EvaluatePosition but the result is transformed by the computer's transform
/// </summary>
/// <param name="percent">Evaluation percent</param>
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
/// <returns></returns>
public Vector3 EvaluatePosition(double percent)
{
if (!hasSamples) return Vector3.zero;
int index;
double lerp;
GetSamplingValues(percent, out index, out lerp);
if (lerp > 0.0)
{
return Vector3.Lerp(samples[index].position, samples[index + 1].position, (float)lerp);
}
return samples[index].position;
}
/// <summary>
/// Same as Spline.Evaluate but the result is transformed by the computer's transform
/// </summary>
/// <param name="percent">Evaluation percent</param>
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
/// <returns></returns>
public SplineSample Evaluate(double percent)
{
SplineSample result = new SplineSample();
Evaluate(percent, ref result);
return result;
}
/// <summary>
/// Evaluates the sample collection and transforms the result by the <see cref="localToWorldMatrix"/>
/// </summary>
/// <param name="result"></param>
/// <param name="percent"></param>
public void Evaluate(double percent, ref SplineSample result)
{
if (!hasSamples)
{
result = new SplineSample();
return;
}
int index;
double lerp;
GetSamplingValues(percent, out index, out lerp);
if (lerp > 0.0)
{
SplineSample.Lerp(ref samples[index], ref samples[index + 1], lerp, ref result);
}
else
{
result.FastCopy(ref samples[index]);
}
}
/// <summary>
/// Evaluates the sample collection and transforms the results by the <see cref="localToWorldMatrix"/>
/// </summary>
/// <param name="from">Start position [0-1]</param>
/// <param name="to">Target position [from-1]</param>
/// <returns></returns>
public void Evaluate(ref SplineSample[] results, double from = 0.0, double to = 1.0)
{
if (!hasSamples)
{
results = new SplineSample[0];
return;
}
Spline.FormatFromTo(ref from, ref to);
int fromIndex, toIndex;
double lerp;
GetSamplingValues(from, out fromIndex, out lerp);
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < samples.Length - 1)
{
toIndex++;
}
int clippedIterations = toIndex - fromIndex + 1;
if (results == null)
{
results = new SplineSample[clippedIterations];
}
else if (results.Length != clippedIterations)
{
results = new SplineSample[clippedIterations];
}
results[0] = Evaluate(from);
results[results.Length - 1] = Evaluate(to);
for (int i = 1; i < results.Length - 1; i++)
{
results[i].FastCopy(ref samples[i + fromIndex]);
}
}
/// <summary>
/// Same as Spline.EvaluatePositions but the results are transformed by the computer's transform
/// </summary>
/// <param name="from">Start position [0-1]</param>
/// <param name="to">Target position [from-1]</param>
/// <returns></returns>
public void EvaluatePositions(ref Vector3[] positions, double from = 0.0, double to = 1.0)
{
if (!hasSamples)
{
positions = new Vector3[0];
return;
}
Spline.FormatFromTo(ref from, ref to);
int fromIndex, toIndex;
double lerp;
GetSamplingValues(from, out fromIndex, out lerp);
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < samples.Length - 1)
{
toIndex++;
}
int clippedIterations = toIndex - fromIndex + 1;
if (positions == null)
{
positions = new Vector3[clippedIterations];
}
else if (positions.Length != clippedIterations)
{
positions = new Vector3[clippedIterations];
}
positions[0] = EvaluatePosition(from);
positions[positions.Length - 1] = EvaluatePosition(to);
for (int i = 1; i < positions.Length - 1; i++)
{
positions[i] = samples[i + fromIndex].position;
}
}
/// <summary>
/// Returns the percent from the spline at a given distance from the start point
/// </summary>
/// <param name="start">The start point</param>
/// <param name="distance">The distance to travel</param>
/// <param name="direction">The direction towards which to move</param>
/// <returns></returns>
public double Travel(double start, float distance, Spline.Direction direction, out float moved, double clipFrom = 0.0, double clipTo = 1.0)
{
moved = 0f;
if (!hasSamples) return 0.0;
if (direction == Spline.Direction.Forward && start >= 1.0) return clipTo;
else if (direction == Spline.Direction.Backward && start <= 0.0) return clipFrom;
double lastPercent = start;
if (distance == 0f) return lastPercent;
Vector3 lastPos = EvaluatePosition(start);
int sampleIndex;
double lerp;
GetSamplingValues(lastPercent, out sampleIndex, out lerp);
if (direction == Spline.Direction.Forward && lerp > 0.0) sampleIndex++;
float lastDistance = 0f;
int minIndex = 0;
int maxIndex = samples.Length - 1;
bool samplesAreLooped = clipTo < clipFrom;
if (samplesAreLooped)
{
GetSamplingValues(clipFrom, out minIndex, out lerp);
GetSamplingValues(clipTo, out maxIndex, out lerp);
if (lerp > 0.0) maxIndex++;
}
while (moved < distance)
{
Vector3 transformedPos = samples[sampleIndex].position;
lastDistance = Vector3.Distance(transformedPos, lastPos);
moved += lastDistance;
if (moved >= distance) break;
lastPos = transformedPos;
lastPercent = samples[sampleIndex].percent;
if (direction == Spline.Direction.Forward)
{
if (sampleIndex == samples.Length - 1)
{
if (samplesAreLooped)
{
lastPos = samples[0].position;
lastPercent = samples[0].percent;
sampleIndex = 1;
}
else break;
}
if (samplesAreLooped && sampleIndex == maxIndex) break;
sampleIndex++;
}
else
{
if (sampleIndex == 0)
{
if (samplesAreLooped)
{
lastPos = samples[samples.Length - 1].position;
lastPercent = samples[samples.Length - 1].percent;
sampleIndex = samples.Length - 2;
}
else break;
}
if (samplesAreLooped && sampleIndex == minIndex) break;
sampleIndex--;
}
}
float moveExcess = 0f;
if (moved > distance)
{
moveExcess = moved - distance;
}
double lerpPercent = 0.0;
if(lastDistance > 0.0)
{
lerpPercent = moveExcess / lastDistance;
}
double p = DMath.Lerp(lastPercent, samples[sampleIndex].percent, 1f - lerpPercent);
moved -= moveExcess;
return p;
}
/// <summary>
/// Returns the percent from the spline at a given distance from the start point while applying a local <paramref name="offset"/> to each sample
/// The offset is multiplied by the sample sizes
/// </summary>
/// <param name="start">The start point</param>
/// /// <param name="distance">The distance to travel</param>
/// <param name="direction">The direction towards which to move</param>
/// <returns></returns>
public double TravelWithOffset(double start, float distance, Spline.Direction direction, Vector3 offset, out float moved, double clipFrom = 0.0, double clipTo = 1.0)
{
moved = 0f;
if (!hasSamples) return 0.0;
if (direction == Spline.Direction.Forward && start >= 1.0) return clipTo;
else if (direction == Spline.Direction.Backward && start <= 0.0) return clipFrom;
double lastPercent = start;
if (distance == 0f) return lastPercent;
Evaluate(start, ref _workSample);
Vector3 lastPos = _workSample.position + _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
int sampleIndex;
double lerp;
GetSamplingValues(lastPercent, out sampleIndex, out lerp);
if (direction == Spline.Direction.Forward && lerp > 0.0) sampleIndex++;
float lastDistance = 0f;
int minIndex = 0;
int maxIndex = length - 1;
bool samplesAreLooped = clipTo < clipFrom;
if (samplesAreLooped)
{
GetSamplingValues(clipFrom, out minIndex, out lerp);
GetSamplingValues(clipTo, out maxIndex, out lerp);
if (lerp > 0.0) maxIndex++;
}
while (moved < distance)
{
Vector3 newPos = samples[sampleIndex].position +
samples[sampleIndex].up * (offset.y * samples[sampleIndex].size) +
samples[sampleIndex].right * (offset.x * samples[sampleIndex].size) +
samples[sampleIndex].forward * (offset.z * samples[sampleIndex].size);
lastDistance = Vector3.Distance(newPos, lastPos);
moved += lastDistance;
if (moved >= distance)
{
break;
}
lastPos = newPos;
lastPercent = samples[sampleIndex].percent;
if (direction == Spline.Direction.Forward)
{
if (sampleIndex == length - 1)
{
if (samplesAreLooped)
{
lastPos = samples[0].position +
samples[0].up * (offset.y * samples[0].size) +
samples[0].right * (offset.x * samples[0].size) +
samples[0].forward * (offset.z * samples[0].size);
lastPercent = samples[0].percent;
sampleIndex = 1;
}
else break;
}
if (samplesAreLooped && sampleIndex == maxIndex) break;
sampleIndex++;
}
else
{
if (sampleIndex == 0)
{
if (samplesAreLooped)
{
int lastIndex = samples.Length - 1;
lastPos = samples[lastIndex].position +
samples[lastIndex].up * (offset.y * samples[lastIndex].size) +
samples[lastIndex].right * (offset.x * samples[lastIndex].size) +
samples[lastIndex].forward * (offset.z * samples[lastIndex].size);
lastPercent = samples[lastIndex].percent;
sampleIndex = samples.Length - 2;
}
else break;
}
if (samplesAreLooped && sampleIndex == minIndex) break;
sampleIndex--;
}
}
float moveExcess = 0f;
if (moved > distance)
{
moveExcess = moved - distance;
}
double p = DMath.Lerp(lastPercent, samples[sampleIndex].percent, 1f - moveExcess / lastDistance);
moved -= moveExcess;
return p;
}
public double Travel(double start, float distance, Spline.Direction direction = Spline.Direction.Forward)
{
float moved;
return Travel(start, distance, direction, out moved);
}
private Vector3 GetModifiedPosition(ref SplineSample sample)
{
if (!__useModifier) return sample.position;
return __modifier.GetModifiedSamplePosition(ref sample);
}
/// <summary>
/// Same as Spline.Project but the point is transformed by the computer's transform.
/// </summary>
/// <param name="position">Point in space</param>
/// <param name="subdivide">Subdivisions default: 4</param>
/// <param name="from">Sample from [0-1] default: 0f</param>
/// <param name="to">Sample to [0-1] default: 1f</param>
/// <param name="mode">Mode to use the method in. Cached uses the cached samples while Calculate is more accurate but heavier</param>
/// <param name="subdivisions">Subdivisions for the Calculate mode. Don't assign if not using Calculated mode.</param>
/// <returns></returns>
public void Project(Vector3 position, int controlPointCount, ref SplineSample result, double from = 0.0, double to = 1.0, ISampleModifier modifier = null)
{
if (!hasSamples) return;
if (samples.Length == 1)
{
result.FastCopy(ref samples[0]);
return;
}
__useModifier = modifier != null;
__modifier = modifier;
Spline.FormatFromTo(ref from, ref to);
//First make a very rough sample of the from-to region
int steps = (controlPointCount - 1) * 4; //Sampling four points per segment is enough to find the closest point range
int step = samples.Length / steps;
if (step < 1) step = 1;
float minDist = (position - GetModifiedPosition(ref samples[0])).sqrMagnitude;
int fromIndex = 0;
int toIndex = samples.Length - 1;
double lerp;
if (from != 0.0) GetSamplingValues(from, out fromIndex, out lerp);
if (to != 1.0)
{
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < samples.Length - 1) toIndex++;
}
int checkFrom = fromIndex;
int checkTo = toIndex;
//Find the closest point range which will be checked in detail later
for (int i = fromIndex; i < toIndex; i += step)
{
if (i >= toIndex) i = toIndex-1;
Vector3 pos1 = GetModifiedPosition(ref samples[i]);
Vector3 pos2 = GetModifiedPosition(ref samples[Mathf.Min(i + step, toIndex)]);
Vector3 projected = LinearAlgebraUtility.ProjectOnLine(pos1, pos2, position);
float dist = (position - projected).sqrMagnitude;
if (dist < minDist)
{
minDist = dist;
checkFrom = Mathf.Max(i - step, 0);
checkTo = Mathf.Min(i + step, samples.Length - 1);
}
if (i == toIndex) break;
}
minDist = (position - samples[checkFrom].position).sqrMagnitude;
int index = checkFrom;
//Find the closest result within the range
for (int i = checkFrom + 1; i <= checkTo; i++)
{
float dist = (position - GetModifiedPosition(ref samples[i])).sqrMagnitude;
if (dist < minDist)
{
minDist = dist;
index = i;
}
}
//Project the point on the line between the two closest samples
int backIndex = index - 1;
if (backIndex < 0) backIndex = 0;
int frontIndex = index + 1;
if (frontIndex > samples.Length - 1) frontIndex = samples.Length - 1;
Vector3 backPos = GetModifiedPosition(ref samples[backIndex]);
Vector3 currentPos = GetModifiedPosition(ref samples[index]);
Vector3 frontPos = GetModifiedPosition(ref samples[frontIndex]);
Vector3 back = LinearAlgebraUtility.ProjectOnLine(backPos, currentPos, position);
Vector3 front = LinearAlgebraUtility.ProjectOnLine(currentPos, frontPos, position);
float backLength = (currentPos - backPos).magnitude;
float frontLength = (currentPos - frontPos).magnitude;
float backProjectDist = (back - backPos).magnitude;
float frontProjectDist = (front - frontPos).magnitude;
if (backIndex < index && index < frontIndex)
{
if ((position - back).sqrMagnitude < (position - front).sqrMagnitude)
{
SplineSample.Lerp(ref samples[backIndex], ref samples[index], backProjectDist / backLength, ref result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
}
else
{
SplineSample.Lerp(ref samples[frontIndex], ref samples[index], frontProjectDist / frontLength, ref result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
}
}
else if (backIndex < index)
{
SplineSample.Lerp(ref samples[backIndex], ref samples[index], backProjectDist / backLength, ref result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(backIndex), GetSamplePercent(index), backProjectDist / backLength);
}
else
{
SplineSample.Lerp(ref samples[frontIndex], ref samples[index], frontProjectDist / frontLength, ref result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(frontIndex), GetSamplePercent(index), frontProjectDist / frontLength);
}
if (from == 0.0 && to == 1.0 && result.percent < samples[1].percent) //Handle looped splines
{
Vector3 pos1 = GetModifiedPosition(ref samples[samples.Length - 1]);
Vector3 pos2 = GetModifiedPosition(ref samples[samples.Length - 2]);
Vector3 projected = LinearAlgebraUtility.ProjectOnLine(pos1, pos2, position);
if ((position - projected).sqrMagnitude < (position - result.position).sqrMagnitude)
{
double l = LinearAlgebraUtility.InverseLerp(pos1, pos2, projected);
SplineSample.Lerp(ref samples[samples.Length - 1], ref samples[samples.Length - 2], l, ref result);
if (sampleMode == SplineComputer.SampleMode.Uniform) result.percent = DMath.Lerp(GetSamplePercent(samples.Length - 1), GetSamplePercent(samples.Length - 2), l);
}
}
if (__useModifier)
{
modifier.ApplySampleModifiers(ref result);
}
}
private double GetSamplePercent(int sampleIndex)
{
if (sampleMode == SplineComputer.SampleMode.Optimized)
{
return samples[optimizedIndices[sampleIndex]].percent;
}
return (double)sampleIndex / (samples.Length - 1);
}
/// <summary>
/// Same as Spline.CalculateLength but this takes the computer's transform into account when calculating the length.
/// </summary>
/// <param name="from">Calculate from [0-1] default: 0f</param>
/// <param name="to">Calculate to [0-1] default: 1f</param>
/// <param name="resolution">Resolution [0-1] default: 1f</param>
/// <param name="address">Node address of junctions</param>
/// <returns></returns>
public float CalculateLength(double from = 0.0, double to = 1.0, bool preventInvert = true)
{
if (!hasSamples) return 0f;
Spline.FormatFromTo(ref from, ref to, preventInvert);
float length = 0f;
Vector3 lastPos = EvaluatePosition(from);
int fromIndex, toIndex;
double lerp;
GetSamplingValues(from, out fromIndex, out lerp);
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < this.length - 1)
{
toIndex++;
}
for (int i = fromIndex + 1; i < toIndex; i++)
{
Vector3 currentPos = samples[i].position;
length += Vector3.Distance(currentPos, lastPos);
lastPos = currentPos;
}
length += Vector3.Distance(EvaluatePosition(to), lastPos);
return length;
}
/// <summary>
/// Calculates the length between <paramref name="from"/> and <paramref name="to"/> with applied local offset to to the samples
/// The offset is multiplied by the sample sizes
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <param name="offset"></param>
/// <returns></returns>
public float CalculateLengthWithOffset(Vector3 offset, double from = 0.0, double to = 1.0)
{
if (!hasSamples) return 0f;
Spline.FormatFromTo(ref from, ref to);
float length = 0f;
Evaluate(from, ref _workSample);
Vector3 lastPos = _workSample.position + _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
int fromIndex, toIndex;
double lerp;
GetSamplingValues(from, out fromIndex, out lerp);
GetSamplingValues(to, out toIndex, out lerp);
if (lerp > 0.0 && toIndex < this.length - 1)
{
toIndex++;
}
for (int i = fromIndex + 1; i < toIndex; i++)
{
Vector3 newPos = samples[i].position + samples[i].up * (offset.y * samples[i].size) + samples[i].right * (offset.x * samples[i].size) + samples[i].forward * (offset.z * samples[i].size);
length += Vector3.Distance(newPos, lastPos);
lastPos = newPos;
}
Evaluate(to, ref _workSample);
_workSample.position += _workSample.up * (offset.y * _workSample.size) + _workSample.right * (offset.x * _workSample.size) + _workSample.forward * (offset.z * _workSample.size);
length += Vector3.Distance(_workSample.position, lastPos);
return length;
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

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,412 @@
using UnityEngine;
using UnityEngine.Serialization;
using System;
namespace Dreamteck.Splines{
[System.Serializable]
//A control point used by the SplineClass
public struct SplinePoint {
public enum Type {SmoothMirrored, Broken, SmoothFree};
public Type type
{
get { return _type; }
set
{
isDirty = _type != value;
_type = value;
if (value == Type.SmoothMirrored)
{
SmoothMirrorTangent2();
}
}
}
/// <summary>
/// Getting the value of isDirty will set the point not dirty
/// </summary>
[NonSerialized]
public bool isDirty;
[FormerlySerializedAs("type")]
[SerializeField]
[HideInInspector]
private Type _type;
[HideInInspector]
[FormerlySerializedAs("_position")]
public Vector3 position;
[HideInInspector]
[FormerlySerializedAs("_color")]
public Color color;
[HideInInspector]
[FormerlySerializedAs("_normal")]
public Vector3 normal;
[HideInInspector]
[FormerlySerializedAs("_size")]
public float size;
[HideInInspector]
[FormerlySerializedAs("_tangent")]
public Vector3 tangent;
[HideInInspector]
[FormerlySerializedAs("_tangent2")]
public Vector3 tangent2;
public static SplinePoint Lerp(SplinePoint a, SplinePoint b, float t)
{
SplinePoint result = a;
if (a.type == Type.Broken || b.type == Type.Broken) result.type = Type.Broken;
else if (a.type == Type.SmoothFree || b.type == Type.SmoothFree) result.type = Type.SmoothFree;
else result.type = Type.SmoothMirrored;
result.position = Vector3.Lerp(a.position, b.position, t);
GetInterpolatedTangents(a, b, t, ref result);
result.color = Color.Lerp(a.color, b.color, t);
result.size = Mathf.Lerp(a.size, b.size, t);
result.normal = Vector3.Slerp(a.normal, b.normal, t);
return result;
}
private static void GetInterpolatedTangents(SplinePoint a, SplinePoint b, float t, ref SplinePoint target)
{
Vector3 P0_1 = (1f - t) * a.position + t * a.tangent2;
Vector3 P1_2 = (1f - t) * a.tangent2 + t * b.tangent;
Vector3 P2_3 = (1f - t) * b.tangent + t * b.position;
Vector3 P01_12 = (1 - t) * P0_1 + t * P1_2;
Vector3 P12_23 = (1 - t) * P1_2 + t * P2_3;
target.tangent = P01_12;
target.tangent2 = P12_23;
}
public override bool Equals(object obj)
{
if(obj is SplinePoint)
{
return EqualsFast((SplinePoint)obj);
}
return false;
}
private bool EqualsFast(SplinePoint obj)
{
SplinePoint other = (SplinePoint)obj;
if (position != other.position) return false;
if (tangent != other.tangent) return false;
if (tangent2 != other.tangent2) return false;
if (normal != other.normal) return false;
if (_type != other._type) return false;
if (size != other.size) return false;
if (color != other.color) return false;
return true;
}
public static bool operator == (SplinePoint p1, SplinePoint p2)
{
return p1.EqualsFast(p2);
}
public static bool operator != (SplinePoint p1, SplinePoint p2)
{
return !p1.EqualsFast(p2);
}
public void SetPosition(Vector3 pos)
{
tangent -= position - pos;
tangent2 -= position - pos;
position = pos;
}
public void SetTangentPosition(Vector3 pos)
{
tangent = pos;
switch (_type)
{
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
case Type.SmoothFree: SmoothFreeTangent2(); break;
}
}
public void SetTangent2Position(Vector3 pos)
{
tangent2 = pos;
switch (_type)
{
case Type.SmoothMirrored: SmoothMirrorTangent(); break;
case Type.SmoothFree: SmoothFreeTangent(); break;
}
}
public SplinePoint(Vector3 p)
{
position = p;
tangent = p;
tangent2 = p;
color = Color.white;
normal = Vector3.up;
size = 1f;
_type = Type.SmoothMirrored;
isDirty = false;
SmoothMirrorTangent2();
}
public SplinePoint(Vector3 p, Vector3 t){
position = p;
tangent = t;
tangent2 = p + (p - t);
color = Color.white;
normal = Vector3.up;
size = 1f;
_type = Type.SmoothMirrored;
isDirty = false;
SmoothMirrorTangent2();
}
public SplinePoint(Vector3 pos, Vector3 tan, Vector3 nor, float s, Color col){
position = pos;
tangent = tan;
tangent2 = pos + (pos - tan);
normal = nor;
size = s;
color = col;
_type = Type.SmoothMirrored;
isDirty = false;
SmoothMirrorTangent2();
}
public SplinePoint(Vector3 pos, Vector3 tan, Vector3 tan2, Vector3 nor, float s, Color col)
{
position = pos;
tangent = tan;
tangent2 = tan2;
normal = nor;
size = s;
color = col;
_type = Type.Broken;
isDirty = false;
switch (_type)
{
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
case Type.SmoothFree: SmoothFreeTangent2(); break;
}
}
public SplinePoint(SplinePoint source)
{
position = source.position;
tangent = source.tangent;
tangent2 = source.tangent2;
color = source.color;
normal = source.normal;
size = source.size;
_type = source.type;
isDirty = false;
switch (_type)
{
case Type.SmoothMirrored: SmoothMirrorTangent2(); break;
case Type.SmoothFree: SmoothFreeTangent2(); break;
}
}
public void Flatten(LinearAlgebraUtility.Axis axis, float flatValue = 0f)
{
position = LinearAlgebraUtility.FlattenVector(position, axis, flatValue);
tangent = LinearAlgebraUtility.FlattenVector(tangent, axis, flatValue);
tangent2 = LinearAlgebraUtility.FlattenVector(tangent2, axis, flatValue);
switch (axis)
{
case LinearAlgebraUtility.Axis.X: normal = Vector3.right; break;
case LinearAlgebraUtility.Axis.Y: normal = Vector3.up; break;
case LinearAlgebraUtility.Axis.Z: normal = Vector3.forward; break;
}
}
public void SetPositionX(float value)
{
if(position.x != value)
{
isDirty = true;
}
position.x = value;
}
public void SetPositionY(float value)
{
if(position.y != value)
{
isDirty = true;
}
position.y = value;
}
public void SetPositionZ(float value)
{
if(position.z != value)
{
isDirty = true;
}
position.z = value;
}
public void SetTangentX(float value)
{
if(tangent.x != value)
{
isDirty = true;
}
tangent.x = value;
}
public void SetTangentY(float value)
{
if (tangent.y != value)
{
isDirty = true;
}
tangent.y = value;
}
public void SetTangentZ(float value)
{
if(tangent.z != value)
{
isDirty = true;
}
tangent.z = value;
}
public void SetTangent2X(float value)
{
if (tangent2.x != value)
{
isDirty = true;
}
tangent2.x = value;
}
public void SetTangent2Y(float value)
{
if (tangent2.y != value)
{
isDirty = true;
}
tangent2.y = value;
}
public void SetTangent2Z(float value)
{
if (tangent2.z != value)
{
isDirty = true;
}
tangent2.z = value;
}
public void SetNormalX(float value)
{
if (normal.x != value)
{
isDirty = true;
}
normal.x = value;
}
public void SetNormalY(float value)
{
if (normal.y != value)
{
isDirty = true;
}
normal.y = value;
}
public void SetNormalZ(float value)
{
if(normal.z != value)
{
isDirty = true;
}
normal.z = value;
}
public void SetColorR(float value)
{
if (color.r != value)
{
isDirty = true;
}
color.r = value;
}
public void SetColorG(float value)
{
if (color.g != value)
{
isDirty = true;
}
color.g = value;
}
public void SetColorB(float value)
{
if(color.b != value)
{
isDirty = true;
}
color.b = value;
}
public void SetColorA(float value)
{
if (color.a != value)
{
isDirty = true;
}
color.a = value;
}
private void SmoothMirrorTangent2()
{
tangent2 = position + (position - tangent);
isDirty = true;
}
private void SmoothMirrorTangent()
{
tangent = position + (position - tangent2);
isDirty = true;
}
private void SmoothFreeTangent2()
{
tangent2 = position + (position - tangent).normalized * (tangent2 - position).magnitude;
isDirty = true;
}
private void SmoothFreeTangent()
{
tangent = position + (position - tangent2).normalized * (tangent - position).magnitude;
isDirty = true;
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
hash *= hash * 23 + _type.GetHashCode();
hash = hash * 23 + position.GetHashCode();
hash = hash * 23 + normal.GetHashCode();
hash = hash * 23 + tangent.GetHashCode();
hash = hash * 23 + tangent2.GetHashCode();
hash = hash * 23 + color.GetHashCode();
hash = hash * 23 + size.GetHashCode();
return hash;
}
}
}
}

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,156 @@
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace Dreamteck.Splines
{
public static class SplinePrefs
{
private static bool loaded = false;
public static Spline.Direction duplicationDirection = Spline.Direction.Forward;
public static bool defaultAlwaysDraw = false;
public static SplineComputer.EditorUpdateMode defaultEditorUpdateMode = SplineComputer.EditorUpdateMode.Default;
public static bool defaultShowThickness = false;
public static bool default2D = false;
public static bool startInCreationMode = false;
public static SplineComputer.Space pointEditSpace = SplineComputer.Space.Local;
public static Color defaultColor = Color.white;
public static Color highlightColor = Color.white;
public static Color outlineColor = Color.black;
public static Color highlightContentColor = new Color(1f, 1f, 1f, 0.95f);
public static bool showPointNumbers = false;
public static SplineComputer.Space defaultComputerSpace = SplineComputer.Space.Local;
public static Spline.Type defaultType = Spline.Type.CatmullRom;
public static float createPointSize = 1f;
public static Color createPointColor = Color.white;
static SplinePrefs()
{
LoadPrefs();
}
#if UNITY_2019_1_OR_NEWER
[SettingsProvider]
public static SettingsProvider SplinesSettingsProvider()
{
SettingsProvider provider = new SettingsProvider("Dreamteck/Splines", SettingsScope.User)
{
label = "Splines",
guiHandler = (searchContext) =>
{
OnGUI();
},
keywords = new HashSet<string>(new[] { "Dreamteck", "Splines", "Path", "Curve"})
};
return provider;
}
#else
[PreferenceItem("DTK Splines")]
#endif
public static void OnGUI()
{
if (!loaded) LoadPrefs();
EditorGUILayout.LabelField("Newly created splines:", EditorStyles.boldLabel);
startInCreationMode = EditorGUILayout.Toggle("Start in Creation Mode", startInCreationMode);
defaultComputerSpace = (SplineComputer.Space)EditorGUILayout.EnumPopup("Space", defaultComputerSpace);
defaultType = (Spline.Type)EditorGUILayout.EnumPopup("Type", defaultType);
defaultAlwaysDraw = EditorGUILayout.Toggle("Always draw", defaultAlwaysDraw);
defaultEditorUpdateMode = (SplineComputer.EditorUpdateMode)EditorGUILayout.EnumPopup("Default Editor Update Mode", defaultEditorUpdateMode);
defaultShowThickness = EditorGUILayout.Toggle("Show thickness", defaultShowThickness);
default2D = EditorGUILayout.Toggle("2D Mode", default2D);
defaultColor = EditorGUILayout.ColorField("Spline color", defaultColor);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Newly created points:", EditorStyles.boldLabel);
createPointSize = EditorGUILayout.FloatField("Default Size", createPointSize);
createPointColor = EditorGUILayout.ColorField("Default Color", createPointColor);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Editor", EditorStyles.boldLabel);
outlineColor = EditorGUILayout.ColorField("Outline color", outlineColor);
highlightColor = EditorGUILayout.ColorField("Highlight color", highlightColor);
highlightContentColor = EditorGUILayout.ColorField("Highlight content color", highlightContentColor);
duplicationDirection = (Spline.Direction)EditorGUILayout.EnumPopup("Duplicate Direction", duplicationDirection);
showPointNumbers = EditorGUILayout.Toggle("Show point numbers", showPointNumbers);
if (GUILayout.Button("Use Defaults", GUILayout.Width(120)))
{
duplicationDirection = Spline.Direction.Forward;
defaultAlwaysDraw = false;
defaultEditorUpdateMode = SplineComputer.EditorUpdateMode.Default;
defaultShowThickness = false;
default2D = false;
startInCreationMode = true;
defaultColor = Color.white;
highlightColor = new Color(0.0313f, 0.737f, 0.796f, 1f);
outlineColor = Color.Lerp(defaultColor, Color.black, 0.65f);
highlightContentColor = new Color(1f, 1f, 1f, 0.95f);
showPointNumbers = false;
defaultComputerSpace = SplineComputer.Space.Local;
defaultType = Spline.Type.CatmullRom;
createPointSize = 1f;
createPointColor = Color.white;
SavePrefs();
}
if (GUI.changed) SavePrefs();
}
public static void LoadPrefs()
{
defaultAlwaysDraw = EditorPrefs.GetBool("Dreamteck.Splines.defaultAlwaysDraw", false);
defaultEditorUpdateMode = (SplineComputer.EditorUpdateMode) EditorPrefs.GetInt("Dreamteck.Splines.defaultEditorUpdateMode", 0);
defaultShowThickness = EditorPrefs.GetBool("Dreamteck.Splines.defaultShowThickness", false);
default2D = EditorPrefs.GetBool("Dreamteck.Splines.default2D", false);
startInCreationMode = EditorPrefs.GetBool("Dreamteck.Splines.startInCreationMode", true);
showPointNumbers = EditorPrefs.GetBool("Dreamteck.Splines.showPointNumbers", false);
pointEditSpace = (SplineComputer.Space)EditorPrefs.GetInt("Dreamteck.Splines.pointEditSpace", 1);
defaultColor = LoadColor("Dreamteck.Splines.defaultColor", Color.white);
highlightColor = LoadColor("Dreamteck.Splines.highlightColor", new Color(0f, 0.564f, 1f, 1f));
outlineColor = LoadColor("Dreamteck.Splines.outlineColor", Color.Lerp(defaultColor, Color.black, 0.65f));
highlightContentColor = LoadColor("Dreamteck.Splines.highlightContentColor", new Color(1f, 1f, 1f, 0.95f));
defaultComputerSpace = (SplineComputer.Space)EditorPrefs.GetInt("Dreamteck.Splines.defaultComputerSpace", 1);
defaultType = (Spline.Type)EditorPrefs.GetInt("Dreamteck.Splines.defaultType", 0);
duplicationDirection = (Spline.Direction)EditorPrefs.GetInt("Dreamteck.Splines.duplicationDirection", 0);
createPointSize = EditorPrefs.GetFloat("Dreamteck.Splines.createPointSize", 1f);
createPointColor = LoadColor("Dreamteck.Splines.createPointColor", Color.white);
loaded = true;
}
private static Color LoadColor(string name, Color defaultValue)
{
Color col = Color.white;
string colorString = EditorPrefs.GetString(name, defaultValue.r+":"+defaultValue.g+ ":" + defaultValue.b+ ":" + defaultValue.a);
string[] elements = colorString.Split(':');
if (elements.Length < 4) return col;
float r = 0f, g = 0f, b = 0f, a = 0f;
float.TryParse(elements[0], out r);
float.TryParse(elements[1], out g);
float.TryParse(elements[2], out b);
float.TryParse(elements[3], out a);
col = new Color(r, g, b, a);
return col;
}
public static void SavePrefs()
{
EditorPrefs.SetBool("Dreamteck.Splines.startInCreationMode", startInCreationMode);
EditorPrefs.SetBool("Dreamteck.Splines.defaultAlwaysDraw", defaultAlwaysDraw);
EditorPrefs.SetInt("Dreamteck.Splines.defaultEditorUpdateMode", (int)defaultEditorUpdateMode);
EditorPrefs.SetBool("Dreamteck.Splines.defaultShowThickness", defaultShowThickness);
EditorPrefs.SetBool("Dreamteck.Splines.default2D", default2D);
EditorPrefs.SetBool("Dreamteck.Splines.showPointNumbers", showPointNumbers);
EditorPrefs.SetInt("Dreamteck.Splines.pointEditSpace", (int)pointEditSpace);
EditorPrefs.SetString("Dreamteck.Splines.defaultColor", defaultColor.r+ ":" + defaultColor.g+ ":" + defaultColor.b+ ":" + defaultColor.a);
EditorPrefs.SetString("Dreamteck.Splines.highlightColor", highlightColor.r + ":" + highlightColor.g + ":" + highlightColor.b + ":" + highlightColor.a);
EditorPrefs.SetString("Dreamteck.Splines.outlineColor", outlineColor.r + ":" + outlineColor.g + ":" + outlineColor.b + ":" + outlineColor.a);
EditorPrefs.SetString("Dreamteck.Splines.highlightContentColor", highlightContentColor.r + ":" + highlightContentColor.g + ":" + highlightContentColor.b + ":" + highlightContentColor.a);
EditorPrefs.SetInt("Dreamteck.Splines.defaultComputerSpace", (int)defaultComputerSpace);
EditorPrefs.SetInt("Dreamteck.Splines.defaultType", (int)defaultType);
EditorPrefs.SetInt("Dreamteck.Splines.duplicationDirection", (int)duplicationDirection);
EditorPrefs.SetFloat("Dreamteck.Splines.createPointSize", createPointSize);
EditorPrefs.SetString("Dreamteck.Splines.createPointColor", createPointColor.r + ":" + createPointColor.g + ":" + createPointColor.b + ":" + createPointColor.a);
}
}
}
#endif

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,103 @@
using UnityEngine;
using Dreamteck;
namespace Dreamteck.Splines{
[System.Serializable]
public struct SplineSample {
public Vector3 position;
public Vector3 up;
public Vector3 forward;
public Color color;
public float size;
public double percent;
public void FastCopy(ref SplineSample sample)
{
position = sample.position;
up = sample.up;
forward = sample.forward;
color = sample.color;
size = sample.size;
percent = sample.percent;
}
public Quaternion rotation
{
get {
if (up == forward)
{
if (up == Vector3.up) return Quaternion.LookRotation(Vector3.up, Vector3.back);
else return Quaternion.LookRotation(forward, Vector3.up);
}
return Quaternion.LookRotation(forward, up); }
}
public Vector3 right
{
get {
if(up == forward)
{
if (up == Vector3.up) return Vector3.right;
else return Vector3.Cross(Vector3.up, forward).normalized;
}
return Vector3.Cross(up, forward).normalized; }
}
public static SplineSample Lerp(ref SplineSample a, ref SplineSample b, float t)
{
SplineSample result = new SplineSample();
Lerp(ref a, ref b, t, ref result);
return result;
}
public static SplineSample Lerp(ref SplineSample a, ref SplineSample b, double t)
{
SplineSample result = new SplineSample();
Lerp(ref a, ref b, t, ref result);
return result;
}
public static void Lerp(ref SplineSample a, ref SplineSample b, double t, ref SplineSample target)
{
float ft = (float)t;
DMath.LerpVector3NonAlloc(a.position, b.position, t, ref target.position);
target.forward = Vector3.Slerp(a.forward, b.forward, ft);
target.up = Vector3.Slerp(a.up, b.up, ft);
target.color = Color.Lerp(a.color, b.color, ft);
target.size = Mathf.Lerp(a.size, b.size, ft);
target.percent = DMath.Lerp(a.percent, b.percent, t);
}
public static void Lerp(ref SplineSample a, ref SplineSample b, float t, ref SplineSample target)
{
DMath.LerpVector3NonAlloc(a.position, b.position, t, ref target.position);
target.forward = Vector3.Slerp(a.forward, b.forward, t);
target.up = Vector3.Slerp(a.up, b.up, t);
target.color = Color.Lerp(a.color, b.color, t);
target.size = Mathf.Lerp(a.size, b.size, t);
target.percent = DMath.Lerp(a.percent, b.percent, t);
}
public void Lerp(ref SplineSample b, double t)
{
Lerp(ref this, ref b, t, ref this);
}
public void Lerp(ref SplineSample b, float t)
{
Lerp(ref this, ref b, t, ref this);
}
public SplineSample(Vector3 position, Vector3 up, Vector3 forward, Color color, float size, double percent)
{
this.position = position;
this.up = up;
this.forward = forward;
this.color = color;
this.size = size;
this.percent = percent;
}
}
}

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,208 @@
namespace Dreamteck.Splines
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
#if !UNITY_WSA
using System.Threading;
#endif
public static class SplineThreading
{
public delegate void EmptyHandler();
public static int threadCount
{
get {
#if UNITY_WSA
return 0;
#else
return threads.Length;
#endif
}
set
{
#if !UNITY_WSA
if(value > threads.Length)
{
while (threads.Length < value)
{
ThreadDef thread = new ThreadDef();
#if UNITY_EDITOR
if (Application.isPlaying)
{
thread.Restart();
}
#else
thread.Restart();
#endif
ArrayUtility.Add(ref threads, thread);
}
}
#endif
}
}
#if !UNITY_WSA
internal class ThreadDef
{
internal class Worker
{
internal bool computing = false;
internal Queue<EmptyHandler> instructions = new Queue<EmptyHandler>();
}
internal delegate void BoolHandler(bool flag);
private ParameterizedThreadStart start = null;
internal Thread thread = null;
private Worker worker = new Worker();
internal bool isAlive
{
get { return thread != null && thread.IsAlive; }
}
internal bool computing
{
get
{
return worker.computing;
}
}
internal ThreadDef()
{
start = new ParameterizedThreadStart(RunThread);
}
internal void Queue(EmptyHandler handler)
{
worker.instructions.Enqueue(handler);
}
internal void Interrupt()
{
thread.Interrupt();
}
internal void Restart()
{
thread = new Thread(start);
thread.Start(worker);
}
internal void Abort()
{
if (isAlive)
{
thread.Abort();
}
}
}
internal static ThreadDef[] threads = new ThreadDef[2];
internal static readonly object locker = new object();
static SplineThreading()
{
Application.quitting += Quitting;
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new ThreadDef();
}
#if UNITY_EDITOR
PrewarmThreads();
UnityEditor.EditorApplication.playModeStateChanged += OnPlayStateChanged;
#endif
}
#if UNITY_EDITOR
static void OnPlayStateChanged(UnityEditor.PlayModeStateChange state)
{
if (state == UnityEditor.PlayModeStateChange.ExitingPlayMode)
{
Quitting();
}
}
#endif
static void Quitting()
{
Stop();
}
static void RunThread(object o)
{
ThreadDef.Worker work = (ThreadDef.Worker)o;
while (true)
{
try
{
work.computing = false;
Thread.Sleep(Timeout.Infinite);
}
catch (ThreadInterruptedException)
{
work.computing = true;
lock (locker)
{
while (work.instructions.Count > 0)
{
EmptyHandler h = work.instructions.Dequeue();
if (h != null) h();
}
}
}
catch (System.Exception ex)
{
if(ex.Message != "") Debug.LogError("THREAD EXCEPTION " + ex.Message);
break;
}
}
Debug.Log("Thread stopped");
work.computing = false;
}
#endif
public static void Run(EmptyHandler handler)
{
#if !UNITY_WSA
#if UNITY_EDITOR
if (!Application.isPlaying)
{
handler();
return;
}
#endif
for (int i = 0; i < threads.Length; i++)
{
if (!threads[i].isAlive) threads[i].Restart();
if (!threads[i].computing || i == threads.Length - 1)
{
threads[i].Queue(handler);
if(!threads[i].computing)threads[i].Interrupt();
break;
}
}
#endif
}
public static void PrewarmThreads()
{
#if !UNITY_WSA
for (int i = 0; i < threads.Length; i++)
{
if (!threads[i].isAlive)
{
threads[i].Restart();
}
}
#endif
}
public static void Stop()
{
#if !UNITY_WSA
for (int i = 0; i < threads.Length; i++)
{
threads[i].Abort();
}
#endif
}
}
}

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,173 @@
namespace Dreamteck.Splines
{
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[System.Serializable]
public class TriggerGroup{
#if UNITY_EDITOR
public bool open = false;
#endif
public bool enabled = true;
public string name = "";
public Color color = Color.white;
public SplineTrigger[] triggers = new SplineTrigger[0];
public void Check(double start, double end, SplineUser user = null)
{
for (int i = 0; i < triggers.Length; i++)
{
if (triggers[i] == null)
{
continue;
}
if (triggers[i].Check(start, end))
{
triggers[i].Invoke(user);
}
}
}
public void Reset()
{
for (int i = 0; i < triggers.Length; i++) triggers[i].Reset();
}
/// <summary>
/// Returns all triggers within the specified range
/// </summary>
public List<SplineTrigger> GetTriggers(double from, double to)
{
List<SplineTrigger> triggerList = new List<SplineTrigger>();
for (int i = 0; i < triggers.Length; i++)
{
if (triggers[i] == null)
{
continue;
}
if(triggers[i].position >= from && triggers[i].position <= to)
{
triggerList.Add(triggers[i]);
}
}
return triggerList;
}
/// <summary>
/// Creates a new trigger inside the group
/// </summary>
public SplineTrigger AddTrigger(double position, SplineTrigger.Type type)
{
return AddTrigger(position, type, "Trigger " + (triggers.Length + 1), Color.white);
}
/// <summary>
/// Creates a new trigger inside the group
/// </summary>
public SplineTrigger AddTrigger(double position, SplineTrigger.Type type, string name, Color color)
{
SplineTrigger newTrigger = new SplineTrigger(type);
newTrigger.position = position;
newTrigger.color = color;
newTrigger.name = name;
ArrayUtility.Add(ref triggers, newTrigger);
return newTrigger;
}
/// <summary>
/// Removes the trigger at the given index from the group
/// </summary>
public void RemoveTrigger(int index)
{
ArrayUtility.RemoveAt(ref triggers, index);
}
}
[System.Serializable]
public class SplineTrigger
{
public string name = "Trigger";
public enum Type { Double, Forward, Backward}
[SerializeField]
public Type type = Type.Double;
public bool workOnce = false;
private bool worked = false;
[Range(0f, 1f)]
public double position = 0.5;
[SerializeField]
public bool enabled = true;
[SerializeField]
public Color color = Color.white;
[SerializeField]
[HideInInspector]
public TriggerEvent onCross = new TriggerEvent();
public SplineTrigger(Type t)
{
type = t;
enabled = true;
onCross = new TriggerEvent();
}
/// <summary>
/// Add a new UnityAction to the trigger
/// </summary>
/// <param name="action"></param>
public void AddListener(UnityAction<SplineUser> action)
{
onCross.AddListener(action);
}
public void AddListener(UnityAction action)
{
UnityAction<SplineUser> addAction = new UnityAction<SplineUser>((user) => { action.Invoke(); });
onCross.AddListener(addAction);
}
public void RemoveListener(UnityAction<SplineUser> action)
{
onCross.RemoveListener(action);
}
public void RemoveAllListeners()
{
onCross.RemoveAllListeners();
}
public void Reset()
{
worked = false;
}
public bool Check(double previousPercent, double currentPercent)
{
if (!enabled) return false;
if (workOnce && worked) return false;
bool passed = false;
switch (type)
{
case Type.Double: passed = (previousPercent <= position && currentPercent >= position) || (currentPercent <= position && previousPercent >= position); break;
case Type.Forward: passed = previousPercent <= position && currentPercent >= position; break;
case Type.Backward: passed = currentPercent <= position && previousPercent >= position; break;
}
if (passed) worked = true;
return passed;
}
public void Invoke(SplineUser user = null)
{
#if UNITY_EDITOR
if (!Application.isPlaying) return;
#endif
onCross.Invoke(user);
}
[System.Serializable]
public class TriggerEvent : UnityEvent<SplineUser>
{
}
}
}

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,118 @@
namespace Dreamteck.Splines
{
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public static class SplineUtility
{
public enum MergeSide { Start, End }
/// <summary>
/// Merges two spline objects into one. The result will be merged into <paramref name="baseSpline"/>
/// </summary>
/// <param name="baseSpline">The base spline object that</param>
/// <param name="addedSpline">The object that will be merged into the base spline</param>
/// <param name="side">Which side of the base spline to append to - beginning or end?</param>
/// <param name="mergeEndpoints">Should the end points of the splines be merged or should they be bridged?</param>
/// <param name="destroyAddedSpline">If true, the added spline's game object will be destroyed after merge</param>
public static void Merge(SplineComputer baseSpline, SplineComputer addedSpline, MergeSide side, bool mergeEndpoints = false, bool destroyAddedSpline = false)
{
SplinePoint[] mergedPoints = addedSpline.GetPoints();
SplinePoint[] basePoints = baseSpline.GetPoints();
List<SplinePoint> pointsList = new List<SplinePoint>();
SplinePoint[] points;
if (!mergeEndpoints) points = new SplinePoint[mergedPoints.Length + basePoints.Length];
else points = new SplinePoint[mergedPoints.Length + basePoints.Length - 1];
if (side == MergeSide.End)
{
if (side == MergeSide.Start)
{
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
for (int i = mergeEndpoints ? 1 : 0; i < mergedPoints.Length; i++) pointsList.Add(mergedPoints[i]);
}
else
{
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
for (int i = 0; i < mergedPoints.Length - (mergeEndpoints ? 1 : 0); i++) pointsList.Add(mergedPoints[(mergedPoints.Length - 1) - i]);
}
}
else
{
if (side == MergeSide.Start)
{
for (int i = 0; i < mergedPoints.Length - (mergeEndpoints ? 1 : 0); i++) pointsList.Add(mergedPoints[(mergedPoints.Length - 1) - i]);
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
}
else
{
for (int i = mergeEndpoints ? 1 : 0; i < mergedPoints.Length; i++) pointsList.Add(mergedPoints[i]);
for (int i = 0; i < basePoints.Length; i++) pointsList.Add(basePoints[i]);
}
}
points = pointsList.ToArray();
double mergedPercent = (double)(mergedPoints.Length - 1) / (points.Length - 1);
double from = 0.0;
double to = 1.0;
if (side == MergeSide.End)
{
from = 1.0 - mergedPercent;
to = 1.0;
}
else
{
from = 0.0;
to = mergedPercent;
}
List<Node> mergedNodes = new List<Node>();
List<int> mergedIndices = new List<int>();
for (int i = 0; i < addedSpline.pointCount; i++)
{
Node node = addedSpline.GetNode(i);
if (node != null)
{
mergedNodes.Add(node);
mergedIndices.Add(i);
addedSpline.DisconnectNode(i);
i--;
}
}
SplineUser[] subs = addedSpline.GetSubscribers();
for (int i = 0; i < subs.Length; i++)
{
addedSpline.Unsubscribe(subs[i]);
subs[i].spline = baseSpline;
subs[i].clipFrom = DMath.Lerp(from, to, subs[i].clipFrom);
subs[i].clipTo = DMath.Lerp(from, to, subs[i].clipTo);
}
baseSpline.SetPoints(points);
if (side == MergeSide.Start)
{
baseSpline.ShiftNodes(0, baseSpline.pointCount - 1, addedSpline.pointCount);
for (int i = 0; i < mergedNodes.Count; i++)
{
baseSpline.ConnectNode(mergedNodes[i], mergedIndices[i]);
}
}
else
{
for (int i = 0; i < mergedNodes.Count; i++)
{
int connectIndex = mergedIndices[i] + basePoints.Length;
if (mergeEndpoints) connectIndex--;
baseSpline.ConnectNode(mergedNodes[i], connectIndex);
}
}
if (destroyAddedSpline)
{
Object.Destroy(addedSpline.gameObject);
}
}
}
}

View File

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

View File

@@ -0,0 +1,363 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
namespace Dreamteck.Splines
{
[System.Serializable]
public class TransformModule : ISerializationCallbackReceiver
{
public Vector2 offset
{
get { return _offset; }
set
{
if (value != _offset)
{
_offset = value;
_hasOffset = _offset != Vector2.zero;
if (targetUser != null)
{
targetUser.Rebuild();
}
}
}
}
public Vector3 rotationOffset
{
get { return _rotationOffset; }
set
{
if (value != _rotationOffset)
{
_rotationOffset = value;
_hasRotationOffset = _rotationOffset != Vector3.zero;
if (targetUser != null)
{
targetUser.Rebuild();
}
}
}
}
public bool hasOffset
{
get { return _hasOffset; }
}
public bool hasRotationOffset
{
get { return _hasRotationOffset; }
}
public Vector3 baseScale
{
get { return _baseScale; }
set
{
if (value != _baseScale)
{
_baseScale = value;
if (targetUser != null)
{
targetUser.Rebuild();
}
}
}
}
public bool is2D
{
get { return _2dMode; }
set
{
_2dMode = value;
}
}
[SerializeField]
[HideInInspector]
private bool _hasOffset = false;
[SerializeField]
[HideInInspector]
private bool _hasRotationOffset = false;
[SerializeField]
[HideInInspector]
private Vector2 _offset;
[SerializeField]
[HideInInspector]
private Vector3 _rotationOffset = Vector3.zero;
[SerializeField]
[HideInInspector]
private Vector3 _baseScale = Vector3.one;
[SerializeField]
[HideInInspector]
private bool _2dMode = false;
public enum VelocityHandleMode { Zero, Preserve, Align, AlignRealistic }
public VelocityHandleMode velocityHandleMode = VelocityHandleMode.Zero;
public SplineSample splineResult
{
get
{
return _splineResult;
}
set
{
_splineResult = value;
}
}
private SplineSample _splineResult;
public bool applyPositionX = true;
public bool applyPositionY = true;
public bool applyPositionZ = true;
public bool applyPosition2D = true;
public bool retainLocalPosition = false;
public Spline.Direction direction = Spline.Direction.Forward;
public bool applyPosition
{
get
{
if (_2dMode)
{
return applyPosition2D;
}
return applyPositionX || applyPositionY || applyPositionZ;
}
set
{
applyPositionX = applyPositionY = applyPositionZ = applyPosition2D = value;
}
}
public bool applyRotationX = true;
public bool applyRotationY = true;
public bool applyRotationZ = true;
public bool applyRotation2D = true;
public bool retainLocalRotation = false;
public bool applyRotation
{
get
{
if (_2dMode)
{
return applyRotation2D;
}
return applyRotationX || applyRotationY || applyRotationZ;
}
set
{
applyRotationX = applyRotationY = applyRotationZ = applyRotation2D = value;
}
}
public bool applyScaleX = false;
public bool applyScaleY = false;
public bool applyScaleZ = false;
public bool applyScale
{
get
{
return applyScaleX || applyScaleY || applyScaleZ;
}
set
{
applyScaleX = applyScaleY = applyScaleZ = value;
}
}
[HideInInspector]
public SplineUser targetUser = null;
//These are used to save allocations
private static Vector3 position = Vector3.zero;
private static Quaternion rotation = Quaternion.identity;
public void ApplyTransform(Transform input)
{
input.position = GetPosition(input.position);
input.rotation = GetRotation(input.rotation);
input.localScale = GetScale(input.localScale);
}
public void ApplyRigidbody(Rigidbody input)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
ApplyTransform(input.transform);
return;
}
#endif
input.transform.localScale = GetScale(input.transform.localScale);
input.MovePosition(GetPosition(input.position));
if (!input.isKinematic)
{
#if UNITY_6000_0_OR_NEWER
input.linearVelocity = HandleVelocity(input.linearVelocity);
#else
input.velocity = HandleVelocity(input.velocity);
#endif
}
input.MoveRotation(GetRotation(input.rotation));
Vector3 angularVelocity = input.angularVelocity;
if (applyRotationX)
{
angularVelocity.x = 0f;
}
if (applyRotationY)
{
angularVelocity.y = 0f;
}
if (applyRotationZ || applyRotation2D)
{
angularVelocity.z = 0f;
}
input.angularVelocity = angularVelocity;
}
public void ApplyRigidbody2D(Rigidbody2D input)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
ApplyTransform(input.transform);
input.transform.rotation = Quaternion.AngleAxis(GetRotation(Quaternion.Euler(0f, 0f, input.rotation)).eulerAngles.z, Vector3.forward);
return;
}
#endif
input.transform.localScale = GetScale(input.transform.localScale);
input.position = GetPosition(input.position);
if (!input.isKinematic)
{
#if UNITY_6000_OR_NEWER
input.linearVelocity = HandleVelocity(input.linearVelocity);
#else
input.velocity = HandleVelocity(input.velocity);
#endif
}
input.rotation = GetRotation(Quaternion.Euler(0f, 0f, input.rotation)).eulerAngles.z;
if (applyRotationX)
{
input.angularVelocity = 0f;
}
}
Vector3 HandleVelocity(Vector3 velocity)
{
Vector3 idealVelocity = Vector3.zero;
Vector3 direction = Vector3.right;
switch (velocityHandleMode)
{
case VelocityHandleMode.Preserve: idealVelocity = velocity; break;
case VelocityHandleMode.Align:
direction = _splineResult.forward;
if (Vector3.Dot(velocity, direction) < 0f) direction *= -1f;
idealVelocity = direction * velocity.magnitude; break;
case VelocityHandleMode.AlignRealistic:
direction = _splineResult.forward;
if (Vector3.Dot(velocity, direction) < 0f) direction *= -1f;
idealVelocity = direction * velocity.magnitude * Vector3.Dot(velocity.normalized, direction); break;
}
if (applyPositionX) velocity.x = idealVelocity.x;
if (applyPositionY) velocity.y = idealVelocity.y;
if (applyPositionZ) velocity.z = idealVelocity.z;
return velocity;
}
private Vector3 GetPosition(Vector3 inputPosition)
{
position = _splineResult.position;
Vector2 finalOffset = _offset;
if (finalOffset != Vector2.zero)
{
position += _splineResult.right * finalOffset.x * _splineResult.size + _splineResult.up * finalOffset.y * _splineResult.size;
}
if (retainLocalPosition)
{
Matrix4x4 matrix = Matrix4x4.TRS(position, _splineResult.rotation, Vector3.one);
Vector3 splineLocalPosition = matrix.inverse.MultiplyPoint3x4(targetUser.transform.position);
splineLocalPosition.x = applyPositionX ? 0f : splineLocalPosition.x;
splineLocalPosition.y = applyPositionY ? 0f : splineLocalPosition.y;
splineLocalPosition.z = applyPositionZ ? 0f : splineLocalPosition.z;
inputPosition = matrix.MultiplyPoint3x4(splineLocalPosition);
} else
{
if (applyPositionX) inputPosition.x = position.x;
if (applyPositionY) inputPosition.y = position.y;
if (applyPositionZ) inputPosition.z = position.z;
}
return inputPosition;
}
private Quaternion GetRotation(Quaternion inputRotation)
{
rotation = Quaternion.LookRotation(_splineResult.forward * (direction == Spline.Direction.Forward ? 1f : -1f), _splineResult.up);
if (_2dMode)
{
if (applyRotation2D)
{
rotation *= Quaternion.Euler(90, -90, 0);
inputRotation = Quaternion.AngleAxis(rotation.eulerAngles.z + _rotationOffset.z, Vector3.forward);
}
return inputRotation;
}
else
{
if (_rotationOffset != Vector3.zero)
{
rotation = rotation * Quaternion.Euler(_rotationOffset);
}
}
if (retainLocalRotation)
{
Quaternion localRotation = Quaternion.Inverse(rotation) * inputRotation;
Vector3 targetEuler = localRotation.eulerAngles;
targetEuler.x = applyRotationX ? 0f : targetEuler.x;
targetEuler.y = applyRotationY ? 0f : targetEuler.y;
targetEuler.z = applyRotationZ ? 0f : targetEuler.z;
inputRotation = rotation * Quaternion.Euler(targetEuler);
} else
{
if (!applyRotationX || !applyRotationY || !applyRotationZ)
{
Vector3 targetEuler = rotation.eulerAngles;
Vector3 sourceEuler = inputRotation.eulerAngles;
if (!applyRotationX) targetEuler.x = sourceEuler.x;
if (!applyRotationY) targetEuler.y = sourceEuler.y;
if (!applyRotationZ) targetEuler.z = sourceEuler.z;
inputRotation.eulerAngles = targetEuler;
}
else
{
inputRotation = rotation;
}
}
return inputRotation;
}
private Vector3 GetScale(Vector3 inputScale)
{
if (applyScaleX) inputScale.x = _baseScale.x * _splineResult.size;
if (applyScaleY) inputScale.y = _baseScale.y * _splineResult.size;
if (applyScaleZ) inputScale.z = _baseScale.z * _splineResult.size;
return inputScale;
}
public void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
_hasRotationOffset = _rotationOffset != Vector3.zero;
_hasOffset = _offset != Vector2.zero;
}
}
}

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: