// Queries the root node for the expected nodes.
		internal GameNodes (SCNNode sceneRoot)
		{
			Object = sceneRoot.FindChildNode ("teapot", true);
			if (Object == null)
				throw new InvalidProgramException ();

			ObjectMaterial = Object.Geometry?.FirstMaterial;
			if (ObjectMaterial == null)
				throw new InvalidProgramException ();

			Confetti = sceneRoot.FindChildNode ("particles", true);
			if (Confetti == null)
				throw new InvalidProgramException ();

			Camera = sceneRoot.FindChildNode ("camera", true).Camera;
			if (Camera == null)
				throw new InvalidProgramException ();

			CountdownLabel = new SKLabelNode ("Chalkduster") {
				HorizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center
			};

			CongratulationsLabel = new SKLabelNode ("Chalkduster") {
				FontColor = GameColors.DefaultFont,
				Text = "You Win!",
				FontSize = 45
			};
		}
Esempio n. 2
0
        private void AddPlane(string nodeName, SCNNode portalNode, string imageName)
        {
            SCNNode child = portalNode.FindChildNode(nodeName, true);

            child.Geometry.FirstMaterial.Diffuse.Contents = new UIImage(imageName);
            child.RenderingOrder = 200;
        }
Esempio n. 3
0
        private void AddCar()
        {
            SCNNode pointOfView = sceneView.PointOfView;

            if (pointOfView == null)
            {
                return;
            }

            SCNMatrix4 transform               = pointOfView.Transform;
            SCNVector3 orientation             = new SCNVector3(-transform.M31, -transform.M32, -transform.M33);
            SCNVector3 location                = new SCNVector3(transform.M41, transform.M42, transform.M43);
            SCNVector3 currentPositionOfCamera = orientation + location;

            //TODO 4.2 Creando el coche
            SCNScene carScene = SCNScene.FromFile("art.scnassets/CarScene.scn");
            SCNNode  carNode  = carScene.RootNode.FindChildNode("frame", false);

            SCNNode frontLeftWheel = carNode.FindChildNode("frontLeftParent", false);
            SCNPhysicsVehicleWheel v_frontLeftWheel = SCNPhysicsVehicleWheel.Create(frontLeftWheel);

            SCNNode frontRightWheel = carNode.FindChildNode("frontRightParent", false);
            SCNPhysicsVehicleWheel v_frontRightWheel = SCNPhysicsVehicleWheel.Create(frontRightWheel);

            SCNNode rearLeftWheel = carNode.FindChildNode("rearLeftParent", false);
            SCNPhysicsVehicleWheel v_rearLeftWheel = SCNPhysicsVehicleWheel.Create(rearLeftWheel);

            SCNNode rearRightWheel = carNode.FindChildNode("rearRightParent", false);
            SCNPhysicsVehicleWheel v_rearRightWheel = SCNPhysicsVehicleWheel.Create(rearRightWheel);

            carNode.Position = currentPositionOfCamera;

            SCNPhysicsBody body = SCNPhysicsBody.CreateBody(SCNPhysicsBodyType.Dynamic, SCNPhysicsShape.Create(carNode, keepAsCompound: true));

            carNode.PhysicsBody = body;
            PhysicsVehicle      = SCNPhysicsVehicle.Create(carNode.PhysicsBody, new SCNPhysicsVehicleWheel[] { v_frontLeftWheel, v_frontRightWheel, v_rearLeftWheel, v_rearRightWheel });

            sceneView.Scene.PhysicsWorld.AddBehavior(PhysicsVehicle);
            sceneView.Scene.RootNode.AddChildNode(carNode);
        }
Esempio n. 4
0
        private void AddWalls(string nodeName, SCNNode portalNode, string imageName)
        {
            SCNNode child = portalNode.FindChildNode(nodeName, true);

            child.Geometry.FirstMaterial.Diffuse.Contents = new UIImage(imageName);
            child.RenderingOrder = 200;
            SCNNode mask = child.FindChildNode("mask", false);

            if (mask != null)
            {
                mask.Geometry.FirstMaterial.Transparency = 0.000001f;
            }
        }
        // Queries the root node for the expected nodes.
        internal GameNodes(SCNNode sceneRoot)
        {
            Object = sceneRoot.FindChildNode("teapot", true);
            if (Object == null)
            {
                throw new InvalidProgramException();
            }

            ObjectMaterial = Object.Geometry?.FirstMaterial;
            if (ObjectMaterial == null)
            {
                throw new InvalidProgramException();
            }

            Confetti = sceneRoot.FindChildNode("particles", true);
            if (Confetti == null)
            {
                throw new InvalidProgramException();
            }

            Camera = sceneRoot.FindChildNode("camera", true).Camera;
            if (Camera == null)
            {
                throw new InvalidProgramException();
            }

            CountdownLabel = new SKLabelNode("Chalkduster")
            {
                HorizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center
            };

            CongratulationsLabel = new SKLabelNode("Chalkduster")
            {
                FontColor = GameColors.DefaultFont,
                Text      = "You Win!",
                FontSize  = 45
            };
        }
