예제 #1
0
        private SCNNode GetFillPlane()
        {
            var length = 1f - 2f * BorderSegment.Thickness;
            var plane  = SCNPlane.Create(length, length * this.AspectRatio);
            var node   = SCNNode.FromGeometry(plane);

            node.Name    = "fillPlane";
            node.Opacity = 0.6f;

            var material = plane.FirstMaterial;

            material.Diffuse.Contents = UIImage.FromBundle("art.scnassets/textures/grid.png");

            var textureScale = SimdExtensions.CreateFromScale(new SCNVector3(40f, 40f * this.AspectRatio, 1f));

            material.Diffuse.ContentsTransform  = textureScale;
            material.Emission.Contents          = UIImage.FromBundle("art.scnassets/textures/grid.png");
            material.Emission.ContentsTransform = textureScale;
            material.Diffuse.WrapS     = SCNWrapMode.Repeat;
            material.Diffuse.WrapT     = SCNWrapMode.Repeat;
            material.DoubleSided       = true;
            material.Ambient.Contents  = UIColor.Black;
            material.LightingModelName = SCNLightingModel.Constant;

            return(node);
        }
예제 #2
0
        /// <summary>
        /// Aligns the rigid bodies by correcting their orienation
        /// This should be called after the simulation step
        /// </summary>
        private void AlignBones()
        {
            // orient the bodies accordingly
            for (var i = this.BoneInset; i < this.simulatedTransforms.Count - this.BoneInset; i++)
            {
                if (!this.simulatedTransforms[i].Dynamic)
                {
                    continue;
                }

                var a = this.simulatedTransforms[i - 1].Position;
                var b = this.simulatedTransforms[i + 1].Position;

                // this is the upVector computed for each bone of the rest pose
                var transform = this.restPoseSpace * this.restPoseTransforms[i]; // todo: direction of multiply?
                var y         = SimdExtensions.CreateQuaternion(transform).Act(new SCNVector3(0f, 1f, 0f));

                var x = SCNVector3.Normalize(b - a);
                var z = SCNVector3.Normalize(SCNVector3.Cross(x, y));
                y = SCNVector3.Normalize(SCNVector3.Cross(z, x));

                var rot = new OpenTK.Matrix3(x.X, y.X, z.X, x.Y, y.Y, z.Y, x.Z, y.Z, z.Z);
                this.simulatedTransforms[i].Orientation = new SCNQuaternion(rot.ToQuaternion());
            }
        }
예제 #3
0
        /// <summary>
        /// Returns the interpolated tangent at a given length (l from 0.0 to totalLength)
        /// </summary>
        public SCNVector3 Tangent(float l)
        {
            var s = this.FindIndex(l);

            return(SCNVector3.Normalize(SimdExtensions.Mix(this.Tangents[this.lastIndex],
                                                           this.Tangents[this.lastIndex + 1],
                                                           new SCNVector3(s, s, s))));
        }
예제 #4
0
        /// <summary>
        /// Returns the interpolated position at a given length (l from 0.0 to totalLength)
        /// </summary>
        public SCNVector3 Position(float l)
        {
            var s = this.FindIndex(l);

            return(SimdExtensions.Mix(this.Positions[this.lastIndex],
                                      this.Positions[this.lastIndex + 1],
                                      new SCNVector3(s, s, s)));
        }
예제 #5
0
        private void UpdateColors()
        {
            var baseColor = SimdExtensions.CreateVector4(this.Team.GetColor());

            for (var i = 0; i < this.colors.Count; i++)
            {
                var scale = (float)i / (float)this.colors.Count;
                this.colors[i] = baseColor * scale;
            }
        }
