去掉obi,使用自写绳索
This commit is contained in:
@@ -1,372 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
[CreateAssetMenu(fileName = "bone blueprint", menuName = "Obi/Bone Blueprint", order = 142)]
|
||||
public class ObiBoneBlueprint : ObiActorBlueprint
|
||||
{
|
||||
public Transform root;
|
||||
|
||||
public const float DEFAULT_PARTICLE_MASS = 0.1f;
|
||||
public const float DEFAULT_PARTICLE_ROTATIONAL_MASS = 0.1f;
|
||||
public const float DEFAULT_PARTICLE_RADIUS = 0.05f;
|
||||
|
||||
[HideInInspector] public List<Transform> transforms = new List<Transform>();
|
||||
[HideInInspector] public List<Quaternion> restTransformOrientations = new List<Quaternion>();
|
||||
[HideInInspector] public List<int> parentIndices = new List<int>();
|
||||
[HideInInspector] public List<float> normalizedLengths = new List<float>();
|
||||
|
||||
[HideInInspector] public int[] deformableEdges = null; /**< Indices of deformable edges (2 per edge)*/
|
||||
|
||||
[HideInInspector] [NonSerialized] public List<ObiBone.IgnoredBone> ignored;
|
||||
[HideInInspector] [NonSerialized] public ObiBone.BonePropertyCurve mass;
|
||||
[HideInInspector] [NonSerialized] public ObiBone.BonePropertyCurve rotationalMass;
|
||||
[HideInInspector] [NonSerialized] public ObiBone.BonePropertyCurve radius;
|
||||
|
||||
public Quaternion root2WorldR;
|
||||
|
||||
private GraphColoring colorizer;
|
||||
|
||||
private ObiBone.IgnoredBone GetIgnoredBone(Transform bone)
|
||||
{
|
||||
for (int i = 0; i < ignored.Count; ++i)
|
||||
if (ignored[i].bone == bone)
|
||||
return ignored[i];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ObiBoneOverride GetOverride(int particleIndex, out float normalizedLength)
|
||||
{
|
||||
int overrideIndex = particleIndex;
|
||||
normalizedLength = normalizedLengths[overrideIndex];
|
||||
|
||||
while (overrideIndex >= 0)
|
||||
{
|
||||
if (transforms[overrideIndex].TryGetComponent(out ObiBoneOverride over))
|
||||
{
|
||||
normalizedLength = 1 - (1 - normalizedLengths[particleIndex]) / Mathf.Max(ObiUtils.epsilon,1 - normalizedLengths[overrideIndex]);
|
||||
return over;
|
||||
}
|
||||
|
||||
overrideIndex = parentIndices[overrideIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IEnumerator Initialize()
|
||||
{
|
||||
ClearParticleGroups();
|
||||
|
||||
transforms.Clear();
|
||||
restTransformOrientations.Clear();
|
||||
parentIndices.Clear();
|
||||
normalizedLengths.Clear();
|
||||
|
||||
List<Vector3> particlePositions = new List<Vector3>();
|
||||
List<Quaternion> particleOrientations = new List<Quaternion>();
|
||||
|
||||
var world2Root = root.transform.worldToLocalMatrix;
|
||||
var world2RootR = world2Root.rotation;
|
||||
root2WorldR = Quaternion.Inverse(world2RootR);
|
||||
|
||||
// create a queue to traverse the hierarchy in a width-first fashion.
|
||||
Queue<Transform> bones = new Queue<Transform>();
|
||||
|
||||
// insert the root bone:
|
||||
bones.Enqueue(root);
|
||||
parentIndices.Add(-1);
|
||||
normalizedLengths.Add(0);
|
||||
|
||||
// initialize hierarchy length:
|
||||
float maxLength = 0;
|
||||
|
||||
while (bones.Count > 0)
|
||||
{
|
||||
var bone = bones.Dequeue();
|
||||
|
||||
if (bone != null)
|
||||
{
|
||||
var ig = GetIgnoredBone(bone);
|
||||
|
||||
if (ig == null)
|
||||
{
|
||||
transforms.Add(bone);
|
||||
restTransformOrientations.Add(bone.localRotation);
|
||||
particlePositions.Add(world2Root.MultiplyPoint3x4(bone.position));
|
||||
particleOrientations.Add(world2RootR * bone.rotation);
|
||||
}
|
||||
|
||||
if (ig == null || !ig.ignoreChildren)
|
||||
{
|
||||
foreach (Transform child in bone)
|
||||
{
|
||||
ig = GetIgnoredBone(child);
|
||||
|
||||
if (ig == null)
|
||||
{
|
||||
int parentIndex = transforms.Count - 1;
|
||||
parentIndices.Add(parentIndex);
|
||||
|
||||
float distanceToParent = Vector3.Distance(child.position, bone.position);
|
||||
float distanceToRoot = normalizedLengths[parentIndex] + distanceToParent;
|
||||
maxLength = Mathf.Max(maxLength, distanceToRoot);
|
||||
normalizedLengths.Add(distanceToRoot);
|
||||
}
|
||||
|
||||
bones.Enqueue(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// normalize lengths:
|
||||
if (maxLength > 0)
|
||||
{
|
||||
for (int i = 0; i < normalizedLengths.Count; ++i)
|
||||
normalizedLengths[i] /= maxLength;
|
||||
}
|
||||
|
||||
|
||||
// calculate orientations that minimize the Darboux vector:
|
||||
Vector3[] avgChildrenDirection = new Vector3[parentIndices.Count];
|
||||
int[] childCount = new int[parentIndices.Count];
|
||||
|
||||
for (int i = 0; i < parentIndices.Count; ++i)
|
||||
{
|
||||
int parent = parentIndices[i];
|
||||
if (parent >= 0)
|
||||
{
|
||||
var vector = particlePositions[i] - particlePositions[parent];
|
||||
avgChildrenDirection[parent] += vector;
|
||||
childCount[parent]++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < parentIndices.Count; ++i)
|
||||
{
|
||||
if (childCount[i] > 0)
|
||||
particleOrientations[i] = Quaternion.LookRotation(avgChildrenDirection[i] / childCount[i]);
|
||||
else if (parentIndices[i] >= 0)
|
||||
particleOrientations[i] = particleOrientations[parentIndices[i]];
|
||||
}
|
||||
|
||||
|
||||
m_ActiveParticleCount = particlePositions.Count;
|
||||
|
||||
positions = new Vector3[m_ActiveParticleCount];
|
||||
orientations = new Quaternion[m_ActiveParticleCount];
|
||||
velocities = new Vector3[m_ActiveParticleCount];
|
||||
angularVelocities = new Vector3[m_ActiveParticleCount];
|
||||
invMasses = new float[m_ActiveParticleCount];
|
||||
invRotationalMasses = new float[m_ActiveParticleCount];
|
||||
principalRadii = new Vector3[m_ActiveParticleCount];
|
||||
filters = new int[m_ActiveParticleCount];
|
||||
restPositions = new Vector4[m_ActiveParticleCount];
|
||||
restOrientations = new Quaternion[m_ActiveParticleCount];
|
||||
colors = new Color[m_ActiveParticleCount];
|
||||
|
||||
for (int i = 0; i < m_ActiveParticleCount; i++)
|
||||
{
|
||||
invMasses[i] = ObiUtils.MassToInvMass(mass != null ? mass.Evaluate(normalizedLengths[i]) : DEFAULT_PARTICLE_MASS);
|
||||
invRotationalMasses[i] = ObiUtils.MassToInvMass(rotationalMass != null ? rotationalMass.Evaluate(normalizedLengths[i]) : DEFAULT_PARTICLE_ROTATIONAL_MASS);
|
||||
positions[i] = particlePositions[i];
|
||||
restPositions[i] = positions[i];
|
||||
restPositions[i][3] = 1; // activate rest position.
|
||||
orientations[i] = particleOrientations[i];
|
||||
restOrientations[i] = /*world2RootR */ transforms[i].rotation;
|
||||
principalRadii[i] = Vector3.one * (radius != null ? radius.Evaluate(normalizedLengths[i]) : DEFAULT_PARTICLE_RADIUS);
|
||||
filters[i] = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 0);
|
||||
colors[i] = Color.white;
|
||||
|
||||
var group = ScriptableObject.CreateInstance<ObiParticleGroup>();
|
||||
group.SetSourceBlueprint(this);
|
||||
group.name = transforms[i].name;
|
||||
group.particleIndices.Add(i);
|
||||
groups.Add(group);
|
||||
|
||||
if (i % 100 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRod: generating particles...", i / (float)m_ActiveParticleCount);
|
||||
}
|
||||
|
||||
colorizer = new GraphColoring(m_ActiveParticleCount);
|
||||
|
||||
// Deformable edges:
|
||||
CreateDeformableEdges();
|
||||
|
||||
// Create edge simplices:
|
||||
CreateSimplices();
|
||||
|
||||
// Create stretch constraints:
|
||||
IEnumerator dc = CreateStretchShearConstraints(particlePositions);
|
||||
while (dc.MoveNext()) yield return dc.Current;
|
||||
|
||||
// Create bending constraints:
|
||||
IEnumerator bc = CreateBendTwistConstraints(particlePositions);
|
||||
while (bc.MoveNext()) yield return bc.Current;
|
||||
|
||||
// Create skin constraints:
|
||||
IEnumerator sc = CreateSkinConstraints(particlePositions);
|
||||
while (sc.MoveNext()) yield return sc.Current;
|
||||
|
||||
// Create aerodynamic constraints:
|
||||
IEnumerator ac = CreateAerodynamicConstraints();
|
||||
while (ac.MoveNext()) yield return ac.Current;
|
||||
|
||||
yield return new CoroutineJob.ProgressInfo("ObiBone: complete", 1);
|
||||
}
|
||||
|
||||
protected void CreateDeformableEdges()
|
||||
{
|
||||
deformableEdges = new int[(parentIndices.Count - 1) * 2];
|
||||
for (int i = 0; i < parentIndices.Count - 1; ++i)
|
||||
{
|
||||
deformableEdges[i * 2] = i + 1;
|
||||
deformableEdges[i * 2 + 1] = parentIndices[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateSimplices()
|
||||
{
|
||||
edges = new int[(parentIndices.Count - 1) * 2];
|
||||
for (int i = 0; i < parentIndices.Count-1; ++i)
|
||||
{
|
||||
edges[i * 2] = i + 1;
|
||||
edges[i * 2 + 1] = parentIndices[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateAerodynamicConstraints()
|
||||
{
|
||||
aerodynamicConstraintsData = new ObiAerodynamicConstraintsData();
|
||||
var aeroBatch = new ObiAerodynamicConstraintsBatch();
|
||||
aerodynamicConstraintsData.AddBatch(aeroBatch);
|
||||
|
||||
for (int i = 0; i < m_ActiveParticleCount; i++)
|
||||
{
|
||||
aeroBatch.AddConstraint(i, 2 * principalRadii[i].x, 1, 1);
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope generating aerodynamic constraints...", i / (float)m_ActiveParticleCount);
|
||||
}
|
||||
|
||||
// Set initial amount of active constraints:
|
||||
for (int i = 0; i < aerodynamicConstraintsData.batches.Count; ++i)
|
||||
{
|
||||
aerodynamicConstraintsData.batches[i].activeConstraintCount = m_ActiveParticleCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateStretchShearConstraints(List<Vector3> particlePositions)
|
||||
{
|
||||
colorizer.Clear();
|
||||
|
||||
for (int i = 1; i < particlePositions.Count; ++i)
|
||||
{
|
||||
int parent = parentIndices[i];
|
||||
if (parent >= 0)
|
||||
{
|
||||
colorizer.AddConstraint(new[] { parent, i });
|
||||
}
|
||||
}
|
||||
|
||||
stretchShearConstraintsData = new ObiStretchShearConstraintsData();
|
||||
|
||||
List<int> constraintColors = new List<int>();
|
||||
var colorize = colorizer.Colorize("ObiBone: coloring stretch/shear constraints...", constraintColors);
|
||||
while (colorize.MoveNext())
|
||||
yield return colorize.Current;
|
||||
|
||||
var particleIndices = colorizer.particleIndices;
|
||||
var constraintIndices = colorizer.constraintIndices;
|
||||
|
||||
for (int i = 0; i < constraintColors.Count; ++i)
|
||||
{
|
||||
int color = constraintColors[i];
|
||||
int cIndex = constraintIndices[i];
|
||||
|
||||
// Add a new batch if needed:
|
||||
if (color >= stretchShearConstraintsData.batchCount)
|
||||
stretchShearConstraintsData.AddBatch(new ObiStretchShearConstraintsBatch());
|
||||
|
||||
int index1 = particleIndices[cIndex];
|
||||
int index2 = particleIndices[cIndex + 1];
|
||||
|
||||
var vector = particlePositions[index2] - particlePositions[index1];
|
||||
var rest = Quaternion.LookRotation(Quaternion.Inverse(orientations[index1]) * vector);
|
||||
|
||||
stretchShearConstraintsData.batches[color].AddConstraint(new Vector2Int(index1, index2), index1, vector.magnitude, rest);
|
||||
stretchShearConstraintsData.batches[color].activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiBone: generating stretch constraints...", i / constraintColors.Count);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateBendTwistConstraints(List<Vector3> particlePositions)
|
||||
{
|
||||
colorizer.Clear();
|
||||
|
||||
for (int i = 1; i < particlePositions.Count; ++i)
|
||||
{
|
||||
int parent = parentIndices[i];
|
||||
if (parent >= 0)
|
||||
{
|
||||
colorizer.AddConstraint(new[] { parent, i });
|
||||
}
|
||||
}
|
||||
|
||||
bendTwistConstraintsData = new ObiBendTwistConstraintsData();
|
||||
|
||||
List<int> constraintColors = new List<int>();
|
||||
var colorize = colorizer.Colorize("ObiBone: colorizing bend/twist constraints...", constraintColors);
|
||||
while (colorize.MoveNext())
|
||||
yield return colorize.Current;
|
||||
|
||||
var particleIndices = colorizer.particleIndices;
|
||||
var constraintIndices = colorizer.constraintIndices;
|
||||
|
||||
for (int i = 0; i < constraintColors.Count; ++i)
|
||||
{
|
||||
int color = constraintColors[i];
|
||||
int cIndex = constraintIndices[i];
|
||||
|
||||
// Add a new batch if needed:
|
||||
if (color >= bendTwistConstraintsData.batchCount)
|
||||
bendTwistConstraintsData.AddBatch(new ObiBendTwistConstraintsBatch());
|
||||
|
||||
int index1 = particleIndices[cIndex];
|
||||
int index2 = particleIndices[cIndex + 1];
|
||||
|
||||
Quaternion darboux = ObiUtils.RestDarboux(orientations[index1], orientations[index2]);
|
||||
bendTwistConstraintsData.batches[color].AddConstraint(new Vector2Int(index1, index2), darboux);
|
||||
bendTwistConstraintsData.batches[color].activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiBone: generating bend constraints...", i / constraintColors.Count);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateSkinConstraints(List<Vector3> particlePositions)
|
||||
{
|
||||
skinConstraintsData = new ObiSkinConstraintsData();
|
||||
ObiSkinConstraintsBatch skinBatch = new ObiSkinConstraintsBatch();
|
||||
skinConstraintsData.AddBatch(skinBatch);
|
||||
|
||||
for (int i = 0; i < particlePositions.Count; ++i)
|
||||
{
|
||||
skinBatch.AddConstraint(i, particlePositions[i], Vector3.up, 0, 0, 0, 0);
|
||||
skinBatch.activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiCloth: generating skin constraints...", i / (float)particlePositions.Count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1df6a43b2a0884e8da322992da465f32
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: d0123218ec6144d0983c099fc7339924, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,288 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
|
||||
[CreateAssetMenu(fileName = "rod blueprint", menuName = "Obi/Rod Blueprint", order = 141)]
|
||||
public class ObiRodBlueprint : ObiRopeBlueprintBase
|
||||
{
|
||||
|
||||
public bool keepInitialShape = true;
|
||||
|
||||
public const float DEFAULT_PARTICLE_MASS = 0.1f;
|
||||
public const float DEFAULT_PARTICLE_ROTATIONAL_MASS = 0.01f;
|
||||
|
||||
|
||||
protected override IEnumerator Initialize()
|
||||
{
|
||||
|
||||
if (path.ControlPointCount < 2)
|
||||
{
|
||||
ClearParticleGroups();
|
||||
path.InsertControlPoint(0, Vector3.left, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, DEFAULT_PARTICLE_ROTATIONAL_MASS, 1, ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 1), Color.white, "control point");
|
||||
path.InsertControlPoint(1, Vector3.right, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, DEFAULT_PARTICLE_ROTATIONAL_MASS, 1, ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 1), Color.white, "control point");
|
||||
}
|
||||
|
||||
path.RecalculateLenght(Matrix4x4.identity, 0.00001f, 7);
|
||||
|
||||
List<Vector3> particlePositions = new List<Vector3>();
|
||||
List<Vector3> particleNormals = new List<Vector3>();
|
||||
List<float> particleThicknesses = new List<float>();
|
||||
List<float> particleInvMasses = new List<float>();
|
||||
List<float> particleInvRotationalMasses = new List<float>();
|
||||
List<int> particleFilters = new List<int>();
|
||||
List<Color> particleColors = new List<Color>();
|
||||
|
||||
// In case the path is open, add a first particle. In closed paths, the last particle is also the first one.
|
||||
if (!path.Closed)
|
||||
{
|
||||
particlePositions.Add(path.points.GetPositionAtMu(path.Closed, 0));
|
||||
particleNormals.Add(path.normals.GetAtMu(path.Closed, 0));
|
||||
particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, 0));
|
||||
particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, 0)));
|
||||
particleInvRotationalMasses.Add(ObiUtils.MassToInvMass(path.rotationalMasses.GetAtMu(path.Closed, 0)));
|
||||
particleFilters.Add(path.filters.GetAtMu(path.Closed, 0));
|
||||
particleColors.Add(path.colors.GetAtMu(path.Closed, 0));
|
||||
}
|
||||
|
||||
// Create a particle group for the first control point:
|
||||
groups[0].particleIndices.Clear();
|
||||
groups[0].particleIndices.Add(0);
|
||||
|
||||
ReadOnlyCollection<float> lengthTable = path.ArcLengthTable;
|
||||
int spans = path.GetSpanCount();
|
||||
|
||||
for (int i = 0; i < spans; i++)
|
||||
{
|
||||
int firstArcLengthSample = i * (path.ArcLengthSamples + 1);
|
||||
int lastArcLengthSample = (i + 1) * (path.ArcLengthSamples + 1);
|
||||
|
||||
float upToSpanLength = lengthTable[firstArcLengthSample];
|
||||
float spanLength = lengthTable[lastArcLengthSample] - upToSpanLength;
|
||||
|
||||
int particlesInSpan = 1 + Mathf.FloorToInt(spanLength / thickness * resolution);
|
||||
float distance = spanLength / particlesInSpan;
|
||||
|
||||
for (int j = 0; j < particlesInSpan; ++j)
|
||||
{
|
||||
float mu = path.GetMuAtLenght(upToSpanLength + distance * (j + 1));
|
||||
particlePositions.Add(path.points.GetPositionAtMu(path.Closed, mu));
|
||||
particleNormals.Add(path.normals.GetAtMu(path.Closed, mu));
|
||||
particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, mu));
|
||||
particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, mu)));
|
||||
particleInvRotationalMasses.Add(ObiUtils.MassToInvMass(path.rotationalMasses.GetAtMu(path.Closed, mu)));
|
||||
particleFilters.Add(path.filters.GetAtMu(path.Closed, mu));
|
||||
particleColors.Add(path.colors.GetAtMu(path.Closed, mu));
|
||||
}
|
||||
|
||||
// Create a particle group for each control point:
|
||||
if (!(path.Closed && i == spans - 1))
|
||||
{
|
||||
groups[i + 1].particleIndices.Clear();
|
||||
groups[i + 1].particleIndices.Add(particlePositions.Count - 1);
|
||||
}
|
||||
|
||||
if (i % 100 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)spans);
|
||||
}
|
||||
|
||||
m_ActiveParticleCount = particlePositions.Count;
|
||||
totalParticles = m_ActiveParticleCount;
|
||||
|
||||
int numSegments = m_ActiveParticleCount - (path.Closed ? 0 : 1);
|
||||
if (numSegments > 0)
|
||||
m_InterParticleDistance = path.Length / (float)numSegments;
|
||||
else
|
||||
m_InterParticleDistance = 0;
|
||||
|
||||
positions = new Vector3[totalParticles];
|
||||
orientations = new Quaternion[totalParticles];
|
||||
velocities = new Vector3[totalParticles];
|
||||
angularVelocities = new Vector3[totalParticles];
|
||||
invMasses = new float[totalParticles];
|
||||
invRotationalMasses = new float[totalParticles];
|
||||
principalRadii = new Vector3[totalParticles];
|
||||
filters = new int[totalParticles];
|
||||
restPositions = new Vector4[totalParticles];
|
||||
restOrientations = new Quaternion[totalParticles];
|
||||
colors = new Color[totalParticles];
|
||||
restLengths = new float[totalParticles];
|
||||
|
||||
for (int i = 0; i < m_ActiveParticleCount; i++)
|
||||
{
|
||||
invMasses[i] = particleInvMasses[i];
|
||||
invRotationalMasses[i] = particleInvRotationalMasses[i];
|
||||
positions[i] = particlePositions[i];
|
||||
restPositions[i] = positions[i];
|
||||
restPositions[i][3] = 1; // activate rest position.
|
||||
principalRadii[i] = Vector3.one * particleThicknesses[i] * thickness;
|
||||
filters[i] = particleFilters[i];
|
||||
colors[i] = particleColors[i];
|
||||
|
||||
if (i % 100 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRod: generating particles...", i / (float)m_ActiveParticleCount);
|
||||
}
|
||||
|
||||
// Deformable edges:
|
||||
CreateDeformableEdges(numSegments);
|
||||
|
||||
// Create edge simplices:
|
||||
CreateSimplices(numSegments);
|
||||
|
||||
// Create distance constraints for the total number of particles, but only activate for the used ones.
|
||||
IEnumerator dc = CreateStretchShearConstraints(particleNormals);
|
||||
|
||||
while (dc.MoveNext())
|
||||
yield return dc.Current;
|
||||
|
||||
// Create bending constraints:
|
||||
IEnumerator bc = CreateBendTwistConstraints();
|
||||
|
||||
while (bc.MoveNext())
|
||||
yield return bc.Current;
|
||||
|
||||
// Create aerodynamic constraints:
|
||||
IEnumerator ac = CreateAerodynamicConstraints();
|
||||
|
||||
while (ac.MoveNext())
|
||||
yield return ac.Current;
|
||||
|
||||
// Create chain constraints:
|
||||
IEnumerator cc = CreateChainConstraints();
|
||||
|
||||
while (cc.MoveNext())
|
||||
yield return cc.Current;
|
||||
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateStretchShearConstraints(List<Vector3> particleNormals)
|
||||
{
|
||||
stretchShearConstraintsData = new ObiStretchShearConstraintsData();
|
||||
|
||||
stretchShearConstraintsData.AddBatch(new ObiStretchShearConstraintsBatch());
|
||||
stretchShearConstraintsData.AddBatch(new ObiStretchShearConstraintsBatch());
|
||||
|
||||
// rotation minimizing frame:
|
||||
ObiPathFrame frame = ObiPathFrame.Identity;
|
||||
|
||||
for (int i = 0; i < totalParticles - 1; i++)
|
||||
{
|
||||
var batch = stretchShearConstraintsData.batches[i % 2] as ObiStretchShearConstraintsBatch;
|
||||
|
||||
Vector2Int indices = new Vector2Int(i, i + 1);
|
||||
Vector3 d = positions[indices.y] - positions[indices.x];
|
||||
restLengths[i] = d.magnitude;
|
||||
|
||||
frame.Transport(positions[indices.x], d.normalized, 0);
|
||||
|
||||
orientations[i] = Quaternion.LookRotation(frame.tangent, particleNormals[indices.x]);
|
||||
restOrientations[i] = orientations[i];
|
||||
|
||||
// Also set the orientation of the next particle. If it is not the last one, we will overwrite it.
|
||||
// This makes sure that open rods provide an orientation for their last particle (or rather, a phantom segment past the last particle).
|
||||
|
||||
orientations[indices.y] = orientations[i];
|
||||
restOrientations[indices.y] = orientations[i];
|
||||
|
||||
batch.AddConstraint(indices, indices.x, restLengths[i], Quaternion.identity);
|
||||
batch.activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)(totalParticles - 1));
|
||||
|
||||
}
|
||||
|
||||
// if the path is closed, add the last, loop closing constraint to a new batch to avoid sharing particles.
|
||||
if (path.Closed)
|
||||
{
|
||||
var loopClosingBatch = new ObiStretchShearConstraintsBatch();
|
||||
stretchShearConstraintsData.AddBatch(loopClosingBatch);
|
||||
|
||||
Vector2Int indices = new Vector2Int(m_ActiveParticleCount - 1, 0);
|
||||
Vector3 d = positions[indices.y] - positions[indices.x];
|
||||
restLengths[m_ActiveParticleCount - 2] = d.magnitude;
|
||||
|
||||
frame.Transport(positions[indices.x], d.normalized, 0);
|
||||
|
||||
orientations[m_ActiveParticleCount - 1] = Quaternion.LookRotation(frame.tangent, particleNormals[indices.x]);
|
||||
restOrientations[m_ActiveParticleCount - 1] = orientations[m_ActiveParticleCount - 1];
|
||||
|
||||
loopClosingBatch.AddConstraint(indices, indices.x, restLengths[m_ActiveParticleCount - 2], Quaternion.identity);
|
||||
loopClosingBatch.activeConstraintCount++;
|
||||
}
|
||||
|
||||
// Recalculate rest length:
|
||||
m_RestLength = 0;
|
||||
foreach (float length in restLengths)
|
||||
m_RestLength += length;
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateBendTwistConstraints()
|
||||
{
|
||||
bendTwistConstraintsData = new ObiBendTwistConstraintsData();
|
||||
|
||||
// Add two batches:
|
||||
bendTwistConstraintsData.AddBatch(new ObiBendTwistConstraintsBatch());
|
||||
bendTwistConstraintsData.AddBatch(new ObiBendTwistConstraintsBatch());
|
||||
|
||||
// the last bend constraint couples the last segment and a phantom segment past the last particle.
|
||||
for (int i = 0; i < totalParticles - 1; i++)
|
||||
{
|
||||
|
||||
var batch = bendTwistConstraintsData.batches[i % 2] as ObiBendTwistConstraintsBatch;
|
||||
|
||||
Vector2Int indices = new Vector2Int(i, i + 1);
|
||||
|
||||
Quaternion darboux = keepInitialShape ? ObiUtils.RestDarboux(orientations[indices.x], orientations[indices.y]) : Quaternion.identity;
|
||||
batch.AddConstraint(indices, darboux);
|
||||
batch.activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)(totalParticles - 1));
|
||||
|
||||
}
|
||||
|
||||
// if the path is closed, add the last, loop closing constraints to a new batch to avoid sharing particles.
|
||||
if (path.Closed)
|
||||
{
|
||||
var loopClosingBatch = new ObiBendTwistConstraintsBatch();
|
||||
bendTwistConstraintsData.AddBatch(loopClosingBatch);
|
||||
|
||||
Vector2Int indices = new Vector2Int(m_ActiveParticleCount - 1, 0);
|
||||
Quaternion darboux = keepInitialShape ? ObiUtils.RestDarboux(orientations[indices.x], orientations[indices.y]) : Quaternion.identity;
|
||||
loopClosingBatch.AddConstraint(indices, darboux);
|
||||
loopClosingBatch.activeConstraintCount++;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateChainConstraints()
|
||||
{
|
||||
chainConstraintsData = new ObiChainConstraintsData();
|
||||
|
||||
// Add a single batch:
|
||||
var batch = new ObiChainConstraintsBatch();
|
||||
chainConstraintsData.AddBatch(batch);
|
||||
|
||||
int[] indices = new int[m_ActiveParticleCount + (path.Closed ? 1 : 0)];
|
||||
|
||||
for (int i = 0; i < m_ActiveParticleCount; ++i)
|
||||
indices[i] = i;
|
||||
|
||||
// Add the first particle as the last index of the chain, if closed.
|
||||
if (path.Closed)
|
||||
indices[m_ActiveParticleCount] = 0;
|
||||
|
||||
// TODO: variable distance between particles:
|
||||
batch.AddConstraint(indices, m_InterParticleDistance, 1, 1);
|
||||
batch.activeConstraintCount++;
|
||||
|
||||
yield return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e028b099aa5d14e399bc3d87df5b7737
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: d0123218ec6144d0983c099fc7339924, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,236 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[CreateAssetMenu(fileName = "rope blueprint", menuName = "Obi/Rope Blueprint", order = 140)]
|
||||
public class ObiRopeBlueprint : ObiRopeBlueprintBase
|
||||
{
|
||||
public int pooledParticles = 100;
|
||||
|
||||
public const float DEFAULT_PARTICLE_MASS = 0.1f;
|
||||
|
||||
protected override IEnumerator Initialize()
|
||||
{
|
||||
if (path.ControlPointCount < 2)
|
||||
{
|
||||
ClearParticleGroups();
|
||||
path.InsertControlPoint(0, Vector3.left, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, 1, 1, ObiUtils.MakeFilter(ObiUtils.CollideWithEverything,1), Color.white, "control point");
|
||||
path.InsertControlPoint(1, Vector3.right, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, 1, 1, ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 1), Color.white, "control point");
|
||||
}
|
||||
|
||||
path.RecalculateLenght(Matrix4x4.identity, 0.00001f, 7);
|
||||
|
||||
List<Vector3> particlePositions = new List<Vector3>();
|
||||
List<float> particleThicknesses = new List<float>();
|
||||
List<float> particleInvMasses = new List<float>();
|
||||
List<int> particleFilters = new List<int>();
|
||||
List<Color> particleColors = new List<Color>();
|
||||
|
||||
// In case the path is open, add a first particle. In closed paths, the last particle is also the first one.
|
||||
if (!path.Closed)
|
||||
{
|
||||
particlePositions.Add(path.points.GetPositionAtMu(path.Closed, 0));
|
||||
particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, 0));
|
||||
particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, 0)));
|
||||
particleFilters.Add(path.filters.GetAtMu(path.Closed, 0));
|
||||
particleColors.Add(path.colors.GetAtMu(path.Closed, 0));
|
||||
}
|
||||
|
||||
// Create a particle group for the first control point:
|
||||
groups[0].particleIndices.Clear();
|
||||
groups[0].particleIndices.Add(0);
|
||||
|
||||
ReadOnlyCollection<float> lengthTable = path.ArcLengthTable;
|
||||
int spans = path.GetSpanCount();
|
||||
|
||||
for (int i = 0; i < spans; i++)
|
||||
{
|
||||
int firstArcLengthSample = i * (path.ArcLengthSamples + 1);
|
||||
int lastArcLengthSample = (i + 1) * (path.ArcLengthSamples + 1);
|
||||
|
||||
float upToSpanLength = lengthTable[firstArcLengthSample];
|
||||
float spanLength = lengthTable[lastArcLengthSample] - upToSpanLength;
|
||||
|
||||
int particlesInSpan = 1 + Mathf.FloorToInt(spanLength / thickness * resolution);
|
||||
float distance = spanLength / particlesInSpan;
|
||||
|
||||
for (int j = 0; j < particlesInSpan; ++j)
|
||||
{
|
||||
float mu = path.GetMuAtLenght(upToSpanLength + distance * (j + 1));
|
||||
particlePositions.Add(path.points.GetPositionAtMu(path.Closed, mu));
|
||||
particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, mu));
|
||||
particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, mu)));
|
||||
particleFilters.Add(path.filters.GetAtMu(path.Closed, mu));
|
||||
particleColors.Add(path.colors.GetAtMu(path.Closed, mu));
|
||||
}
|
||||
|
||||
// Create a particle group for each control point:
|
||||
if (!(path.Closed && i == spans - 1))
|
||||
{
|
||||
groups[i + 1].particleIndices.Clear();
|
||||
groups[i + 1].particleIndices.Add(particlePositions.Count - 1);
|
||||
}
|
||||
|
||||
if (i % 100 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)spans);
|
||||
}
|
||||
|
||||
m_ActiveParticleCount = particlePositions.Count;
|
||||
totalParticles = m_ActiveParticleCount + pooledParticles;
|
||||
|
||||
int numSegments = m_ActiveParticleCount - (path.Closed ? 0 : 1);
|
||||
if (numSegments > 0)
|
||||
m_InterParticleDistance = path.Length / (float)numSegments;
|
||||
else
|
||||
m_InterParticleDistance = 0;
|
||||
|
||||
positions = new Vector3[totalParticles];
|
||||
restPositions = new Vector4[totalParticles];
|
||||
velocities = new Vector3[totalParticles];
|
||||
invMasses = new float[totalParticles];
|
||||
principalRadii = new Vector3[totalParticles];
|
||||
filters = new int[totalParticles];
|
||||
colors = new Color[totalParticles];
|
||||
restLengths = new float[totalParticles];
|
||||
|
||||
for (int i = 0; i < m_ActiveParticleCount; i++)
|
||||
{
|
||||
invMasses[i] = particleInvMasses[i];
|
||||
positions[i] = particlePositions[i];
|
||||
restPositions[i] = positions[i];
|
||||
restPositions[i][3] = 1; // activate rest position.
|
||||
principalRadii[i] = Vector3.one * particleThicknesses[i] * thickness;
|
||||
filters[i] = particleFilters[i];
|
||||
colors[i] = particleColors[i];
|
||||
|
||||
if (i % 100 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)m_ActiveParticleCount);
|
||||
}
|
||||
|
||||
// Deformable edges:
|
||||
CreateDeformableEdges(numSegments);
|
||||
|
||||
// Create edge simplices:
|
||||
CreateSimplices(numSegments);
|
||||
|
||||
//Create distance constraints for the total number of particles, but only activate for the used ones.
|
||||
IEnumerator dc = CreateDistanceConstraints();
|
||||
|
||||
while (dc.MoveNext())
|
||||
yield return dc.Current;
|
||||
|
||||
//Create bending constraints:
|
||||
IEnumerator bc = CreateBendingConstraints();
|
||||
|
||||
while (bc.MoveNext())
|
||||
yield return bc.Current;
|
||||
|
||||
// Create aerodynamic constraints:
|
||||
IEnumerator ac = CreateAerodynamicConstraints();
|
||||
|
||||
while (ac.MoveNext())
|
||||
yield return ac.Current;
|
||||
|
||||
// Recalculate rest length:
|
||||
m_RestLength = 0;
|
||||
foreach (float length in restLengths)
|
||||
m_RestLength += length;
|
||||
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateDistanceConstraints()
|
||||
{
|
||||
distanceConstraintsData = new ObiDistanceConstraintsData();
|
||||
|
||||
// Add two batches: for even and odd constraints:
|
||||
distanceConstraintsData.AddBatch(new ObiDistanceConstraintsBatch());
|
||||
distanceConstraintsData.AddBatch(new ObiDistanceConstraintsBatch());
|
||||
|
||||
for (int i = 0; i < totalParticles - 1; i++)
|
||||
{
|
||||
var batch = distanceConstraintsData.batches[i % 2] as ObiDistanceConstraintsBatch;
|
||||
|
||||
if (i < m_ActiveParticleCount - 1)
|
||||
{
|
||||
Vector2Int indices = new Vector2Int(i, i + 1);
|
||||
restLengths[i] = Vector3.Distance(positions[indices.x], positions[indices.y]);
|
||||
batch.AddConstraint(indices, restLengths[i]);
|
||||
batch.activeConstraintCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
restLengths[i] = m_InterParticleDistance;
|
||||
batch.AddConstraint(Vector2Int.zero, 0);
|
||||
}
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope: generating structural constraints...", i / (float)(totalParticles - 1));
|
||||
|
||||
}
|
||||
|
||||
// if the path is closed, add the last, loop closing constraint to a new batch to avoid sharing particles.
|
||||
if (path.Closed)
|
||||
{
|
||||
var loopClosingBatch = new ObiDistanceConstraintsBatch();
|
||||
distanceConstraintsData.AddBatch(loopClosingBatch);
|
||||
|
||||
Vector2Int indices = new Vector2Int(m_ActiveParticleCount - 1, 0);
|
||||
restLengths[m_ActiveParticleCount - 2] = Vector3.Distance(positions[indices.x], positions[indices.y]);
|
||||
loopClosingBatch.AddConstraint(indices, restLengths[m_ActiveParticleCount - 2]);
|
||||
loopClosingBatch.activeConstraintCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateBendingConstraints()
|
||||
{
|
||||
bendConstraintsData = new ObiBendConstraintsData();
|
||||
|
||||
// Add three batches:
|
||||
bendConstraintsData.AddBatch(new ObiBendConstraintsBatch());
|
||||
bendConstraintsData.AddBatch(new ObiBendConstraintsBatch());
|
||||
bendConstraintsData.AddBatch(new ObiBendConstraintsBatch());
|
||||
|
||||
for (int i = 0; i < totalParticles - 2; i++)
|
||||
{
|
||||
var batch = bendConstraintsData.batches[i % 3] as ObiBendConstraintsBatch;
|
||||
|
||||
Vector3Int indices = new Vector3Int(i, i + 2, i + 1);
|
||||
float restBend = 0;//ObiUtils.RestBendingConstraint(restPositions[indices[0]], restPositions[indices[1]], restPositions[indices[2]]);
|
||||
batch.AddConstraint(indices, restBend);
|
||||
|
||||
if (i < m_ActiveParticleCount - 2)
|
||||
batch.activeConstraintCount++;
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope: generating structural constraints...", i / (float)(totalParticles - 2));
|
||||
|
||||
}
|
||||
|
||||
// if the path is closed, add the last, loop closing constraints to a new batch to avoid sharing particles.
|
||||
if (path.Closed)
|
||||
{
|
||||
var loopClosingBatch = new ObiBendConstraintsBatch();
|
||||
bendConstraintsData.AddBatch(loopClosingBatch);
|
||||
|
||||
Vector3Int indices = new Vector3Int(m_ActiveParticleCount - 2, 0, m_ActiveParticleCount - 1);
|
||||
loopClosingBatch.AddConstraint(indices, 0);
|
||||
loopClosingBatch.activeConstraintCount++;
|
||||
|
||||
var loopClosingBatch2 = new ObiBendConstraintsBatch();
|
||||
bendConstraintsData.AddBatch(loopClosingBatch2);
|
||||
|
||||
indices = new Vector3Int(m_ActiveParticleCount - 1, 1, 0);
|
||||
loopClosingBatch2.AddConstraint(indices, 0);
|
||||
loopClosingBatch2.activeConstraintCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f7e67b5626124d0db9886e6cd2aacff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: d0123218ec6144d0983c099fc7339924, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,109 +0,0 @@
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public abstract class ObiRopeBlueprintBase : ObiActorBlueprint
|
||||
{
|
||||
[HideInInspector] [SerializeField] public ObiPath path = new ObiPath();
|
||||
public float thickness = 0.1f;
|
||||
|
||||
[Range(0, 1)]
|
||||
public float resolution = 1;
|
||||
|
||||
[HideInInspector] [SerializeField] protected float m_InterParticleDistance;
|
||||
[HideInInspector] [SerializeField] protected int totalParticles;
|
||||
[HideInInspector] [SerializeField] protected float m_RestLength;
|
||||
|
||||
[HideInInspector] public int[] deformableEdges = null; /**< Indices of deformable edges (2 per edge)*/
|
||||
[HideInInspector] public float[] restLengths;
|
||||
|
||||
public float interParticleDistance
|
||||
{
|
||||
get { return m_InterParticleDistance; }
|
||||
}
|
||||
|
||||
public float restLength
|
||||
{
|
||||
get { return m_RestLength; }
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
path.OnPathChanged.AddListener(GenerateImmediate);
|
||||
path.OnControlPointAdded.AddListener(ControlPointAdded);
|
||||
path.OnControlPointRemoved.AddListener(ControlPointRemoved);
|
||||
path.OnControlPointRenamed.AddListener(ControlPointRenamed);
|
||||
}
|
||||
|
||||
public void OnDisable()
|
||||
{
|
||||
path.OnPathChanged.RemoveAllListeners();
|
||||
path.OnControlPointAdded.RemoveAllListeners();
|
||||
path.OnControlPointRemoved.RemoveAllListeners();
|
||||
path.OnControlPointRenamed.RemoveAllListeners();
|
||||
}
|
||||
|
||||
protected void ControlPointAdded(int index)
|
||||
{
|
||||
var group = InsertNewParticleGroup(path.GetName(index), index);
|
||||
}
|
||||
|
||||
protected void ControlPointRenamed(int index)
|
||||
{
|
||||
SetParticleGroupName(index, path.GetName(index));
|
||||
}
|
||||
|
||||
protected void ControlPointRemoved(int index)
|
||||
{
|
||||
RemoveParticleGroupAt(index);
|
||||
}
|
||||
|
||||
protected virtual IEnumerator CreateAerodynamicConstraints()
|
||||
{
|
||||
aerodynamicConstraintsData = new ObiAerodynamicConstraintsData();
|
||||
var aeroBatch = new ObiAerodynamicConstraintsBatch();
|
||||
aerodynamicConstraintsData.AddBatch(aeroBatch);
|
||||
|
||||
for (int i = 0; i < totalParticles; i++)
|
||||
{
|
||||
aeroBatch.AddConstraint(i, 2 * principalRadii[i].x, 1, 1);
|
||||
|
||||
if (i % 500 == 0)
|
||||
yield return new CoroutineJob.ProgressInfo("ObiRope generating aerodynamic constraints...", i / (float)totalParticles);
|
||||
}
|
||||
|
||||
// Set initial amount of active constraints:
|
||||
for (int i = 0; i < aerodynamicConstraintsData.batches.Count; ++i)
|
||||
{
|
||||
aerodynamicConstraintsData.batches[i].activeConstraintCount = m_ActiveParticleCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateDeformableEdges(int numSegments)
|
||||
{
|
||||
deformableEdges = new int[numSegments * 2];
|
||||
for (int i = 0; i < numSegments; ++i)
|
||||
{
|
||||
deformableEdges[i * 2] = i % activeParticleCount;
|
||||
deformableEdges[i * 2 + 1] = (i + 1) % activeParticleCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected void CreateSimplices(int numSegments)
|
||||
{
|
||||
edges = new int[numSegments * 2];
|
||||
for (int i = 0; i < numSegments; ++i)
|
||||
{
|
||||
edges[i * 2] = i % activeParticleCount;
|
||||
edges[i * 2 + 1] = (i + 1) % activeParticleCount;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator Initialize() { yield return null; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4abe69b6abd2c4b0e8a621b8fba92dc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user