Esempio n. 6
0
        void SetupAutomaticCameraPositions()
        {
            SCNNode root = GameView.Scene.RootNode;

            mainGround = root.FindChildNode("bloc05_collisionMesh_02", true);

            groundToCameraPosition = new Dictionary <SCNNode, SCNVector3> ();

            groundToCameraPosition.Add(root.FindChildNode("bloc04_collisionMesh_02", true), new SCNVector3(-0.188683f, 4.719608f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc03_collisionMesh", true), new SCNVector3(-0.435909f, 6.297167f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc07_collisionMesh", true), new SCNVector3(-0.333663f, 7.868592f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc08_collisionMesh", true), new SCNVector3(-0.575011f, 8.739003f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc06_collisionMesh", true), new SCNVector3(-1.095519f, 9.425292f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc05_collisionMesh_02", true), new SCNVector3(-0.072051f, 8.202264f, 0f));
            groundToCameraPosition.Add(root.FindChildNode("bloc05_collisionMesh_01", true), new SCNVector3(-0.072051f, 8.202264f, 0));
        }
Esempio n. 7
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);
            }
        }
Esempio n. 8
0
        public void CreateFlagSimulationFromNode(SCNNode node)
        {
            var meshData  = new ClothSimMetalNode(this.device, Width, Height);
            var clothNode = SCNNode.FromGeometry(meshData.Geometry);

            var flag = node.FindChildNode("flagStaticWave", true);

            if (flag != null)
            {
                var boundingBoxMax = SCNVector3.Zero;
                var boundingBoxMin = SCNVector3.Zero;
                flag.GetBoundingBox(ref boundingBoxMin, ref boundingBoxMax);
                var existingFlagBV = boundingBoxMax - boundingBoxMin;

                var rescaleToMatchSizeMatrix = SCNMatrix4.Scale(existingFlagBV.X / (float)Width);

                var rotation       = SCNQuaternion.FromAxisAngle(SCNVector3.UnitX, (float)Math.PI / 2f);
                var localTransform = rescaleToMatchSizeMatrix * SCNMatrix4.Rotate(rotation.ToQuaternion());

                localTransform.Transpose();
                var currentTransform = SCNMatrix4.Transpose(flag.Transform);
                var newTransform     = currentTransform * localTransform;

                clothNode.Transform = SCNMatrix4.Transpose(newTransform);// flag.Transform * localTransform;
                if (clothNode.Geometry != null)
                {
                    clothNode.Geometry.FirstMaterial = flag.Geometry?.FirstMaterial;
                    if (clothNode.Geometry.FirstMaterial != null)
                    {
                        clothNode.Geometry.FirstMaterial.DoubleSided = true;
                    }
                }

                flag.ParentNode.ReplaceChildNode(flag, clothNode);
                clothNode.Geometry.SetupPaintColorMask("flag_flagA");
                clothNode.SetPaintColors();
                clothNode.FixNormalMaps();

                this.clothData.Add(new ClothData(clothNode, meshData));
            }
        }
Esempio n. 9
0
        public static SCNNode LoadNodeWithName(string name, string path)
        {
            var loadingOptions = NSDictionary.FromObjectsAndKeys(new object[] { "YES", SCNSceneSourceLoading.AnimationImportPolicyPlayRepeatedly },
                                                                 new object[] { SCNSceneSourceLoading.ConvertToYUpKey, SCNSceneSourceLoading.AnimationImportPolicyKey });

            string   sceneName = new NSUrl(path).LastPathComponent.ToString();
            string   scenePath = new NSUrl(path).RemoveLastPathComponent().ToString();
            SCNScene scene     = SCNScene.FromFile(sceneName, scenePath, loadingOptions);
            SCNNode  node      = scene.RootNode;

            // Search for the node named "name"
            if (!string.IsNullOrEmpty(name))
            {
                node = node.FindChildNode(name, true);
            }
            else
            {
                node = node.ChildNodes [0];
            }

            return(node);
        }
