Example #1
0
		public Slide ()
		{
			ContentNode = SCNNode.Create ();

			GroundNode = SCNNode.Create ();
			ContentNode.AddChildNode (GroundNode);

			TextManager = new SlideTextManager ();
			ContentNode.AddChildNode (TextManager.TextNode);

			// Default parameters
			LightIntensities = new float[] { 0.0f, 0.9f, 0.7f };
			MainLightPosition = new SCNVector3 (0, 3, -13);
			EnableShadows = false;
			FloorImageName = null;
			FloorImageExtension = null;
			FloorReflectivity = 0.25f;
			FloorFalloff = 3.0f;
			TransitionDuration = 1.0f;
			TransitionOffsetX = 0.0f;
			TransitionOffsetZ = 0.0f;
			TransitionRotation = 0.0f;
			Altitude = 5.0f;
			Pitch = 0.0f;
			IsNewIn10_10 = false;
		}
Example #2
0
        SCNVector3 LocationAlongPath(nfloat percent)
        {
            if (PathPositions.Count <= 3)
            {
                return(SCNVector3.Zero);
            }

            int numSections = PathPositions.Count - 3;

            nfloat dist = percent * numSections;

            int currentPointIndex = (int)Math.Min((uint)Math.Floor(dist), numSections - 1);

            dist -= currentPointIndex;
            var a = new SCNVector3(PathPositions [currentPointIndex]);
            var b = new SCNVector3(PathPositions [currentPointIndex + 1]);
            var c = new SCNVector3(PathPositions [currentPointIndex + 2]);
            var d = new SCNVector3(PathPositions [currentPointIndex + 3]);

            var location = new SCNVector3();

            location.X = CatmullRomValue(a.X, b.X, c.X, d.X, dist);
            location.Y = CatmullRomValue(a.Y, b.Y, c.Y, d.Y, dist);
            location.Z = CatmullRomValue(a.Z, b.Z, c.Z, d.Z, dist);

            return(location);
        }
Example #3
0
		public static SCNMatrix4 TranslateWithVector (this SCNMatrix4 matrix, SCNVector3 vector)
		{
			matrix.M41 += matrix.M11 * vector.X + matrix.M21 * vector.Y + matrix.M31 * vector.Z;
			matrix.M42 += matrix.M12 * vector.X + matrix.M22 * vector.Y + matrix.M32 * vector.Z;
			matrix.M43 += matrix.M13 * vector.X + matrix.M23 * vector.Y + matrix.M33 * vector.Z;
			return matrix;
		}
        public static HitTestRay HitTestRayFromScreenPos(this ARSCNView self, CGPoint point)
        {
            if (self.Session == null || ViewController.CurrentFrame == null)
            {
                return(null);
            }

            var frame = ViewController.CurrentFrame;

            if (frame == null || frame.Camera == null || frame.Camera.Transform == null)
            {
                return(null);
            }

            var cameraPos = SCNVector3Extensions.PositionFromTransform(frame.Camera.Transform);

            // Note: z: 1.0 will unproject() the screen position to the far clipping plane.
            var positionVec = new SCNVector3((float)point.X, (float)point.Y, 1.0f);
            var screenPosOnFarClippingPlane = self.UnprojectPoint(positionVec);

            var rayDirection = screenPosOnFarClippingPlane.Subtract(cameraPos);

            rayDirection.Normalize();

            return(new HitTestRay(cameraPos, rayDirection));
        }
        private void ShowCodeExample(string code, string imageName, string extension)
        {
            SCNTransaction.Begin();
            SCNTransaction.AnimationDuration = 0;
            TextManager.FadeOutText(SlideTextManager.TextType.Code);

            if (code != null)
            {
                var codeNode = TextManager.AddCode(code);

                SCNVector3 min, max;
                min = new SCNVector3(0, 0, 0);
                max = new SCNVector3(0, 0, 0);
                codeNode.GetBoundingBox(ref min, ref max);

                if (imageName != null)
                {
                    SCNNode imageNode = Utils.SCPlaneNode(NSBundle.MainBundle.PathForResource("Scenes/earth/" + imageName, extension), 4.0f, false);
                    imageNode.Position = new SCNVector3(max.X + 2.5f, min.Y + 0.2f, 0);
                    codeNode.AddChildNode(imageNode);

                    max.X += 4.0f;
                }

                codeNode.Position = new SCNVector3(6 - (min.X + max.X) / 2, 10 - min.Y, 0);
            }
            SCNTransaction.Commit();
        }
Example #6
0
 public static OpenTK.NMatrix4 CreateTranslation(SCNVector3 vector)
 {
     return(new OpenTK.NMatrix4(new OpenTK.Vector4(1, 0, 0, vector.X),
                                new OpenTK.Vector4(0, 1, 0, vector.Y),
                                new OpenTK.Vector4(0, 0, 1, vector.Z),
                                new OpenTK.Vector4(0, 0, 0, 1)));
 }
        public override void TouchesBegan(NSSet touches, UIEvent evt)
        {
            var forcePower = 10;

            base.TouchesBegan(touches, evt);
            var pointOfView = this.SceneView.PointOfView;
            var transform   = pointOfView.Transform;
            var location    = new SCNVector3(transform.M41, transform.M42, transform.M43);
            var orientation = new SCNVector3(-transform.M31, -transform.M32, -transform.M33);
            var position    = location + orientation;
            var pokeball    = new SCNNode()
            {
                Geometry = SCNSphere.Create(0.15f),
            };

            pokeball.Geometry.FirstMaterial.Diffuse.ContentImage = UIImage.FromBundle("pokeball");
            pokeball.Position    = position;
            pokeball.PhysicsBody = SCNPhysicsBody.CreateDynamicBody();
            pokeball.PhysicsBody.PhysicsShape       = SCNPhysicsShape.Create(pokeball);
            pokeball.PhysicsBody.ContactTestBitMask = (int)BitMaskCategory.Pokemon;
            pokeball.PhysicsBody.CategoryBitMask    = (int)BitMaskCategory.Pokeball;

            pokeball.PhysicsBody.ApplyForce(new SCNVector3(orientation.X * forcePower, orientation.Y * forcePower, orientation.Z * forcePower), true);
            SceneView.Scene.RootNode.AddChildNode(pokeball);
        }
        public void BounceStuff(float multiplier)
        {
            if (i++ % 4 != 0)
            {
                return;
            }

            foreach (var node in Faces)
            {
                foreach (var i in Enumerable.Range(0, 1))
                {
                    var box = GetBox();

                    box.Position = new SCNVector3(0, .15f, 0);

                    var vec = new SCNVector3(((float)r.NextDouble() - .5f) * multiplier, ((float)r.NextDouble() + (float)2) * multiplier, 0);
                    box.PhysicsBody.ApplyForce(vec, true);
                    node.Add(box);

                    var newPosition = node.ConvertPositionToNode(box.Position, SCNView.Scene.RootNode);
                    SCNView.Scene.RootNode.Add(box);
                    box.Position = newPosition;
                }
            }
        }
Example #9
0
        private SCNNode GenerateEarth(SCNVector3 position)
        {
            var earth = SCNSphere.Create(EartRadious);

            eartParentNode = new SCNNode
            {
                Position = position,
                Geometry = earth
            };
            earthNode = new SCNNode
            {
                Position = new SCNVector3(distanceEart, 0, 0),
                Geometry = earth
            };
            earthNode.Geometry.FirstMaterial.Diffuse.ContentImage  = UIImage.FromBundle("EartDifuse");
            earthNode.Geometry.FirstMaterial.Specular.ContentImage = UIImage.FromBundle("EarthSpecular");
            earthNode.Geometry.FirstMaterial.Emission.ContentImage = UIImage.FromBundle("EarthEmision");
            earthNode.Geometry.FirstMaterial.Normal.ContentImage   = UIImage.FromBundle("EarthNormal");
            earthNode.Name = "Eart";

            var moon = SCNSphere.Create(radiousMoon);

            moonNode = new SCNNode
            {
                Position = new SCNVector3(distanceMoon, 0, 0),
                Geometry = moon
            };
            moonNode.Geometry.FirstMaterial.Diffuse.ContentImage = UIImage.FromBundle("moon_back");
            moonNode.Name = "Moon";
            eartParentNode.AddChildNode(moonNode);
            earthNode.AddChildNode(moonNode);
            eartParentNode.AddChildNode(earthNode);
            return(eartParentNode);
        }
Example #10
0
        void TapAction(UITapGestureRecognizer tap)
        {
            if (tap.State == UIGestureRecognizerState.Ended)
            {
                var tapLocation = tap.LocationInView(sceneView);
                var hitResults  = sceneView.HitTest(tapLocation, new NSDictionary());
                if (hitResults.Length != 0)
                {
                    var NodeResult = hitResults[0].Node;
                    if (NodeResult != null)
                    {
                        var nodeText = SCNText.Create(NodeResult.Name, 5);

                        var textNode = SCNNode.Create();
                        textNode.Geometry = nodeText;
                        textNode.Scale    = new SCNVector3(0.01f, 0.01f, 0.01f);
                        var min = new SCNVector3();
                        var max = new SCNVector3();
                        textNode.GetBoundingBox(ref min, ref max);

                        textNode.Position = new SCNVector3((textNode.Position.X - (textNode.Scale.X / 2)), textNode.Position.Y + 0.5f, textNode.Position.Z);
                        NodeResult.AddChildNode(textNode);
                    }
                }
            }
        }
