private void InitializeHelpers() { this.computedInputPose = new ComputedValue <SlingShotPose>(() => this.ComputeInputPose()); this.computedTangentPositionR = new ComputedValue <SCNVector3>(() => this.TangentPosition(this.FixturePositionR)); this.computedTangentPositionL = new ComputedValue <SCNVector3>(() => this.TangentPosition(this.FixturePositionL)); this.computedBetaAngle = new ComputedValue <float>(() => { var d = SCNVector3.Normalize(this.ballPosition - this.CenterPosition); var t = SCNVector3.Normalize(this.TangentPositionL - this.ballPosition); var quaternion = SimdExtensions.CreateQuaternion(d, t); quaternion.ToAxisAngle(out SCNVector3 _, out float angle); return(2f * angle); }); this.computedCenterPosition = new ComputedValue <SCNVector3>(() => { var direction = SCNVector3.Cross(this.UpVector, this.TangentPositionR - this.TangentPositionL); return(this.ballPosition - SCNVector3.Normalize(direction) * 1.25f * this.ballRadius); }); this.computedRestPose = new ComputedValue <SlingShotPose>(() => { var data = new SlingShotPose(); for (var i = 0; i < this.restPoseTransforms.Count; i++) { var transform = this.restPoseSpace * this.restPoseTransforms[i]; var p = new SCNVector3(transform.Column3.X, transform.Column3.Y, transform.Column3.Z); var t = SimdExtensions.CreateQuaternion(transform).Act(new SCNVector3(1f, 0f, 0f)); var l = 0f; if (i > 0) { l = data.Lengths[i - 1] + (p - data.Positions[i - 1]).Length; } data.Positions.Add(p); data.Tangents.Add(t); data.Lengths.Add(l); } return(data); }); }
public SlingShotPose ComputeInputPose() { // note the -1 here differs from other usage var data = new SlingShotPose { UpVector = -this.UpVector /* negated because the strap Y-axis points down */ }; var startBend = this.CurrentLengthL / this.CurrentTotalLength; var endBend = 1f - this.CurrentLengthR / this.CurrentTotalLength; var leatherOnStraights = this.OriginalLeatherLength - this.CurrentLengthOnBall; var segmentAStart = 0f; var segmentAEnd = this.CurrentLengthL - leatherOnStraights * 0.5f; var segmentCStart = segmentAEnd + this.OriginalLeatherLength; var segmentCEnd = this.CurrentTotalLength; var originalLeatherRange = this.OriginalLeatherLength / this.OriginalTotalLength; var currentLeatherRange = this.OriginalLeatherLength / this.CurrentTotalLength; for (var i = 0; i < this.SimulatedTransformCount; i++) { var l = this.OriginalTotalLength * (float)i / (float)(this.SimulatedTransformCount - 1f); var u = l / this.OriginalTotalLength; // remap the u value depending on the material (rubber vs leather) var isRubber = Math.Abs(0.5f - u) > originalLeatherRange * 0.5f; if (isRubber) { if (u < 0.5f) { u = u / (0.5f - originalLeatherRange * 0.5f); u = (segmentAStart + (segmentAEnd - segmentAStart) * u) / this.CurrentTotalLength; } else { u = 1f - (1f - u) / (0.5f - originalLeatherRange * 0.5f); u = (segmentCStart + (segmentCEnd - segmentCStart) * u) / this.CurrentTotalLength; } } else { u = (startBend + endBend) * 0.5f - (0.5f - u) * (currentLeatherRange / originalLeatherRange); } var p = SCNVector3.Zero; var t = SCNVector3.UnitX; if (u < startBend) { // left straight var value = u / startBend; p = SimdExtensions.Mix(this.FixturePositionL, this.TangentPositionL, new SCNVector3(value, value, value)); // left rubber band t = SCNVector3.Normalize(this.TangentPositionL - this.FixturePositionL); } else if (u > endBend) { // right straight var value = (1f - u) / (1f - endBend); p = SimdExtensions.Mix(this.FixturePositionR, this.TangentPositionR, new SCNVector3(value, value, value)); // right rubber band t = SCNVector3.Normalize(this.FixturePositionR - this.TangentPositionR); } else { // on the ball var upv = this.UpVector; var rot = SCNQuaternion.FromAxisAngle(upv, -this.BetaAngle * (u - startBend) / (endBend - startBend)); p = this.ballPosition + rot.Act(this.TangentPositionL - this.ballPosition); t = SCNVector3.Cross(upv, SCNVector3.Normalize(this.ballPosition - p)); } data.Positions.Add(p); data.Tangents.Add(t); data.Lengths.Add(l); } return(data); }