重新导入obi
This commit is contained in:
553
Assets/Obi/Scripts/RopeAndRod/Actors/ObiBone.cs
Normal file
553
Assets/Obi/Scripts/RopeAndRod/Actors/ObiBone.cs
Normal file
@@ -0,0 +1,553 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[AddComponentMenu("Physics/Obi/Obi Bone", 882)]
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
public class ObiBone : ObiActor, IStretchShearConstraintsUser, IBendTwistConstraintsUser, ISkinConstraintsUser
|
||||
{
|
||||
[Serializable]
|
||||
public class BonePropertyCurve
|
||||
{
|
||||
[Min(0)]
|
||||
public float multiplier;
|
||||
public AnimationCurve curve;
|
||||
|
||||
public BonePropertyCurve(float multiplier, float curveValue)
|
||||
{
|
||||
this.multiplier = multiplier;
|
||||
this.curve = new AnimationCurve(new Keyframe(0, curveValue), new Keyframe(1, curveValue));
|
||||
}
|
||||
|
||||
public float Evaluate(float time)
|
||||
{
|
||||
return curve.Evaluate(time) * multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class IgnoredBone
|
||||
{
|
||||
public Transform bone;
|
||||
public bool ignoreChildren;
|
||||
}
|
||||
|
||||
[NonSerialized] protected ObiBoneBlueprint m_BoneBlueprint;
|
||||
|
||||
[SerializeField] protected bool m_SelfCollisions = false;
|
||||
|
||||
[SerializeField] protected BonePropertyCurve _radius = new BonePropertyCurve(0.1f,1);
|
||||
[SerializeField] protected BonePropertyCurve _mass = new BonePropertyCurve(0.1f,1);
|
||||
[SerializeField] protected BonePropertyCurve _rotationalMass = new BonePropertyCurve(0.1f,1);
|
||||
|
||||
// skin constraints:
|
||||
[SerializeField] protected bool _skinConstraintsEnabled = true;
|
||||
[SerializeField] protected BonePropertyCurve _skinCompliance = new BonePropertyCurve(0.01f, 1);
|
||||
[SerializeField] protected BonePropertyCurve _skinRadius = new BonePropertyCurve(0.1f, 1);
|
||||
|
||||
// distance constraints:
|
||||
[SerializeField] protected bool _stretchShearConstraintsEnabled = true;
|
||||
[SerializeField] protected BonePropertyCurve _stretchCompliance = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _shear1Compliance = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _shear2Compliance = new BonePropertyCurve(0, 1);
|
||||
|
||||
// bend constraints:
|
||||
[SerializeField] protected bool _bendTwistConstraintsEnabled = true;
|
||||
[SerializeField] protected BonePropertyCurve _torsionCompliance = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _bend1Compliance = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _bend2Compliance = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _plasticYield = new BonePropertyCurve(0, 1);
|
||||
[SerializeField] protected BonePropertyCurve _plasticCreep = new BonePropertyCurve(0, 1);
|
||||
|
||||
[Tooltip("Filter used for collision detection.")]
|
||||
[SerializeField] private int filter = ObiUtils.MakeFilter(ObiUtils.CollideWithEverything, 1);
|
||||
|
||||
public bool fixRoot = true;
|
||||
public bool stretchBones = true;
|
||||
public List<IgnoredBone> ignored = new List<IgnoredBone>();
|
||||
|
||||
/// <summary>
|
||||
/// Collision filter value used by fluid particles.
|
||||
/// </summary>
|
||||
public int Filter
|
||||
{
|
||||
set
|
||||
{
|
||||
if (filter != value)
|
||||
{
|
||||
filter = value;
|
||||
UpdateFilter();
|
||||
}
|
||||
}
|
||||
get { return filter; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether particles in this actor colide with particles using the same phase value.
|
||||
/// </summary>
|
||||
public bool selfCollisions
|
||||
{
|
||||
get { return m_SelfCollisions; }
|
||||
set { if (value != m_SelfCollisions) { m_SelfCollisions = value; SetSelfCollisions(m_SelfCollisions); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Particle radius distribution over this bone hierarchy length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve radius
|
||||
{
|
||||
get { return _radius; }
|
||||
set { _radius = value; UpdateRadius(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mass distribution over this bone hierarchy length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve mass
|
||||
{
|
||||
get { return _mass; }
|
||||
set { _mass = value; UpdateMasses(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotational mass distribution over this bone hierarchy length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve rotationalMass
|
||||
{
|
||||
get { return _rotationalMass; }
|
||||
set { _rotationalMass = value; UpdateMasses(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's skin constraints are enabled.
|
||||
/// </summary>
|
||||
public bool skinConstraintsEnabled
|
||||
{
|
||||
get { return _skinConstraintsEnabled; }
|
||||
set { if (value != _skinConstraintsEnabled) { _skinConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.Skin); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's skin constraints.
|
||||
/// </summary>
|
||||
public BonePropertyCurve skinCompliance
|
||||
{
|
||||
get { return _skinCompliance; }
|
||||
set { _skinCompliance = value; SetConstraintsDirty(Oni.ConstraintType.Skin); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's skin radius
|
||||
/// </summary>
|
||||
public BonePropertyCurve skinRadius
|
||||
{
|
||||
get { return _skinRadius; }
|
||||
set { _skinRadius = value; SetConstraintsDirty(Oni.ConstraintType.Skin); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's stretch/shear constraints are enabled.
|
||||
/// </summary>
|
||||
public bool stretchShearConstraintsEnabled
|
||||
{
|
||||
get { return _stretchShearConstraintsEnabled; }
|
||||
set { if (value != _stretchShearConstraintsEnabled) { _stretchShearConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's stretch/shear constraints, along their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve stretchCompliance
|
||||
{
|
||||
get { return _stretchCompliance; }
|
||||
set { _stretchCompliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shearing compliance of this actor's stretch/shear constraints, along the first axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve shear1Compliance
|
||||
{
|
||||
get { return _shear1Compliance; }
|
||||
set { _shear1Compliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shearing compliance of this actor's stretch/shear constraints, along the second axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve shear2Compliance
|
||||
{
|
||||
get { return _shear2Compliance; }
|
||||
set { _shear2Compliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's bend/twist constraints are enabled.
|
||||
/// </summary>
|
||||
public bool bendTwistConstraintsEnabled
|
||||
{
|
||||
get { return _bendTwistConstraintsEnabled; }
|
||||
set { if (value != _bendTwistConstraintsEnabled) { _bendTwistConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Torsional compliance of this actor's bend/twist constraints along their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve torsionCompliance
|
||||
{
|
||||
get { return _torsionCompliance; }
|
||||
set { _torsionCompliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bending compliance of this actor's bend/twist constraints along the first axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve bend1Compliance
|
||||
{
|
||||
get { return _bend1Compliance; }
|
||||
set { _bend1Compliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bending compliance of this actor's bend/twist constraints along the second axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public BonePropertyCurve bend2Compliance
|
||||
{
|
||||
get { return _bend2Compliance; }
|
||||
set { _bend2Compliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Threshold for plastic behavior.
|
||||
/// </summary>
|
||||
/// Once bending goes above this value, a percentage of the deformation (determined by <see cref="plasticCreep"/>) will be permanently absorbed into the rod's rest shape.
|
||||
public BonePropertyCurve plasticYield
|
||||
{
|
||||
get { return _plasticYield; }
|
||||
set { _plasticYield = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of deformation that gets absorbed into the rest shape per second, once deformation goes above the <see cref="plasticYield"/> threshold.
|
||||
/// </summary>
|
||||
public BonePropertyCurve plasticCreep
|
||||
{
|
||||
get { return _plasticCreep; }
|
||||
set { _plasticCreep = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
|
||||
public override ObiActorBlueprint sourceBlueprint
|
||||
{
|
||||
get { return m_BoneBlueprint; }
|
||||
}
|
||||
|
||||
public ObiBoneBlueprint boneBlueprint
|
||||
{
|
||||
get { return m_BoneBlueprint; }
|
||||
set
|
||||
{
|
||||
if (m_BoneBlueprint != value)
|
||||
{
|
||||
RemoveFromSolver();
|
||||
ClearState();
|
||||
m_BoneBlueprint = value;
|
||||
AddToSolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
m_BoneBlueprint = ScriptableObject.CreateInstance<ObiBoneBlueprint>();
|
||||
UpdateBlueprint();
|
||||
base.Awake();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
if (m_BoneBlueprint != null)
|
||||
DestroyImmediate(m_BoneBlueprint);
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
UpdateFilter();
|
||||
UpdateRadius();
|
||||
UpdateMasses();
|
||||
SetupRuntimeConstraints();
|
||||
}
|
||||
|
||||
public void UpdateBlueprint()
|
||||
{
|
||||
if (m_BoneBlueprint != null)
|
||||
{
|
||||
m_BoneBlueprint.root = transform;
|
||||
m_BoneBlueprint.ignored = ignored;
|
||||
m_BoneBlueprint.mass = mass;
|
||||
m_BoneBlueprint.rotationalMass = rotationalMass;
|
||||
m_BoneBlueprint.radius = radius;
|
||||
m_BoneBlueprint.GenerateImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
public override void LoadBlueprint(ObiSolver solver)
|
||||
{
|
||||
base.LoadBlueprint(solver);
|
||||
SetupRuntimeConstraints();
|
||||
ResetToCurrentShape();
|
||||
}
|
||||
|
||||
public override void UnloadBlueprint(ObiSolver solver)
|
||||
{
|
||||
ResetParticles();
|
||||
CopyParticleDataToTransforms();
|
||||
base.UnloadBlueprint(solver);
|
||||
}
|
||||
|
||||
private void SetupRuntimeConstraints()
|
||||
{
|
||||
SetConstraintsDirty(Oni.ConstraintType.Skin);
|
||||
SetConstraintsDirty(Oni.ConstraintType.StretchShear);
|
||||
SetConstraintsDirty(Oni.ConstraintType.BendTwist);
|
||||
SetSelfCollisions(selfCollisions);
|
||||
SetSimplicesDirty();
|
||||
UpdateFilter();
|
||||
UpdateCollisionMaterials();
|
||||
}
|
||||
|
||||
private void FixRoot()
|
||||
{
|
||||
if (isLoaded)
|
||||
{
|
||||
int rootIndex = solverIndices[0];
|
||||
|
||||
var actor2Solver = actorLocalToSolverMatrix;
|
||||
var actor2SolverR = actor2Solver.rotation;
|
||||
|
||||
solver.invMasses[rootIndex] = 0;
|
||||
solver.invRotationalMasses[rootIndex] = 0;
|
||||
solver.velocities[rootIndex] = Vector4.zero;
|
||||
solver.angularVelocities[rootIndex] = Vector4.zero;
|
||||
|
||||
// take particle rest position in actor space (which is always zero), converts to solver space:
|
||||
solver.renderablePositions[rootIndex] = solver.positions[rootIndex] = actor2Solver.MultiplyPoint3x4(Vector3.zero);
|
||||
|
||||
// take particle rest orientation in actor space, and convert to solver space:
|
||||
solver.renderableOrientations[rootIndex] = solver.orientations[rootIndex] = actor2SolverR * boneBlueprint.orientations[0];
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFilter()
|
||||
{
|
||||
for (int i = 0; i < particleCount; i++)
|
||||
{
|
||||
boneBlueprint.filters[i] = filter;
|
||||
if (isLoaded)
|
||||
solver.filters[solverIndices[i]] = filter;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateRadius()
|
||||
{
|
||||
for (int i = 0; i < particleCount; ++i)
|
||||
{
|
||||
var normalizedCoord = boneBlueprint.normalizedLengths[i];
|
||||
var radii = Vector3.one * radius.Evaluate(normalizedCoord);
|
||||
boneBlueprint.principalRadii[i] = radii;
|
||||
|
||||
if (isLoaded)
|
||||
solver.principalRadii[solverIndices[i]] = radii;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateMasses()
|
||||
{
|
||||
for (int i = 0; i < particleCount; ++i)
|
||||
{
|
||||
var normalizedCoord = boneBlueprint.normalizedLengths[i];
|
||||
var invMass = ObiUtils.MassToInvMass(mass.Evaluate(normalizedCoord));
|
||||
var invRotMass = ObiUtils.MassToInvMass(rotationalMass.Evaluate(normalizedCoord));
|
||||
|
||||
boneBlueprint.invMasses[i] = invMass;
|
||||
boneBlueprint.invRotationalMasses[i] = invRotMass;
|
||||
|
||||
if (isLoaded)
|
||||
{
|
||||
solver.invMasses[solverIndices[i]] = invMass;
|
||||
solver.invRotationalMasses[solverIndices[i]] = invRotMass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetSkinRadiiBackstop(ObiSkinConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
float normalizedCoord = boneBlueprint.normalizedLengths[batch.particleIndices[constraintIndex]];
|
||||
return new Vector3(skinRadius.Evaluate(normalizedCoord),0,0);
|
||||
}
|
||||
|
||||
public float GetSkinCompliance(ObiSkinConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
float normalizedCoord = boneBlueprint.normalizedLengths[batch.particleIndices[constraintIndex]];
|
||||
return skinCompliance.Evaluate(normalizedCoord);
|
||||
}
|
||||
|
||||
|
||||
public Vector3 GetBendTwistCompliance(ObiBendTwistConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
float normalizedCoord = boneBlueprint.normalizedLengths[batch.particleIndices[constraintIndex * 2]];
|
||||
return new Vector3(bend1Compliance.Evaluate(normalizedCoord),
|
||||
bend2Compliance.Evaluate(normalizedCoord),
|
||||
torsionCompliance.Evaluate(normalizedCoord));
|
||||
}
|
||||
|
||||
public Vector2 GetBendTwistPlasticity(ObiBendTwistConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
float normalizedCoord = boneBlueprint.normalizedLengths[batch.particleIndices[constraintIndex * 2]];
|
||||
return new Vector2(plasticYield.Evaluate(normalizedCoord),
|
||||
plasticCreep.Evaluate(normalizedCoord));
|
||||
}
|
||||
|
||||
public Vector3 GetStretchShearCompliance(ObiStretchShearConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
float normalizedCoord = boneBlueprint.normalizedLengths[batch.particleIndices[constraintIndex * 2]];
|
||||
return new Vector3(shear1Compliance.Evaluate(normalizedCoord),
|
||||
shear2Compliance.Evaluate(normalizedCoord),
|
||||
stretchCompliance.Evaluate(normalizedCoord));
|
||||
}
|
||||
|
||||
public override void BeginStep(float stepTime)
|
||||
{
|
||||
base.BeginStep(stepTime);
|
||||
|
||||
if (fixRoot)
|
||||
FixRoot();
|
||||
|
||||
UpdateRestShape();
|
||||
}
|
||||
|
||||
public override void PrepareFrame()
|
||||
{
|
||||
ResetReferenceOrientations();
|
||||
base.PrepareFrame();
|
||||
}
|
||||
|
||||
public override void Interpolate()
|
||||
{
|
||||
if (Application.isPlaying && isActiveAndEnabled)
|
||||
CopyParticleDataToTransforms();
|
||||
|
||||
base.Interpolate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets particle orientations/positions to match the current pose of the bone hierarchy, and sets all their velocities to zero.
|
||||
/// </summary>
|
||||
public void ResetToCurrentShape()
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
|
||||
var world2Solver = solver.transform.worldToLocalMatrix;
|
||||
|
||||
for (int i = 0; i < particleCount; ++i)
|
||||
{
|
||||
var trfm = boneBlueprint.transforms[i];
|
||||
int solverIndex = solverIndices[i];
|
||||
|
||||
solver.velocities[solverIndex] = Vector4.zero;
|
||||
solver.angularVelocities[solverIndex] = Vector4.zero;
|
||||
|
||||
solver.renderablePositions[solverIndex] = solver.positions[solverIndex] = world2Solver.MultiplyPoint3x4(trfm.position);
|
||||
|
||||
var boneDeltaAWS = trfm.rotation * Quaternion.Inverse(boneBlueprint.restOrientations[i]);
|
||||
solver.renderableOrientations[solverIndex] = solver.orientations[solverIndex] = world2Solver.rotation * boneDeltaAWS * boneBlueprint.root2WorldR * boneBlueprint.orientations[i];
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetReferenceOrientations()
|
||||
{
|
||||
if (boneBlueprint != null)
|
||||
for (int i = 1; i < boneBlueprint.restTransformOrientations.Count; ++i)
|
||||
boneBlueprint.transforms[i].localRotation = boneBlueprint.restTransformOrientations[i];
|
||||
}
|
||||
|
||||
private void UpdateRestShape()
|
||||
{
|
||||
// use current bone transforms as rest state for the simulation:
|
||||
var bc = GetConstraintsByType(Oni.ConstraintType.BendTwist) as ObiConstraints<ObiBendTwistConstraintsBatch>;
|
||||
var sbc = solver.GetConstraintsByType(Oni.ConstraintType.BendTwist) as ObiConstraints<ObiBendTwistConstraintsBatch>;
|
||||
|
||||
if (bendTwistConstraintsEnabled && bc != null && sbc != null)
|
||||
for (int j = 0; j < bc.GetBatchCount(); ++j)
|
||||
{
|
||||
var batch = bc.GetBatch(j) as ObiBendTwistConstraintsBatch;
|
||||
var solverBatch = sbc.batches[j] as ObiBendTwistConstraintsBatch;
|
||||
int offset = solverBatchOffsets[(int)solverBatch.constraintType][j];
|
||||
|
||||
if (solverBatch.restDarbouxVectors.isCreated)
|
||||
{
|
||||
for (int i = 0; i < batch.activeConstraintCount; i++)
|
||||
{
|
||||
int indexA = batch.particleIndices[i * 2];
|
||||
int indexB = batch.particleIndices[i * 2 + 1];
|
||||
|
||||
// calculate bone rotation delta in world space:
|
||||
var boneDeltaAWS = boneBlueprint.transforms[indexA].rotation * Quaternion.Inverse(boneBlueprint.restOrientations[indexA]);
|
||||
var boneDeltaBWS = boneBlueprint.transforms[indexB].rotation * Quaternion.Inverse(boneBlueprint.restOrientations[indexB]);
|
||||
|
||||
// apply delta to rest particle orientation:
|
||||
var orientationA = boneDeltaAWS * boneBlueprint.root2WorldR * boneBlueprint.orientations[indexA];
|
||||
var orientationB = boneDeltaBWS * boneBlueprint.root2WorldR * boneBlueprint.orientations[indexB];
|
||||
|
||||
solverBatch.restDarbouxVectors[offset + i] = ObiUtils.RestDarboux(orientationA, orientationB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sc = GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints<ObiSkinConstraintsBatch>;
|
||||
var ssc = solver.GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints<ObiSkinConstraintsBatch>;
|
||||
|
||||
if (skinConstraintsEnabled && sc != null && ssc != null)
|
||||
for (int j = 0; j < sc.GetBatchCount(); ++j)
|
||||
{
|
||||
var batch = sc.GetBatch(j) as ObiSkinConstraintsBatch;
|
||||
var solverBatch = ssc.batches[j] as ObiSkinConstraintsBatch;
|
||||
int offset = solverBatchOffsets[(int)solverBatch.constraintType][j];
|
||||
|
||||
if (solverBatch.skinPoints.isCreated)
|
||||
{
|
||||
for (int i = 0; i < batch.activeConstraintCount; i++)
|
||||
{
|
||||
int index = batch.particleIndices[i];
|
||||
solverBatch.skinPoints[offset + i] = solver.transform.worldToLocalMatrix.MultiplyPoint3x4(boneBlueprint.transforms[index].position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyParticleDataToTransforms()
|
||||
{
|
||||
|
||||
if (boneBlueprint != null)
|
||||
{
|
||||
// copy current particle transforms to bones:
|
||||
for (int i = 1; i < particleCount; ++i)
|
||||
{
|
||||
var trfm = boneBlueprint.transforms[i];
|
||||
|
||||
if (stretchBones)
|
||||
trfm.position = GetParticlePosition(solverIndices[i]);
|
||||
|
||||
var delta = GetParticleOrientation(solverIndices[i]) * Quaternion.Inverse(boneBlueprint.root2WorldR * boneBlueprint.orientations[i]);
|
||||
trfm.rotation = delta * boneBlueprint.restOrientations[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiBone.cs.meta
Normal file
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiBone.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7be315bc5964b4fbca6cf0a978ff76a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 0a18e0376cc184a9b96ebb3bf0175cc2, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
253
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRod.cs
Normal file
253
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRod.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[AddComponentMenu("Physics/Obi/Obi Rod", 881)]
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
public class ObiRod : ObiRopeBase, IStretchShearConstraintsUser, IBendTwistConstraintsUser, IChainConstraintsUser
|
||||
{
|
||||
[SerializeField] protected ObiRodBlueprint m_RodBlueprint;
|
||||
|
||||
// distance constraints:
|
||||
[SerializeField] protected bool _stretchShearConstraintsEnabled = true;
|
||||
[SerializeField] protected float _stretchCompliance = 0;
|
||||
[SerializeField] protected float _shear1Compliance = 0;
|
||||
[SerializeField] protected float _shear2Compliance = 0;
|
||||
|
||||
// bend constraints:
|
||||
[SerializeField] protected bool _bendTwistConstraintsEnabled = true;
|
||||
[SerializeField] protected float _torsionCompliance = 0;
|
||||
[SerializeField] protected float _bend1Compliance = 0;
|
||||
[SerializeField] protected float _bend2Compliance = 0;
|
||||
[SerializeField] [Range(0, 0.1f)] protected float _plasticYield = 0;
|
||||
[SerializeField] protected float _plasticCreep = 0;
|
||||
|
||||
// chain constraints:
|
||||
[SerializeField] protected bool _chainConstraintsEnabled = true;
|
||||
[SerializeField] [Range(0, 1)] protected float _tightness = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether particles in this actor colide with particles using the same phase value.
|
||||
/// </summary>
|
||||
public bool selfCollisions
|
||||
{
|
||||
get { return m_SelfCollisions; }
|
||||
set { if (value != m_SelfCollisions) { m_SelfCollisions = value; SetSelfCollisions(m_SelfCollisions); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's stretch/shear constraints are enabled.
|
||||
/// </summary>
|
||||
public bool stretchShearConstraintsEnabled
|
||||
{
|
||||
get { return _stretchShearConstraintsEnabled; }
|
||||
set { if (value != _stretchShearConstraintsEnabled) { _stretchShearConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's stretch/shear constraints, along their length.
|
||||
/// </summary>
|
||||
public float stretchCompliance
|
||||
{
|
||||
get { return _stretchCompliance; }
|
||||
set { _stretchCompliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shearing compliance of this actor's stretch/shear constraints, along the first axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public float shear1Compliance
|
||||
{
|
||||
get { return _shear1Compliance; }
|
||||
set { _shear1Compliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shearing compliance of this actor's stretch/shear constraints, along the second axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public float shear2Compliance
|
||||
{
|
||||
get { return _shear2Compliance; }
|
||||
set { _shear2Compliance = value; SetConstraintsDirty(Oni.ConstraintType.StretchShear); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's bend/twist constraints are enabled.
|
||||
/// </summary>
|
||||
public bool bendTwistConstraintsEnabled
|
||||
{
|
||||
get { return _bendTwistConstraintsEnabled; }
|
||||
set { if (value != _bendTwistConstraintsEnabled) { _bendTwistConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Torsional compliance of this actor's bend/twist constraints along their length.
|
||||
/// </summary>
|
||||
public float torsionCompliance
|
||||
{
|
||||
get { return _torsionCompliance; }
|
||||
set { _torsionCompliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bending compliance of this actor's bend/twist constraints along the first axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public float bend1Compliance
|
||||
{
|
||||
get { return _bend1Compliance; }
|
||||
set { _bend1Compliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bending compliance of this actor's bend/twist constraints along the second axis orthogonal to their length.
|
||||
/// </summary>
|
||||
public float bend2Compliance
|
||||
{
|
||||
get { return _bend2Compliance; }
|
||||
set { _bend2Compliance = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Threshold for plastic behavior.
|
||||
/// </summary>
|
||||
/// Once bending goes above this value, a percentage of the deformation (determined by <see cref="plasticCreep"/>) will be permanently absorbed into the rod's rest shape.
|
||||
public float plasticYield
|
||||
{
|
||||
get { return _plasticYield; }
|
||||
set { _plasticYield = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of deformation that gets absorbed into the rest shape per second, once deformation goes above the <see cref="plasticYield"/> threshold.
|
||||
/// </summary>
|
||||
public float plasticCreep
|
||||
{
|
||||
get { return _plasticCreep; }
|
||||
set { _plasticCreep = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's chain constraints are enabled.
|
||||
/// </summary>
|
||||
public bool chainConstraintsEnabled
|
||||
{
|
||||
get { return _chainConstraintsEnabled; }
|
||||
set { if (value != _chainConstraintsEnabled) { _chainConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.BendTwist); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tightness of this actor's chain constraints.
|
||||
/// </summary>
|
||||
/// Controls how much chain constraints are allowed to compress.
|
||||
public float tightness
|
||||
{
|
||||
get { return _tightness; }
|
||||
set { _tightness = value; SetConstraintsDirty(Oni.ConstraintType.Chain); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Average distance between consecutive particle centers in this rod.
|
||||
/// </summary>
|
||||
public float interParticleDistance
|
||||
{
|
||||
get { return m_RodBlueprint.interParticleDistance; }
|
||||
}
|
||||
|
||||
public override ObiActorBlueprint sourceBlueprint
|
||||
{
|
||||
get { return m_RodBlueprint; }
|
||||
}
|
||||
|
||||
public ObiRodBlueprint rodBlueprint
|
||||
{
|
||||
get { return m_RodBlueprint; }
|
||||
set
|
||||
{
|
||||
if (m_RodBlueprint != value)
|
||||
{
|
||||
RemoveFromSolver();
|
||||
ClearState();
|
||||
m_RodBlueprint = value;
|
||||
AddToSolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
SetConstraintsDirty(Oni.ConstraintType.BendTwist);
|
||||
SetupRuntimeConstraints();
|
||||
}
|
||||
|
||||
public override void LoadBlueprint(ObiSolver solver)
|
||||
{
|
||||
base.LoadBlueprint(solver);
|
||||
RebuildElementsFromConstraints();
|
||||
SetupRuntimeConstraints();
|
||||
}
|
||||
|
||||
private void SetupRuntimeConstraints()
|
||||
{
|
||||
SetConstraintsDirty(Oni.ConstraintType.StretchShear);
|
||||
//SetConstraintsDirty(Oni.ConstraintType.BendTwist);
|
||||
SetConstraintsDirty(Oni.ConstraintType.Chain);
|
||||
SetSelfCollisions(selfCollisions);
|
||||
RecalculateRestLength();
|
||||
SetSimplicesDirty();
|
||||
UpdateCollisionMaterials();
|
||||
}
|
||||
|
||||
public Vector3 GetBendTwistCompliance(ObiBendTwistConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
return new Vector3(bend1Compliance, bend2Compliance, torsionCompliance);
|
||||
}
|
||||
|
||||
public Vector2 GetBendTwistPlasticity(ObiBendTwistConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
return new Vector2(plasticYield, plasticCreep);
|
||||
}
|
||||
|
||||
public Vector3 GetStretchShearCompliance(ObiStretchShearConstraintsBatch batch, int constraintIndex)
|
||||
{
|
||||
return new Vector3(shear1Compliance, shear2Compliance, stretchCompliance);
|
||||
}
|
||||
|
||||
protected override void RebuildElementsFromConstraintsInternal()
|
||||
{
|
||||
var dc = GetConstraintsByType(Oni.ConstraintType.StretchShear) as ObiConstraints<ObiStretchShearConstraintsBatch>;
|
||||
if (dc == null || dc.GetBatchCount() < 2)
|
||||
return;
|
||||
|
||||
int constraintCount = dc.batches[0].activeConstraintCount + dc.batches[1].activeConstraintCount;
|
||||
|
||||
elements = new List<ObiStructuralElement>(constraintCount);
|
||||
|
||||
for (int i = 0; i < constraintCount; ++i)
|
||||
{
|
||||
var batch = dc.batches[i % 2] as ObiStretchShearConstraintsBatch;
|
||||
int constraintIndex = i / 2;
|
||||
|
||||
var e = new ObiStructuralElement();
|
||||
e.particle1 = solverIndices[batch.particleIndices[constraintIndex * 2]];
|
||||
e.particle2 = solverIndices[batch.particleIndices[constraintIndex * 2 + 1]];
|
||||
e.restLength = batch.restLengths[constraintIndex];
|
||||
elements.Add(e);
|
||||
}
|
||||
|
||||
if (dc.batches.Count > 2)
|
||||
{
|
||||
var batch = dc.batches[2];
|
||||
var e = new ObiStructuralElement();
|
||||
e.particle1 = solverIndices[batch.particleIndices[0]];
|
||||
e.particle2 = solverIndices[batch.particleIndices[1]];
|
||||
e.restLength = batch.restLengths[0];
|
||||
elements.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRod.cs.meta
Normal file
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRod.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f2bc32c4faf04f039a86a6e3cea12a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7125c3eebd3cc4f86aac51cca3ee1592, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
446
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRope.cs
Normal file
446
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRope.cs
Normal file
@@ -0,0 +1,446 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[AddComponentMenu("Physics/Obi/Obi Rope", 880)]
|
||||
[ExecuteInEditMode]
|
||||
[DisallowMultipleComponent]
|
||||
public class ObiRope : ObiRopeBase, IDistanceConstraintsUser, IBendConstraintsUser
|
||||
{
|
||||
[SerializeField] protected ObiRopeBlueprint m_RopeBlueprint;
|
||||
private ObiRopeBlueprint m_RopeBlueprintInstance;
|
||||
|
||||
// rope has a list of structural elements.
|
||||
// each structural element is equivalent to 1 distance constraint and 2 bend constraints (with previous, and following element).
|
||||
// a structural element has force and rest length.
|
||||
// a function re-generates constraints from structural elements when needed, placing them in the appropiate batches.
|
||||
|
||||
public bool tearingEnabled = false;
|
||||
public float tearResistanceMultiplier = 1000; /**< Factor that controls how much a structural cloth spring can stretch before breaking.*/
|
||||
public int tearRate = 1;
|
||||
|
||||
// distance constraints:
|
||||
[SerializeField] protected bool _distanceConstraintsEnabled = true;
|
||||
[SerializeField] protected float _stretchingScale = 1;
|
||||
[SerializeField] protected float _stretchCompliance = 0;
|
||||
[SerializeField] [Range(0, 1)] protected float _maxCompression = 0;
|
||||
|
||||
// bend constraints:
|
||||
[SerializeField] protected bool _bendConstraintsEnabled = true;
|
||||
[SerializeField] protected float _bendCompliance = 0;
|
||||
[SerializeField] [Range(0, 0.5f)] protected float _maxBending = 0.025f;
|
||||
[SerializeField] [Range(0, 0.1f)] protected float _plasticYield = 0;
|
||||
[SerializeField] protected float _plasticCreep = 0;
|
||||
|
||||
List<ObiStructuralElement> tornElements = new List<ObiStructuralElement>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether particles in this actor colide with particles using the same phase value.
|
||||
/// </summary>
|
||||
public bool selfCollisions
|
||||
{
|
||||
get { return m_SelfCollisions; }
|
||||
set { if (value != m_SelfCollisions) { m_SelfCollisions = value; SetSelfCollisions(selfCollisions); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's distance constraints are enabled.
|
||||
/// </summary>
|
||||
public bool distanceConstraintsEnabled
|
||||
{
|
||||
get { return _distanceConstraintsEnabled; }
|
||||
set { if (value != _distanceConstraintsEnabled) { _distanceConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.Distance); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scale value for this actor's distance constraints rest length.
|
||||
/// </summary>
|
||||
/// The default is 1. For instamce, a value of 2 will make the distance constraints twice as long, 0.5 will reduce their length in half.
|
||||
public float stretchingScale
|
||||
{
|
||||
get { return _stretchingScale; }
|
||||
set { _stretchingScale = value; SetConstraintsDirty(Oni.ConstraintType.Distance); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's stretch constraints.
|
||||
/// </summary>
|
||||
public float stretchCompliance
|
||||
{
|
||||
get { return _stretchCompliance; }
|
||||
set { _stretchCompliance = value; SetConstraintsDirty(Oni.ConstraintType.Distance); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maximum compression this actor's distance constraints can undergo.
|
||||
/// </summary>
|
||||
/// This is expressed as a percentage of the scaled rest length.
|
||||
public float maxCompression
|
||||
{
|
||||
get { return _maxCompression; }
|
||||
set { _maxCompression = value; SetConstraintsDirty(Oni.ConstraintType.Distance); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this actor's bend constraints are enabled.
|
||||
/// </summary>
|
||||
public bool bendConstraintsEnabled
|
||||
{
|
||||
get { return _bendConstraintsEnabled; }
|
||||
set { if (value != _bendConstraintsEnabled) { _bendConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.Bending); } }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compliance of this actor's bend constraints.
|
||||
/// </summary>
|
||||
public float bendCompliance
|
||||
{
|
||||
get { return _bendCompliance; }
|
||||
set { _bendCompliance = value; SetConstraintsDirty(Oni.ConstraintType.Bending); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max bending value that constraints can undergo before resisting bending.
|
||||
/// </summary>
|
||||
public float maxBending
|
||||
{
|
||||
get { return _maxBending; }
|
||||
set { _maxBending = value; SetConstraintsDirty(Oni.ConstraintType.Bending); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Threshold for plastic behavior.
|
||||
/// </summary>
|
||||
/// Once bending goes above this value, a percentage of the deformation (determined by <see cref="plasticCreep"/>) will be permanently absorbed into the rope's rest shape.
|
||||
public float plasticYield
|
||||
{
|
||||
get { return _plasticYield; }
|
||||
set { _plasticYield = value; SetConstraintsDirty(Oni.ConstraintType.Bending); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Percentage of deformation that gets absorbed into the rest shape per second, once deformation goes above the <see cref="plasticYield"/> threshold.
|
||||
/// </summary>
|
||||
public float plasticCreep
|
||||
{
|
||||
get { return _plasticCreep; }
|
||||
set { _plasticCreep = value; SetConstraintsDirty(Oni.ConstraintType.Bending); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Average distance between consecutive particle centers in this rope.
|
||||
/// </summary>
|
||||
public float interParticleDistance
|
||||
{
|
||||
get { return m_RopeBlueprint.interParticleDistance; }
|
||||
}
|
||||
|
||||
public override ObiActorBlueprint sourceBlueprint
|
||||
{
|
||||
get { return m_RopeBlueprint; }
|
||||
}
|
||||
|
||||
public ObiRopeBlueprint ropeBlueprint
|
||||
{
|
||||
get { return m_RopeBlueprint; }
|
||||
set
|
||||
{
|
||||
if (m_RopeBlueprint != value)
|
||||
{
|
||||
RemoveFromSolver();
|
||||
ClearState();
|
||||
m_RopeBlueprint = value;
|
||||
AddToSolver();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void RopeTornCallback(ObiRope rope, ObiRopeTornEventArgs tearInfo);
|
||||
public event RopeTornCallback OnRopeTorn; /**< Called when a constraint is torn.*/
|
||||
|
||||
public class ObiRopeTornEventArgs
|
||||
{
|
||||
public ObiStructuralElement element; /**< info about the element being torn.*/
|
||||
public int particleIndex; /**< index of the particle being torn*/
|
||||
|
||||
public ObiRopeTornEventArgs(ObiStructuralElement element, int particle)
|
||||
{
|
||||
this.element = element;
|
||||
this.particleIndex = particle;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
SetupRuntimeConstraints();
|
||||
}
|
||||
|
||||
public override void LoadBlueprint(ObiSolver solver)
|
||||
{
|
||||
// create a copy of the blueprint for this cloth:
|
||||
if (Application.isPlaying)
|
||||
m_RopeBlueprintInstance = this.blueprint as ObiRopeBlueprint;
|
||||
|
||||
base.LoadBlueprint(solver);
|
||||
RebuildElementsFromConstraints();
|
||||
SetupRuntimeConstraints();
|
||||
}
|
||||
|
||||
public override void UnloadBlueprint(ObiSolver solver)
|
||||
{
|
||||
base.UnloadBlueprint(solver);
|
||||
|
||||
// delete the blueprint instance:
|
||||
if (m_RopeBlueprintInstance != null)
|
||||
DestroyImmediate(m_RopeBlueprintInstance);
|
||||
}
|
||||
|
||||
private void SetupRuntimeConstraints()
|
||||
{
|
||||
SetConstraintsDirty(Oni.ConstraintType.Distance);
|
||||
SetConstraintsDirty(Oni.ConstraintType.Bending);
|
||||
SetSelfCollisions(selfCollisions);
|
||||
RecalculateRestLength();
|
||||
SetSimplicesDirty();
|
||||
UpdateCollisionMaterials();
|
||||
}
|
||||
|
||||
public override void Substep(float substepTime)
|
||||
{
|
||||
base.Substep(substepTime);
|
||||
|
||||
if (isActiveAndEnabled)
|
||||
ApplyTearing(substepTime);
|
||||
}
|
||||
|
||||
protected void ApplyTearing(float substepTime)
|
||||
{
|
||||
|
||||
if (!tearingEnabled)
|
||||
return;
|
||||
|
||||
float sqrTime = substepTime * substepTime;
|
||||
|
||||
tornElements.Clear();
|
||||
|
||||
var dc = GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;
|
||||
var sc = this.solver.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;
|
||||
|
||||
if (dc != null && sc != null)
|
||||
for (int j = 0; j < dc.GetBatchCount(); ++j)
|
||||
{
|
||||
var batch = dc.GetBatch(j) as ObiDistanceConstraintsBatch;
|
||||
var solverBatch = sc.batches[j] as ObiDistanceConstraintsBatch;
|
||||
|
||||
for (int i = 0; i < batch.activeConstraintCount; i++)
|
||||
{
|
||||
int elementIndex = j + 2 * i;
|
||||
|
||||
// divide lambda by squared delta time to get force in newtons:
|
||||
int offset = solverBatchOffsets[(int)Oni.ConstraintType.Distance][j];
|
||||
float force = solverBatch.lambdas[offset + i] / sqrTime;
|
||||
|
||||
elements[elementIndex].constraintForce = force;
|
||||
|
||||
if (-force > tearResistanceMultiplier)
|
||||
{
|
||||
tornElements.Add(elements[elementIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tornElements.Count > 0)
|
||||
{
|
||||
|
||||
// sort edges by force:
|
||||
tornElements.Sort(delegate (ObiStructuralElement x, ObiStructuralElement y)
|
||||
{
|
||||
return x.constraintForce.CompareTo(y.constraintForce);
|
||||
});
|
||||
|
||||
int tornCount = 0;
|
||||
for (int i = 0; i < tornElements.Count; i++)
|
||||
{
|
||||
if (Tear(tornElements[i]))
|
||||
tornCount++;
|
||||
if (tornCount >= tearRate)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tornCount > 0)
|
||||
RebuildConstraintsFromElements();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int SplitParticle(int splitIndex)
|
||||
{
|
||||
// halve the original particle's mass:
|
||||
m_Solver.invMasses[splitIndex] *= 2;
|
||||
|
||||
CopyParticle(solver.particleToActor[splitIndex].indexInActor, activeParticleCount);
|
||||
ActivateParticle(activeParticleCount);
|
||||
|
||||
return solverIndices[activeParticleCount - 1];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tears any given rope element. After calling Tear() one or multiple times, a call to RebuildConstraintsFromElements is needed to
|
||||
/// update the rope particle/constraint representation.
|
||||
/// </summary>
|
||||
public bool Tear(ObiStructuralElement element)
|
||||
{
|
||||
// don't allow splitting if there are no free particles left in the pool.
|
||||
if (activeParticleCount >= m_RopeBlueprint.particleCount)
|
||||
return false;
|
||||
|
||||
// Cannot split fixed particles:
|
||||
if (m_Solver.invMasses[element.particle1] == 0)
|
||||
return false;
|
||||
|
||||
// Or particles that have been already split.
|
||||
int index = elements.IndexOf(element);
|
||||
if (index > 0 && elements[index - 1].particle2 != element.particle1)
|
||||
return false;
|
||||
|
||||
element.particle1 = SplitParticle(element.particle1);
|
||||
|
||||
if (OnRopeTorn != null)
|
||||
OnRopeTorn(this, new ObiRopeTornEventArgs(element, element.particle1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void RebuildElementsFromConstraintsInternal()
|
||||
{
|
||||
var dc = GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;
|
||||
if (dc == null || dc.GetBatchCount() < 2)
|
||||
return;
|
||||
|
||||
int constraintCount = dc.batches[0].activeConstraintCount + dc.batches[1].activeConstraintCount;
|
||||
|
||||
elements = new List<ObiStructuralElement>(constraintCount);
|
||||
|
||||
for (int i = 0; i < constraintCount; ++i)
|
||||
{
|
||||
var batch = dc.batches[i % 2] as ObiDistanceConstraintsBatch;
|
||||
int constraintIndex = i / 2;
|
||||
|
||||
var e = new ObiStructuralElement();
|
||||
e.particle1 = solverIndices[batch.particleIndices[constraintIndex * 2]];
|
||||
e.particle2 = solverIndices[batch.particleIndices[constraintIndex * 2 + 1]];
|
||||
e.restLength = batch.restLengths[constraintIndex];
|
||||
e.tearResistance = 1;
|
||||
elements.Add(e);
|
||||
}
|
||||
|
||||
// loop-closing element:
|
||||
if (dc.batches.Count > 2)
|
||||
{
|
||||
var batch = dc.batches[2];
|
||||
var e = new ObiStructuralElement();
|
||||
e.particle1 = solverIndices[batch.particleIndices[0]];
|
||||
e.particle2 = solverIndices[batch.particleIndices[1]];
|
||||
e.restLength = batch.restLengths[0];
|
||||
e.tearResistance = 1;
|
||||
elements.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RebuildConstraintsFromElements()
|
||||
{
|
||||
// regenerate constraints from elements:
|
||||
var dc = GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;
|
||||
var bc = GetConstraintsByType(Oni.ConstraintType.Bending) as ObiConstraints<ObiBendConstraintsBatch>;
|
||||
|
||||
dc.DeactivateAllConstraints();
|
||||
bc.DeactivateAllConstraints();
|
||||
|
||||
int elementsCount = elements.Count - (ropeBlueprint.path.Closed ? 1 : 0);
|
||||
for (int i = 0; i < elementsCount; ++i)
|
||||
{
|
||||
var db = dc.batches[i % 2] as ObiDistanceConstraintsBatch;
|
||||
int constraint = db.activeConstraintCount;
|
||||
|
||||
db.particleIndices[constraint * 2] = solver.particleToActor[elements[i].particle1].indexInActor;
|
||||
db.particleIndices[constraint * 2 + 1] = solver.particleToActor[elements[i].particle2].indexInActor;
|
||||
db.restLengths[constraint] = elements[i].restLength;
|
||||
db.stiffnesses[constraint] = new Vector2(_stretchCompliance, _maxCompression * db.restLengths[constraint]);
|
||||
db.ActivateConstraint(constraint);
|
||||
|
||||
if (i < elementsCount - 1)
|
||||
{
|
||||
var bb = bc.batches[i % 3] as ObiBendConstraintsBatch;
|
||||
|
||||
// create bend constraint only if there's continuity between elements:
|
||||
if (elements[i].particle2 == elements[i + 1].particle1)
|
||||
{
|
||||
constraint = bb.activeConstraintCount;
|
||||
|
||||
int indexA = elements[i].particle1;
|
||||
int indexB = elements[i + 1].particle2;
|
||||
int indexC = elements[i].particle2;
|
||||
float restBend = ObiUtils.RestBendingConstraint(solver.restPositions[indexA], solver.restPositions[indexB], solver.restPositions[indexC]);
|
||||
|
||||
bb.particleIndices[constraint * 3] = solver.particleToActor[indexA].indexInActor;
|
||||
bb.particleIndices[constraint * 3 + 1] = solver.particleToActor[indexB].indexInActor;
|
||||
bb.particleIndices[constraint * 3 + 2] = solver.particleToActor[indexC].indexInActor;
|
||||
bb.restBends[constraint] = restBend;
|
||||
bb.bendingStiffnesses[constraint] = new Vector2(_maxBending, _bendCompliance);
|
||||
bb.ActivateConstraint(constraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop-closing constraints:
|
||||
if (dc.batches.Count > 2)
|
||||
{
|
||||
var loopClosingBatch = dc.batches[2];
|
||||
var lastElement = elements[elements.Count - 1];
|
||||
loopClosingBatch.particleIndices[0] = solver.particleToActor[lastElement.particle1].indexInActor;
|
||||
loopClosingBatch.particleIndices[1] = solver.particleToActor[lastElement.particle2].indexInActor;
|
||||
loopClosingBatch.restLengths[0] = lastElement.restLength;
|
||||
loopClosingBatch.stiffnesses[0] = new Vector2(_stretchCompliance, _maxCompression * loopClosingBatch.restLengths[0]);
|
||||
loopClosingBatch.ActivateConstraint(0);
|
||||
}
|
||||
|
||||
if (bc.batches.Count > 4 && elements.Count > 2)
|
||||
{
|
||||
var loopClosingBatch = bc.batches[3];
|
||||
var lastElement = elements[elements.Count - 2];
|
||||
|
||||
// for loop constraints, 0 is our best approximation of rest bend:
|
||||
loopClosingBatch.particleIndices[0] = solver.particleToActor[lastElement.particle1].indexInActor;
|
||||
loopClosingBatch.particleIndices[1] = solver.particleToActor[elements[0].particle1].indexInActor;
|
||||
loopClosingBatch.particleIndices[2] = solver.particleToActor[lastElement.particle2].indexInActor;
|
||||
loopClosingBatch.restBends[0] = 0;
|
||||
loopClosingBatch.bendingStiffnesses[0] = new Vector2(_maxBending, _bendCompliance);
|
||||
loopClosingBatch.ActivateConstraint(0);
|
||||
|
||||
loopClosingBatch = bc.batches[4];
|
||||
loopClosingBatch.particleIndices[0] = solver.particleToActor[lastElement.particle2].indexInActor;
|
||||
loopClosingBatch.particleIndices[1] = solver.particleToActor[elements[0].particle2].indexInActor;
|
||||
loopClosingBatch.particleIndices[2] = solver.particleToActor[elements[0].particle1].indexInActor;
|
||||
loopClosingBatch.restBends[0] = 0;
|
||||
loopClosingBatch.bendingStiffnesses[0] = new Vector2(_maxBending, _bendCompliance);
|
||||
loopClosingBatch.ActivateConstraint(0);
|
||||
}
|
||||
|
||||
// edge simplices:
|
||||
sharedBlueprint.edges = new int[elements.Count*2];
|
||||
for (int i = 0; i < elements.Count; ++i)
|
||||
{
|
||||
sharedBlueprint.edges[i * 2] = solver.particleToActor[elements[i].particle1].indexInActor;
|
||||
sharedBlueprint.edges[i * 2 + 1] = solver.particleToActor[elements[i].particle2].indexInActor;
|
||||
}
|
||||
|
||||
SetConstraintsDirty(Oni.ConstraintType.Distance);
|
||||
SetConstraintsDirty(Oni.ConstraintType.Bending);
|
||||
SetSimplicesDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRope.cs.meta
Normal file
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRope.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61104f33a3f344db9b7e0d0cda41a9fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7a860725829a34375a627c13703f0798, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeBase.cs
Normal file
109
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeBase.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
public abstract class ObiRopeBase : ObiActor
|
||||
{
|
||||
|
||||
[SerializeField] protected bool m_SelfCollisions = false;
|
||||
[HideInInspector] [SerializeField] protected float restLength_ = 0;
|
||||
[HideInInspector] public List<ObiStructuralElement> elements = new List<ObiStructuralElement>(); /**< Elements.*/
|
||||
public event ActorCallback OnElementsGenerated;
|
||||
|
||||
public float restLength
|
||||
{
|
||||
get { return restLength_; }
|
||||
}
|
||||
|
||||
public ObiPath path
|
||||
{
|
||||
get {
|
||||
var ropeBlueprint = (sourceBlueprint as ObiRopeBlueprintBase);
|
||||
return ropeBlueprint != null ? ropeBlueprint.path : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates and returns current rope length, including stretching/compression.
|
||||
/// </summary>
|
||||
public float CalculateLength()
|
||||
{
|
||||
float length = 0;
|
||||
|
||||
if (isLoaded)
|
||||
{
|
||||
// Iterate trough all distance constraints in order:
|
||||
int elementCount = elements.Count;
|
||||
for (int i = 0; i < elementCount; ++i)
|
||||
length += Vector4.Distance(solver.positions[elements[i].particle1], solver.positions[elements[i].particle2]);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates the rope's rest length, that is, its length as specified by the blueprint.
|
||||
/// </summary>
|
||||
public void RecalculateRestLength()
|
||||
{
|
||||
restLength_ = 0;
|
||||
|
||||
// Iterate trough all distance elements and accumulate their rest lengths.
|
||||
int elementCount = elements.Count;
|
||||
for (int i = 0; i < elementCount; ++i)
|
||||
restLength_ += elements[i].restLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recalculates all particle rest positions, used when filtering self-collisions.
|
||||
/// </summary>
|
||||
public void RecalculateRestPositions()
|
||||
{
|
||||
float pos = 0;
|
||||
int elementCount = elements.Count;
|
||||
for (int i = 0; i < elementCount; ++i)
|
||||
{
|
||||
solver.restPositions[elements[i].particle1] = new Vector4(pos, 0, 0, 1);
|
||||
pos += elements[i].restLength;
|
||||
solver.restPositions[elements[i].particle2] = new Vector4(pos, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates all rope elements using constraints. It's the opposite of RebuildConstraintsFromElements(). This is automatically called when loading a blueprint, but should also be called when manually
|
||||
/// altering rope constraints (adding/removing/updating constraints and/or batches).
|
||||
/// </summary>
|
||||
public void RebuildElementsFromConstraints()
|
||||
{
|
||||
RebuildElementsFromConstraintsInternal();
|
||||
if (OnElementsGenerated != null)
|
||||
OnElementsGenerated(this);
|
||||
}
|
||||
|
||||
protected abstract void RebuildElementsFromConstraintsInternal();
|
||||
|
||||
/// <summary>
|
||||
/// Regenerates all rope constraints using rope elements. It's the opposite of RebuildElementsFromConstraints().This should be called anytime the element representation of the rope
|
||||
/// is changed (adding/removing/updating elements). This is usually the case after tearing the rope or changing its length using a cursor.
|
||||
/// </summary>
|
||||
public virtual void RebuildConstraintsFromElements() { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a rope element that contains a length-normalized coordinate. It will also return the length-normalized coordinate within the element.
|
||||
/// </summary>
|
||||
public ObiStructuralElement GetElementAt(float mu, out float elementMu)
|
||||
{
|
||||
float edgeMu = elements.Count * Mathf.Clamp(mu, 0, 0.99999f);
|
||||
|
||||
int index = (int)edgeMu;
|
||||
elementMu = edgeMu - index;
|
||||
|
||||
if (elements != null && index < elements.Count)
|
||||
return elements[index];
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeBase.cs.meta
Normal file
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeBase.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab4d6f4281d1c4331b6bacd307c61a95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
269
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeCursor.cs
Normal file
269
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeCursor.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
namespace Obi
|
||||
{
|
||||
[AddComponentMenu("Physics/Obi/Obi Rope Cursor", 883)]
|
||||
[RequireComponent(typeof(ObiRope))]
|
||||
public class ObiRopeCursor : MonoBehaviour
|
||||
{
|
||||
ObiRope rope;
|
||||
|
||||
[Range(0, 1)]
|
||||
[HideInInspector] [SerializeField] private float m_CursorMu;
|
||||
|
||||
[Range(0, 1)]
|
||||
[HideInInspector] [SerializeField] private float m_SourceMu;
|
||||
|
||||
public bool direction = true;
|
||||
|
||||
ObiStructuralElement m_CursorElement = null;
|
||||
private int m_SourceIndex = -1;
|
||||
|
||||
public float cursorMu
|
||||
{
|
||||
set
|
||||
{
|
||||
m_CursorMu = value;
|
||||
UpdateCursor();
|
||||
}
|
||||
get { return m_CursorMu; }
|
||||
}
|
||||
|
||||
public float sourceMu
|
||||
{
|
||||
set
|
||||
{
|
||||
m_SourceMu = value;
|
||||
UpdateSource();
|
||||
}
|
||||
get { return m_SourceMu; }
|
||||
}
|
||||
|
||||
public ObiStructuralElement cursorElement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_CursorElement == null)
|
||||
UpdateCursor();
|
||||
return m_CursorElement;
|
||||
}
|
||||
}
|
||||
|
||||
public int sourceParticleIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_SourceIndex < 0)
|
||||
UpdateSource();
|
||||
return m_SourceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
|
||||
rope.OnElementsGenerated += Actor_OnElementsGenerated;
|
||||
if (rope.elements != null && rope.elements.Count > 0)
|
||||
Actor_OnElementsGenerated(rope);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
rope.OnElementsGenerated -= Actor_OnElementsGenerated;
|
||||
}
|
||||
|
||||
private void Actor_OnElementsGenerated(ObiActor actor)
|
||||
{
|
||||
UpdateCursor();
|
||||
UpdateSource();
|
||||
}
|
||||
|
||||
public void UpdateCursor()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
m_CursorElement = null;
|
||||
if (rope.isLoaded)
|
||||
{
|
||||
float elmMu;
|
||||
m_CursorElement = rope.GetElementAt(cursorMu, out elmMu);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSource()
|
||||
{
|
||||
rope = GetComponent<ObiRope>();
|
||||
m_SourceIndex = -1;
|
||||
if (rope.isLoaded)
|
||||
{
|
||||
float elmMu;
|
||||
var elm = rope.GetElementAt(sourceMu, out elmMu);
|
||||
if (elm != null && rope.solver != null)
|
||||
{
|
||||
m_SourceIndex = elmMu < 0.5f ? elm.particle1 : elm.particle2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int AddParticleAt(int index)
|
||||
{
|
||||
// Copy data from the particle where we will insert new particles, to the particles we will insert:
|
||||
int targetIndex = rope.activeParticleCount;
|
||||
rope.CopyParticle(rope.solver.particleToActor[m_SourceIndex].indexInActor, targetIndex);
|
||||
|
||||
// Move the new particle to the one at the place where we will insert it:
|
||||
rope.TeleportParticle(targetIndex, rope.solver.positions[rope.solverIndices[index]]);
|
||||
|
||||
// Activate the particle:
|
||||
rope.ActivateParticle(targetIndex);
|
||||
return rope.solverIndices[targetIndex];
|
||||
}
|
||||
|
||||
private void RemoveParticleAt(int index)
|
||||
{
|
||||
rope.DeactivateParticle(index);
|
||||
}
|
||||
|
||||
public void ChangeLength(float newLength)
|
||||
{
|
||||
if (!rope.isLoaded)
|
||||
return;
|
||||
|
||||
var solver = rope.solver;
|
||||
|
||||
// clamp new length to sane limits:
|
||||
newLength = Mathf.Clamp(newLength, 0, (rope.sourceBlueprint.particleCount - 1) * rope.ropeBlueprint.interParticleDistance);
|
||||
|
||||
// calculate the change in rope length:
|
||||
float lengthChange = newLength - rope.restLength;
|
||||
|
||||
// remove:
|
||||
if (lengthChange < 0)
|
||||
{
|
||||
lengthChange = -lengthChange;
|
||||
|
||||
while (lengthChange > m_CursorElement.restLength)
|
||||
{
|
||||
lengthChange -= m_CursorElement.restLength;
|
||||
|
||||
int index = rope.elements.IndexOf(m_CursorElement);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
// positive direction:
|
||||
if (direction)
|
||||
{
|
||||
RemoveParticleAt(solver.particleToActor[m_CursorElement.particle2].indexInActor);
|
||||
rope.elements.RemoveAt(index);
|
||||
|
||||
if (index < rope.elements.Count)
|
||||
{
|
||||
if (rope.elements[index].particle1 == m_CursorElement.particle2)
|
||||
rope.elements[index].particle1 = m_CursorElement.particle1;
|
||||
|
||||
m_CursorElement = rope.elements[index];
|
||||
}
|
||||
else
|
||||
m_CursorElement = rope.elements[Mathf.Max(0,index - 1)];
|
||||
}
|
||||
else // negative direction:
|
||||
{
|
||||
RemoveParticleAt(solver.particleToActor[m_CursorElement.particle1].indexInActor);
|
||||
rope.elements.RemoveAt(index);
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
if (rope.elements[index - 1].particle2 == m_CursorElement.particle1)
|
||||
rope.elements[index - 1].particle2 = m_CursorElement.particle2;
|
||||
m_CursorElement = rope.elements[index - 1];
|
||||
}
|
||||
else
|
||||
m_CursorElement = rope.elements[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the remaining length is subtracted from the current constraint:
|
||||
if (lengthChange > 0)
|
||||
m_CursorElement.restLength = Mathf.Max(0, m_CursorElement.restLength - lengthChange);
|
||||
|
||||
}
|
||||
// add
|
||||
else
|
||||
{
|
||||
float lengthDelta = Mathf.Min(lengthChange, Mathf.Max(0, rope.ropeBlueprint.interParticleDistance - m_CursorElement.restLength));
|
||||
|
||||
// extend the current element, if possible:
|
||||
if (lengthDelta > 0)
|
||||
{
|
||||
m_CursorElement.restLength += lengthDelta;
|
||||
lengthChange -= lengthDelta;
|
||||
}
|
||||
|
||||
// once the current element has been extended, see if we must add new elements, if there's enough particles left:
|
||||
while (rope.activeParticleCount < rope.sourceBlueprint.particleCount &&
|
||||
m_CursorElement.restLength + lengthChange > rope.ropeBlueprint.interParticleDistance)
|
||||
{
|
||||
// calculate added length:
|
||||
lengthDelta = Mathf.Min(lengthChange, rope.ropeBlueprint.interParticleDistance);
|
||||
lengthChange -= lengthDelta;
|
||||
|
||||
if (direction)
|
||||
{
|
||||
// add new particle:
|
||||
int newParticleSolverIndex = AddParticleAt(solver.particleToActor[m_CursorElement.particle1].indexInActor);
|
||||
|
||||
// set position of the new particle:
|
||||
solver.positions[newParticleSolverIndex] = solver.positions[m_CursorElement.particle1] +
|
||||
(solver.positions[m_CursorElement.particle2] - solver.positions[m_CursorElement.particle1]) * lengthDelta;
|
||||
|
||||
// insert a new element:
|
||||
ObiStructuralElement newElement = new ObiStructuralElement();
|
||||
newElement.restLength = lengthDelta;
|
||||
newElement.particle1 = m_CursorElement.particle1;
|
||||
newElement.particle2 = newParticleSolverIndex;
|
||||
m_CursorElement.particle1 = newParticleSolverIndex;
|
||||
int index = rope.elements.IndexOf(m_CursorElement);
|
||||
rope.elements.Insert(index, newElement);
|
||||
|
||||
m_CursorElement = newElement;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new particle:
|
||||
int newParticleSolverIndex = AddParticleAt(solver.particleToActor[m_CursorElement.particle2].indexInActor);
|
||||
|
||||
// set position of the new particle:
|
||||
solver.positions[newParticleSolverIndex] = solver.positions[m_CursorElement.particle2] +
|
||||
(solver.positions[m_CursorElement.particle1] - solver.positions[m_CursorElement.particle2]) * lengthDelta;
|
||||
|
||||
// insert a new element:
|
||||
ObiStructuralElement newElement = new ObiStructuralElement();
|
||||
newElement.restLength = lengthDelta;
|
||||
newElement.particle1 = newParticleSolverIndex;
|
||||
newElement.particle2 = m_CursorElement.particle2;
|
||||
m_CursorElement.particle2 = newParticleSolverIndex;
|
||||
int index = rope.elements.IndexOf(m_CursorElement);
|
||||
rope.elements.Insert(index + 1, newElement);
|
||||
|
||||
m_CursorElement = newElement;
|
||||
}
|
||||
}
|
||||
|
||||
// the remaining length is added to the current constraint:
|
||||
if (lengthChange > 0)
|
||||
m_CursorElement.restLength += lengthChange;
|
||||
|
||||
}
|
||||
|
||||
// recalculate rest positions and length prior to constraints (bend constraints need rest positions):
|
||||
rope.RecalculateRestPositions();
|
||||
rope.RecalculateRestLength();
|
||||
|
||||
// rebuild constraints:
|
||||
rope.RebuildConstraintsFromElements();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeCursor.cs.meta
Normal file
11
Assets/Obi/Scripts/RopeAndRod/Actors/ObiRopeCursor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8285ac97113f74d449053378d0e3a56b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 618cf36eb21e34118ac5697fe77e2c3e, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user