Ejemplo n.º 1
0
        private bool HitTestPull(Ray cameraRay)
        {
            // Careful not to make this too large or you'll pick a neighboring slingshot or one across the table.
            // We're not sorting hits across all of the slignshots and picking the smallest, but we visit them all.
            // Within radius behind the ball/pull allow the slingshot to be picked.
            var playerDistanceFromPull = cameraRay.Position - this.pullOrigin.WorldPosition;

            // This is a linear distance along the current firing direction of the catapult
            // Just using it here to make sure you're behind the pull (the pull is visible).
            var stretchDistance = SCNVector3.Dot(playerDistanceFromPull, -this.FiringDirection());

            // make sure player is on positive side of pull + some fudge factor to make sure we can see it
            // to avoid flickering highlight on and off, we add a buffer when highlighted
            if (stretchDistance <= 0.01f && this.HighlightObject != null && this.HighlightObject.Hidden)
            {
                return(false);
            }
            else if (stretchDistance < -0.03f)
            {
                // slack during highlight mode
                return(false);
            }

            // player can be inside a highlight radius or the pick radius
            // one approach is to auto grab when within radius (but facing the catapult)
            if (playerDistanceFromPull.Length > properties.PickRadius)
            {
                return(false);
            }

            return(true);
        }
Ejemplo n.º 2
0
        private void ScaleToPlane(ARPlaneAnchor planeAnchor)
        {
            // Determine if extent should be flipped (plane is 90 degrees rotated)
            var planeXAxis  = planeAnchor.Transform.Column0.Xyz;
            var axisFlipped = Math.Abs(SCNVector3.Dot(planeXAxis, this.WorldRight)) < 0.5f;

            // Flip dimensions if necessary
            var planeExtent = planeAnchor.Extent;

            if (axisFlipped)
            {
                planeExtent = new OpenTK.NVector3(planeExtent.Z, 0f, planeExtent.X);
            }

            // Scale board to the max extent that fits in the plane
            var width = Math.Min(planeExtent.X, GameBoard.MaximumScale);
            var depth = Math.Min(planeExtent.Z, width * this.AspectRatio);

            width      = depth / this.AspectRatio;
            this.Scale = new SCNVector3(width, width, width);

            // Adjust position of board within plane's bounds
            var planeLocalExtent = new SCNVector3(width, 0f, depth);

            if (axisFlipped)
            {
                planeLocalExtent = new SCNVector3(planeLocalExtent.Z, 0f, planeLocalExtent.X);
            }

            this.AdjustPosition(planeAnchor, planeLocalExtent);
        }
Ejemplo n.º 3
0
        public GameVelocity TryGetLaunchVelocity(CameraInfo cameraInfo)
        {
            GameVelocity result = null;

            if (this.Projectile == null)
            {
                throw new Exception("Trying to launch without a ball");
            }

            // Move the catapult to make sure that it is moved at least once before launch (prevent NaN in launch direction)
            this.Move(cameraInfo);

            var stretchNormalized = DigitExtensions.Clamp((this.stretch - this.properties.MinStretch) / (this.properties.MaxStretch - this.properties.MinStretch), 0.0, 1.0);

            // this is a lerp
            var velocity = (float)(this.properties.MinVelocity * (1d - stretchNormalized) + this.properties.MaxVelocity * stretchNormalized);

            var launchDir = SCNVector3.Normalize(this.pullOrigin.WorldPosition - this.Projectile.WorldPosition);

            if (!launchDir.HasNaN())
            {
                var liftFactor = 0.05f * Math.Abs(1f - SCNVector3.Dot(launchDir, SCNVector3.UnitY)); // used to keep ball in air longer
                var lift       = SCNVector3.UnitY * velocity * liftFactor;

                result = new GameVelocity(this.Projectile.WorldPosition, launchDir * velocity + lift);
            }

            return(result);
        }
Ejemplo n.º 4
0
        public static SCNQuaternion CreateQuaternion(SCNVector3 v1, SCNVector3 v2)
        {
            var a      = SCNVector3.Cross(v1, v2);
            var w      = (float)Math.Sqrt(v1.LengthSquared * v2.LengthSquared) + SCNVector3.Dot(v1, v2);
            var result = new SCNQuaternion(a.X, a.Y, a.Z, w);

            result.Normalize();

            return(result);
        }