Example #11
0
        internal DetectedBoundingBox(NVector3[] points, double scale, UIColor color = null) : base()
        {
            // Cannot use asset-group defined color as default in args (since not compile-time constant),
            // so assign it now if default
            if (color == null)
            {
                color = Utilities.AppYellow;
            }

            var localMin = new SCNVector3(float.MaxValue, float.MaxValue, float.MaxValue);
            var localMax = new SCNVector3(float.MinValue, float.MinValue, float.MinValue);

            foreach (var point in points)
            {
                var scnVector = point.ToSCNVector3();
                localMin = localMin.Min(scnVector);
                localMax = localMax.Max(scnVector);
            }

            Position = Position + (localMax + localMin) / 2;
            var extent    = localMax - localMin;
            var wireframe = new Wireframe(extent.ToNVector3(), color, scale);

            AddChildNode(wireframe);
        }
Example #12
0
        /// <summary>
        /// Translates the location by comparing with a given position.
        /// </summary>
        /// <returns>The location.</returns>
        /// <param name="self">Self.</param>
        /// <param name="to">To.</param>
        public static CLLocation TranslatedLocation(this SceneLocationEstimate self, SCNVector3 to)
        {
            var translation        = self.LocationTranslation(to);
            var translatedLocation = self.Location.TranslatedLocation(translation);

            return(translatedLocation);
        }
Example #13
0
        private void PresentPrimitives(PresentationViewController presentationViewController)
        {
            var count  = 100;
            var spread = 0.0f;

            // create a cube with a sphere shape
            for (int i = 0; i < count; ++i)
            {
                var model = SCNNode.Create();
                model.Position    = GroundNode.ConvertPositionToNode(new SCNVector3(RandFloat(-1, 1), RandFloat(30, 50), RandFloat(-1, 1)), null);
                model.EulerAngles = new SCNVector3(RandFloat(0, (nfloat)Math.PI * 2), RandFloat(0, (nfloat)Math.PI * 2), RandFloat(0, (nfloat)Math.PI * 2));

                var size          = new SCNVector3(RandFloat(1.0, 1.5), RandFloat(1.0, 1.5), RandFloat(1.0, 1.5));
                var random        = new Random((int)DateTime.Now.Ticks);
                int geometryIndex = random.Next(0, 7);
                switch (geometryIndex)
                {
                case 0:                 // Box
                    model.Geometry = SCNBox.Create(size.X, size.Y, size.Z, 0);
                    break;

                case 1:                 // Pyramid
                    model.Geometry = SCNPyramid.Create(size.X, size.Y, size.Z);
                    break;

                case 2:                 // Sphere
                    model.Geometry = SCNSphere.Create(size.X);
                    break;

                case 3:                 // Cylinder
                    model.Geometry = SCNCylinder.Create(size.X, size.Y);
                    break;

                case 4:                 // Tube
                    model.Geometry = SCNTube.Create(size.X, size.X + size.Z, size.Y);
                    break;

                case 5:                 // Capsule
                    model.Geometry = SCNCapsule.Create(size.X, size.Y + 2 * size.X);
                    break;

                case 6:                 // Torus
                    model.Geometry = SCNTorus.Create(size.X, (nfloat)Math.Min(size.X, size.Y) / 2);
                    break;

                default:
                    break;
                }

                model.Geometry.FirstMaterial.Multiply.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/texture", "png"));

                model.PhysicsBody                 = SCNPhysicsBody.CreateDynamicBody();
                model.PhysicsBody.Velocity        = new SCNVector3(RandFloat(-spread, spread), -10, RandFloat(-spread, spread));
                model.PhysicsBody.AngularVelocity = new SCNVector4(RandFloat(-1, 1), RandFloat(-1, 1), RandFloat(-1, 1), RandFloat(-3, 3));

                Shapes.Add(model);

                ((SCNView)presentationViewController.View).Scene.RootNode.AddChildNode(model);
            }
        }
Example #14
0
		public static SCNMatrix4 SetPosition (this SCNMatrix4 matrix, SCNVector3 vector)
		{
			matrix.M41 = vector.X; 
			matrix.M42 = vector.Y;
			matrix.M43 = vector.Z;

			return matrix;
		}
 public void SetGrabMode(bool state)
 {
     this.physicsMode = !state;  // physics mode is off when grab mode is on
     if (this.physicsMode)
     {
         this.currentPosition = catapult.Position;
     }
 }
 public SlingshotComponent(SCNNode catapult) : base()
 {
     this.catapult        = catapult;
     this.restPosition    = catapult.Position;
     this.currentPosition = this.restPosition;
     this.physicsMode     = false; // Started off and gets turned on only if needed
     this.velocity        = SCNVector3.Zero;
 }
Example #17
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))));
        }
Example #18
0
        public override void MagnifyWithEvent(NSEvent theEvent)
        {
            base.MagnifyWithEvent(theEvent);
            var magnification = theEvent.Magnification;
            var zoom          = new SCNVector3(magnification, magnification, magnification);

            Trackball.Zoom(zoom);
        }
Example #19
0
        public static SCNMatrix4 SetPosition(this SCNMatrix4 matrix, SCNVector3 vector)
        {
            matrix.M41 = vector.X;
            matrix.M42 = vector.Y;
            matrix.M43 = vector.Z;

            return(matrix);
        }
Example #20
0
        public static void Normalize(this SCNVector3 vector3)
        {
            var normalizedVector = vector3.Normalized();

            vector3.X = normalizedVector.X;
            vector3.Y = normalizedVector.Y;
            vector3.Z = normalizedVector.Z;
        }
Example #21
0
        SCNVector3 MakeThrowPosition(SCNMatrix4 transform)
        {
            var force        = new Vector3(0.0f, 0.0f, -0.3f);
            var rotation     = new SCNVector3(transform.M31, transform.M32, transform.M33);
            var rotatedForce = SCNVector3.Multiply(rotation, force) + new Vector3(0.0f, 0.1f, 0.0f);

            return(new SCNVector3(rotatedForce));
        }
Example #22
0
        public static unsafe SCNGeometrySource FromVertices(SCNVector3 [] vertices)
        {
            if (vertices == null)
                throw new ArgumentNullException ("vertices");

            fixed (SCNVector3 *ptr = &vertices [0])
                return FromVertices ((IntPtr)ptr, vertices.Length);
        }
 public FeatureHitTestResult(SCNVector3 position, float distanceToRayOrigin, SCNVector3 featureHit, float featureDistanceToHitResult)
 {
     // Initialize
     Position                   = position;
     DistanceToRayOrigin        = distanceToRayOrigin;
     FeatureHit                 = featureHit;
     FeatureDistanceToHitResult = featureDistanceToHitResult;
 }
Example #24
0
        public static unsafe SCNGeometrySource FromNormals(SCNVector3 [] normals)
        {
            if (normals == null)
                throw new ArgumentNullException ("normals");

            fixed (SCNVector3 *ptr = &normals[0])
                return FromNormals ((IntPtr)ptr, normals.Length);
        }
Example #25
0
        internal Ray(SCNNode pointOfView, float length)
        {
            pointOfView.WorldFront.NormalizeFast();
            var cameraNormal = pointOfView.WorldFront.Times(length);

            Origin    = pointOfView.WorldPosition;
            Direction = cameraNormal.ToSCNVector3();
        }
Example #26
0
 public void Update(ARPlaneAnchor planeAnchor)
 {
     planeGeometry.Width  = planeAnchor.Extent.X;
     planeGeometry.Length = planeAnchor.Extent.Z;
     Position             = new SCNVector3(
         planeAnchor.Center.X,
         planeAnchor.Center.Y,
         planeAnchor.Center.Z);
 }
Example #27
0
        public static SCNVector3 Reduce(this IList <SCNVector3> items, SCNVector3 initialResult)
        {
            foreach (var item in items)
            {
                initialResult += item;
            }

            return(initialResult);
        }
        public GamePhysicsSmoothComponent(SCNNode physicsNode, SCNNode geometryNode) : base()
        {
            this.physicsNode  = physicsNode;
            this.geometryNode = geometryNode;

            // get initial offset
            this.parentOffPos = this.geometryNode.Position;
            this.parentOffRot = this.geometryNode.Orientation;
        }
Example #29
0
        public void Update(double deltaTime, SCNSceneRenderer aRenderer)
        {
            // Based on gamestate:
            // ingame: Move the character if running.
            // ingame: prevent movement of the character past our level bounds.
            // ingame: perform logic for the player character.
            // any: move the directional light with any player movement.
            // ingame: update the coconuts kinematically.
            // ingame: perform logic for each monkey.
            // ingame: because our camera could have moved, update the transforms needs to fly
            //         collected bananas from the player (world space) to score (screen space)

            var appDelegate  = SharedAppDelegate.AppDelegate;
            var currentState = GameSimulation.Sim.CurrentGameState;

            // Move character along path if walking.
            MoveCharacterAlongPathWith(deltaTime, currentState);

            // Based on the time along path, rotation the character to face the correct direction.
            PlayerCharacter.Rotation = GetPlayerDirectionFromCurrentPosition();
            if (currentState == GameState.InGame)
            {
                PlayerCharacter.Update(deltaTime);
            }

            // Move the light
            UpdateSunLightPosition();

            if (currentState == GameState.PreGame ||
                currentState == GameState.PostGame ||
                currentState == GameState.Paused)
            {
                return;
            }

            foreach (MonkeyCharacter monkey in Monkeys)
            {
                monkey.Update(deltaTime);
            }

            // Update timer and check for Game Over.
            SecondsRemaining -= deltaTime;
            if (SecondsRemaining < 0.0)
            {
                DoGameOver();
            }

            // update the player's SP position.
            SCNVector3 playerPosition = PlayerCharacter.WorldTransform.GetPosition();

            screenSpacePlayerPosition = appDelegate.Scene.ProjectPoint(playerPosition);

            // Update the SP position of the score label
            CGPoint pt = ScoreLabelLocation;

            worldSpaceLabelScorePosition = appDelegate.Scene.UnprojectPoint(new SCNVector3(pt.X, pt.Y, screenSpacePlayerPosition.Z));
        }
