重新导入obi
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[CreateAssetMenu(fileName = "rope section", menuName = "Obi/Rope Section", order = 142)]
|
||||
public class ObiRopeSection : ScriptableObject
|
||||
{
|
||||
[HideInInspector] public List<Vector2> vertices;
|
||||
public int snapX = 0;
|
||||
public int snapY = 0;
|
||||
|
||||
public int Segments{
|
||||
get{return vertices.Count-1;}
|
||||
}
|
||||
|
||||
public void OnEnable(){
|
||||
|
||||
if (vertices == null){
|
||||
vertices = new List<Vector2>();
|
||||
CirclePreset(8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void CirclePreset(int segments){
|
||||
|
||||
vertices.Clear();
|
||||
|
||||
for (int j = 0; j <= segments; ++j){
|
||||
float angle = 2 * Mathf.PI / segments * j;
|
||||
vertices.Add(Mathf.Cos(angle)*Vector2.right + Mathf.Sin(angle)*Vector2.up);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Snaps a float value to the nearest multiple of snapInterval.
|
||||
*/
|
||||
public static int SnapTo(float val, int snapInterval, int threshold){
|
||||
int intVal = (int) val;
|
||||
if (snapInterval <= 0)
|
||||
return intVal;
|
||||
int under = Mathf.FloorToInt(val / snapInterval) * snapInterval;
|
||||
int over = under + snapInterval;
|
||||
if (intVal - under < threshold) return under;
|
||||
if (over - intVal < threshold) return over;
|
||||
return intVal;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee7737c43f5734f87be9e49d1bbfba78
|
||||
labels:
|
||||
- ObiRope
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: fdb742a900c8d453ea5ce5027e80ad00, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
// Abstracts rope topolgy as a list of elements.
|
||||
[System.Serializable]
|
||||
public class ObiStructuralElement
|
||||
{
|
||||
public int particle1;
|
||||
public int particle2;
|
||||
public float restLength;
|
||||
public float constraintForce;
|
||||
public float tearResistance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2aecff76dda4241c5a0c15349e2419d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Obi/Scripts/RopeAndRod/DataStructures/Path.meta
Normal file
8
Assets/Obi/Scripts/RopeAndRod/DataStructures/Path.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75e833f2b944640a5934c15fbec8ac00
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5172d3d605ca94f2db0ad73eaecbc4d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiColorDataChannel : ObiPathDataChannelIdentity<Color>
|
||||
{
|
||||
public ObiColorDataChannel() : base(new ObiColorInterpolator3D()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4078c779a63154dddac14f5a1e65a692
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiPhaseDataChannel : ObiPathDataChannelIdentity<int>
|
||||
{
|
||||
public ObiPhaseDataChannel() : base(new ObiConstantInterpolator()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cfdcf019ed7249799993815bfc3c0bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiMassDataChannel : ObiPathDataChannelIdentity<float>
|
||||
{
|
||||
public ObiMassDataChannel() : base(new ObiCatmullRomInterpolator()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e55025008fa8f4bccae82dbaded0f019
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiNormalDataChannel : ObiPathDataChannelIdentity<Vector3>
|
||||
{
|
||||
public ObiNormalDataChannel() : base(new ObiCatmullRomInterpolator3D()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4721087e73d0d422ab48ad62a2fd3d3b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,88 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public interface IObiPathDataChannel
|
||||
{
|
||||
int Count { get; }
|
||||
bool Dirty { get; }
|
||||
void Clean();
|
||||
void RemoveAt(int index);
|
||||
}
|
||||
|
||||
public abstract class ObiPathDataChannel<T,U> : IObiPathDataChannel
|
||||
{
|
||||
protected ObiInterpolator<U> interpolator;
|
||||
protected bool dirty = false;
|
||||
public List<T> data = new List<T>();
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return data.Count; }
|
||||
}
|
||||
|
||||
public bool Dirty
|
||||
{
|
||||
get { return dirty; }
|
||||
}
|
||||
|
||||
public void Clean()
|
||||
{
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public ObiPathDataChannel(ObiInterpolator<U> interpolator)
|
||||
{
|
||||
this.interpolator = interpolator;
|
||||
}
|
||||
|
||||
public T this[int i]
|
||||
{
|
||||
get { return data[i]; }
|
||||
set { data[i] = value; dirty = true; }
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
data.RemoveAt(index);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public U Evaluate(U v0, U v1, U v2, U v3, float mu)
|
||||
{
|
||||
return interpolator.Evaluate(v0, v1, v2, v3, mu);
|
||||
}
|
||||
public U EvaluateFirstDerivative(U v0, U v1, U v2, U v3, float mu)
|
||||
{
|
||||
return interpolator.EvaluateFirstDerivative(v0, v1, v2, v3, mu);
|
||||
}
|
||||
public U EvaluateSecondDerivative(U v0, U v1, U v2, U v3, float mu)
|
||||
{
|
||||
return interpolator.EvaluateSecondDerivative(v0, v1, v2, v3, mu);
|
||||
}
|
||||
|
||||
public int GetSpanCount(bool closed)
|
||||
{
|
||||
int cps = Count;
|
||||
if (cps < 2)
|
||||
return 0;
|
||||
|
||||
return closed ? cps : cps - 1;
|
||||
}
|
||||
|
||||
public int GetSpanControlPointAtMu(bool closed, float mu, out float spanMu)
|
||||
{
|
||||
|
||||
int spanCount = GetSpanCount(closed);
|
||||
spanMu = mu * spanCount;
|
||||
int i = (mu >= 1f) ? (spanCount - 1) : (int)spanMu;
|
||||
spanMu -= i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43ab47ea5c62b45248edce6a9d099626
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public abstract class ObiPathDataChannelIdentity<T> : ObiPathDataChannel<T,T>
|
||||
{
|
||||
public ObiPathDataChannelIdentity(ObiInterpolator<T> interpolator) : base(interpolator)
|
||||
{
|
||||
}
|
||||
|
||||
public T GetFirstDerivative(int index)
|
||||
{
|
||||
int nextCP = (index + 1) % Count;
|
||||
|
||||
return EvaluateFirstDerivative(this[index],
|
||||
this[index],
|
||||
this[nextCP],
|
||||
this[nextCP], 0);
|
||||
}
|
||||
|
||||
public T GetSecondDerivative(int index)
|
||||
{
|
||||
int nextCP = (index + 1) % Count;
|
||||
|
||||
return EvaluateSecondDerivative(this[index],
|
||||
this[index],
|
||||
this[nextCP],
|
||||
this[nextCP], 0);
|
||||
}
|
||||
|
||||
public T GetAtMu(bool closed, float mu)
|
||||
{
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
return Evaluate(this[i],
|
||||
this[i],
|
||||
this[nextCP],
|
||||
this[nextCP], p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get property in path because it has less than 2 control points.");
|
||||
}
|
||||
}
|
||||
|
||||
public T GetFirstDerivativeAtMu(bool closed, float mu)
|
||||
{
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
return EvaluateFirstDerivative(this[i],
|
||||
this[i],
|
||||
this[nextCP],
|
||||
this[nextCP], p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get derivative in path because it has less than 2 control points.");
|
||||
}
|
||||
}
|
||||
|
||||
public T GetSecondDerivativeAtMu(bool closed, float mu)
|
||||
{
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
return EvaluateSecondDerivative(this[i],
|
||||
this[i],
|
||||
this[nextCP],
|
||||
this[nextCP], p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get second derivative in path because it has less than 2 control points.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61b5c82c04eb14f1999e94baa1f073bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,125 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiPointsDataChannel : ObiPathDataChannel<ObiWingedPoint, Vector3>
|
||||
{
|
||||
|
||||
public ObiPointsDataChannel() : base(new ObiCatmullRomInterpolator3D()) { }
|
||||
|
||||
public Vector3 GetTangent(int index)
|
||||
{
|
||||
int nextCP = (index + 1) % Count;
|
||||
|
||||
var wp1 = this[index];
|
||||
var wp2 = this[nextCP];
|
||||
|
||||
return EvaluateFirstDerivative(wp1.position,
|
||||
wp1.outTangentEndpoint,
|
||||
wp2.inTangentEndpoint,
|
||||
wp2.position, 0);
|
||||
}
|
||||
|
||||
public Vector3 GetAcceleration(int index)
|
||||
{
|
||||
int nextCP = (index + 1) % Count;
|
||||
|
||||
var wp1 = this[index];
|
||||
var wp2 = this[nextCP];
|
||||
|
||||
return EvaluateSecondDerivative(wp1.position,
|
||||
wp1.outTangentEndpoint,
|
||||
wp2.inTangentEndpoint,
|
||||
wp2.position, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns spline position at time mu, with 0<=mu<=1 where 0 is the start of the spline
|
||||
* and 1 is the end.
|
||||
*/
|
||||
public Vector3 GetPositionAtMu(bool closed,float mu)
|
||||
{
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
var wp1 = this[i];
|
||||
var wp2 = this[nextCP];
|
||||
|
||||
return Evaluate(wp1.position,
|
||||
wp1.outTangentEndpoint,
|
||||
wp2.inTangentEndpoint,
|
||||
wp2.position, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get position in path because it has zero control points.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns normal tangent vector at time mu, with 0<=mu<=1 where 0 is the start of the spline
|
||||
* and 1 is the end.
|
||||
*/
|
||||
public Vector3 GetTangentAtMu(bool closed, float mu)
|
||||
{
|
||||
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
var wp1 = this[i];
|
||||
var wp2 = this[nextCP];
|
||||
|
||||
return EvaluateFirstDerivative(wp1.position,
|
||||
wp1.outTangentEndpoint,
|
||||
wp2.inTangentEndpoint,
|
||||
wp2.position, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get derivative in path because it has less than 2 control points.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns acceleration at time mu, with 0<=mu<=1 where 0 is the start of the spline
|
||||
* and 1 is the end.
|
||||
*/
|
||||
public Vector3 GetAccelerationAtMu(bool closed, float mu)
|
||||
{
|
||||
|
||||
int cps = Count;
|
||||
if (cps >= 2)
|
||||
{
|
||||
float p;
|
||||
int i = GetSpanControlPointAtMu(closed, mu, out p);
|
||||
int nextCP = (i + 1) % cps;
|
||||
|
||||
var wp1 = this[i];
|
||||
var wp2 = this[nextCP];
|
||||
|
||||
return EvaluateSecondDerivative(wp1.position,
|
||||
wp1.outTangentEndpoint,
|
||||
wp2.inTangentEndpoint,
|
||||
wp2.position, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot get second derivative in path because it has less than 2 control points.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d710ff448ae4e4a6f9351a678af9a8ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiRotationalMassDataChannel : ObiPathDataChannelIdentity<float>
|
||||
{
|
||||
public ObiRotationalMassDataChannel() : base(new ObiCatmullRomInterpolator()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28c38822e5a79441a8177689f83644e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public class ObiThicknessDataChannel : ObiPathDataChannelIdentity<float>
|
||||
{
|
||||
public ObiThicknessDataChannel() : base(new ObiCatmullRomInterpolator()) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b414fbc6ce2db42299dc01619ed4fc97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e85cc8f3b96cf4b4c9be1e6af0a0a99b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ObiCatmullRomInterpolator : ObiInterpolator<float>
|
||||
{
|
||||
/**
|
||||
* 1D bezier spline interpolation
|
||||
*/
|
||||
public float Evaluate(float y0, float y1, float y2, float y3, float mu)
|
||||
{
|
||||
|
||||
float imu = 1 - mu;
|
||||
return imu * imu * imu * y0 +
|
||||
3f * imu * imu * mu * y1 +
|
||||
3f * imu * mu * mu * y2 +
|
||||
mu * mu * mu * y3;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 1D catmull rom spline second derivative
|
||||
*/
|
||||
public float EvaluateFirstDerivative(float y0, float y1, float y2, float y3, float mu)
|
||||
{
|
||||
|
||||
float imu = 1 - mu;
|
||||
return 3f * imu * imu * (y1 - y0) +
|
||||
6f * imu * mu * (y2 - y1) +
|
||||
3f * mu * mu * (y3 - y2);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 1D catmull rom spline second derivative
|
||||
*/
|
||||
public float EvaluateSecondDerivative(float y0, float y1, float y2, float y3, float mu)
|
||||
{
|
||||
|
||||
float imu = 1 - mu;
|
||||
return 3f * imu * imu * (y1 - y0) +
|
||||
6f * imu * mu * (y2 - y1) +
|
||||
3f * mu * mu * (y3 - y2);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 93918aac247384d8289d151d77dc4dc2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ObiCatmullRomInterpolator3D : ObiInterpolator<Vector3>
|
||||
{
|
||||
private ObiCatmullRomInterpolator interpolator = new ObiCatmullRomInterpolator();
|
||||
|
||||
/**
|
||||
* 3D spline interpolation
|
||||
*/
|
||||
public Vector3 Evaluate(Vector3 y0, Vector3 y1, Vector3 y2, Vector3 y3, float mu)
|
||||
{
|
||||
|
||||
return new Vector3(interpolator.Evaluate(y0.x, y1.x, y2.x, y3.x, mu),
|
||||
interpolator.Evaluate(y0.y, y1.y, y2.y, y3.y, mu),
|
||||
interpolator.Evaluate(y0.z, y1.z, y2.z, y3.z, mu));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 3D spline first derivative
|
||||
*/
|
||||
public Vector3 EvaluateFirstDerivative(Vector3 y0, Vector3 y1, Vector3 y2, Vector3 y3, float mu)
|
||||
{
|
||||
|
||||
return new Vector3(interpolator.EvaluateFirstDerivative(y0.x, y1.x, y2.x, y3.x, mu),
|
||||
interpolator.EvaluateFirstDerivative(y0.y, y1.y, y2.y, y3.y, mu),
|
||||
interpolator.EvaluateFirstDerivative(y0.z, y1.z, y2.z, y3.z, mu));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 3D spline second derivative
|
||||
*/
|
||||
public Vector3 EvaluateSecondDerivative(Vector3 y0, Vector3 y1, Vector3 y2, Vector3 y3, float mu)
|
||||
{
|
||||
|
||||
return new Vector3(interpolator.EvaluateSecondDerivative(y0.x, y1.x, y2.x, y3.x, mu),
|
||||
interpolator.EvaluateSecondDerivative(y0.y, y1.y, y2.y, y3.y, mu),
|
||||
interpolator.EvaluateSecondDerivative(y0.z, y1.z, y2.z, y3.z, mu));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 845dbd641b61b490bb94d745b6879e97
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ObiColorInterpolator3D : ObiInterpolator<Color>
|
||||
{
|
||||
private ObiCatmullRomInterpolator interpolator = new ObiCatmullRomInterpolator();
|
||||
|
||||
/**
|
||||
* 3D spline interpolation
|
||||
*/
|
||||
public Color Evaluate(Color y0, Color y1, Color y2, Color y3, float mu)
|
||||
{
|
||||
|
||||
return new Color(interpolator.Evaluate(y0.r, y1.r, y2.r, y3.r, mu),
|
||||
interpolator.Evaluate(y0.g, y1.g, y2.g, y3.g, mu),
|
||||
interpolator.Evaluate(y0.b, y1.b, y2.b, y3.b, mu),
|
||||
interpolator.Evaluate(y0.a, y1.a, y2.a, y3.a, mu));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 3D spline first derivative
|
||||
*/
|
||||
public Color EvaluateFirstDerivative(Color y0, Color y1, Color y2, Color y3, float mu)
|
||||
{
|
||||
|
||||
return new Color(interpolator.EvaluateFirstDerivative(y0.r, y1.r, y2.r, y3.r, mu),
|
||||
interpolator.EvaluateFirstDerivative(y0.g, y1.g, y2.g, y3.g, mu),
|
||||
interpolator.EvaluateFirstDerivative(y0.b, y1.b, y2.b, y3.b, mu),
|
||||
interpolator.EvaluateFirstDerivative(y0.a, y1.a, y2.a, y3.a, mu));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 3D spline second derivative
|
||||
*/
|
||||
public Color EvaluateSecondDerivative(Color y0, Color y1, Color y2, Color y3, float mu)
|
||||
{
|
||||
|
||||
return new Color(interpolator.EvaluateSecondDerivative(y0.r, y1.r, y2.r, y3.r, mu),
|
||||
interpolator.EvaluateSecondDerivative(y0.g, y1.g, y2.g, y3.g, mu),
|
||||
interpolator.EvaluateSecondDerivative(y0.b, y1.b, y2.b, y3.b, mu),
|
||||
interpolator.EvaluateSecondDerivative(y0.a, y1.a, y2.a, y3.a, mu));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c2bda61c040346db8efd82f64c2422e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public class ObiConstantInterpolator : ObiInterpolator<int>
|
||||
{
|
||||
/**
|
||||
* constant interpolator
|
||||
*/
|
||||
public int Evaluate(int y0, int y1, int y2, int y3, float mu)
|
||||
{
|
||||
return mu < 0.5f ? y1 : y2;
|
||||
}
|
||||
|
||||
/**
|
||||
* derivative of constant value:
|
||||
*/
|
||||
public int EvaluateFirstDerivative(int y0, int y1, int y2, int y3, float mu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* second derivative of constant value:
|
||||
*/
|
||||
public int EvaluateSecondDerivative(int y0, int y1, int y2, int y3, float mu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33f10aef9ed544b14a762a9c6e8bf411
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
public interface ObiInterpolator<T>
|
||||
{
|
||||
T Evaluate(T v0, T v1, T v2, T v3, float mu);
|
||||
T EvaluateFirstDerivative(T v0, T v1, T v2, T v3, float mu);
|
||||
T EvaluateSecondDerivative(T v0, T v1, T v2, T v3, float mu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af25f2a4d723548bfafa551b6da56d25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
408
Assets/Obi/Scripts/RopeAndRod/DataStructures/Path/ObiPath.cs
Normal file
408
Assets/Obi/Scripts/RopeAndRod/DataStructures/Path/ObiPath.cs
Normal file
@@ -0,0 +1,408 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[System.Serializable]
|
||||
public class PathControlPointEvent : UnityEvent<int>
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ObiPath
|
||||
{
|
||||
[HideInInspector] [SerializeField] List<string> m_Names = new List<string>();
|
||||
[HideInInspector] [SerializeField] public ObiPointsDataChannel m_Points = new ObiPointsDataChannel();
|
||||
[HideInInspector] [SerializeField] ObiNormalDataChannel m_Normals = new ObiNormalDataChannel();
|
||||
[HideInInspector] [SerializeField] ObiColorDataChannel m_Colors = new ObiColorDataChannel();
|
||||
[HideInInspector] [SerializeField] ObiThicknessDataChannel m_Thickness = new ObiThicknessDataChannel();
|
||||
[HideInInspector] [SerializeField] ObiMassDataChannel m_Masses = new ObiMassDataChannel();
|
||||
[HideInInspector] [SerializeField] ObiRotationalMassDataChannel m_RotationalMasses = new ObiRotationalMassDataChannel();
|
||||
|
||||
[FormerlySerializedAs("m_Phases")]
|
||||
[HideInInspector] [SerializeField] ObiPhaseDataChannel m_Filters = new ObiPhaseDataChannel();
|
||||
|
||||
[HideInInspector] [SerializeField] private bool m_Closed = false;
|
||||
|
||||
protected bool dirty = false;
|
||||
protected const int arcLenghtSamples = 20;
|
||||
[HideInInspector] [SerializeField] protected List<float> m_ArcLengthTable = new List<float>();
|
||||
[HideInInspector] [SerializeField] protected float m_TotalSplineLenght = 0.0f;
|
||||
|
||||
public UnityEvent OnPathChanged = new UnityEvent();
|
||||
public PathControlPointEvent OnControlPointAdded = new PathControlPointEvent();
|
||||
public PathControlPointEvent OnControlPointRemoved = new PathControlPointEvent();
|
||||
public PathControlPointEvent OnControlPointRenamed = new PathControlPointEvent();
|
||||
|
||||
private IEnumerable<IObiPathDataChannel> GetDataChannels()
|
||||
{
|
||||
yield return m_Points;
|
||||
yield return m_Normals;
|
||||
yield return m_Colors;
|
||||
yield return m_Thickness;
|
||||
yield return m_Masses;
|
||||
yield return m_RotationalMasses;
|
||||
yield return m_Filters;
|
||||
}
|
||||
|
||||
public ObiPointsDataChannel points { get { return m_Points; }}
|
||||
public ObiNormalDataChannel normals { get { return m_Normals; } }
|
||||
public ObiColorDataChannel colors { get { return m_Colors; } }
|
||||
public ObiThicknessDataChannel thicknesses { get { return m_Thickness; } }
|
||||
public ObiMassDataChannel masses { get { return m_Masses; } }
|
||||
public ObiRotationalMassDataChannel rotationalMasses { get { return m_RotationalMasses; } }
|
||||
public ObiPhaseDataChannel filters { get { return m_Filters; } }
|
||||
|
||||
public ReadOnlyCollection<float> ArcLengthTable
|
||||
{
|
||||
get { return m_ArcLengthTable.AsReadOnly(); }
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get { return m_TotalSplineLenght; }
|
||||
}
|
||||
|
||||
public int ArcLengthSamples
|
||||
{
|
||||
get { return arcLenghtSamples; }
|
||||
}
|
||||
|
||||
public int ControlPointCount
|
||||
{
|
||||
get { return m_Points.Count;}
|
||||
}
|
||||
|
||||
public bool Closed
|
||||
{
|
||||
get { return m_Closed; }
|
||||
set
|
||||
{
|
||||
if (value != m_Closed)
|
||||
{
|
||||
m_Closed = value;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int GetSpanCount()
|
||||
{
|
||||
return m_Points.GetSpanCount(m_Closed);
|
||||
}
|
||||
|
||||
public int GetSpanControlPointForMu(float mu, out float spanMu)
|
||||
{
|
||||
return m_Points.GetSpanControlPointAtMu(m_Closed, mu, out spanMu);
|
||||
}
|
||||
|
||||
public int GetClosestControlPointIndex(float mu)
|
||||
{
|
||||
float spanMu;
|
||||
int cp = GetSpanControlPointForMu(mu, out spanMu);
|
||||
|
||||
if (spanMu > 0.5f)
|
||||
return (cp + 1) % ControlPointCount;
|
||||
else
|
||||
return cp % ControlPointCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the curve parameter (mu) at a certain length of the curve, using linear interpolation
|
||||
* of the values cached in arcLengthTable.
|
||||
*/
|
||||
public float GetMuAtLenght(float length)
|
||||
{
|
||||
if (length <= 0) return 0;
|
||||
if (length >= m_TotalSplineLenght) return 1;
|
||||
|
||||
int i;
|
||||
for (i = 1; i < m_ArcLengthTable.Count; ++i)
|
||||
{
|
||||
if (length < m_ArcLengthTable[i]) break;
|
||||
}
|
||||
|
||||
float prevMu = (i - 1) / (float)(m_ArcLengthTable.Count - 1);
|
||||
float nextMu = i / (float)(m_ArcLengthTable.Count - 1);
|
||||
|
||||
float s = (length - m_ArcLengthTable[i - 1]) / (m_ArcLengthTable[i] - m_ArcLengthTable[i - 1]);
|
||||
|
||||
return prevMu + (nextMu - prevMu) * s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates spline arc lenght in world space using Gauss-Lobatto adaptive integration.
|
||||
* @param acc minimum accuray desired (eg 0.00001f)
|
||||
* @param maxevals maximum number of spline evaluations we want to allow per segment.
|
||||
*/
|
||||
public float RecalculateLenght(Matrix4x4 referenceFrame, float acc, int maxevals)
|
||||
{
|
||||
if (referenceFrame == null)
|
||||
{
|
||||
m_TotalSplineLenght = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_TotalSplineLenght = 0.0f;
|
||||
m_ArcLengthTable.Clear();
|
||||
m_ArcLengthTable.Add(0);
|
||||
|
||||
float step = 1 / (float)(arcLenghtSamples + 1);
|
||||
int controlPoints = ControlPointCount;
|
||||
|
||||
if (controlPoints >= 2)
|
||||
{
|
||||
|
||||
int spans = GetSpanCount();
|
||||
|
||||
for (int cp = 0; cp < spans; ++cp)
|
||||
{
|
||||
int nextCP = (cp + 1) % controlPoints;
|
||||
var wp1 = m_Points[cp];
|
||||
var wp2 = m_Points[nextCP];
|
||||
|
||||
Vector3 _p = referenceFrame.MultiplyPoint3x4(wp1.position);
|
||||
Vector3 p = referenceFrame.MultiplyPoint3x4(wp1.outTangentEndpoint);
|
||||
Vector3 p_ = referenceFrame.MultiplyPoint3x4(wp2.inTangentEndpoint);
|
||||
Vector3 p__ = referenceFrame.MultiplyPoint3x4(wp2.position);
|
||||
|
||||
for (int i = 0; i <= Mathf.Max(1, arcLenghtSamples); ++i)
|
||||
{
|
||||
|
||||
float a = i * step;
|
||||
float b = (i + 1) * step;
|
||||
|
||||
float segmentLength = GaussLobattoIntegrationStep(_p, p, p_, p__, a, b,
|
||||
m_Points.EvaluateFirstDerivative(_p, p, p_, p__, a).magnitude,
|
||||
m_Points.EvaluateFirstDerivative(_p, p, p_, p__, b).magnitude, 0, maxevals, acc);
|
||||
|
||||
m_TotalSplineLenght += segmentLength;
|
||||
|
||||
m_ArcLengthTable.Add(m_TotalSplineLenght);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("A path needs at least 2 control points to be defined.");
|
||||
}
|
||||
|
||||
return m_TotalSplineLenght;
|
||||
}
|
||||
|
||||
/**
|
||||
* One step of the adaptive integration method using Gauss-Lobatto quadrature.
|
||||
* Takes advantage of the fact that the arc lenght of a vector function is equal to the
|
||||
* integral of the magnitude of first derivative.
|
||||
*/
|
||||
private float GaussLobattoIntegrationStep(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4,
|
||||
float a, float b,
|
||||
float fa, float fb, int nevals, int maxevals, float acc)
|
||||
{
|
||||
|
||||
if (nevals >= maxevals) return 0;
|
||||
|
||||
// Constants used in the algorithm
|
||||
float alpha = Mathf.Sqrt(2.0f / 3.0f);
|
||||
float beta = 1.0f / Mathf.Sqrt(5.0f);
|
||||
|
||||
// Here the abcissa points and function values for both the 4-point
|
||||
// and the 7-point rule are calculated (the points at the end of
|
||||
// interval come from the function call, i.e., fa and fb. Also note
|
||||
// the 7-point rule re-uses all the points of the 4-point rule.)
|
||||
float h = (b - a) / 2;
|
||||
float m = (a + b) / 2;
|
||||
|
||||
float mll = m - alpha * h;
|
||||
float ml = m - beta * h;
|
||||
float mr = m + beta * h;
|
||||
float mrr = m + alpha * h;
|
||||
nevals += 5;
|
||||
|
||||
float fmll = m_Points.EvaluateFirstDerivative(p1, p2, p3, p4, mll).magnitude;
|
||||
float fml = m_Points.EvaluateFirstDerivative(p1, p2, p3, p4, ml).magnitude;
|
||||
float fm = m_Points.EvaluateFirstDerivative(p1, p2, p3, p4, m).magnitude;
|
||||
float fmr = m_Points.EvaluateFirstDerivative(p1, p2, p3, p4, mr).magnitude;
|
||||
float fmrr = m_Points.EvaluateFirstDerivative(p1, p2, p3, p4, mrr).magnitude;
|
||||
|
||||
// Both the 4-point and 7-point rule integrals are evaluted
|
||||
float integral4 = (h / 6) * (fa + fb + 5 * (fml + fmr));
|
||||
float integral7 = (h / 1470) * (77 * (fa + fb) + 432 * (fmll + fmrr) + 625 * (fml + fmr) + 672 * fm);
|
||||
|
||||
// The difference betwen the 4-point and 7-point integrals is the
|
||||
// estimate of the accuracy
|
||||
|
||||
if ((integral4 - integral7) < acc || mll <= a || b <= mrr)
|
||||
{
|
||||
if (!(m > a && b > m))
|
||||
{
|
||||
Debug.LogError("Spline integration reached an interval with no more machine numbers");
|
||||
}
|
||||
return integral7;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GaussLobattoIntegrationStep(p1, p2, p3, p4, a, mll, fa, fmll, nevals, maxevals, acc)
|
||||
+ GaussLobattoIntegrationStep(p1, p2, p3, p4, mll, ml, fmll, fml, nevals, maxevals, acc)
|
||||
+ GaussLobattoIntegrationStep(p1, p2, p3, p4, ml, m, fml, fm, nevals, maxevals, acc)
|
||||
+ GaussLobattoIntegrationStep(p1, p2, p3, p4, m, mr, fm, fmr, nevals, maxevals, acc)
|
||||
+ GaussLobattoIntegrationStep(p1, p2, p3, p4, mr, mrr, fmr, fmrr, nevals, maxevals, acc)
|
||||
+ GaussLobattoIntegrationStep(p1, p2, p3, p4, mrr, b, fmrr, fb, nevals, maxevals, acc);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void SetName(int index, string name)
|
||||
{
|
||||
m_Names[index] = name;
|
||||
if (OnControlPointRenamed != null)
|
||||
OnControlPointRenamed.Invoke(index);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public string GetName(int index)
|
||||
{
|
||||
return m_Names[index];
|
||||
}
|
||||
|
||||
public void AddControlPoint(Vector3 position, Vector3 inTangentVector, Vector3 outTangentVector, Vector3 normal, float mass, float rotationalMass, float thickness, int filter, Color color, string name)
|
||||
{
|
||||
InsertControlPoint(ControlPointCount, position, inTangentVector, outTangentVector, normal, mass, rotationalMass, thickness, filter, color, name);
|
||||
}
|
||||
|
||||
public void InsertControlPoint(int index, Vector3 position, Vector3 inTangentVector, Vector3 outTangentVector, Vector3 normal, float mass, float rotationalMass, float thickness, int filter, Color color, string name)
|
||||
{
|
||||
m_Points.data.Insert(index, new ObiWingedPoint(inTangentVector,position,outTangentVector));
|
||||
m_Colors.data.Insert(index, color);
|
||||
m_Normals.data.Insert(index, normal);
|
||||
m_Thickness.data.Insert(index, thickness);
|
||||
m_Masses.data.Insert(index, mass);
|
||||
m_RotationalMasses.data.Insert(index, rotationalMass);
|
||||
m_Filters.data.Insert(index, filter);
|
||||
m_Names.Insert(index,name);
|
||||
|
||||
if (OnControlPointAdded != null)
|
||||
OnControlPointAdded.Invoke(index);
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public int InsertControlPoint(float mu)
|
||||
{
|
||||
|
||||
int controlPoints = ControlPointCount;
|
||||
if (controlPoints >= 2)
|
||||
{
|
||||
|
||||
if (!System.Single.IsNaN(mu))
|
||||
{
|
||||
|
||||
float p;
|
||||
int i = GetSpanControlPointForMu(mu, out p);
|
||||
|
||||
int next = (i + 1) % controlPoints;
|
||||
|
||||
var wp1 = m_Points[i];
|
||||
var wp2 = m_Points[next];
|
||||
|
||||
Vector3 P0_1 = (1 - p) * wp1.position + p * wp1.outTangentEndpoint;
|
||||
Vector3 P1_2 = (1 - p) * wp1.outTangentEndpoint + p * wp2.inTangentEndpoint;
|
||||
Vector3 P2_3 = (1 - p) * wp2.inTangentEndpoint + p * wp2.position;
|
||||
|
||||
Vector3 P01_12 = (1 - p) * P0_1 + p * P1_2;
|
||||
Vector3 P12_23 = (1 - p) * P1_2 + p * P2_3;
|
||||
|
||||
Vector3 P0112_1223 = (1 - p) * P01_12 + p * P12_23;
|
||||
|
||||
wp1.SetOutTangentEndpoint(P0_1);
|
||||
wp2.SetInTangentEndpoint(P2_3);
|
||||
|
||||
m_Points[i] = wp1;
|
||||
m_Points[next] = wp2;
|
||||
|
||||
Color color = m_Colors.Evaluate(m_Colors[i],
|
||||
m_Colors[i],
|
||||
m_Colors[next],
|
||||
m_Colors[next], p);
|
||||
|
||||
Vector3 normal = m_Normals.Evaluate(m_Normals[i],
|
||||
m_Normals[i],
|
||||
m_Normals[next],
|
||||
m_Normals[next], p);
|
||||
|
||||
float thickness = m_Thickness.Evaluate(m_Thickness[i],
|
||||
m_Thickness[i],
|
||||
m_Thickness[next],
|
||||
m_Thickness[next], p);
|
||||
|
||||
float mass = m_Masses.Evaluate(m_Masses[i],
|
||||
m_Masses[i],
|
||||
m_Masses[next],
|
||||
m_Masses[next], p);
|
||||
|
||||
float rotationalMass = m_RotationalMasses.Evaluate(m_RotationalMasses[i],
|
||||
m_RotationalMasses[i],
|
||||
m_RotationalMasses[next],
|
||||
m_RotationalMasses[next], p);
|
||||
|
||||
int filter = m_Filters.Evaluate(m_Filters[i],
|
||||
m_Filters[i],
|
||||
m_Filters[next],
|
||||
m_Filters[next], p);
|
||||
|
||||
InsertControlPoint(i + 1, P0112_1223, P01_12 - P0112_1223, P12_23 - P0112_1223, normal, mass,rotationalMass, thickness, filter, color, GetName(i));
|
||||
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = ControlPointCount-1; i >= 0; --i)
|
||||
RemoveControlPoint(i);
|
||||
|
||||
m_TotalSplineLenght = 0.0f;
|
||||
m_ArcLengthTable.Clear();
|
||||
m_ArcLengthTable.Add(0);
|
||||
}
|
||||
|
||||
public void RemoveControlPoint(int index)
|
||||
{
|
||||
foreach (var channel in GetDataChannels())
|
||||
channel.RemoveAt(index);
|
||||
|
||||
m_Names.RemoveAt(index);
|
||||
|
||||
if (OnControlPointRemoved != null)
|
||||
OnControlPointRemoved.Invoke(index);
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
public void FlushEvents()
|
||||
{
|
||||
bool isDirty = dirty;
|
||||
foreach (var channel in GetDataChannels())
|
||||
{
|
||||
isDirty |= channel.Dirty;
|
||||
channel.Clean();
|
||||
}
|
||||
|
||||
if (OnPathChanged != null && isDirty)
|
||||
{
|
||||
dirty = false;
|
||||
OnPathChanged.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee1575a4e45ef4a8ebafa0e5e2e51da5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public struct ObiPathFrame
|
||||
{
|
||||
public enum Axis
|
||||
{
|
||||
X = 0,
|
||||
Y = 1,
|
||||
Z = 2
|
||||
}
|
||||
|
||||
public Vector3 position;
|
||||
|
||||
public Vector3 tangent;
|
||||
public Vector3 normal;
|
||||
public Vector3 binormal;
|
||||
|
||||
public Vector4 color;
|
||||
public float thickness;
|
||||
|
||||
public ObiPathFrame(Vector3 position, Vector3 tangent, Vector3 normal, Vector3 binormal, Vector4 color, float thickness){
|
||||
this.position = position;
|
||||
this.normal = normal;
|
||||
this.tangent = tangent;
|
||||
this.binormal = binormal;
|
||||
this.color = color;
|
||||
this.thickness = thickness;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
position = Vector3.zero;
|
||||
tangent = Vector3.forward;
|
||||
normal = Vector3.up;
|
||||
binormal = Vector3.right;
|
||||
color = Color.white;
|
||||
thickness = 0;
|
||||
}
|
||||
|
||||
public static ObiPathFrame operator +(ObiPathFrame c1, ObiPathFrame c2)
|
||||
{
|
||||
return new ObiPathFrame(c1.position + c2.position,c1.tangent + c2.tangent,c1.normal + c2.normal,c1.binormal + c2.binormal,c1.color + c2.color, c1.thickness + c2.thickness);
|
||||
}
|
||||
|
||||
public static ObiPathFrame operator *(float f,ObiPathFrame c)
|
||||
{
|
||||
return new ObiPathFrame(c.position * f, c.tangent * f, c.normal * f, c.binormal * f,c.color * f, c.thickness * f);
|
||||
}
|
||||
|
||||
public static void WeightedSum(float w1, float w2, float w3, ref ObiPathFrame c1, ref ObiPathFrame c2, ref ObiPathFrame c3, ref ObiPathFrame sum)
|
||||
{
|
||||
sum.position.x = c1.position.x * w1 + c2.position.x * w2 + c3.position.x * w3;
|
||||
sum.position.y = c1.position.y * w1 + c2.position.y * w2 + c3.position.y * w3;
|
||||
sum.position.z = c1.position.z * w1 + c2.position.z * w2 + c3.position.z * w3;
|
||||
|
||||
sum.tangent.x = c1.tangent.x * w1 + c2.tangent.x * w2 + c3.tangent.x * w3;
|
||||
sum.tangent.y = c1.tangent.y * w1 + c2.tangent.y * w2 + c3.tangent.y * w3;
|
||||
sum.tangent.z = c1.tangent.z * w1 + c2.tangent.z * w2 + c3.tangent.z * w3;
|
||||
|
||||
sum.normal.x = c1.normal.x * w1 + c2.normal.x * w2 + c3.normal.x * w3;
|
||||
sum.normal.y = c1.normal.y * w1 + c2.normal.y * w2 + c3.normal.y * w3;
|
||||
sum.normal.z = c1.normal.z * w1 + c2.normal.z * w2 + c3.normal.z * w3;
|
||||
|
||||
sum.binormal.x = c1.binormal.x * w1 + c2.binormal.x * w2 + c3.binormal.x * w3;
|
||||
sum.binormal.y = c1.binormal.y * w1 + c2.binormal.y * w2 + c3.binormal.y * w3;
|
||||
sum.binormal.z = c1.binormal.z * w1 + c2.binormal.z * w2 + c3.binormal.z * w3;
|
||||
|
||||
sum.color.x = c1.color.x * w1 + c2.color.x * w2 + c3.color.x * w3;
|
||||
sum.color.y = c1.color.y * w1 + c2.color.y * w2 + c3.color.y * w3;
|
||||
sum.color.z = c1.color.z * w1 + c2.color.z * w2 + c3.color.z * w3;
|
||||
sum.color.w = c1.color.w * w1 + c2.color.w * w2 + c3.color.w * w3;
|
||||
|
||||
sum.thickness = c1.thickness * w1 + c2.thickness * w2 + c3.thickness * w3;
|
||||
}
|
||||
|
||||
public void SetTwist(float twist)
|
||||
{
|
||||
Quaternion twistQ = Quaternion.AngleAxis(twist, tangent);
|
||||
normal = twistQ * normal;
|
||||
binormal = twistQ * binormal;
|
||||
}
|
||||
|
||||
public void SetTwistAndTangent(float twist, Vector3 tangent)
|
||||
{
|
||||
this.tangent = tangent;
|
||||
normal = new Vector3(tangent.y, tangent.x, 0).normalized;
|
||||
binormal = Vector3.Cross(normal, tangent);
|
||||
|
||||
Quaternion twistQ = Quaternion.AngleAxis(twist, tangent);
|
||||
normal = twistQ * normal;
|
||||
binormal = twistQ * binormal;
|
||||
}
|
||||
|
||||
public void Transport(ObiPathFrame frame, float twist)
|
||||
{
|
||||
// Calculate delta rotation:
|
||||
Quaternion rotQ = Quaternion.FromToRotation(tangent, frame.tangent);
|
||||
Quaternion twistQ = Quaternion.AngleAxis(twist, frame.tangent);
|
||||
Quaternion finalQ = twistQ * rotQ;
|
||||
|
||||
// Rotate previous frame axes to obtain the new ones:
|
||||
normal = finalQ * normal;
|
||||
binormal = finalQ * binormal;
|
||||
tangent = frame.tangent;
|
||||
position = frame.position;
|
||||
thickness = frame.thickness;
|
||||
color = frame.color;
|
||||
}
|
||||
|
||||
public void Transport(Vector3 newPosition, Vector3 newTangent, float twist)
|
||||
{
|
||||
// Calculate delta rotation:
|
||||
Quaternion rotQ = Quaternion.FromToRotation(tangent, newTangent);
|
||||
Quaternion twistQ = Quaternion.AngleAxis(twist, newTangent);
|
||||
Quaternion finalQ = twistQ * rotQ;
|
||||
|
||||
// Rotate previous frame axes to obtain the new ones:
|
||||
normal = finalQ * normal;
|
||||
binormal = finalQ * binormal;
|
||||
tangent = newTangent;
|
||||
position = newPosition;
|
||||
|
||||
}
|
||||
|
||||
// Transport, hinting the normal.
|
||||
public void Transport(Vector3 newPosition, Vector3 newTangent, Vector3 newNormal, float twist)
|
||||
{
|
||||
normal = Quaternion.AngleAxis(twist, newTangent) * newNormal;
|
||||
tangent = newTangent;
|
||||
binormal = Vector3.Cross(normal, tangent);
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
public Matrix4x4 ToMatrix(Axis mainAxis)
|
||||
{
|
||||
Matrix4x4 basis = new Matrix4x4();
|
||||
|
||||
int xo = ((int)mainAxis) % 3 * 4;
|
||||
int yo = ((int)mainAxis + 1) % 3 * 4;
|
||||
int zo = ((int)mainAxis + 2) % 3 * 4;
|
||||
|
||||
basis[xo] = tangent[0];
|
||||
basis[xo + 1] = tangent[1];
|
||||
basis[xo + 2] = tangent[2];
|
||||
|
||||
basis[yo] = binormal[0];
|
||||
basis[yo + 1] = binormal[1];
|
||||
basis[yo + 2] = binormal[2];
|
||||
|
||||
basis[zo] = normal[0];
|
||||
basis[zo + 1] = normal[1];
|
||||
basis[zo + 2] = normal[2];
|
||||
|
||||
return basis;
|
||||
}
|
||||
|
||||
public void DebugDraw(float size)
|
||||
{
|
||||
Debug.DrawRay(position, binormal * size, Color.red);
|
||||
Debug.DrawRay(position, normal * size, Color.green);
|
||||
Debug.DrawRay(position, tangent * size, Color.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 237e9ecf813f646fd8502768445f4ab6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,379 @@
|
||||
using UnityEngine;
|
||||
using Unity.Profiling;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[ExecuteInEditMode]
|
||||
[RequireComponent(typeof(ObiRopeBase))]
|
||||
public class ObiPathSmoother : MonoBehaviour
|
||||
{
|
||||
static ProfilerMarker m_AllocateRawChunksPerfMarker = new ProfilerMarker("AllocateRawChunks");
|
||||
static ProfilerMarker m_GenerateSmoothChunksPerfMarker = new ProfilerMarker("GenerateSmoothChunks");
|
||||
|
||||
private Matrix4x4 w2l;
|
||||
private Quaternion w2lRotation;
|
||||
|
||||
[Range(0, 1)]
|
||||
[Tooltip("Curvature threshold below which the path will be decimated. A value of 0 won't apply any decimation. As you increase the value, decimation will become more aggresive.")]
|
||||
public float decimation = 0;
|
||||
|
||||
[Range(0, 3)]
|
||||
[Tooltip("Smoothing iterations applied to the path. A smoothing value of 0 won't perform any smoothing at all. Note that smoothing is applied after decimation.")]
|
||||
public uint smoothing = 0;
|
||||
|
||||
[Tooltip("Twist in degrees applied to each sucessive path section.")]
|
||||
public float twist = 0;
|
||||
|
||||
public event ObiActor.ActorCallback OnCurveGenerated;
|
||||
|
||||
protected float smoothLength = 0;
|
||||
protected int smoothSections = 0;
|
||||
|
||||
[HideInInspector] public ObiList<ObiList<ObiPathFrame>> rawChunks = new ObiList<ObiList<ObiPathFrame>>();
|
||||
[HideInInspector] public ObiList<ObiList<ObiPathFrame>> smoothChunks = new ObiList<ObiList<ObiPathFrame>>();
|
||||
private Stack<Vector2Int> stack = new Stack<Vector2Int>();
|
||||
private BitArray decimateBitArray = new BitArray(0);
|
||||
|
||||
public float SmoothLength
|
||||
{
|
||||
get { return smoothLength; }
|
||||
}
|
||||
|
||||
public float SmoothSections
|
||||
{
|
||||
get { return smoothSections; }
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
GetComponent<ObiRopeBase>().OnInterpolate += Actor_OnInterpolate;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
GetComponent<ObiRopeBase>().OnInterpolate -= Actor_OnInterpolate;
|
||||
}
|
||||
|
||||
void Actor_OnInterpolate(ObiActor actor)
|
||||
{
|
||||
GenerateSmoothChunks(((ObiRopeBase)actor), smoothing);
|
||||
|
||||
if (OnCurveGenerated != null)
|
||||
OnCurveGenerated(actor);
|
||||
}
|
||||
|
||||
private void AllocateChunk(int sections)
|
||||
{
|
||||
if (sections > 1)
|
||||
{
|
||||
|
||||
if (rawChunks.Data[rawChunks.Count] == null)
|
||||
{
|
||||
rawChunks.Data[rawChunks.Count] = new ObiList<ObiPathFrame>();
|
||||
smoothChunks.Data[smoothChunks.Count] = new ObiList<ObiPathFrame>();
|
||||
}
|
||||
|
||||
rawChunks.Data[rawChunks.Count].SetCount(sections);
|
||||
|
||||
rawChunks.SetCount(rawChunks.Count + 1);
|
||||
smoothChunks.SetCount(smoothChunks.Count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private float CalculateChunkLength(ObiList<ObiPathFrame> chunk)
|
||||
{
|
||||
float length = 0;
|
||||
for (int i = 1; i < chunk.Count; ++i)
|
||||
length += Vector3.Distance(chunk[i].position, chunk[i - 1].position);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates raw curve chunks from the rope description.
|
||||
*/
|
||||
private void AllocateRawChunks(ObiRopeBase actor)
|
||||
{
|
||||
using (m_AllocateRawChunksPerfMarker.Auto())
|
||||
{
|
||||
rawChunks.Clear();
|
||||
|
||||
if (actor.path == null)
|
||||
return;
|
||||
|
||||
// Count particles for each chunk.
|
||||
int particles = 0;
|
||||
for (int i = 0; i < actor.elements.Count; ++i)
|
||||
{
|
||||
particles++;
|
||||
// At discontinuities, start a new chunk.
|
||||
if (i < actor.elements.Count - 1 && actor.elements[i].particle2 != actor.elements[i + 1].particle1)
|
||||
{
|
||||
AllocateChunk(++particles);
|
||||
particles = 0;
|
||||
}
|
||||
}
|
||||
AllocateChunk(++particles);
|
||||
}
|
||||
}
|
||||
|
||||
private void PathFrameFromParticle(ObiRopeBase actor, ref ObiPathFrame frame, int particleIndex, bool interpolateOrientation = true)
|
||||
{
|
||||
// Update current frame values from particles:
|
||||
frame.position = w2l.MultiplyPoint3x4(actor.GetParticlePosition(particleIndex));
|
||||
frame.thickness = actor.GetParticleMaxRadius(particleIndex);
|
||||
frame.color = actor.GetParticleColor(particleIndex);
|
||||
|
||||
// Use particle orientation if possible:
|
||||
if (actor.usesOrientedParticles)
|
||||
{
|
||||
Quaternion current = actor.GetParticleOrientation(particleIndex);
|
||||
Quaternion previous = actor.GetParticleOrientation(Mathf.Max(0, particleIndex - 1));
|
||||
Quaternion average = w2lRotation * (interpolateOrientation ? Quaternion.SlerpUnclamped(current, previous, 0.5f) : current);
|
||||
frame.normal = average * Vector3.up;
|
||||
frame.binormal = average * Vector3.right;
|
||||
frame.tangent = average * Vector3.forward;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates smooth curve chunks.
|
||||
*/
|
||||
public void GenerateSmoothChunks(ObiRopeBase actor, uint smoothingLevels)
|
||||
{
|
||||
using (m_GenerateSmoothChunksPerfMarker.Auto())
|
||||
{
|
||||
smoothChunks.Clear();
|
||||
smoothSections = 0;
|
||||
smoothLength = 0;
|
||||
|
||||
if (!Application.isPlaying)
|
||||
actor.RebuildElementsFromConstraints();
|
||||
|
||||
AllocateRawChunks(actor);
|
||||
|
||||
w2l = actor.transform.worldToLocalMatrix;
|
||||
w2lRotation = w2l.rotation;
|
||||
|
||||
// keep track of the first element of each chunk
|
||||
int chunkStart = 0;
|
||||
|
||||
ObiPathFrame frame_0 = new ObiPathFrame(); // "next" frame
|
||||
ObiPathFrame frame_1 = new ObiPathFrame(); // current frame
|
||||
ObiPathFrame frame_2 = new ObiPathFrame(); // previous frame
|
||||
|
||||
// generate curve for each rope chunk:
|
||||
for (int i = 0; i < rawChunks.Count; ++i)
|
||||
{
|
||||
int elementCount = rawChunks[i].Count - 1;
|
||||
|
||||
// Initialize frames:
|
||||
frame_0.Reset();
|
||||
frame_1.Reset();
|
||||
frame_2.Reset();
|
||||
|
||||
PathFrameFromParticle(actor, ref frame_1, actor.elements[chunkStart].particle1, false);
|
||||
|
||||
frame_2 = frame_1;
|
||||
|
||||
for (int m = 1; m <= rawChunks[i].Count; ++m)
|
||||
{
|
||||
|
||||
int index;
|
||||
if (m >= elementCount)
|
||||
// second particle of last element in the chunk.
|
||||
index = actor.elements[chunkStart + elementCount - 1].particle2;
|
||||
else
|
||||
//first particle of current element.
|
||||
index = actor.elements[chunkStart + m].particle1;
|
||||
|
||||
// generate curve frame from particle:
|
||||
PathFrameFromParticle(actor, ref frame_0, index);
|
||||
|
||||
if (actor.usesOrientedParticles)
|
||||
{
|
||||
// copy frame directly.
|
||||
frame_2 = frame_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// perform parallel transport, using forward / backward average to calculate tangent.
|
||||
frame_1.tangent = ((frame_1.position - frame_2.position) + (frame_0.position - frame_1.position)).normalized;
|
||||
frame_2.Transport(frame_1, twist);
|
||||
}
|
||||
|
||||
// in case we wrapped around the rope, average first and last frames:
|
||||
if (chunkStart + m > actor.activeParticleCount)
|
||||
{
|
||||
frame_2 = rawChunks[0][0] = 0.5f * frame_2 + 0.5f * rawChunks[0][0];
|
||||
}
|
||||
|
||||
frame_1 = frame_0;
|
||||
|
||||
rawChunks[i][m - 1] = frame_2;
|
||||
}
|
||||
|
||||
// increment chunkStart by the amount of elements in this chunk:
|
||||
chunkStart += elementCount;
|
||||
|
||||
// adaptive curvature-based decimation:
|
||||
if (Decimate(rawChunks[i], smoothChunks[i], decimation))
|
||||
{
|
||||
// if any decimation took place, swap raw and smooth chunks:
|
||||
var aux = rawChunks[i];
|
||||
rawChunks[i] = smoothChunks[i];
|
||||
smoothChunks[i] = aux;
|
||||
}
|
||||
|
||||
// get smooth curve points:
|
||||
Chaikin(rawChunks[i], smoothChunks[i], smoothingLevels);
|
||||
|
||||
// count total curve sections and total curve length:
|
||||
smoothSections += smoothChunks[i].Count;
|
||||
smoothLength += CalculateChunkLength(smoothChunks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObiPathFrame GetSectionAt(float mu)
|
||||
{
|
||||
float edgeMu = smoothSections * Mathf.Clamp(mu,0,0.9999f);
|
||||
int index = (int)edgeMu;
|
||||
float sectionMu = edgeMu - index;
|
||||
|
||||
int counter = 0;
|
||||
int chunkIndex = -1;
|
||||
int indexInChunk = -1;
|
||||
for (int i = 0; i < smoothChunks.Count; ++i)
|
||||
{
|
||||
if (counter + smoothChunks[i].Count > index)
|
||||
{
|
||||
chunkIndex = i;
|
||||
indexInChunk = index - counter;
|
||||
break;
|
||||
}
|
||||
counter += smoothChunks[i].Count;
|
||||
}
|
||||
|
||||
ObiList<ObiPathFrame> chunk = smoothChunks[chunkIndex];
|
||||
ObiPathFrame s1 = chunk[indexInChunk];
|
||||
ObiPathFrame s2 = chunk[Mathf.Min(indexInChunk + 1, chunk.Count - 1)];
|
||||
|
||||
return (1 - sectionMu) * s1 + sectionMu * s2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterative version of the Ramer-Douglas-Peucker path decimation algorithm.
|
||||
*/
|
||||
private bool Decimate(ObiList<ObiPathFrame> input, ObiList<ObiPathFrame> output, float threshold)
|
||||
{
|
||||
// no decimation, no work to do, just return:
|
||||
if (threshold < 0.00001f || input.Count < 3)
|
||||
return false;
|
||||
|
||||
float scaledThreshold = threshold * threshold * 0.01f;
|
||||
|
||||
stack.Push(new Vector2Int(0, input.Count - 1));
|
||||
|
||||
decimateBitArray.Length = Mathf.Max(decimateBitArray.Length, input.Count);
|
||||
decimateBitArray.SetAll(true);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var range = stack.Pop();
|
||||
|
||||
float dmax = 0;
|
||||
int index = range.x;
|
||||
float mu;
|
||||
|
||||
for (int i = index + 1; i < range.y; ++i)
|
||||
{
|
||||
if (decimateBitArray[i])
|
||||
{
|
||||
float d = Vector3.SqrMagnitude(ObiUtils.ProjectPointLine(input[i].position, input[range.x].position, input[range.y].position, out mu) - input[i].position);
|
||||
|
||||
if (d > dmax)
|
||||
{
|
||||
index = i;
|
||||
dmax = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dmax > scaledThreshold)
|
||||
{
|
||||
stack.Push(new Vector2Int(range.x, index));
|
||||
stack.Push(new Vector2Int(index, range.y));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = range.x + 1; i < range.y; ++i)
|
||||
decimateBitArray[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
output.Clear();
|
||||
for (int i = 0; i < input.Count; ++i)
|
||||
if (decimateBitArray[i])
|
||||
output.Add(input[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses a variant of Chainkin's algorithm to produce a smooth curve from a set of control points. It is specially fast
|
||||
* because it directly calculates subdivision level k, instead of recursively calculating levels 1..k.
|
||||
*/
|
||||
private void Chaikin(ObiList<ObiPathFrame> input, ObiList<ObiPathFrame> output, uint k)
|
||||
{
|
||||
// no subdivision levels, no work to do. just copy the input to the output:
|
||||
if (k == 0 || input.Count < 3)
|
||||
{
|
||||
output.SetCount(input.Count);
|
||||
for (int i = 0; i < input.Count; ++i)
|
||||
output[i] = input[i];
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate amount of new points generated by each inner control point:
|
||||
int pCount = (int)Mathf.Pow(2, k);
|
||||
|
||||
// precalculate some quantities:
|
||||
int n0 = input.Count - 1;
|
||||
float twoRaisedToMinusKPlus1 = Mathf.Pow(2, -(k + 1));
|
||||
float twoRaisedToMinusK = Mathf.Pow(2, -k);
|
||||
float twoRaisedToMinus2K = Mathf.Pow(2, -2 * k);
|
||||
float twoRaisedToMinus2KMinus1 = Mathf.Pow(2, -2 * k - 1);
|
||||
|
||||
// allocate ouput:
|
||||
output.SetCount((n0 - 1) * pCount + 2);
|
||||
|
||||
// calculate initial curve points:
|
||||
output[0] = (0.5f + twoRaisedToMinusKPlus1) * input[0] + (0.5f - twoRaisedToMinusKPlus1) * input[1];
|
||||
output[pCount * n0 - pCount + 1] = (0.5f - twoRaisedToMinusKPlus1) * input[n0 - 1] + (0.5f + twoRaisedToMinusKPlus1) * input[n0];
|
||||
|
||||
// calculate internal points:
|
||||
for (int j = 1; j <= pCount; ++j)
|
||||
{
|
||||
// precalculate coefficients:
|
||||
float F = 0.5f - twoRaisedToMinusKPlus1 - (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2KMinus1);
|
||||
float G = 0.5f + twoRaisedToMinusKPlus1 + (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2K);
|
||||
float H = (j - 1) * j * twoRaisedToMinus2KMinus1;
|
||||
|
||||
for (int i = 1; i < n0; ++i)
|
||||
ObiPathFrame.WeightedSum(F, G, H,
|
||||
ref input.Data[i - 1],
|
||||
ref input.Data[i],
|
||||
ref input.Data[i + 1],
|
||||
ref output.Data[(i - 1) * pCount + j]);
|
||||
}
|
||||
|
||||
// make first and last curve points coincide with original points:
|
||||
output[0] = input[0];
|
||||
output[output.Count - 1] = input[input.Count - 1];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 958c969cfb16745f192d4d7bd28b7178
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,100 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[Serializable]
|
||||
public struct ObiWingedPoint
|
||||
{
|
||||
public enum TangentMode
|
||||
{
|
||||
Aligned,
|
||||
Mirrored,
|
||||
Free,
|
||||
}
|
||||
|
||||
public TangentMode tangentMode;
|
||||
public Vector3 inTangent;
|
||||
public Vector3 position;
|
||||
public Vector3 outTangent;
|
||||
|
||||
public Vector3 inTangentEndpoint
|
||||
{
|
||||
get { return position + inTangent; }
|
||||
}
|
||||
|
||||
public Vector3 outTangentEndpoint
|
||||
{
|
||||
get { return position + outTangent; }
|
||||
}
|
||||
|
||||
public ObiWingedPoint(Vector3 inTangent, Vector3 point, Vector3 outTangent)
|
||||
{
|
||||
this.tangentMode = TangentMode.Aligned;
|
||||
this.inTangent = inTangent;
|
||||
this.position = point;
|
||||
this.outTangent = outTangent;
|
||||
}
|
||||
|
||||
public void SetInTangentEndpoint(Vector3 value)
|
||||
{
|
||||
Vector3 newTangent = value - position;
|
||||
|
||||
switch (tangentMode)
|
||||
{
|
||||
case TangentMode.Mirrored: outTangent = -newTangent; break;
|
||||
case TangentMode.Aligned: outTangent = -newTangent.normalized * outTangent.magnitude; break;
|
||||
}
|
||||
|
||||
inTangent = newTangent;
|
||||
}
|
||||
|
||||
public void SetOutTangentEndpoint(Vector3 value)
|
||||
{
|
||||
Vector3 newTangent = value - position;
|
||||
|
||||
switch (tangentMode)
|
||||
{
|
||||
case TangentMode.Mirrored: inTangent = -newTangent; break;
|
||||
case TangentMode.Aligned: inTangent = -newTangent.normalized * inTangent.magnitude; break;
|
||||
}
|
||||
|
||||
outTangent = newTangent;
|
||||
}
|
||||
|
||||
public void SetInTangent(Vector3 value)
|
||||
{
|
||||
Vector3 newTangent = value;
|
||||
|
||||
switch (tangentMode)
|
||||
{
|
||||
case TangentMode.Mirrored: outTangent = -newTangent; break;
|
||||
case TangentMode.Aligned: outTangent = -newTangent.normalized * outTangent.magnitude; break;
|
||||
}
|
||||
|
||||
inTangent = newTangent;
|
||||
}
|
||||
|
||||
public void SetOutTangent(Vector3 value)
|
||||
{
|
||||
Vector3 newTangent = value;
|
||||
|
||||
switch (tangentMode)
|
||||
{
|
||||
case TangentMode.Mirrored: inTangent = -newTangent; break;
|
||||
case TangentMode.Aligned: inTangent = -newTangent.normalized * inTangent.magnitude; break;
|
||||
}
|
||||
|
||||
outTangent = newTangent;
|
||||
}
|
||||
|
||||
public void Transform(Vector3 translation, Quaternion rotation, Vector3 scale)
|
||||
{
|
||||
position += translation;
|
||||
inTangent = rotation * Vector3.Scale(inTangent, scale);
|
||||
outTangent = rotation * Vector3.Scale(outTangent, scale);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76ee869b5f2e5440ea0a1c8a1e161cde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user