Ejemplo n.º 5
0
        public static FeatureHitTestResult?HitTestFromOrigin(this ARSCNView view, SCNVector3 origin, SCNVector3 direction)
        {
            FeatureHitTestResult?result = null;

            ARPointCloud features = null;

            using (var frame = view.Session.CurrentFrame)
            {
                features = frame?.RawFeaturePoints;
            }

            if (features != null)
            {
                var points = features.Points;

                // Determine the point from the whole point cloud which is closest to the hit test ray.
                var closestFeaturePoint = origin;
                var minDistance         = float.MaxValue; // Float.greatestFiniteMagnitude

                for (nuint i = 0; i < features.Count; i++)
                {
                    var feature = points[i];

                    var featurePosition = new SCNVector3((Vector3)feature);
                    var originVector    = origin - featurePosition;

                    var crossProduct = SCNVector3.Cross(originVector, direction);
                    var featureDistanceFromResult = crossProduct.Length;

                    if (featureDistanceFromResult < minDistance)
                    {
                        closestFeaturePoint = featurePosition;
                        minDistance         = featureDistanceFromResult;
                    }
                }

                // Compute the point along the ray that is closest to the selected feature.
                var originToFeature       = closestFeaturePoint - origin;
                var hitTestResult         = origin + (direction * SCNVector3.Dot(direction, originToFeature));
                var hitTestResultDistance = (hitTestResult - origin).Length;

                result = new FeatureHitTestResult
                {
                    Position                   = hitTestResult,
                    DistanceToRayOrigin        = hitTestResultDistance,
                    FeatureHit                 = closestFeaturePoint,
                    FeatureDistanceToHitResult = minDistance
                };
            }

            return(result);
        }
Ejemplo n.º 6
0
        public static FeatureHitTestResult HitTestFromOrigin(this ARSCNView self, SCNVector3 origin, SCNVector3 direction)
        {
            if (self.Session == null || ViewController.CurrentFrame == null)
            {
                return(null);
            }

            var currentFrame = ViewController.CurrentFrame;

            var features = currentFrame.RawFeaturePoints;

            if (features == null)
            {
                return(null);
            }

            var points = features.Points;

            // Determine the point from the whole point cloud which is closest to the hit test ray.
            var closestFeaturePoint = origin;
            var minDistance         = float.MaxValue;

            for (int n = 0; n < (int)features.Count; ++n)
            {
                var feature    = points[n];
                var featurePos = new SCNVector3(feature.X, feature.Y, feature.Z);

                var originVector = origin.Subtract(featurePos);
                var crossProduct = originVector.Cross(direction);
                var featureDistanceFromResult = crossProduct.Length;

                if (featureDistanceFromResult < minDistance)
                {
                    closestFeaturePoint = featurePos;
                    minDistance         = featureDistanceFromResult;
                }
            }

            // Compute the point along the ray that is closest to the selected feature.
            var originToFeature       = closestFeaturePoint.Subtract(origin);
            var hitTestResult         = origin.Add(direction * direction.Dot(originToFeature));
            var hitTestResultDistance = hitTestResult.Subtract(origin).LengthFast;

            // Return result
            return(new FeatureHitTestResult(hitTestResult, hitTestResultDistance, closestFeaturePoint, minDistance));
        }
Ejemplo n.º 7
0
        private Tuple <SCNVector3, SCNVector3> HandleSlidingAtContact(SCNPhysicsContact closestContact, SCNVector3 start, SCNVector3 velocity)
        {
            var originalDistance = velocity.Length;

            var colliderPositionAtContact = start + (float)closestContact.SweepTestFraction * velocity;

            // Compute the sliding plane.
            var slidePlaneNormal = new SCNVector3(closestContact.ContactNormal);
            var slidePlaneOrigin = new SCNVector3(closestContact.ContactPoint);
            var centerOffset     = slidePlaneOrigin - colliderPositionAtContact;

            // Compute destination relative to the point of contact.
            var destinationPoint = slidePlaneOrigin + velocity;

            // We now project the destination point onto the sliding plane.
            var distPlane = SCNVector3.Dot(slidePlaneOrigin, slidePlaneNormal);

            // Project on plane.
            var t = Utils.PlaneIntersect(slidePlaneNormal, distPlane, destinationPoint, slidePlaneNormal);

            var normalizedVelocity = velocity * (1f / originalDistance);
            var angle = SCNVector3.Dot(slidePlaneNormal, normalizedVelocity);

            var frictionCoeff = 0.3f;

            if (Math.Abs(angle) < 0.9f)
            {
                t            += (float)10E-3;
                frictionCoeff = 1.0f;
            }

            var newDestinationPoint = (destinationPoint + t * slidePlaneNormal) - centerOffset;
            // Advance start position to nearest point without collision.
            var computedVelocity = frictionCoeff * (float)(1f - closestContact.SweepTestFraction) * originalDistance * SCNVector3.Normalize(newDestinationPoint - start);

            return(new Tuple <SCNVector3, SCNVector3>(computedVelocity, colliderPositionAtContact));
        }