Example #30
0
 public void DidCollision(SCNNode nodeA, SCNNode nodeB, SCNVector3 position, float impulse)
 {
     foreach (var interaction in this.interactions.Values)
     {
         // nodeA and nodeB take turn to be the main node
         interaction.DidCollision(nodeA, nodeB, position, impulse);
         interaction.DidCollision(nodeB, nodeA, position, impulse);
     }
 }
Example #31
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);
        }
Example #32
0
        private static SCNVector3 Cross(SCNVector3 a, SCNVector3 b)
        {
            SCNVector3 c;

            c.X = a.Y * b.Z - a.Z * b.Y;
            c.Y = a.Z * b.X - a.X * b.Z;
            c.Z = a.X * b.Y - a.Y * b.X;

            return(c);
        }
Example #33
0
        private SCNNode CreatePoolBall(SCNVector3 position)
        {
            var model = SCNNode.Create();

            model.Position = position;
            model.Geometry = SCNSphere.Create(0.7f);
            model.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/pool/pool_8", "png"));
            model.PhysicsBody = SCNPhysicsBody.CreateDynamicBody();
            return(model);
        }
        private void ApplyRandomTorque(SCNPhysicsBody physicsBody, float maxTorque)
        {
            var randomAxis = new SCNVector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble());

            randomAxis = SCNVector3.Normalize(randomAxis);

            var randomTorque = new SCNVector4(randomAxis, (float)(random.NextDouble() * 2d - 1d) * maxTorque);

            physicsBody.ApplyTorque(randomTorque, true);
        }
Example #35
0
        public PlaneNode(float width, float length, SCNVector3 position, SKScene videoScene)
        {
            var rootNode = new SCNNode
            {
                Geometry = CreateGeometry(width, length, videoScene),
                Position = position
            };

            AddChildNode(rootNode);
        }
Example #36
0
 public static SCNPhysicsShape Create(SCNGeometry geometry,
     SCNPhysicsShapeType? shapeType = null,
     bool? keepAsCompound = null,
     SCNVector3? scale = null)
 {
     return Create (geometry, new SCNPhysicsShapeOptions {
         ShapeType = shapeType,
         KeepAsCompound = keepAsCompound,
         Scale = scale
     }.ToDictionary ());
 }
Example #37
0
		public static SCNVector3 GetMinVector (SCNVector3 vectorLeft, SCNVector3 vectorRight)
		{
			SCNVector3 min = vectorLeft;
			if (vectorRight.X < vectorLeft.X)
				min.X = vectorRight.X;
			if (vectorRight.Y < vectorLeft.Y)
				min.Y = vectorRight.Y;
			if (vectorRight.Z < vectorLeft.Z)
				min.Z = vectorRight.Z;
			return min;
		}
Example #38
0
		public static SCNVector3 GetMaxVector (SCNVector3 vectorLeft, SCNVector3 vectorRight)
		{
			SCNVector3 max = vectorLeft;
			if (vectorRight.X > vectorLeft.X)
				max.X = vectorRight.X;
			if (vectorRight.Y > vectorLeft.Y)
				max.Y = vectorRight.Y;
			if (vectorRight.Z > vectorLeft.Z)
				max.Z = vectorRight.Z;
			return max;
		}
Example #39
0
        public static SCNPhysicsShape Create(SCNPhysicsShape [] shapes, SCNVector3 [] transforms)
        {
            if (shapes == null)
                throw new ArgumentNullException ("shapes");

            if (transforms == null)
                throw new ArgumentNullException ("transforms");

            var t = new NSValue [transforms.Length];
            for (var i = 0; i < t.Length; i++)
                t [i] = NSValue.FromVector (transforms [i]);

            return Create (shapes, t);
        }
Example #40
0
		public static SCNNode SCAddChildNode (SCNNode container, string name, string path, nfloat scale)
		{
			// Load the scene from the specified file
			var scene = SCNScene.FromFile (path);

			// Retrieve the root node
			var node = scene.RootNode;

			// Search for the node named "name"
			if (name.Length > 0)
				node = node.FindChildNode (name, true);
			else
				node = node.ChildNodes [0]; // Take the first child if no name is passed

			if (scale != 0) {
				// Rescale based on the current bounding box and the desired scale
				// Align the node to 0 on the Y axis
				var min = new SCNVector3 ();
				var max = new SCNVector3 ();
				node.GetBoundingBox (ref min, ref max);

				var mid = SCNVector3.Add (min, max);
				mid = SCNVector3.Multiply (mid, 0.5f);
				mid.Y = min.Y; // Align on bottom

				var size = SCNVector3.Subtract (max, min);
				var maxSize = NMath.Max (NMath.Max (size.X, size.Y), size.Z);

				scale = scale / maxSize;
				mid = SCNVector3.Multiply (mid, scale);
				mid = -mid;

				node.Scale = new SCNVector3 (scale, scale, scale);
				node.Position = new SCNVector3 (mid);
			}

			// Add to the container passed in argument
			container.AddChildNode (node);

			return node;
		}
