private void UpdateJoints() { for (int i = 0; i < joints.Length; ++i) { if (joints[i] != null && links[i].body != null && links[i + 1].body != null) { CableJoint joint = joints[i]; Vector3 t1, t2; FindCommonTangents(links[i], links[i + 1], out t1, out t2); Vector2 currentT1 = joint.body1.WorldSpaceToCablePlane(joint.WorldSpaceAttachment1); Vector2 currentT2 = joint.body2.WorldSpaceToCablePlane(joint.WorldSpaceAttachment2); // Get surface distances between old and new tangents: float d1 = joint.body1.SurfaceDistance(currentT1, joint.body1.WorldSpaceToCablePlane(t1), links[i].orientation); float d2 = joint.body2.SurfaceDistance(currentT2, joint.body2.WorldSpaceToCablePlane(t2), links[i + 1].orientation); // Update stored lengths: links[i].storedCable -= d1; links[i + 1].storedCable += d2; // Update rest lengths: joint.restLength += d1; joint.restLength -= d2; // Update tangent points: joint.offset1 = joint.body1.transform.InverseTransformPoint(t1 - joint.body1.GetCablePlaneNormal() * links[i].storedCable * links[i].spoolSeparation); joint.offset2 = joint.body2.transform.InverseTransformPoint(t2 - joint.body2.GetCablePlaneNormal() * links[i + 1].storedCable * links[i + 1].spoolSeparation); } } }
public void Setup() { joints = null; if (links != null && links.Length > 0) { joints = new CableJoint[links.Length - 1]; for (int i = 0; i < links.Length - 1; ++i) { if (links[i].body != null && links[i + 1].body != null) { Vector3 t1, t2; FindCommonTangents(links[i], links[i + 1], out t1, out t2); t1 -= links[i].body.GetCablePlaneNormal() * links[i].storedCable * links[i].spoolSeparation; t2 -= links[i + 1].body.GetCablePlaneNormal() * links[i + 1].storedCable * links[i + 1].spoolSeparation; joints[i] = new CableJoint(links[i].body, links[i + 1].body, links[i].body.transform.InverseTransformPoint(t1), links[i + 1].body.transform.InverseTransformPoint(t2), (t2 - t1).magnitude); } } } CalculateRestLength(); }
private void CalculateRestLength() { restLength = 0; if (joints == null) { return; } bool closed = (links[0].body == links[links.Count - 1].body); for (int i = 0; i < links.Count; ++i) { Link link = links[i]; if (link.body != null) { CableJoint prevJoint = GetPreviousJoint(i, closed); CableJoint nextJoint = GetNextJoint(i, closed); if (nextJoint != null && prevJoint != null && (!(i == 0 && closed))) { link.storedCable = Mathf.Abs(link.body.SurfaceDistance(link.body.WorldSpaceToCablePlane(prevJoint.WorldSpaceAttachment2), link.body.WorldSpaceToCablePlane(nextJoint.WorldSpaceAttachment1), link.orientation)); restLength += link.storedCable; } else if (link.type == Link.LinkType.Hybrid) { restLength += link.storedCable; if (nextJoint != null) { Vector2 tangent = link.body.WorldSpaceToCablePlane(nextJoint.WorldSpaceAttachment1); int j = 0; link.outAnchor = link.body.transform.InverseTransformPoint(link.body.SurfacePointAtDistance(tangent, link.storedCable, link.orientation, out j)); } else if (prevJoint != null) { Vector2 tangent = link.body.WorldSpaceToCablePlane(prevJoint.WorldSpaceAttachment2); int j = 0; link.inAnchor = link.body.transform.InverseTransformPoint(link.body.SurfacePointAtDistance(tangent, link.storedCable, !link.orientation, out j)); } } if (i < links.Count - 1 && joints[i] != null) { restLength += joints[i].restLength; } } links[i] = link; } }
private void SampleLink(CableJoint prevJoint, Link link, CableJoint nextJoint) { Vector3?t1 = null, t2 = null; if (prevJoint != null) { t1 = prevJoint.body2.WorldToCable(prevJoint.WorldSpaceAttachment2); } if (nextJoint != null) { t2 = nextJoint.body1.WorldToCable(nextJoint.WorldSpaceAttachment1); } // Hybrid links (only at the start or the end of the cable) if (link.type == Link.LinkType.Hybrid) { if (t1.HasValue) { link.body.AppendSamples(sampledCable, t1.Value, link.storedCable, link.spoolSeparation, false, link.orientation); } else if (t2.HasValue) { link.body.AppendSamples(sampledCable, t2.Value, link.storedCable, link.spoolSeparation, true, link.orientation); } } // Rolling links (only mid-cable) else if (link.type == Link.LinkType.Rolling) { if (t1.HasValue && t2.HasValue) { float distance = link.body.SurfaceDistance(t1.Value, t2.Value, !link.orientation, false); link.body.AppendSamples(sampledCable, t1.Value, distance, 0, false, link.orientation); } } // Attachment, source and pinhole links: else { if (t1.HasValue) { sampledCable.AppendSample(prevJoint.body2.transform.TransformPoint(link.inAnchor)); } if (t1.HasValue && t2.HasValue && t1.Value != t2.Value) { sampledCable.NewSegment(); } if (t2.HasValue) { sampledCable.AppendSample(nextJoint.body1.transform.TransformPoint(link.outAnchor)); } } }
private void SampleJoint(CableJoint joint) { Vector3 p1 = joint.WorldSpaceAttachment1; Vector3 p2 = joint.WorldSpaceAttachment2; if (joint.length < joint.restLength) { Vector3 point = p2 - p1; Vector3 dir = Vector3.Scale(point, new Vector3(1, 0, 1)); if (loosenessScale > 0) { float sampledLength = Mathf.Lerp(joint.length, Mathf.Min(joint.restLength, joint.length + maxLooseCable), loosenessScale); if (dir.sqrMagnitude > verticalThreshold) { Quaternion rot = Quaternion.LookRotation(dir); Quaternion irot = Quaternion.Inverse(rot); Vector3 n = irot * point; if (Utils.Catenary(Vector2.zero, new Vector2(n.z, n.y), sampledLength, ref catenaryBuffer)) { for (int j = 1; j < catenaryBuffer.Length - 1; ++j) { sampledCable.AppendSample(p1 + rot * new Vector3(0, catenaryBuffer[j].y, catenaryBuffer[j].x)); } } } else { if (Utils.Sinusoid(p1, point, sampledLength, verticalCurlyness, ref sinusoidBuffer)) { for (int j = 1; j < sinusoidBuffer.Length - 1; ++j) { sampledCable.AppendSample(sinusoidBuffer[j]); } } } } } }
private void UpdatePinhole(CableJoint joint1, CableJoint joint2) { if (joint1 != null && joint2 != null) { float restLenght1 = joint1.restLength; float restLength2 = joint2.restLength; if (joint1.length > restLenght1) { float delta = joint1.length - restLenght1; joint1.restLength += delta; joint2.restLength -= delta; } if (joint2.length > restLength2) { float delta = joint2.length - restLength2; joint1.restLength -= delta; joint2.restLength += delta; } } }
private void Update() { sampledCable.Clear(); if (joints == null || links.Length == 0) { return; } bool closed = (links[0].body == links[links.Length - 1].body); for (int i = 0; i < links.Length; ++i) { if (links[i].body != null) { CableJoint prevJoint = GetPreviousJoint(i, closed); CableJoint nextJoint = GetNextJoint(i, closed); // Sample the link, except if the cable is closed and this is the first link. if (!(i == 0 && closed) || links[i].type == Link.LinkType.Attachment || links[i].type == Link.LinkType.Pinhole) { SampleLink(prevJoint, links[i], nextJoint); } // Sample the joint (only adds sample points if cable is not tense): if (i < joints.Length && joints[i] != null) { SampleJoint(joints[i]); } } } if (closed) { sampledCable.Close(); } }
private void SplitMerge() { if (!dynamicSplitMerge) { return; } // merge links with negative stored cable: for (int i = 1; i < links.Count - 1; ++i) { CableJoint prevJoint = joints[i - 1]; CableJoint nextJoint = joints[i]; if (links[i].type == Link.LinkType.Rolling && links[i].body != null && prevJoint != null && nextJoint != null) { if (links[i].storedCable < 0) { prevJoint.restLength += nextJoint.restLength; prevJoint.Body2 = nextJoint.Body2; // Update joint attachment points: Vector3 t1, t2; FindCommonTangents(links[i - 1], links[i + 1], out t1, out t2); prevJoint.offset1 = prevJoint.Body1.transform.InverseTransformPoint(t1); prevJoint.offset2 = prevJoint.Body2.transform.InverseTransformPoint(t2); prevJoint.Initialize(); links.RemoveAt(i); joints.RemoveAt(i); } } } // split joints that intersect a body: for (int i = 0; i < joints.Count; ++i) { CableJoint currentJoint = joints[i]; if (currentJoint != null) { RaycastHit hit; if (Physics.Raycast(new Ray(currentJoint.WorldSpaceAttachment1, currentJoint.WorldSpaceAttachment2 - currentJoint.WorldSpaceAttachment1), out hit, currentJoint.length)) { CableBody body = hit.collider.GetComponent <CableBody>(); // Only split if the body is a disc or a convex shape, and the raycast hit is sane. if ((body is CableDisc || body is CableShape) && hit.distance > 0.1f && hit.distance + 0.1f < currentJoint.length) { float initialRestLength = currentJoint.restLength; // Create new joint and link: CableJoint newJoint = new CableJoint(body, currentJoint.Body2, Vector3.zero, Vector3.zero, currentJoint.restLength); currentJoint.Body2 = body; Link newLink = new Link(); newLink.type = Link.LinkType.Rolling; newLink.body = body; // Calculate orientation. Vector3 v = body.transform.position - currentJoint.WorldSpaceAttachment1; Vector3 cross = Vector3.Cross(body.GetCablePlaneNormal(), v); newLink.orientation = Vector3.Dot(cross, hit.point - currentJoint.WorldSpaceAttachment1) < 0; // Update joint attachment points: Vector3 t1, t2; FindCommonTangents(links[i], newLink, out t1, out t2); currentJoint.offset1 = currentJoint.Body1.transform.InverseTransformPoint(t1); currentJoint.offset2 = currentJoint.Body2.transform.InverseTransformPoint(t2); FindCommonTangents(newLink, links[i + 1], out t1, out t2); newJoint.offset1 = newJoint.Body1.transform.InverseTransformPoint(t1); newJoint.offset2 = newJoint.Body2.transform.InverseTransformPoint(t2); currentJoint.Initialize(); newJoint.Initialize(); // Adjust rest lengths so that tensions are equal: float tension = initialRestLength / (currentJoint.length + newJoint.length); currentJoint.restLength = currentJoint.length * tension; newJoint.restLength = newJoint.length * tension; // Insert new joint/link: joints.Insert(i + 1, newJoint); links.Insert(i + 1, newLink); } } } } }
private void UpdateJoints() { for (int i = 0; i < joints.Count; ++i) { if (joints[i] != null && links[i].body != null && links[i + 1].body != null) { CableJoint joint = joints[i]; Link link1 = links[i]; Link link2 = links[i + 1]; Vector3 t1, t2; FindCommonTangents(links[i], links[i + 1], out t1, out t2); Vector2 currentT1 = joint.body1.WorldSpaceToCablePlane(joint.WorldSpaceAttachment1); Vector2 currentT2 = joint.body2.WorldSpaceToCablePlane(joint.WorldSpaceAttachment2); // Get surface distances between old and new tangents: float d1 = joint.body1.SurfaceDistance(currentT1, joint.body1.WorldSpaceToCablePlane(t1), links[i].orientation); float d2 = joint.body2.SurfaceDistance(currentT2, joint.body2.WorldSpaceToCablePlane(t2), links[i + 1].orientation); // Spawn more cable if necessary if (links[i].type == Link.LinkType.Attachment) { d1 += links[i].cableSpawnSpeed; restLength += links[i].cableSpawnSpeed; } if (links[i + 1].type == Link.LinkType.Attachment) { d2 -= links[i + 1].cableSpawnSpeed; restLength += links[i + 1].cableSpawnSpeed; } // Update stored lengths: link1.storedCable -= d1; link2.storedCable += d2; // Update rest lengths: joint.restLength += d1; joint.restLength -= d2; // Update hybrid link attachment points (displace along the plane normal based on amount of stored cable): if (links[i].type == Link.LinkType.Hybrid) { t1 -= joint.body1.GetCablePlaneNormal() * links[i].storedCable * links[i].spoolSeparation; } if (links[i + 1].type == Link.LinkType.Hybrid) { t2 -= joint.body2.GetCablePlaneNormal() * links[i + 1].storedCable * links[i + 1].spoolSeparation; } // Update tangent points: joint.offset1 = joint.body1.transform.InverseTransformPoint(t1); joint.offset2 = joint.body2.transform.InverseTransformPoint(t2); links[i] = link1; links[i + 1] = link2; } } }