Ejemplo n.º 8
0
        public void AnimateVortex()
        {
            if (this.Delegate == null)
            {
                throw new Exception("No delegate");
            }

            if (this.vortexCylinder == null)
            {
                throw new Exception("Vortex animation cylinder not set");
            }

            // Vortex shape from animation
            var vortexShape       = this.vortexCylinder.PresentationNode.Scale;
            var vortexHeightDelta = vortexShape.Y - this.lastVortexHeight;

            this.lastVortexHeight = vortexShape.Y;

            var vortexCenterY      = this.vortexCylinder.PresentationNode.WorldPosition.Y;
            var vortexCenterYDelta = vortexCenterY - this.lastVortexCenterY;

            this.lastVortexCenterY = vortexCenterY;

            // Deform shape over time
            var maxOuterRadius      = vortexShape.X;
            var maxInnerRadius      = maxOuterRadius * 0.2f; // 20 % from experiment
            var maxOuterRadiusDelta = maxOuterRadius - this.lastOuterRadius;

            this.lastOuterRadius = maxOuterRadius;

            // Orbital velocity
            var currentFront     = this.vortexCylinder.PresentationNode.WorldFront;
            var orbitalMoveDelta = (currentFront - this.lastFront).Length * maxInnerRadius;

            this.lastFront = currentFront;

            var orbitalVelocityFactor = 5f;
            var orbitalVelocity       = (orbitalMoveDelta / (float)GameTime.DeltaTime) * orbitalVelocityFactor;

            var topBound    = vortexCenterY + vortexShape.Y * 0.5f;
            var bottomBound = vortexCenterY - vortexShape.Y * 0.5f;

            var blockObjects = this.Delegate.AllBlockObjects;
            var up           = SCNVector3.UnitY;

            foreach (var block in blockObjects)
            {
                if (block.PhysicsNode?.PhysicsBody != null)
                {
                    var position           = block.PhysicsNode.PresentationNode.WorldPosition;
                    var positionWithoutY   = new SCNVector3(position.X, 0f, position.Z);
                    var distanceFromCenter = positionWithoutY.Length;
                    var directionToCenter  = -SCNVector3.Normalize(positionWithoutY);

                    // Adjust radius into curve
                    // Equation representing a half radius chord of circle equation
                    var normalizedY  = DigitExtensions.Clamp(position.Y / topBound, 0f, 1f);
                    var radiusFactor = (float)Math.Sqrt(4f - 3f * normalizedY * normalizedY) - 1f;
                    radiusFactor = radiusFactor * 0.8f + 0.2f;
                    var innerRadius = maxInnerRadius * radiusFactor;
                    var outerRadius = maxOuterRadius * radiusFactor;

                    // Cap velocity
                    var maxVelocity = 30f;
                    if (block.PhysicsNode.PhysicsBody.Velocity.Length > maxVelocity)
                    {
                        block.PhysicsNode.PhysicsBody.Velocity = SCNVector3.Normalize(block.PhysicsNode.PhysicsBody.Velocity) * maxVelocity;
                    }

                    var force = SCNVector3.Zero;

                    // Stage specific manipulation
                    var vortexDirection        = SCNVector3.Cross(directionToCenter, up);
                    var speedInVortexDirection = SCNVector3.Dot(block.PhysicsNode.PhysicsBody.Velocity, vortexDirection);

                    // Stable vortex pull
                    var pullForceMagnitude = (speedInVortexDirection * speedInVortexDirection) * (float)(block.PhysicsNode.PhysicsBody.Mass) / distanceFromCenter;
                    force += pullForceMagnitude * directionToCenter;

                    // Pull into outer radius
                    var radialInwardForceMagnitude = RadialSpringConstant * (float)Math.Max(0d, distanceFromCenter - outerRadius);
                    force += radialInwardForceMagnitude * directionToCenter;

                    // Pull away from inner radius
                    var radialOutwardForceMagnitude = RadialSpringConstant * (float)Math.Max(0d, innerRadius - distanceFromCenter);
                    force += -radialOutwardForceMagnitude * directionToCenter;

                    // Vortex velocity adjustment
                    if (distanceFromCenter > innerRadius)
                    {
                        var tangentForceMagnitude = TangentVelocitySpringContant * (speedInVortexDirection - orbitalVelocity);
                        force += -tangentForceMagnitude * vortexDirection * (0.5f + (float)(random.NextDouble() * 1d));
                    }

                    // Random forces/torque
                    force += force.Length * (float)((random.NextDouble() * 2d - 1d) * MaxRandomVortexForce) * up;
                    this.ApplyRandomTorque(block.PhysicsNode.PhysicsBody, MaxRandomVortexTorque);

                    // Top bound pull down
                    var topBoundForceMagnitude = RadialSpringConstant * (float)Math.Max(0d, position.Y - topBound);
                    force += topBoundForceMagnitude * -up;

                    // Bottom bound pull up
                    var bottomBoundForceMagnitude = RadialSpringConstant * (float)Math.Max(0d, bottomBound - position.Y);
                    force += bottomBoundForceMagnitude * up;

                    block.PhysicsNode.PhysicsBody.ApplyForce(force, false);

                    // Scale the vortex
                    // The higher position in the bound, more it should move upward to scale the vortex
                    var normalizedPositionInBoundY = DigitExtensions.Clamp((position.Y - bottomBound) / vortexShape.Y, 0f, 1f);
                    var heightMoveFactor           = Math.Abs(normalizedPositionInBoundY - 0.5f);
                    var newPositionY = position.Y + vortexCenterYDelta + vortexHeightDelta * heightMoveFactor;

                    var positionXZ       = new SCNVector3(position.X, 0f, position.Z);
                    var radialMoveFactor = DigitExtensions.Clamp(distanceFromCenter / outerRadius, 0f, 1f);
                    var newPositionXZ    = positionXZ + maxOuterRadiusDelta * radialMoveFactor * -directionToCenter;

                    block.PhysicsNode.WorldPosition    = new SCNVector3(newPositionXZ.X, newPositionY, newPositionXZ.Z);
                    block.PhysicsNode.WorldOrientation = block.PhysicsNode.PresentationNode.WorldOrientation;
                    block.PhysicsNode.PhysicsBody.ResetTransform();
                }
            }
        }
