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; private float lengthChange = 0; 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(); rope.OnElementsGenerated += Actor_OnElementsGenerated; rope.OnSimulationStart += Rope_OnSimulate; if (rope.elements != null && rope.elements.Count > 0) Actor_OnElementsGenerated(rope); } private void OnDisable() { rope.OnElementsGenerated -= Actor_OnElementsGenerated; rope.OnSimulationStart -= Rope_OnSimulate; } private void Actor_OnElementsGenerated(ObiActor actor) { UpdateCursor(); UpdateSource(); } private void Rope_OnSimulate(ObiActor actor, float simulatedTime, float substepTime) { if (!rope.isLoaded || Mathf.Abs(lengthChange) < ObiUtils.epsilon) return; var solver = rope.solver; // remove: if (lengthChange < 0) { lengthChange = -lengthChange; while (lengthChange > m_CursorElement.restLength) { lengthChange -= m_CursorElement.restLength; // if we subtracted the length of the last element, break out of the loop. if (rope.elements.Count == 1) break; 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(); lengthChange = 0; } public void UpdateCursor() { rope = GetComponent(); m_CursorElement = null; if (rope.isLoaded) { float elmMu; m_CursorElement = rope.GetElementAt(cursorMu, out elmMu); } } public void UpdateSource() { rope = GetComponent(); 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) { int targetIndex = rope.activeParticleCount; // Copy data from the particle where we will insert new particles, to the particles we will insert: 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(); rope.SetRenderingDirty(Oni.RenderingSystemType.AllRopes); return rope.solverIndices[targetIndex]; } private void RemoveParticleAt(int index) { rope.DeactivateParticle(index); rope.SetRenderingDirty(Oni.RenderingSystemType.AllRopes); } public float ChangeLength(float lengthChange) { // clamp new length to sane limits: //newLength = Mathf.Clamp(newLength, 0, (rope.sourceBlueprint.particleCount - 1) * rope.ropeBlueprint.interParticleDistance); // accumulate length change, we'll reset it to zero after it has been applied. this.lengthChange += lengthChange; // return new length: return this.lengthChange + rope.restLength; } } }