Example #41
0
		public override void PresentStep (int index, PresentationViewController presentationViewController)
		{
			Step = index;

			switch (index) {
			case 0:
				break;
			case 1:
				TextManager.FlipOutText (SlideTextManager.TextType.Bullet);

				TextManager.SetSubtitle ("SCNPhysicsBody");

				TextManager.FlipInText (SlideTextManager.TextType.Subtitle);

				TextManager.AddBulletAtLevel ("Dynamic Bodies", 0);

					// Add some code
				TextManager.AddCode ("#// Make a node dynamic\n"
				+ "aNode.#physicsBody# = [SCNPhysicsBody #dynamicBody#];#");

				TextManager.FlipInText (SlideTextManager.TextType.Bullet);
				TextManager.FlipInText (SlideTextManager.TextType.Code);
				break;
			case 2:
				//add a cube
				var worldPos = GroundNode.ConvertPositionToNode (new SCNVector3 (0, 12, 2), null);
				var dice = CreateBlock (worldPos, new SCNVector3 (1.5f, 1.5f, 1.5f));
				dice.PhysicsBody = null; //wait!
				dice.Rotation = new SCNVector4 (0, 0, 1, (float)(Math.PI / 4) * 0.5f);
				dice.Scale = new SCNVector3 (0.001f, 0.001f, 0.001f);

				((SCNView)presentationViewController.View).Scene.RootNode.AddChildNode (dice);
				SCNTransaction.Begin (); 
				SCNTransaction.AnimationDuration = 0.75f;
				dice.Scale = new SCNVector3 (2, 2, 2);
				SCNTransaction.Commit ();

				Dices.Add (dice);
				break;
			case 3:
				foreach (var node in Dices)
					node.PhysicsBody = SCNPhysicsBody.CreateDynamicBody ();
				break;
			case 4:
				PresentDices (presentationViewController);
				break;
			case 5:
				TextManager.FlipOutText (SlideTextManager.TextType.Bullet);
				TextManager.FlipOutText (SlideTextManager.TextType.Code);

				TextManager.AddBulletAtLevel ("Manipulate with forces", 0);

				// Add some code
				TextManager.AddCode ("#// Apply an impulse\n"
				+ "[aNode.physicsBody #applyForce:#aVector3 #atPosition:#aVector3 #impulse:#YES];#");

				TextManager.FlipInText (SlideTextManager.TextType.Bullet);
				TextManager.FlipInText (SlideTextManager.TextType.Code);
				break;
			case 6:
				// remove dices
				var center = new SCNVector3 (0, -5, 20);
				center = GroundNode.ConvertPositionToNode (center, null);

				Explosion (center, Dices);

				var popTime = new DispatchTime (DispatchTime.Now, (long)(1 * Utils.NSEC_PER_SEC));
				DispatchQueue.MainQueue.DispatchAfter (popTime, () => {
					TextManager.FlipOutText (SlideTextManager.TextType.Code);
					TextManager.FlipOutText (SlideTextManager.TextType.Bullet);

					TextManager.AddBulletAtLevel ("Static Bodies", 0);
					TextManager.AddCode ("#// Make a node static\n"
					+ "aNode.#physicsBody# = [SCNPhysicsBody #staticBody#];#");
					TextManager.FlipInText (SlideTextManager.TextType.Bullet);
					TextManager.FlipInText (SlideTextManager.TextType.Code);
				});
				break;
			case 7:
				PresentWalls (presentationViewController);
				break;
			case 8:
				PresentBalls (presentationViewController);
				break;
			case 9:
				//remove walls
				var walls = new List<SCNNode> ();
				GroundNode.EnumerateChildNodes (delegate(SCNNode node, out bool stop) {
					stop = false;
					if (node.Name == "container-wall") {
						node.RunAction (SCNAction.Sequence (new SCNAction [] {
							SCNAction.MoveBy (new SCNVector3 (0, -2, 0), 0.5),
							SCNAction.RemoveFromParentNode ()
						}));
						walls.Add (node);
					}
					return stop;
				});
				break;
			case 10:
				// remove balls
				center = new SCNVector3 (0, -5, 5);
				center = GroundNode.ConvertPositionToNode (center, null);
				Explosion (center, Balls);

				popTime = new DispatchTime (DispatchTime.Now, (long)(0.5 * Utils.NSEC_PER_SEC));
				DispatchQueue.MainQueue.DispatchAfter (popTime, () => {
					TextManager.FlipOutText (SlideTextManager.TextType.Code);
					TextManager.FlipOutText (SlideTextManager.TextType.Bullet);

					TextManager.AddBulletAtLevel ("Kinematic Bodies", 0);
					TextManager.AddCode ("#// Make a node kinematic\n"
					+ "aNode.#physicsBody# = [SCNPhysicsBody #kinematicBody#];#");
					TextManager.FlipInText (SlideTextManager.TextType.Bullet);
					TextManager.FlipInText (SlideTextManager.TextType.Code);
				});
				break;
			case 11:
				var boxNode = SCNNode.Create ();
				boxNode.Geometry = SCNBox.Create (10, 0.2f, 10, 0);
				boxNode.Position = new SCNVector3 (0, 5, MIDDLE_Z);
				boxNode.Geometry.FirstMaterial.Emission.Contents = NSColor.DarkGray;
				boxNode.PhysicsBody = SCNPhysicsBody.CreateKinematicBody ();
				boxNode.RunAction (SCNAction.RepeatActionForever (SCNAction.RotateBy (0, 0, NMath.PI * 2, 2.0)));
				GroundNode.AddChildNode (boxNode);
				KinematicItems.Add (boxNode);

				var invisibleWall = SCNNode.Create ();
				invisibleWall.Geometry = SCNBox.Create (4, 40, 10, 0);
				invisibleWall.Position = new SCNVector3 (-7, 0, MIDDLE_Z);
				invisibleWall.Geometry.FirstMaterial.Transparency = 0;
				invisibleWall.PhysicsBody = SCNPhysicsBody.CreateStaticBody ();
				GroundNode.AddChildNode (invisibleWall);
				KinematicItems.Add (invisibleWall);

				invisibleWall = (SCNNode)invisibleWall.Copy ();
				invisibleWall.Position = new SCNVector3 (7, 0, MIDDLE_Z);
				GroundNode.AddChildNode (invisibleWall);
				KinematicItems.Add (invisibleWall);

				invisibleWall = (SCNNode)invisibleWall.Copy ();
				invisibleWall.Geometry = SCNBox.Create (10, 40, 4, 0);
				invisibleWall.Geometry.FirstMaterial.Transparency = 0;
				invisibleWall.Position = new SCNVector3 (0, 0, MIDDLE_Z - 7);
				invisibleWall.PhysicsBody = SCNPhysicsBody.CreateStaticBody ();
				GroundNode.AddChildNode (invisibleWall);
				KinematicItems.Add (invisibleWall);

				invisibleWall = (SCNNode)invisibleWall.Copy ();
				invisibleWall.Position = new SCNVector3 (0, 0, MIDDLE_Z + 7);
				GroundNode.AddChildNode (invisibleWall);
				KinematicItems.Add (invisibleWall);


				for (int i = 0; i < 100; i++) {
					var ball = SCNNode.Create ();
					worldPos = boxNode.ConvertPositionToNode (new SCNVector3 (RandFloat (-4, 4), RandFloat (10, 30), RandFloat (-1, 4)), null);
					ball.Position = worldPos;
					ball.Geometry = SCNSphere.Create (0.5f);
					ball.Geometry.FirstMaterial.Diffuse.Contents = NSColor.Cyan;
					ball.PhysicsBody = SCNPhysicsBody.CreateDynamicBody ();
					((SCNView)presentationViewController.View).Scene.RootNode.AddChildNode (ball);

					KinematicItems.Add (ball);
				}
				break;
			case 12:
				TextManager.FlipOutText (SlideTextManager.TextType.Code);
				TextManager.FlipOutText (SlideTextManager.TextType.Bullet);
				TextManager.FlipOutText (SlideTextManager.TextType.Subtitle);
				TextManager.SetSubtitle ("SCNPhysicsShape");
				TextManager.AddCode ("#// Configure the physics shape\n\n"
				+ "aNode.physicsBody.#physicsShape# = \n\t[#SCNPhysicsShape# shapeWithGeometry:aGeometry options:options];#");
				TextManager.FlipInText (SlideTextManager.TextType.Bullet);
				TextManager.FlipInText (SlideTextManager.TextType.Code);

				KinematicItems[0].RunAction (SCNAction.Sequence (new SCNAction[] {
					SCNAction.FadeOut (0.5),
					SCNAction.RemoveFromParentNode ()
				}));
				for (int i = 1; i < 5; i++)
					KinematicItems[i].RemoveFromParentNode ();

				KinematicItems = null;
				break;
			case 13:
				//add meshes
				PresentMeshes (presentationViewController);
				break;
			case 14:
				// remove meshes
				center = new SCNVector3 (0, -5, 20);
				center = GroundNode.ConvertPositionToNode (center, null);
				Explosion (center, Meshes);
				break;
			case 15:
				// add shapes
				PresentPrimitives (presentationViewController);
				break;
			case 16:
				// remove shapes
				center = new SCNVector3 (0, -5, 20);
				center = GroundNode.ConvertPositionToNode (center, null);
				Explosion (center, Shapes);

				popTime = new DispatchTime (DispatchTime.Now, (long)(0.5 * Utils.NSEC_PER_SEC));
				DispatchQueue.MainQueue.DispatchAfter (popTime, () => {
					TextManager.FlipOutText (SlideTextManager.TextType.Code);
					TextManager.FlipOutText (SlideTextManager.TextType.Bullet);
					TextManager.FlipOutText (SlideTextManager.TextType.Subtitle);

					TextManager.SetSubtitle ("SCNPhysicsBehavior");
					TextManager.AddCode ("#// setup a physics behavior\n\n"
						+ "#SCNPhysicsHingeJoint# *joint = [SCNPhysicsHingeJoint\n\n" 
						+ "jointWithBodyA:#nodeA.physicsBody# axisA:[...] anchorA:[...]\n\n"
						+ "bodyB:#nodeB.physicsBody# axisB:[...] anchorB:[...]];\n\n\n"
						+ "[scene.#physicsWorld# addBehavior:joint];#");

					TextManager.FlipInText (SlideTextManager.TextType.Bullet);
					TextManager.FlipInText (SlideTextManager.TextType.Subtitle);
					TextManager.FlipInText (SlideTextManager.TextType.Code);
				});
				break;
			case 17:
				//add meshes
				PresentHinge (presentationViewController);
				break;
			case 18:
				//remove constraints
				((SCNView)presentationViewController.View).Scene.PhysicsWorld.RemoveAllBehaviors ();

				foreach (var node in Hinges)
					node.RunAction (SCNAction.Sequence (new SCNAction[] { SCNAction.Wait (3.0), SCNAction.FadeOut (0.5), SCNAction.RemoveFromParentNode () }));

				break;
			case 19:
				TextManager.FlipOutText (SlideTextManager.TextType.Bullet);
				TextManager.FlipOutText (SlideTextManager.TextType.Subtitle);
				TextManager.FlipOutText (SlideTextManager.TextType.Code);

				TextManager.SetSubtitle ("More...");

				TextManager.FlipInText (SlideTextManager.TextType.Subtitle);

				TextManager.AddBulletAtLevel ("SCNPhysicsField", 0);
				TextManager.AddBulletAtLevel ("SCNPhysicsVehicle", 0);

				TextManager.FlipInText (SlideTextManager.TextType.Bullet);
				TextManager.FlipInText (SlideTextManager.TextType.Code);
				break;
			}
		}
		public virtual void Update (ISCNSceneRenderer renderer, double timeInSeconds)
		{
			// delta time since last update
			if (Math.Abs (previousUpdateTime) < float.Epsilon)
				previousUpdateTime = timeInSeconds;

			double deltaTime = Math.Min (Math.Max (1.0 / 60.0, timeInSeconds - previousUpdateTime), 1f);
			previousUpdateTime = timeInSeconds;

			// Reset some states every frame
			maxPenetrationDistance = 0;
			positionNeedsAdjustment = false;

			SCNVector3 direction = GameView.CurrentDirection;
			SCNVector3 initialPosition = Character.Node.Position;

			// Move
			if (Math.Abs (direction.X) > float.Epsilon && Math.Abs (direction.Z) > float.Epsilon) {
				var characterSpeed = (float)deltaTime * CharacterSpeedFactor * .84f;
				Character.Node.Position = new SCNVector3 (
					initialPosition.X + direction.X * characterSpeed,
					initialPosition.Y + direction.Y * characterSpeed,
					initialPosition.Z + direction.Z * characterSpeed
				);

				// update orientation
				double angle = Math.Atan2 (direction.X, direction.Z);
				Character.Direction = (float)angle;
				Character.Walking = true;
			} else {
				Character.Walking = false;
			}

			var p0 = Character.Node.Position;
			var p1 = Character.Node.Position;

			p0.Y -= MaxJump;
			p1.Y += MaxRise;

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

			SCNHitTestResult[] results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint (p1, p0, options);
			float groundY = -10;

			if (results.Length > 0) {
				
				SCNHitTestResult result = results [0];
				groundY = result.WorldCoordinates.Y;
				UpdateCameraWithCurrentGround (result.Node);
				SCNMaterial groundMaterial = result.Node.ChildNodes [0].Geometry.FirstMaterial;
				if (grassArea == groundMaterial) {
					Character.CurrentFloorMaterial = FloorMaterial.Grass;
				} else if (waterArea == groundMaterial) {
					if (Character.Burning) {
						Character.Pshhhh ();
						Character.Node.RunAction (SCNAction.Sequence (new [] {
							SCNAction.PlayAudioSource (pshhhSound, true),
							SCNAction.PlayAudioSource (aahSound, false)
						}));
					}

					Character.CurrentFloorMaterial = FloorMaterial.Water;

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

					results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint (p1, p0, options);
					result = results [0];
					groundY = result.WorldCoordinates.Y;
				} else {
					Character.CurrentFloorMaterial = FloorMaterial.Rock;
				}
			}

//			var nextPosition = Character.Node.Position;
//			const double threshold = 1e-5;
//
//			if (groundY < nextPosition.Y - threshold) {
//				// approximation of acceleration for a delta time
//				accelerationY += (float)(deltaTime * GravityAcceleration);
//				if (groundY < nextPosition.Y - 0.2)
//					Character.CurrentFloorMaterial = FloorMaterial.Air;
//			} else {
//				accelerationY = 0;
//			}
//
//			nextPosition.Y -= accelerationY;
//
//			// reset acceleration if we touch the ground
//			if (groundY > nextPosition.Y) {
//				accelerationY = 0;
//				nextPosition.Y = groundY;
//			}

			// Flames are static physics bodies, but they are moved by an action - So we need to tell the physics engine that the transforms did change.
			foreach (SCNNode flame in flames)
				flame.PhysicsBody.ResetTransform ();

			// Adjust the volume of the enemy based on the distance with the character.
			float distanceToClosestEnemy = float.MaxValue;
			SCNVector3 pos3 = Character.Node.Position;
			foreach (SCNNode enemy in enemies) {
				// distance to enemy
				SCNMatrix4 enemyMat = enemy.WorldTransform;
				var enemyPosition = new SCNVector3 (enemyMat.M41, enemyMat.M42, enemyMat.M43);
				float distance = SCNVector3.Subtract (pos3, enemyPosition).Length;
				distanceToClosestEnemy = Math.Min (distanceToClosestEnemy, distance);
			}

			// Adjust sounds volumes based on distance with the enemy.
			if (!gameIsComplete) {
				double fireVolume = 0.3 * Math.Max (0.0, Math.Min (1.0, 1.0 - (distanceToClosestEnemy - 1.2) / 1.6));
				var  mixerNode = flameThrowerSound.AudioNode as AVAudioMixerNode;
				if (mixerNode != null)
					mixerNode.Volume = (float)fireVolume;
			}
		}
		void UpdateCameraWithCurrentGround (SCNNode node)
		{
			if (gameIsComplete)
				return;

			if (currentGround == null) {
				currentGround = node;
				return;
			}

			if (node != null && node != currentGround) {
				currentGround = node;

				if (groundToCameraPosition.ContainsKey (node)) {
					var position = groundToCameraPosition [node];

					if (node == mainGround && Character.Node.Position.X < 2.5)
						position = new SCNVector3 (-0.098175f, 3.926991f, 0f);

					SCNAction actionY = SCNAction.RotateTo (0f, position.Y, 0f, 3.0, true);
					actionY.TimingMode = SCNActionTimingMode.EaseInEaseOut;
					SCNAction actionX = SCNAction.RotateTo (position.X, 0f, 0f, 3.0, true);
					actionX.TimingMode = SCNActionTimingMode.EaseInEaseOut;

					cameraXHandle.RunAction (actionY);
					cameraXHandle.RunAction (actionX);
				}
			}
		}