Ejemplo n.º 9
0
        public static System.nfloat PlaneIntersect(SCNVector3 planeNormal, System.nfloat planeDist, SCNVector3 rayOrigin, SCNVector3 rayDirection)
#endif
        {
            return((planeDist - SCNVector3.Dot(planeNormal, rayOrigin)) / SCNVector3.Dot(planeNormal, rayDirection));
        }
Ejemplo n.º 10
0
        public static IList <FeatureHitTestResult> HitTestWithFeatures(this ARSCNView view,
                                                                       CGPoint point,
                                                                       float coneOpeningAngleInDegrees,
                                                                       float minDistance = 0,
                                                                       float maxDistance = float.MaxValue,
                                                                       int maxResults    = 1)
        {
            var results = new List <FeatureHitTestResult>();

            ARPointCloud features = null;

            using (var frame = view.Session.CurrentFrame)
            {
                features = frame?.RawFeaturePoints;
            }

            if (features != null)
            {
                var ray = view.HitTestRayFromScreenPosition(point);
                if (ray.HasValue)
                {
                    var maxAngleInDegrees = Math.Min(coneOpeningAngleInDegrees, 360f) / 2f;
                    var maxAngle          = (maxAngleInDegrees / 180f) * Math.PI;

                    var points = features.Points;
                    for (nuint j = 0; j < features.Count; j++)
                    {
                        var feature = points[j];

                        var featurePosition = new SCNVector3((Vector3)feature);
                        var originToFeature = featurePosition - ray.Value.Origin;

                        var crossProduct = SCNVector3.Cross(originToFeature, ray.Value.Direction);
                        var featureDistanceFromResult = crossProduct.Length;

                        var hitTestResult         = ray.Value.Origin + (ray.Value.Direction * SCNVector3.Dot(ray.Value.Direction, originToFeature));
                        var hitTestResultDistance = (hitTestResult - ray.Value.Origin).Length;

                        if (hitTestResultDistance < minDistance || hitTestResultDistance > maxDistance)
                        {
                            // Skip this feature - it is too close or too far away.
                            continue;
                        }

                        var originToFeatureNormalized = SCNVector3.Normalize(originToFeature);
                        var angleBetweenRayAndFeature = Math.Acos(SCNVector3.Dot(ray.Value.Direction, originToFeatureNormalized));

                        if (angleBetweenRayAndFeature > maxAngle)
                        {
                            // Skip this feature - is outside of the hit test cone.
                            continue;
                        }

                        // All tests passed: Add the hit against this feature to the results.
                        results.Add(new FeatureHitTestResult
                        {
                            Position                   = hitTestResult,
                            DistanceToRayOrigin        = hitTestResultDistance,
                            FeatureHit                 = featurePosition,
                            FeatureDistanceToHitResult = featureDistanceFromResult
                        });
                    }

                    // Sort the results by feature distance to the ray.
                    results = results.OrderBy(result => result.DistanceToRayOrigin).ToList();

                    // Cap the list to maxResults.
                    var cappedResults = new List <FeatureHitTestResult>();
                    var i             = 0;

                    while (i < maxResults && i < results.Count)
                    {
                        cappedResults.Add(results[i]);
                        i += 1;
                    }

                    results = cappedResults;
                }
            }

            return(results);
        }