예제 #6
0
        private void PerformPlaneCollision(List <SimulatedTransform> previousTransforms, float seconds)
        {
            for (var i = this.BoneInset; i < this.simulatedTransforms.Count - this.BoneInset; i++)
            {
                if (!this.simulatedTransforms[i].Dynamic)
                {
                    continue;
                }

                var p = this.simulatedTransforms[i].Position;
                var v = this.simulatedTransforms[i].Velocity;

                // project into the space of the base
                var pM = SCNMatrix4.Identity.SetTranslation((OpenTK.Vector3)p);

                var pLocal = SCNMatrix4.Invert(this.restPoseSpace) * pM;

                if (pLocal.Column3.Z <= CollisionPlane)
                {
                    pLocal.M34 = CollisionPlane;
                    pM         = this.restPoseSpace * pLocal;

                    var pOnPlane = new SCNVector3(pM.Column3.X, pM.Column3.Y, pM.Column3.Z);

                    var blend = new SCNVector3(0.3f, 0.3f, 0.3f);
                    this.simulatedTransforms[i].Position = SimdExtensions.Mix(p, pOnPlane, blend);

                    var correctedVelocity = (this.simulatedTransforms[i].Position - previousTransforms[i].Position) / seconds;
                    correctedVelocity = SCNVector3.Multiply(correctedVelocity, new SCNVector3(0.7f, 0.1f, 0.7f));

                    // verlet integration
                    this.simulatedTransforms[i].Velocity = SimdExtensions.Mix(v, correctedVelocity, blend);

                    p = this.simulatedTransforms[i].Position;
                    v = this.simulatedTransforms[i].Velocity;
                }

                if (pLocal.Column3.Y <= CollisionPlane + 0.3f)
                {
                    pLocal.M24 = CollisionPlane + 0.3f;
                    pM         = this.restPoseSpace * pLocal;

                    var pOnPlane = new SCNVector3(pM.Column3.X, pM.Column3.Y, pM.Column3.Z);

                    var blend = new SCNVector3(0.3f, 0.3f, 0.3f);
                    this.simulatedTransforms[i].Position = SimdExtensions.Mix(p, pOnPlane, blend);

                    var correctedVelocity = (this.simulatedTransforms[i].Position - previousTransforms[i].Position) / seconds;

                    // verlet integration
                    this.simulatedTransforms[i].Velocity = SimdExtensions.Mix(v, correctedVelocity, blend);
                }
            }
        }
예제 #7
0
        /// <summary>
        /// Disables the simulation on the slingshot and sets the rigid bodies to be driven by the input pose
        /// </summary>
        public void EnableInputPose()
        {
            for (var i = 0; i < this.simulatedTransforms.Count; i++)
            {
                var l         = this.OriginalTotalLength * (float)i / (float)(this.simulatedTransforms.Count - 1);
                var transform = this.InputPoseTransform(l);

                var position = new SCNVector3(transform.Column3.X, transform.Column3.Y, transform.Column3.Z);
                this.simulatedTransforms[i].Position    = position;
                this.simulatedTransforms[i].Orientation = SimdExtensions.CreateQuaternion(transform);
                this.simulatedTransforms[i].Velocity    = SCNVector3.Zero;
                this.simulatedTransforms[i].Dynamic     = false;
            }
        }
예제 #8
0
        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);
            });
        }
예제 #9
0
        private void OrientToPlane(ARPlaneAnchor planeAnchor, ARCamera camera)
        {
            // Get board rotation about y
            this.Orientation = SimdExtensions.CreateQuaternion(planeAnchor.Transform.ToSCNMatrix4());
            var boardAngle = this.EulerAngles.Y;

            // If plane is longer than deep, rotate 90 degrees
            if (planeAnchor.Extent.X > planeAnchor.Extent.Z)
            {
                boardAngle += (float)Math.PI / 2f;
            }

            // Normalize angle to closest 180 degrees to camera angle
            boardAngle = boardAngle.NormalizedAngle(camera.EulerAngles.Y, (float)Math.PI);

            this.Rotate(boardAngle);
        }
예제 #10
0
        private void SlideInWorld(SCNVector3 start, SCNVector3 velocity)
        {
            var maxSlideIteration = 4;
            var iteration         = 0;
            var stop = false;

            var replacementPoint = start;

            var options = new SCNPhysicsTest()
            {
                CollisionBitMask = (int)Bitmask.Collision,
                SearchMode       = SCNPhysicsSearchMode.Closest,
            };

            while (!stop)
            {
                var from = SCNMatrix4.Identity;
                SimdExtensions.SetPosition(ref from, start);

                var to = SCNMatrix4.Identity;
                SimdExtensions.SetPosition(ref to, start + velocity);

                var contacts = this.PhysicsWorld.ConvexSweepTest(this.characterCollisionShape, from, to, options.Dictionary);
                if (contacts.Any())
                {
                    (velocity, start) = this.HandleSlidingAtContact(contacts.FirstOrDefault(), start, velocity);
                    iteration        += 1;

                    if (velocity.LengthSquared <= (10E-3 * 10E-3) || iteration >= maxSlideIteration)
                    {
                        replacementPoint = start;
                        stop             = true;
                    }
                }
                else
                {
                    replacementPoint = start + velocity;
                    stop             = true;
                }
            }

            this.characterNode.WorldPosition = replacementPoint - this.collisionShapeOffsetFromModel;
        }