Example #44
0
		public PlayerCharacter (SCNNode characterNode) : base (characterNode)
		{
			CategoryBitMask = NodeCategory.Lava;
			velocity = SCNVector3.Zero;
			IsWalking = false;
			changingDirection = false;
			baseWalkSpeed = 0.0167f;
			JumpBoost = 0.0f;

			WalkSpeed = baseWalkSpeed * 2;
			Jumping = false;
			groundPlaneHeight = 0.0f;
			playerWalkDirection = WalkDirection.Right;

			cameraHelper = new SCNNode {
				Position = new SCNVector3 (1000f, 200f, 0f)
			};

			AddChildNode (cameraHelper);

			CollideSphere = new SCNNode {
				Position = new SCNVector3 (0f, 80f, 0f)
			};

			SCNGeometry geo = SCNCapsule.Create (90f, 160f);
			SCNPhysicsShape shape2 = SCNPhysicsShape.Create (geo, (NSDictionary)null);
			CollideSphere.PhysicsBody = SCNPhysicsBody.CreateBody (SCNPhysicsBodyType.Kinematic, shape2);

			CollideSphere.PhysicsBody.CollisionBitMask = 
				GameCollisionCategory.Banana |
			GameCollisionCategory.Coin |
			GameCollisionCategory.Coconut |
			GameCollisionCategory.Lava;

			CollideSphere.PhysicsBody.CategoryBitMask = GameCollisionCategory.Player;
			AddChildNode (CollideSphere);

			DustPoof = GameSimulation.LoadParticleSystemWithName ("dust");
			NSString artResourcePath = (NSString)GameSimulation.PathForArtResource ("level/effects/effects_transparent.png");
			DustPoof.ParticleImage = artResourcePath;
			DustWalking = GameSimulation.LoadParticleSystemWithName ("dustWalking");
			DustWalking.ParticleImage = artResourcePath;
			dustWalkingBirthRate = DustWalking.BirthRate;

			// Load the animations and store via a lookup table.
			SetupIdleAnimation ();
			SetupRunAnimation ();
			SetupJumpAnimation ();
			SetupBoredAnimation ();
			SetupHitAnimation ();

			PlayIdle ();
		}
Example #45
0
		SCNVector4 GetDirectionFromPosition (SCNVector3 currentPosition)
		{
			SCNVector3 target = LocationAlongPath (TimeAlongPath - 0.05f);
			SCNMatrix4 lookAt = SCNMatrix4.LookAt (currentPosition, target, new SCNVector3 (0f, 1f, 0f));
			SCNQuaternion q = lookAt.ToQuaternion ();

			nfloat angle = 0;
			q.ToAxisAngle (out target, out angle);

			if (PlayerCharacter.PlayerWalkDirection == WalkDirection.Left)
				angle -= (nfloat)Math.PI;

			return  new SCNVector4 (0f, 1f, 0f, angle);
		}
Example #46
0
		public void Update (double deltaTime, SCNSceneRenderer aRenderer)
		{
			// Based on gamestate:
			// ingame: Move the character if running.
			// ingame: prevent movement of the character past our level bounds.
			// ingame: perform logic for the player character.
			// any: move the directional light with any player movement.
			// ingame: update the coconuts kinematically.
			// ingame: perform logic for each monkey.
			// ingame: because our camera could have moved, update the transforms needs to fly
			//         collected bananas from the player (world space) to score (screen space)

			var appDelegate = SharedAppDelegate.AppDelegate;
			var currentState = GameSimulation.Sim.CurrentGameState;

			// Move character along path if walking.
			MoveCharacterAlongPathWith (deltaTime, currentState);

			// Based on the time along path, rotation the character to face the correct direction.
			PlayerCharacter.Rotation = GetPlayerDirectionFromCurrentPosition ();
			if (currentState == GameState.InGame)
				PlayerCharacter.Update (deltaTime);

			// Move the light
			UpdateSunLightPosition ();

			if (currentState == GameState.PreGame ||
			    currentState == GameState.PostGame ||
			    currentState == GameState.Paused)
				return;

			foreach (MonkeyCharacter monkey in Monkeys)
				monkey.Update (deltaTime);

			// Update timer and check for Game Over.
			SecondsRemaining -= deltaTime;
			if (SecondsRemaining < 0.0)
				DoGameOver ();

			// update the player's SP position.
			SCNVector3 playerPosition = PlayerCharacter.WorldTransform.GetPosition ();
			screenSpacePlayerPosition = appDelegate.Scene.ProjectPoint (playerPosition);

			// Update the SP position of the score label
			CGPoint pt = ScoreLabelLocation;

			worldSpaceLabelScorePosition = appDelegate.Scene.UnprojectPoint (new SCNVector3 (pt.X, pt.Y, screenSpacePlayerPosition.Z));
		}
