去掉obi,使用自写绳索

This commit is contained in:
2026-02-23 20:51:03 +08:00
parent cb636f862d
commit 91e2309eeb
2011 changed files with 2593 additions and 190578 deletions

View File

@@ -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);
}
}
}
}

View File

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

View File

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

View File

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

View File

@@ -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++;
}
}
}
}

View File

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

View File

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

View File

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