예제 #11
0
        /// <summary>
        /// Computes and applies the custom forces for the slingshot rope.
        /// This should be called every frame.
        /// </summary>
        private void ApplyForces()
        {
            var b = new SCNVector3(SimBlend, SimBlend, SimBlend);

            for (var i = this.BoneInset; i < this.simulatedTransforms.Count - this.BoneInset; i++)
            {
                if (!this.simulatedTransforms[i].Dynamic)
                {
                    continue;
                }

                var force = SCNVector3.Zero;

                if (i > 0)
                {
                    var restA           = this.RestPose.Positions[i - 1];
                    var restB           = this.RestPose.Positions[i];
                    var currentA        = this.simulatedTransforms[i - 1].Position;
                    var currentB        = this.simulatedTransforms[i].Position;
                    var restDistance    = (restA - restB).Length;
                    var currentDistance = (currentA - currentB).Length;
                    force += SCNVector3.Normalize(currentA - currentB) * (currentDistance - restDistance) * SimNeighborStrength;
                }

                if (i < this.simulatedTransforms.Count - 1)
                {
                    var restA           = this.RestPose.Positions[i + 1];
                    var restB           = this.RestPose.Positions[i];
                    var currentA        = this.simulatedTransforms[i + 1].Position;
                    var currentB        = this.simulatedTransforms[i].Position;
                    var restDistance    = (restA - restB).Length;
                    var currentDistance = (currentA - currentB).Length;
                    force += SCNVector3.Normalize(currentA - currentB) * (currentDistance - restDistance) * SimNeighborStrength;
                }

                force += (this.RestPose.Positions[i] - this.simulatedTransforms[i].Position) * SimRestPoseStrength;


                var vel = this.simulatedTransforms[i].Velocity;
                this.simulatedTransforms[i].Velocity = SimdExtensions.Mix(vel, force, b);
            }
        }
예제 #12
0
        private void AverageVelocities()
        {
            var currentTransforms = new List <SimulatedTransform>();

            currentTransforms.AddRange(this.simulatedTransforms);

            for (var i = this.BoneInset; i < this.SimulatedTransformCount - this.BoneInset; i++)
            {
                if (!this.simulatedTransforms[i].Dynamic)
                {
                    continue;
                }

                var a  = currentTransforms[i - 1].Velocity;
                var b  = currentTransforms[i].Velocity;
                var c  = currentTransforms[i + 1].Velocity;
                var ab = SimdExtensions.Mix(a, b, new SCNVector3(0.5f, 0.5f, 0.5f));
                var bc = SimdExtensions.Mix(b, c, new SCNVector3(0.5f, 0.5f, 0.5f));
                this.simulatedTransforms[i].Velocity = SimdExtensions.Mix(ab, bc, t: new SCNVector3(0.5f, 0.5f, 0.5f));

                var center = SimdExtensions.Mix(currentTransforms[i - 1].Position, currentTransforms[i + 1].Position, new SCNVector3(0.5f, 0.5f, 0.5f));
                this.simulatedTransforms[i].Position = SimdExtensions.Mix(this.simulatedTransforms[i].Position, center, new SCNVector3(SmoothRope, SmoothRope, SmoothRope));
            }
        }
예제 #13
0
        private void UpdateBorderAspectRatio()
        {
            var borderSize = new CGSize(1f, this.AspectRatio);

            foreach (var segment in this.borderSegments)
            {
                segment.BorderSize = borderSize;
            }

            if (this.FillPlane.Geometry is SCNPlane plane)
            {
                var length = 1 - 2 * BorderSegment.Thickness;
                plane.Height = length * this.AspectRatio;

                var textureScale = SimdExtensions.CreateFromScale(new SCNVector3(40f, 40f * this.AspectRatio, 1f));
                if (plane.FirstMaterial != null)
                {
                    plane.FirstMaterial.Diffuse.ContentsTransform  = textureScale;
                    plane.FirstMaterial.Emission.ContentsTransform = textureScale;
                }
            }

            this.isBorderOpen = false;
        }
예제 #14
0
        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);
        }