Example #47
0
		public void CollectBanana (SCNNode banana)
		{
			// Flyoff the banana to the screen space position score label.
			// Don't increment score until the banana hits the score label.

			// ignore collisions
			banana.PhysicsBody = null;
			BananasCollected++;

			int variance = 60;
			nfloat duration = 0.25f;

			nfloat apexY = worldSpaceLabelScorePosition.Y * 0.8f + (new Random ().Next (0, variance) - variance / 2);
			worldSpaceLabelScorePosition.Z = banana.Position.Z;
			var apex = new SCNVector3 (banana.Position.X + 10 + (new Random ().Next (0, variance) - variance / 2), apexY, banana.Position.Z);

			SCNAction startFlyOff = SCNAction.MoveTo (apex, duration);
			startFlyOff.TimingMode = SCNActionTimingMode.EaseOut;

			SCNAction endFlyOff = SCNAction.CustomAction (duration, new SCNActionNodeWithElapsedTimeHandler ((node, elapsedTime) => {
				nfloat t = elapsedTime / duration;
				var v = new SCNVector3 (
					        apex.X + ((worldSpaceLabelScorePosition.X - apex.X) * t),
					        apex.Y + ((worldSpaceLabelScorePosition.Y - apex.Y) * t),
					        apex.X + ((worldSpaceLabelScorePosition.Z - apex.Z) * t));
				node.Position = v;
			}));

			endFlyOff.TimingMode = SCNActionTimingMode.EaseInEaseOut;
			SCNAction flyoffSequence = SCNAction.Sequence (new SCNAction[] { startFlyOff, endFlyOff });

			banana.RunAction (flyoffSequence, () => {
				Bananas.Remove (banana);
				banana.RemoveFromParentNode ();
				// Add to score.
				Score++;
				GameSimulation.Sim.PlaySound ("deposit.caf");

				// Game Over
				if (Bananas.Count == 0)
					DoGameOver ();
			});
		}
		// Takes a string an creates a node hierarchy where each letter is an independent geometry that is animated
		private SCNNode SplittedStylizedText (string message)
		{
			var textNode = SCNNode.Create ();
			var frontMaterial = TextFrontMaterial ();
			var border = TextSideAndChamferMaterial ();

			// Current x position of the next letter to add
			var positionX = 0.0f;

			// For each letter
			for (var i = 0; i < message.Length; i++) {
				var letterNode = SCNNode.Create ();
				var letterString = message.Substring (i, 1);
				var text = SCNText.Create (letterString, 50.0f);
				text.Font = NSFont.FromFontName ("Avenir Next Heavy", 288);

				text.ChamferRadius = 3.0f;
				text.ChamferProfile = TextChamferProfile ();

				// use a different material for the "heart" character
				var finalFrontMaterial = frontMaterial;
				if (i == 1) {
					finalFrontMaterial = (SCNMaterial)finalFrontMaterial.Copy ();
					finalFrontMaterial.Diffuse.Contents = NSColor.Red;
					finalFrontMaterial.Reflective.Contents = NSColor.Black;
					letterNode.Scale = new SCNVector3 (1.1f, 1.1f, 1.0f);
				}

				text.Materials = new SCNMaterial[] { finalFrontMaterial, finalFrontMaterial, border, border, border };

				letterNode.Geometry = text;
				textNode.AddChildNode (letterNode);

				// measure the letter we just added to update the position
				SCNVector3 min, max;
				max = new SCNVector3 (0, 0, 0);
				min = new SCNVector3 (0, 0, 0);
				if (letterNode.GetBoundingBox (ref min, ref max)) {
					letterNode.Position = new SCNVector3 (positionX - min.X + (max.X + min.X) * 0.5f, -min.Y, 0);
					positionX += (float)max.X;
				} else {
					// if we have no bounding box, it is probably because of the "space" character. In that case, move to the right a little bit.
					positionX += 50.0f;
				}

				// Place the pivot at the center of the letter so that the rotation animation looks good
				letterNode.Pivot = SCNMatrix4.CreateTranslation ((max.X + min.X) * 0.5f, 0, 0);

				// Animate the letter
				var animation = CAKeyFrameAnimation.GetFromKeyPath ("rotation");
				animation.Duration = 4.0f;
				animation.KeyTimes = new NSNumber[] { 0.0f, 0.3f, 1.0f };
				animation.Values = new NSObject[] {
					NSValue.FromVector (new SCNVector4 (0, 1, 0, 0)),
					NSValue.FromVector (new SCNVector4 (0, 1, 0, (float)(Math.PI * 2))),
					NSValue.FromVector (new SCNVector4 (0, 1, 0, (float)(Math.PI * 2)))
				};

				var timingFunction = CAMediaTimingFunction.FromName (CAMediaTimingFunction.EaseInEaseOut);
				animation.TimingFunctions = new CAMediaTimingFunction[] { timingFunction, timingFunction, timingFunction };
				animation.RepeatCount = float.MaxValue;
				animation.BeginTime = CAAnimation.CurrentMediaTime () + 1.0 + i * 0.2; // desynchronize animations
				letterNode.AddAnimation (animation, new NSString ("letterNodeAnimation"));
			}

			return textNode;
		}
		private void AddWoodenBlockToScene (SCNScene scene, NSString imageName, SCNVector3 position)
		{
			var block = SCNNode.Create ();
			block.Position = position;
			block.Geometry = new SCNBox {
				Width = 5f,
				Height = 5f,
				Length = 5f,
				ChamferRadius = 0f
			};

			block.Geometry.FirstMaterial.Diffuse.Contents = imageName;
			block.Geometry.FirstMaterial.Diffuse.MipFilter = SCNFilterMode.Linear;
			block.PhysicsBody = SCNPhysicsBody.CreateDynamicBody ();
			scene.RootNode.AddChildNode (block);
		}
		private void AddTrainToScene (SCNScene scene, SCNVector3 position)
		{
			var max = SCNVector3.Zero;
			var min = SCNVector3.Zero;
			var trainScene = SCNScene.FromFile ("train_flat", ResourceManager.ResourceFolder, (NSDictionary)null);

			foreach (var node in trainScene.RootNode.ChildNodes) {
				if (node.Geometry != null) {
					node.Position = new SCNVector3 (
						node.Position.X + position.X,
						node.Position.Y + position.Y,
						node.Position.Z + position.Z
					);

					max = SCNVector3.Zero;
					min = SCNVector3.Zero;

					node.GetBoundingBox (ref min, ref max);

					var body = SCNPhysicsBody.CreateDynamicBody ();
					var boxShape = new SCNBox {
						Width = max.X - min.X,
						Height = max.Y - min.Y,
						Length = max.Z - min.Z,
						ChamferRadius = 0f
					};

					body.PhysicsShape = SCNPhysicsShape.Create (boxShape, (NSDictionary)null);
					node.Pivot = SCNMatrix4.CreateTranslation (0f, -min.Y, 0f);
					node.PhysicsBody = body;
					scene.RootNode.AddChildNode (node);
				}
			}

			var smokeHandle = scene.RootNode.FindChildNode ("Smoke", true);
			var smokeParticleSystem = SCNParticleSystem.Create ("smoke", ResourceManager.ResourceFolder);
			smokeParticleSystem.ParticleImage = ResourceManager.GetResourcePath ("smoke.png");
			smokeHandle.AddParticleSystem (smokeParticleSystem);

			var engineCar = scene.RootNode.FindChildNode ("EngineCar", false);
			var wagon1 = scene.RootNode.FindChildNode ("Wagon1", false);
			var wagon2 = scene.RootNode.FindChildNode ("Wagon2", false);

			max = SCNVector3.Zero;
			min = SCNVector3.Zero;
			engineCar.GetBoundingBox (ref min, ref max);

			var wmax = SCNVector3.Zero;
			var wmin = SCNVector3.Zero;
			wagon1.GetBoundingBox (ref wmin, ref wmax);

			var anchorA = new SCNVector3 (max.X, min.Y, 0f);
			var anchorB = new SCNVector3 (wmin.X, wmin.Y, 0f);

			var joint = SCNPhysicsBallSocketJoint.Create (engineCar.PhysicsBody, anchorA, wagon1.PhysicsBody, anchorB);

			scene.PhysicsWorld.AddBehavior (joint);

			joint = SCNPhysicsBallSocketJoint.Create (wagon1.PhysicsBody,
				new SCNVector3 (wmax.X + 0.1f, wmin.Y, 0f),
				wagon2.PhysicsBody,
				new SCNVector3 (wmin.X - 0.1f, wmin.Y, 0f)
			);

			scene.PhysicsWorld.AddBehavior (joint);
		}
		private void ReorientCarIfNeeded ()
		{
			var rnd = new Random ();
			SCNNode car = vehicleNode.PresentationNode;
			SCNVector3 carPos = car.Position;

			ticks++;
			if (ticks == 30) {
				SCNMatrix4 t = car.WorldTransform;
				if (t.M22 <= 0.1f) {
					check++;
					if (check == 3) {
						tryVar++;
						if (tryVar == 3) {
							tryVar = 0;
							vehicleNode.Rotation = new SCNVector4 (0f, 0f, 0f, 0f);
							vehicleNode.Position = new SCNVector3 (carPos.X, carPos.Y + 10f, carPos.Z);
							vehicleNode.PhysicsBody.ResetTransform ();
						} else {
							float x = -10f * (rnd.Next () / float.MaxValue - 0.5f);
							float z = -10f * (rnd.Next () / float.MaxValue - 0.5f);
							var pos = new SCNVector3 (x, 0f, z);
							vehicleNode.PhysicsBody.ApplyForce (new SCNVector3 (0f, 300f, 0f), pos, true);
						}

						check = 0;
					}
				} else {
					check = 0;
				}

				ticks = 0;
			}
		}
		public virtual void DidSimulatePhysics (ISCNSceneRenderer renderer, double timeInSeconds)
		{
			float defaultEngineForce = 300.0f;
			float defaultBrakingForce = 3.0f;
			float steeringClamp = 0.6f;
			float cameraDamping = 0.3f;

			GameView scnView = GameView;

			float engineForce = 0;
			float brakingForce = 0;

			var controllers = GCController.Controllers;
			float orientation = this.orientation;

			switch (scnView.TouchesCount) {
			case 1:
				engineForce = defaultEngineForce;
				reactor.BirthRate = reactorDefaultBirthRate;
				break;
			case 2:
				engineForce = -defaultEngineForce;
				reactor.BirthRate = 0;
				break;
			case 3:
				brakingForce = 100;
				reactor.BirthRate = 0;
				break;
			default:
				brakingForce = defaultBrakingForce;
				reactor.BirthRate = 0;
				break;
			}

			if (controllers != null && controllers.Length > 0) {
				GCController controller = controllers [0];
				GCGamepad pad = controller.Gamepad;
				GCControllerDirectionPad dpad = pad.DPad;

				if (dpad.Right.IsPressed) {
					if (orientationCum < 0f)
						orientationCum *= decrementOrientation;

					orientationCum += incrementOrientation;

					if (orientationCum > 1f)
						orientationCum = 1f;
				} else if (dpad.Left.IsPressed) {
					if (orientationCum > 0)
						orientationCum *= decrementOrientation;

					orientationCum -= incrementOrientation;

					if (orientationCum < -1)
						orientationCum = -1;
				} else {
					orientationCum *= decrementOrientation;
				}
			}

			vehicleSteering = -orientation;
			if (orientation == 0)
				vehicleSteering *= 0.9f;
			if (vehicleSteering < -steeringClamp)
				vehicleSteering = -steeringClamp;
			if (vehicleSteering > steeringClamp)
				vehicleSteering = steeringClamp;

			vehicle.SetSteeringAngle (vehicleSteering, 0);
			vehicle.SetSteeringAngle (vehicleSteering, 1);

			vehicle.ApplyEngineForce (engineForce, 2);
			vehicle.ApplyEngineForce (engineForce, 3);

			vehicle.ApplyBrakingForce (brakingForce, 2);
			vehicle.ApplyBrakingForce (brakingForce, 3);

			ReorientCarIfNeeded ();

			SCNNode car = vehicleNode.PresentationNode;
			SCNVector3 carPos = car.Position;
			var targetPos = new SCNVector3 (carPos.X, 30f, carPos.Z + 25f);
			var cameraPos = new SCNVector3 (cameraNode.Position);
			cameraPos = SCNVector3.Add (cameraPos, cameraDamping * (SCNVector3.Subtract (targetPos, cameraPos)));
			cameraNode.Position = cameraPos;

			if (scnView.InCarView) {
				var frontPosition = scnView.PointOfView.PresentationNode.ConvertPositionToNode (new SCNVector3 (0f, 0f, -30f), null);
				spotLightNode.Position = new SCNVector3 (frontPosition.X, 80f, frontPosition.Z);
				spotLightNode.Rotation = new SCNVector4 (1f, 0f, 0f, -(float)Math.PI / 2f);
			} else {
				spotLightNode.Position = new SCNVector3 (carPos.X, 80f, carPos.Z + 30f);
				spotLightNode.Rotation = new SCNVector4 (1f, 0f, 0f, -(float)(Math.PI / 2.8));
			}

			var overlayScene = (OverlayScene)scnView.OverlayScene;
			overlayScene.SpeedNeedle.ZRotation = -(vehicle.SpeedInKilometersPerHour * (float)Math.PI / maxSpeed);
		}