Esempio n. 10
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;
		}
Esempio n. 11
0
        public Catapult(SCNNode node, SFXCoordinator sfxCoordinator, int identifier, Dictionary <string, object> gamedefs) : base(node, null, gamedefs, true, false)
        {
            this.Base             = node;
            this.audioEnvironment = sfxCoordinator.AudioEnvironment;

            // Base teamID and name off looking up teamA or teamB folder in the level parents
            // This won't work on the old levels.
            this.Team     = this.Base.GetTeam();
            this.TeamName = this.Team.GetDescription();

            // have team id established
            this.Base.SetPaintColors();

            // correct for the pivot point to place catapult flat on ground
            this.Base.Position = new SCNVector3(this.Base.Position.X, this.Base.Position.Y - 0.13f, this.Base.Position.Z);

            // highlight setup
            this.HighlightObject = node.FindChildNode("Highlight", true);
            if (this.HighlightObject != null)
            {
                this.HighlightObject = this.HighlightObject.FindNodeWithGeometry();
            }

            // hide the highlights on load
            if (this.HighlightObject != null)
            {
                this.HighlightObject.Hidden = true;
            }

            if (this.HighlightObject?.Geometry?.FirstMaterial?.Diffuse?.Contents is UIColor color)
            {
                this.highlightColor = color;
            }

            // they should only have y orientation, nothing in x or z
            // current scene files have the catapults with correct orientation, but the
            // eulerAngles are different - x and z are both π, y is within epsilon of 0
            // That's from bad decomposition of the matrix.  Need to restore the eulerAngles from the source.
            // Especially if we have animations tied to the euler angles.
            if (Math.Abs(node.EulerAngles.X) > 0.001f || Math.Abs(node.EulerAngles.Z) > 0.001f)
            {
                //Console.WriteLine("Catapult can only have y rotation applied");
            }

            // where to place the ball so it sits on the strap
            this.catapultStrap = this.Base.FindChildNode("catapultStrap", true);
            if (this.catapultStrap == null)
            {
                throw new Exception("No node with name catapultStrap");
            }

            // this only rotates, and represents the center of the catapult through which to fire
            this.pullOrigin = this.Base.FindChildNode("pullOrigin", true);
            if (this.pullOrigin == null)
            {
                throw new Exception("No node with name pullOrigin");
            }

            // This is a rope simulation meant for a fixed catapult, the catapult rotates.
            this.rope = new CatapultRope(node);

            // attach ball to the inactive strap, search for ballOriginInactiveBelow
            this.ballOriginInactiveBelow = this.Base.FindChildNode("ballOriginInactiveBelow", true);
            if (this.ballOriginInactiveBelow == null)
            {
                throw new Exception("No node with name ballOriginInactiveBelow");
            }

            this.ballOriginInactiveAbove = this.Base.FindChildNode("ballOriginInactiveAbove", true);
            if (this.ballOriginInactiveAbove == null)
            {
                throw new Exception("No node with name ballOriginInactiveAbove");
            }

            // ball will be made visible and drop once projectile is set and cooldown exceeded
            this.StrapVisible = StrapVisible.Visible;

            this.CatapultId = identifier;

            this.Base.SetValueForKey(NSObject.FromObject(this.CatapultId), new NSString(Catapult.CollisionKey));

            this.AudioPlayer = new CatapultAudioSampler(this.Base, sfxCoordinator);

            // use the teamID to set the collision category mask
            if (this.PhysicsNode?.PhysicsBody != null)
            {
                if (this.Team == Team.TeamA)
                {
                    this.PhysicsNode.PhysicsBody.CategoryBitMask = (int)CollisionMask.CatapultTeamA;
                    var collisionBitMask = (CollisionMask)(int)this.PhysicsNode.PhysicsBody.CollisionBitMask;
                    this.PhysicsNode.PhysicsBody.CollisionBitMask = (nuint)(int)(collisionBitMask | CollisionMask.CatapultTeamB);
                }
                else if (this.Team == Team.TeamB)
                {
                    this.PhysicsNode.PhysicsBody.CategoryBitMask = (int)CollisionMask.CatapultTeamB;
                    var collisionBitMask = (CollisionMask)(int)this.PhysicsNode.PhysicsBody.CollisionBitMask;
                    this.PhysicsNode.PhysicsBody.CollisionBitMask = (nuint)(int)(collisionBitMask | CollisionMask.CatapultTeamA);
                }
            }
        }