Example #53
0
		public SCNNode CreateLevel ()
		{
			RootNode = new SCNNode ();

			// load level dae and add all root children to the scene.
			var options = new SCNSceneLoadingOptions { ConvertToYUp = true };
			SCNScene scene = SCNScene.FromFile ("level", GameSimulation.PathForArtResource ("level/"), options);
			foreach (SCNNode node in scene.RootNode.ChildNodes)
				RootNode.AddChildNode (node);

			// retrieve the main camera
			Camera = RootNode.FindChildNode ("camera_game", true);

			// create our path that the player character will follow.
			CalculatePathPositions ();

			// Sun/Moon light
			SunLight = RootNode.FindChildNode ("FDirect001", true);
			SunLight.EulerAngles = new SCNVector3 (7.1f * (nfloat)Math.PI / 4, (nfloat)Math.PI / 4, 0f);
			SunLight.Light.ShadowSampleCount = 1;
			lightOffsetFromCharacter = new SCNVector3 (1500f, 2000f, 1000f);

			//workaround directional light deserialization issue
			SunLight.Light.ZNear = 100f;
			SunLight.Light.ZFar = 5000f;
			SunLight.Light.OrthographicScale = 1000f;

			// Torches
			var torchesPos = new float[] { 0f, -1f, 0.092467f, -1f, -1f, 0.5f, 0.792f, 0.95383f };
			for (int i = 0; i < torchesPos.Length; i++) {
				if (torchesPos [i] != -1) {
					SCNVector3 location = LocationAlongPath (torchesPos [i]);
					location.Y += 50;
					location.Z += 150;

					SCNNode node = CreateTorchNode ();

					node.Position = location;
					RootNode.AddChildNode (node);
				}
			}

			// After load, we add nodes that are dynamic / animated / or otherwise not static.
			CreateLavaAnimation ();
			CreateSwingingTorch ();
			AnimateDynamicNodes ();

			// Create our player character
			SCNNode characterRoot = GameSimulation.LoadNodeWithName (string.Empty, "art.scnassets/characters/explorer/explorer_skinned.dae");
			PlayerCharacter = new PlayerCharacter (characterRoot); 
			TimeAlongPath = 0;

			PlayerCharacter.Position = LocationAlongPath (TimeAlongPath);
			PlayerCharacter.Rotation = GetPlayerDirectionFromCurrentPosition ();
			RootNode.AddChildNode (PlayerCharacter);

			// Optimize lighting and shadows
			// only the charadcter should cast shadows
			foreach (var child in RootNode.ChildNodes)
				child.CastsShadow = false;

			foreach (var child in PlayerCharacter.ChildNodes)
				child.CastsShadow = true;

			// Add some monkeys to the scene.
			AddMonkeyAtPosition (new SCNVector3 (0f, -30f, -400f), 0f);
			AddMonkeyAtPosition (new SCNVector3 (3211f, 146f, -400f), -(nfloat)Math.PI / 4f);
			AddMonkeyAtPosition (new SCNVector3 (5200f, 330f, 600f), 0f);

			// Volcano
			SCNNode oldVolcano = RootNode.FindChildNode ("volcano", true);
			string volcanoDaeName = GameSimulation.PathForArtResource ("level/volcano_effects.dae");
			SCNNode newVolcano = GameSimulation.LoadNodeWithName ("dummy_master", volcanoDaeName);

			oldVolcano.AddChildNode (newVolcano);
			oldVolcano.Geometry = null;
			oldVolcano = newVolcano.FindChildNode ("volcano", true);
			oldVolcano = oldVolcano.ChildNodes [0];

			// Animate our dynamic volcano node.
			string shaderCode = "uniform float speed;\n" +
			                    "_geometry.color = vec4(a_color.r, a_color.r, a_color.r, a_color.r);\n" +
			                    "_geometry.texcoords[0] += (vec2(0.0, 1.0) * 0.05 * u_time);\n";

			string fragmentShaderCode = "#pragma transparent\n";

			// Dim background
			SCNNode back = RootNode.FindChildNode ("dumy_rear", true);
			foreach (var child in back.ChildNodes) {
				child.CastsShadow = false;

				if (child.Geometry == null)
					continue;

				foreach (SCNMaterial material in child.Geometry.Materials) {
					material.LightingModelName = SCNLightingModel.Constant;
					material.Multiply.Contents = AppKit.NSColor.FromDeviceWhite (0.3f, 1f);

					material.Multiply.Intensity = 1;
				}
			}

			SCNNode backMiddle = RootNode.FindChildNode ("dummy_middle", true);
			foreach (var child in backMiddle.ChildNodes) {

				if (child.Geometry == null)
					continue;

				foreach (SCNMaterial material in child.Geometry.Materials)
					material.LightingModelName = SCNLightingModel.Constant;
			}

			foreach (var child in newVolcano.ChildNodes)
				foreach (var volc in child.ChildNodes) {
					if (volc != oldVolcano && volc.Geometry != null) {
						volc.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant;
						volc.Geometry.FirstMaterial.Multiply.Contents = AppKit.NSColor.White;

						volc.Geometry.ShaderModifiers = new SCNShaderModifiers { 
							EntryPointGeometry = shaderCode,
							EntryPointFragment = fragmentShaderCode
						};
					}
				}

			Coconuts = new List<Coconut> ();
			return RootNode;
		}
Example #54
0
		nfloat GetGroundHeight (SCNMatrix4 matrix)
		{
			SCNVector3 start = new SCNVector3 (matrix.M41, matrix.M42 + 1000, matrix.M43);
			SCNVector3 end = new SCNVector3 (matrix.M41, matrix.M42 - 3000, matrix.M43);

			var options = NSDictionary.FromObjectsAndKeys (new NSObject[] { new NSNumber (GameCollisionCategory.Ground | GameCollisionCategory.Lava), (NSString)"closest" }, 
				              new NSObject[] { SCNPhysicsTestKeys.CollisionBitMaskKey, SCNPhysicsTestKeys.SearchModeKey });

			SCNHitTestResult[] hits = GameSimulation.Sim.PhysicsWorld.RayTestWithSegmentFromPoint (start, end, options);

			if (hits == null || hits.Length == 0)
				return 0;

			foreach (SCNHitTestResult result in hits) {
				if (result.Node.PhysicsBody.CategoryBitMask != (GameCollisionCategory.Ground | GameCollisionCategory.Lava))
					return result.WorldCoordinates.Y;
			}

			return 0;
		}
Example #55
0
		public void CollideWithCoconut (SCNNode coconut, SCNVector3 contactPoint)
		{
			// No more collisions. Let it bounce away and fade out.
			coconut.PhysicsBody.CollisionBitMask = 0;
			coconut.RunAction (SCNAction.Sequence (new SCNAction[] {
				SCNAction.Wait (1.0),
				SCNAction.FadeOut (1.0),
				SCNAction.RemoveFromParentNode ()
			}), () => {
				Coconuts.Remove ((Coconut)coconut);
			});

			// Decrement score
			int amountToDrop = Score / 10;

			amountToDrop = Math.Max (1, amountToDrop);
			amountToDrop = Math.Min (10, amountToDrop);

			if (amountToDrop > Score)
				amountToDrop = Score;

			Score -= amountToDrop;

			// Throw bananas
			float spacing = 40f;
			for (int x = 0; x < amountToDrop; x++) {
				SCNNode banana = CreateBanana ();
				RootNode.AddChildNode (banana);
				banana.Position = contactPoint;
				banana.PhysicsBody.CategoryBitMask = GameCollisionCategory.NoCollide;
				banana.PhysicsBody.CollisionBitMask = GameCollisionCategory.Ground;
				SCNVector3 endPoint = SCNVector3.Zero;
				endPoint.X -= (spacing * x) + spacing;

				SCNAction flyoff = SCNAction.MoveBy (endPoint, MathUtils.RandomPercent () * 0.750f);
				flyoff.TimingMode = SCNActionTimingMode.EaseInEaseOut;

				banana.RunAction (flyoff, () => {
					banana.PhysicsBody.CategoryBitMask = GameCollisionCategory.Banana;
					banana.PhysicsBody.CollisionBitMask = GameCollisionCategory.Ground | GameCollisionCategory.Player;
				});

				Bananas.Add (banana);
			}

			PlayerCharacter.InHitAnimation = true;
		}
Example #56
0
		public override void Update (double deltaTime)
		{
			SCNMatrix4 mtx = Transform;
			var gravity = new SCNVector3 (0f, -90f, 0f);
			var gravitystep = SCNVector3.Multiply (gravity, (nfloat)deltaTime);

			velocity = SCNVector3.Add (velocity, gravitystep);
			var minMovement = new SCNVector3 (0f, -50f, 0f);
			var maxMovement = new SCNVector3 (100f, 100f, 100f);
			velocity = MathUtils.GetMaxVector (velocity, minMovement);
			velocity = MathUtils.GetMinVector (velocity, maxMovement);
			mtx = mtx.TranslateWithVector (velocity);
			groundPlaneHeight = GetGroundHeight (mtx);

			if (mtx.M42 < groundPlaneHeight) {
				if (!Launching && velocity.Y < 0.0f) {
					if (Jumping) {
						Jumping = false;
						if (DustPoof != null) {
							AddParticleSystem (DustPoof);
							DustPoof.Loops = false;
						}
						PlayLand ();
						JumpBoost = 0.0f;
					}
				}
				// tie to ground
				mtx.M42 = groundPlaneHeight;
				
				velocity.Y = 0.0f;
			}

			Transform = mtx;
			//-- move the camera
			SCNNode camera = GameSimulation.Sim.GameLevel.Camera.ParentNode;
			if (camera != null) {
				nfloat x = Position.X + ((PlayerWalkDirection == WalkDirection.Right) ? 250 : -250);
				nfloat y = (Position.Y + 261) - (0.85f * (Position.Y - groundPlaneHeight));
				nfloat z = Position.Z + 1500;

				var pos = new SCNVector3 (x, y, z);

				SCNMatrix4 desiredTransform = camera.Transform.SetPosition (pos);
				camera.Transform = MathUtils.Interpolate (camera.Transform, desiredTransform, 0.025f);
			}
		}
Example #57
0
		SCNVector3 LocationAlongPath (nfloat percent)
		{
			if (PathPositions.Count <= 3)
				return SCNVector3.Zero;

			int numSections = PathPositions.Count - 3;

			nfloat dist = percent * numSections;

			int currentPointIndex = (int)Math.Min ((uint)Math.Floor (dist), numSections - 1);
			dist -= currentPointIndex;
			var a = new SCNVector3 (PathPositions [currentPointIndex]);
			var b = new SCNVector3 (PathPositions [currentPointIndex + 1]);
			var c = new SCNVector3 (PathPositions [currentPointIndex + 2]);
			var d = new SCNVector3 (PathPositions [currentPointIndex + 3]);

			var location = new SCNVector3 ();
			location.X = CatmullRomValue (a.X, b.X, c.X, d.X, dist);
			location.Y = CatmullRomValue (a.Y, b.Y, c.Y, d.Y, dist);
			location.Z = CatmullRomValue (a.Z, b.Z, c.Z, d.Z, dist);

			return location;
		}
Example #58
0
		void SetupJumpAnimation ()
		{
			NSString jumpKey = KeyForAnimationType (CharacterAnimation.Jump);
			NSString fallingKey = KeyForAnimationType (CharacterAnimation.JumpFalling);
			NSString landKey = KeyForAnimationType (CharacterAnimation.JumpLand);
			NSString idleKey = KeyForAnimationType (CharacterAnimation.Idle);

			CAAnimation jumpAnimation = LoadAndCacheAnimation ("art.scnassets/characters/explorer/jump_start", jumpKey);
			CAAnimation fallAnimation = LoadAndCacheAnimation ("art.scnassets/characters/explorer/jump_falling", fallingKey);
			CAAnimation landAnimation = LoadAndCacheAnimation ("art.scnassets/characters/explorer/jump_land", landKey);

			jumpAnimation.FadeInDuration = 0.15f;
			jumpAnimation.FadeOutDuration = 0.15f;
			fallAnimation.FadeInDuration = 0.15f;
			landAnimation.FadeInDuration = 0.15f;
			landAnimation.FadeOutDuration = 0.15f;

			jumpAnimation.RepeatCount = 0;
			fallAnimation.RepeatCount = 0;
			landAnimation.RepeatCount = 0;

			jumpForce = 7.0f;


			SCNAnimationEventHandler leaveGroundBlock = (animation, animatedObject, playingBackward) => {
				var jumpVelocity = new SCNVector3 (0f, jumpForce * 2.1f, 0f);
				velocity = SCNVector3.Add (velocity, jumpVelocity);
				Launching = false;
				InJumpAnimation = false;
			};

			SCNAnimationEventHandler pause = (animation, animatedObject, playingBackward) => {
				mainSkeleton.PauseAnimation (fallingKey);
			};

			jumpAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create (0.25f, leaveGroundBlock) };
			fallAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create (0.5f, pause) };

			// Animation Sequence is to Jump -> Fall -> Land -> Idle.
			ChainAnimation (jumpKey, fallingKey);
			ChainAnimation (landKey, idleKey);
		}
Example #59
0
		void AddMonkeyAtPosition (SCNVector3 worldPos, nfloat rotation)
		{
			if (Monkeys == null)
				Monkeys = new List<MonkeyCharacter> ();

			SCNNode palmTree = CreateMonkeyPalmTree ();
			palmTree.Position = worldPos;
			palmTree.Rotation = new SCNVector4 (0f, 1f, 0f, rotation);
			RootNode.AddChildNode (palmTree);

			MonkeyCharacter monkey = (MonkeyCharacter)palmTree.FindChildNode ("monkey", true);

			if (monkey != null)
				Monkeys.Add (monkey);
		}
		private SCNNode NodeWithText (string message, TextType type, int level)
		{
			var textNode = SCNNode.Create ();

			// Bullet
			if (type == TextType.Bullet) {
				if (level == 0)
					message = "• " + message;
				else {
					var bullet = SCNNode.Create ();
					bullet.Geometry = SCNPlane.Create (10.0f, 10.0f);
					bullet.Geometry.FirstMaterial.Diffuse.Contents = NSColor.FromDeviceRgba ((float)(160 / 255.0), (float)(182 / 255.0), (float)(203 / 255.0), 1);
					bullet.Position = new SCNVector3 (80, 30, 0);
					bullet.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant;
					bullet.Geometry.FirstMaterial.WritesToDepthBuffer = false;
					bullet.RenderingOrder = 1;
					textNode.AddChildNode (bullet);
					message = "\t\t\t\t" + message;
				}
			}

			// Text attributes
			var extrusion = ExtrusionDepthForTextType (type);
			var text = SCNText.Create (message, extrusion);
			textNode.Geometry = text;
			text.Flatness = TEXT_FLATNESS;
			text.ChamferRadius = (extrusion == 0 ? 0 : TEXT_CHAMFER);
			text.Font = FontForTextType (type, level);

			// Layout
			var layoutManager = new NSLayoutManager ();
			var leading = layoutManager.DefaultLineHeightForFont (text.Font);
			var descender = text.Font.Descender;
			int newlineCount = ((string[])(((string)text.String.ToString ()).Split ('\n'))).Length;
			textNode.Pivot = SCNMatrix4.CreateTranslation (0, -descender + newlineCount * leading, 0);

			if (type == TextType.Chapter) {
				var min = new SCNVector3 ();
				var max = new SCNVector3 ();
				textNode.GetBoundingBox (ref min, ref max);
				textNode.Position = new SCNVector3 (-11, (-min.Y + textNode.Pivot.M42) * TEXT_SCALE, 7);
				textNode.Scale = new SCNVector3 (TEXT_SCALE, TEXT_SCALE, TEXT_SCALE);
				textNode.Rotation = new SCNVector4 (0, 1, 0, (float)(Math.PI / 270.0));
			} else {
				textNode.Position = new SCNVector3 (-16, CurrentBaseline, 0);
				textNode.Scale = new SCNVector3 (TEXT_SCALE, TEXT_SCALE, TEXT_SCALE);
			}

			// Material
			if (type == TextType.Chapter) {
				var frontMaterial = SCNMaterial.Create ();
				var sideMaterial = SCNMaterial.Create ();

				frontMaterial.Emission.Contents = NSColor.DarkGray;
				frontMaterial.Diffuse.Contents = ColorForTextType (type, level);
				sideMaterial.Diffuse.Contents = NSColor.LightGray;
				textNode.Geometry.Materials = new SCNMaterial[] {
					frontMaterial,
					frontMaterial,
					sideMaterial,
					frontMaterial,
					frontMaterial
				};
			} else {
				// Full white emissive material (visible even when there is no light)
				textNode.Geometry.FirstMaterial = SCNMaterial.Create ();
				textNode.Geometry.FirstMaterial.Diffuse.Contents = NSColor.Black;
				textNode.Geometry.FirstMaterial.Emission.Contents = ColorForTextType (type, level);

				// Don't write to the depth buffer because we don't want the text to be reflected
				textNode.Geometry.FirstMaterial.WritesToDepthBuffer = false;

				// Render last
				textNode.RenderingOrder = 1;
			}

			return textNode;
		}