Esempio n. 12
0
        public Character()
        {
            for (int i = 0; i < StepsSoundCount; i++)
            {
                steps [i, (int)FloorMaterial.Grass]        = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_grass_0{0}.mp3", i));
                steps [i, (int)FloorMaterial.Grass].Volume = 0.5f;
                steps [i, (int)FloorMaterial.Rock]         = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_rock_0{0}.mp3", i));
                if (i < StepsInWaterSoundCount)
                {
                    steps [i, (int)FloorMaterial.Water] = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_splash_0{0}.mp3", i));
                    steps [i, (int)FloorMaterial.Water].Load();
                }
                else
                {
                    steps [i, (int)FloorMaterial.Water] = steps [i % StepsInWaterSoundCount, (int)FloorMaterial.Water];
                }

                steps [i, (int)FloorMaterial.Rock].Load();
                steps [i, (int)FloorMaterial.Grass].Load();

                // Load the character.
                SCNScene characterScene        = SCNScene.FromFile("game.scnassets/panda.scn");
                SCNNode  characterTopLevelNode = characterScene.RootNode.ChildNodes [0];

                Node = SCNNode.Create();
                Node.AddChildNode(characterTopLevelNode);

                // Configure the "idle" animation to repeat forever
                foreach (var childNode in characterTopLevelNode.ChildNodes)
                {
                    foreach (var key in childNode.GetAnimationKeys())
                    {
                        CAAnimation animation = childNode.GetAnimation(key);
                        animation.UsesSceneTimeBase = false;
                        animation.RepeatCount       = float.PositiveInfinity;
                        childNode.AddAnimation(animation, key);
                    }
                }

                // retrieve some particle systems and save their birth rate
                fireEmitter   = characterTopLevelNode.FindChildNode("fire", true);
                fireBirthRate = fireEmitter.ParticleSystems [0].BirthRate;
                fireEmitter.ParticleSystems [0].BirthRate = 0;
                fireEmitter.Hidden = false;

                smokeEmitter   = characterTopLevelNode.FindChildNode("smoke", true);
                smokeBirthRate = smokeEmitter.ParticleSystems [0].BirthRate;
                smokeEmitter.ParticleSystems [0].BirthRate = 0;
                smokeEmitter.Hidden = false;

                whiteSmokeEmitter   = characterTopLevelNode.FindChildNode("whiteSmoke", true);
                whiteSmokeBirthRate = whiteSmokeEmitter.ParticleSystems [0].BirthRate;
                whiteSmokeEmitter.ParticleSystems [0].BirthRate = 0;
                whiteSmokeEmitter.Hidden = false;

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

                Node.GetBoundingBox(ref min, ref max);

                float radius = (max.X - min.X) * .4f;
                float height = (max.Y - min.Y);

                // Create a kinematic with capsule.
                SCNNode colliderNode = SCNNode.Create();
                colliderNode.Name        = "collider";
                colliderNode.Position    = new SCNVector3(0f, height * .51f, 0f);
                colliderNode.PhysicsBody = SCNPhysicsBody.CreateBody(
                    SCNPhysicsBodyType.Kinematic,
                    SCNPhysicsShape.Create(SCNCapsule.Create(radius, height))
                    );

                // We want contact notifications with the collectables, enemies and walls.
                colliderNode.PhysicsBody.ContactTestBitMask = (nuint)(int)(Bitmask.SuperCollectable | Bitmask.Collectable | Bitmask.Collision | Bitmask.Enemy);
                Node.AddChildNode(colliderNode);

                walkAnimation = LoadAnimationFromSceneNamed("game.scnassets/walk.scn");
                walkAnimation.UsesSceneTimeBase = false;
                walkAnimation.FadeInDuration    = .3f;
                walkAnimation.FadeOutDuration   = .3f;
                walkAnimation.RepeatCount       = float.PositiveInfinity;
                walkAnimation.Speed             = CharacterSpeedFactor;

                // Play foot steps at specific times in the animation
                walkAnimation.AnimationEvents = new [] {
                    SCNAnimationEvent.Create(.1f, (animation, animatedObject, playingBackward) => PlayFootStep()),
                    SCNAnimationEvent.Create(.6f, (animation, animatedObject, playingBackward) => PlayFootStep())
                };
            }
        }
Esempio n. 13
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);
        }