public static void CopyGeometryAndMaterials(this SCNNode node) { // this copies the material, but not the lod if (node.Geometry != null) { node.UpdateGeometry(node.Geometry.CopyGeometryAndMaterials()); if (node.Geometry.LevelsOfDetail != null) { var lodsNew = new List <SCNLevelOfDetail>(); foreach (var lod in node.Geometry.LevelsOfDetail) { if (lod.Geometry != null) { var lodGeometryCopy = lod.Geometry.CopyGeometryAndMaterials(); lodsNew.Add(SCNLevelOfDetail.CreateWithScreenSpaceRadius(lodGeometryCopy, lod.ScreenSpaceRadius)); } } node.Geometry.LevelsOfDetail = lodsNew.ToArray(); } } foreach (var child in node.ChildNodes) { child.CopyGeometryAndMaterials(); } }
// We load all nodes as references which means they share the same // geometry and materials. For team colors, we need to set geometry overrides // and so need unique geometry with shadable overrides for each node created. public static void CopyGeometryForPaintColors(this SCNNode node) { // neutral blocks also need to be tinted if (node.Geometry != null && !string.IsNullOrEmpty(node.Name)) { // does this copy the LOD as well ? if (node.Geometry.Copy() is SCNGeometry geometryCopy) { SetupPaintColorMask(geometryCopy, node.Name); // this may already done by the copy() above, but just be safe if (node.Geometry.LevelsOfDetail != null) { var lodsNew = new List <SCNLevelOfDetail>(); foreach (var lod in node.Geometry.LevelsOfDetail) { if (lod.Geometry != null) { if (lod.ScreenSpaceRadius > 0) { if (lod.Geometry.Copy() is SCNGeometry lodGeometryCopy) { lodGeometryCopy.SetupPaintColorMask(node.Name); lodsNew.Add(SCNLevelOfDetail.CreateWithScreenSpaceRadius(lodGeometryCopy, lod.ScreenSpaceRadius)); } } else { if (lod.Geometry.Copy() is SCNGeometry lodGeometryCopy) { SetupPaintColorMask(lodGeometryCopy, node.Name); lodsNew.Add(SCNLevelOfDetail.CreateWithWorldSpaceDistance(lodGeometryCopy, lod.WorldSpaceDistance)); } } } } geometryCopy.LevelsOfDetail = lodsNew.ToArray(); } // set the new geometry and LOD node.UpdateGeometry(geometryCopy); } } foreach (var child in node.ChildNodes) { child.CopyGeometryForPaintColors(); } }
public static void FixLevelsOfDetail(this SCNNode node, float screenSpaceRadius, bool showLOD) { // find the boundingRadius of the node, and scale to that var lods = node?.Geometry?.LevelsOfDetail; if (lods != null) { var lodsNew = new List <SCNLevelOfDetail>(); foreach (var lod in lods) { if (lod.Geometry != null) { if (showLOD) { // visualize the lod var lodGeometryCopy = lod.Geometry.CopyGeometryAndMaterials(); // override the emission // this is not removed currently foreach (var material in lodGeometryCopy.Materials) { material.Emission.Contents = UIColor.Red; } lodsNew.Add(SCNLevelOfDetail.CreateWithScreenSpaceRadius(lodGeometryCopy, screenSpaceRadius)); } else { lodsNew.Add(SCNLevelOfDetail.CreateWithScreenSpaceRadius(lod.Geometry, screenSpaceRadius)); } } } node.Geometry.LevelsOfDetail = lodsNew.ToArray(); } foreach (var child in node.ChildNodes) { child.FixLevelsOfDetail(screenSpaceRadius, showLOD); } }
public override void PresentStep (int index, PresentationViewController presentationViewController) { switch (index) { case 0: // Hide everything (in case the user went backward) for (var i = 1; i < 4; i++) { var teapot = GroundNode.FindChildNode ("Teapot" + i, true); teapot.Opacity = 0.0f; } break; case 1: // Move the camera and adjust the clipping plane SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 3; presentationViewController.CameraNode.Position = new SCNVector3 (0, 0, 200); presentationViewController.CameraNode.Camera.ZFar = 500.0f; SCNTransaction.Commit (); break; case 2: // Revert to original position SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 1; presentationViewController.CameraNode.Position = new SCNVector3 (0, 0, 0); presentationViewController.CameraNode.Camera.ZFar = 100.0f; SCNTransaction.Commit (); break; case 3: var numberNodes = new SCNNode[] { AddNumberNode ("64k", -17), AddNumberNode ("6k", -9), AddNumberNode ("3k", -1), AddNumberNode ("1k", 6.5f), AddNumberNode ("256", 14) }; SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 1; // Move the camera and the text presentationViewController.CameraHandle.Position = new SCNVector3 (presentationViewController.CameraHandle.Position.X, presentationViewController.CameraHandle.Position.Y + 6, presentationViewController.CameraHandle.Position.Z); TextManager.TextNode.Position = new SCNVector3 (TextManager.TextNode.Position.X, TextManager.TextNode.Position.Y + 6, TextManager.TextNode.Position.Z); // Show the remaining resolutions for (var i = 0; i < 5; i++) { var numberNode = numberNodes [i]; numberNode.Position = new SCNVector3 (numberNode.Position.X, 7, -5); var teapot = GroundNode.FindChildNode ("Teapot" + i, true); teapot.Opacity = 1.0f; teapot.Rotation = new SCNVector4 (0, 0, 1, (float)(Math.PI / 4)); teapot.Position = new SCNVector3 ((i - 2) * 8, 5, teapot.Position.Z); } SCNTransaction.Commit (); break; case 4: presentationViewController.ShowsNewInSceneKitBadge (true); // Remove the numbers RemoveNumberNodes (); SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 1; // Add some text and code TextManager.SetSubtitle ("SCNLevelOfDetail"); TextManager.AddCode ("#var lod1 = SCNLevelOfDetail.#CreateWithWorldSpaceDistance# (aGeometry, aDistance); \n" + "geometry.#LevelsOfDetail# = new SCNLevelOfDetail { lod1, lod2, ..., lodn };#"); // Animation the merge for (int i = 0; i < 5; i++) { var teapot = GroundNode.FindChildNode ("Teapot" + i, true); teapot.Opacity = i == 0 ? 1.0f : 0.0f; teapot.Rotation = new SCNVector4 (0, 0, 1, 0); teapot.Position = new SCNVector3 (0, -5, teapot.Position.Z); } // Move the camera and the text presentationViewController.CameraHandle.Position = new SCNVector3 (presentationViewController.CameraHandle.Position.X, presentationViewController.CameraHandle.Position.Y - 3, presentationViewController.CameraHandle.Position.Z); TextManager.TextNode.Position = new SCNVector3 (TextManager.TextNode.Position.X, TextManager.TextNode.Position.Y - 3, TextManager.TextNode.Position.Z); SCNTransaction.Commit (); break; case 5: presentationViewController.ShowsNewInSceneKitBadge (false); SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 3; // Change the lighting to remove the front light and rise the main light presentationViewController.UpdateLightingWithIntensities (new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.3f }); presentationViewController.RiseMainLight (true); // Remove some text TextManager.FadeOutText (SlideTextManager.TextType.Title); TextManager.FadeOutText (SlideTextManager.TextType.Subtitle); TextManager.FadeOutText (SlideTextManager.TextType.Code); SCNTransaction.Commit (); // Retrieve the main teapot var maintTeapot = GroundNode.FindChildNode ("Teapot0", true); // The distances to use for each LOD var distances = new float [4] { 30, 50, 90, 150 }; // An array of SCNLevelOfDetail instances that we will build var levelsOfDetail = new SCNLevelOfDetail[4]; for (var i = 1; i < 5; i++) { var teapotNode = GroundNode.FindChildNode ("Teapot" + i, true); var teapot = teapotNode.Geometry; // Unshare the material because we will highlight the different levels of detail with different colors in the next step teapot.FirstMaterial = (SCNMaterial)teapot.FirstMaterial.Copy (); // Build the SCNLevelOfDetail instance var levelOfDetail = SCNLevelOfDetail.CreateWithWorldSpaceDistance (teapot, distances [i - 1]); levelsOfDetail [i - 1] = levelOfDetail; } maintTeapot.Geometry.LevelsOfDetail = levelsOfDetail; // Duplicate and move the teapots var startTime = CAAnimation.CurrentMediaTime (); var delay = 0.2; var rowCount = 9; var columnCount = 12; SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 0; // Change the far clipping plane to be able to see far away presentationViewController.CameraNode.Camera.ZFar = 1000.0; for (var j = 0; j < columnCount; j++) { for (var i = 0; i < rowCount; i++) { // Clone var clone = maintTeapot.Clone (); maintTeapot.ParentNode.AddChildNode (clone); // Animate var animation = CABasicAnimation.FromKeyPath ("position"); animation.Additive = true; animation.Duration = 1.0; animation.To = NSValue.FromVector (new SCNVector3 ((i - rowCount / 2.0f) * 12.0f, 5 + (columnCount - j) * 15.0f, 0)); animation.From = NSValue.FromVector (new SCNVector3 (0, 0, 0)); animation.BeginTime = startTime + delay; // desynchronize // Freeze at the end of the animation animation.RemovedOnCompletion = false; animation.FillMode = CAFillMode.Forwards; clone.AddAnimation (animation, new NSString ("cloneAnimation")); // Animate the hidden property to automatically show the node when the position animation starts animation = CABasicAnimation.FromKeyPath ("hidden"); animation.Duration = delay + 0.01; animation.FillMode = CAFillMode.Both; animation.From = new NSNumber (1); animation.To = new NSNumber (0); clone.AddAnimation (animation, new NSString ("cloneAnimation2")); delay += 0.05; } } SCNTransaction.Commit (); // Animate the camera while we duplicate the nodes SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 1.0 + rowCount * columnCount * 0.05; var position = presentationViewController.CameraHandle.Position; presentationViewController.CameraHandle.Position = new SCNVector3 (position.X, position.Y + 5, position.Z); presentationViewController.CameraPitch.Rotation = new SCNVector4 (1, 0, 0, presentationViewController.CameraPitch.Rotation.W - ((float)(Math.PI / 4) * 0.1f)); SCNTransaction.Commit (); break; case 6: // Highlight the levels of detail with colors var teapotChild = GroundNode.FindChildNode ("Teapot0", true); var colors = new NSColor[] { NSColor.Red, NSColor.Orange, NSColor.Yellow, NSColor.Green }; SCNTransaction.Begin (); SCNTransaction.AnimationDuration = 1; for (var i = 0; i < 4; i++) { var levelOfDetail = teapotChild.Geometry.LevelsOfDetail [i]; levelOfDetail.Geometry.FirstMaterial.Multiply.Contents = colors [i]; } SCNTransaction.Commit (); break; } }
public override void PresentStep(int index, PresentationViewController presentationViewController) { switch (index) { case 0: // Hide everything (in case the user went backward) for (var i = 1; i < 4; i++) { var teapot = GroundNode.FindChildNode("Teapot" + i, true); teapot.Opacity = 0.0f; } break; case 1: // Move the camera and adjust the clipping plane SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 3; presentationViewController.CameraNode.Position = new SCNVector3(0, 0, 200); presentationViewController.CameraNode.Camera.ZFar = 500.0f; SCNTransaction.Commit(); break; case 2: // Revert to original position SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; presentationViewController.CameraNode.Position = new SCNVector3(0, 0, 0); presentationViewController.CameraNode.Camera.ZFar = 100.0f; SCNTransaction.Commit(); break; case 3: var numberNodes = new SCNNode[] { AddNumberNode("64k", -17), AddNumberNode("6k", -9), AddNumberNode("3k", -1), AddNumberNode("1k", 6.5f), AddNumberNode("256", 14) }; SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Move the camera and the text presentationViewController.CameraHandle.Position = new SCNVector3(presentationViewController.CameraHandle.Position.X, presentationViewController.CameraHandle.Position.Y + 6, presentationViewController.CameraHandle.Position.Z); TextManager.TextNode.Position = new SCNVector3(TextManager.TextNode.Position.X, TextManager.TextNode.Position.Y + 6, TextManager.TextNode.Position.Z); // Show the remaining resolutions for (var i = 0; i < 5; i++) { var numberNode = numberNodes [i]; numberNode.Position = new SCNVector3(numberNode.Position.X, 7, -5); var teapot = GroundNode.FindChildNode("Teapot" + i, true); teapot.Opacity = 1.0f; teapot.Rotation = new SCNVector4(0, 0, 1, (float)(Math.PI / 4)); teapot.Position = new SCNVector3((i - 2) * 8, 5, teapot.Position.Z); } SCNTransaction.Commit(); break; case 4: presentationViewController.ShowsNewInSceneKitBadge(true); // Remove the numbers RemoveNumberNodes(); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Add some text and code TextManager.SetSubtitle("SCNLevelOfDetail"); TextManager.AddCode("#var lod1 = SCNLevelOfDetail.#CreateWithWorldSpaceDistance# (aGeometry, aDistance); \n" + "geometry.#LevelsOfDetail# = new SCNLevelOfDetail { lod1, lod2, ..., lodn };#"); // Animation the merge for (int i = 0; i < 5; i++) { var teapot = GroundNode.FindChildNode("Teapot" + i, true); teapot.Opacity = i == 0 ? 1.0f : 0.0f; teapot.Rotation = new SCNVector4(0, 0, 1, 0); teapot.Position = new SCNVector3(0, -5, teapot.Position.Z); } // Move the camera and the text presentationViewController.CameraHandle.Position = new SCNVector3(presentationViewController.CameraHandle.Position.X, presentationViewController.CameraHandle.Position.Y - 3, presentationViewController.CameraHandle.Position.Z); TextManager.TextNode.Position = new SCNVector3(TextManager.TextNode.Position.X, TextManager.TextNode.Position.Y - 3, TextManager.TextNode.Position.Z); SCNTransaction.Commit(); break; case 5: presentationViewController.ShowsNewInSceneKitBadge(false); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 3; // Change the lighting to remove the front light and rise the main light presentationViewController.UpdateLightingWithIntensities(new float[] { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.3f }); presentationViewController.RiseMainLight(true); // Remove some text TextManager.FadeOutText(SlideTextManager.TextType.Title); TextManager.FadeOutText(SlideTextManager.TextType.Subtitle); TextManager.FadeOutText(SlideTextManager.TextType.Code); SCNTransaction.Commit(); // Retrieve the main teapot var maintTeapot = GroundNode.FindChildNode("Teapot0", true); // The distances to use for each LOD var distances = new float[] { 30, 50, 90, 150 }; // An array of SCNLevelOfDetail instances that we will build var levelsOfDetail = new SCNLevelOfDetail[4]; for (var i = 1; i < 5; i++) { var teapotNode = GroundNode.FindChildNode("Teapot" + i, true); var teapot = teapotNode.Geometry; // Unshare the material because we will highlight the different levels of detail with different colors in the next step teapot.FirstMaterial = (SCNMaterial)teapot.FirstMaterial.Copy(); // Build the SCNLevelOfDetail instance var levelOfDetail = SCNLevelOfDetail.CreateWithWorldSpaceDistance(teapot, distances [i - 1]); levelsOfDetail [i - 1] = levelOfDetail; } maintTeapot.Geometry.LevelsOfDetail = levelsOfDetail; // Duplicate and move the teapots var startTime = CAAnimation.CurrentMediaTime(); var delay = 0.2; var rowCount = 9; var columnCount = 12; SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0; // Change the far clipping plane to be able to see far away presentationViewController.CameraNode.Camera.ZFar = 1000.0; for (var j = 0; j < columnCount; j++) { for (var i = 0; i < rowCount; i++) { // Clone var clone = maintTeapot.Clone(); maintTeapot.ParentNode.AddChildNode(clone); // Animate var animation = CABasicAnimation.FromKeyPath("position"); animation.Additive = true; animation.Duration = 1.0; animation.To = NSValue.FromVector(new SCNVector3((i - rowCount / 2.0f) * 12.0f, 5 + (columnCount - j) * 15.0f, 0)); animation.From = NSValue.FromVector(new SCNVector3(0, 0, 0)); animation.BeginTime = startTime + delay; // desynchronize // Freeze at the end of the animation animation.RemovedOnCompletion = false; animation.FillMode = CAFillMode.Forwards; clone.AddAnimation(animation, new NSString("cloneAnimation")); // Animate the hidden property to automatically show the node when the position animation starts animation = CABasicAnimation.FromKeyPath("hidden"); animation.Duration = delay + 0.01; animation.FillMode = CAFillMode.Both; animation.From = new NSNumber(1); animation.To = new NSNumber(0); clone.AddAnimation(animation, new NSString("cloneAnimation2")); delay += 0.05; } } SCNTransaction.Commit(); // Animate the camera while we duplicate the nodes SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0 + rowCount * columnCount * 0.05; var position = presentationViewController.CameraHandle.Position; presentationViewController.CameraHandle.Position = new SCNVector3(position.X, position.Y + 5, position.Z); presentationViewController.CameraPitch.Rotation = new SCNVector4(1, 0, 0, presentationViewController.CameraPitch.Rotation.W - ((float)(Math.PI / 4) * 0.1f)); SCNTransaction.Commit(); break; case 6: // Highlight the levels of detail with colors var teapotChild = GroundNode.FindChildNode("Teapot0", true); var colors = new NSColor[] { NSColor.Red, NSColor.Orange, NSColor.Yellow, NSColor.Green }; SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; for (var i = 0; i < 4; i++) { var levelOfDetail = teapotChild.Geometry.LevelsOfDetail [i]; levelOfDetail.Geometry.FirstMaterial.Multiply.Contents = colors [i]; } SCNTransaction.Commit(); break; } }
public CatapultInteraction(IInteractionDelegate @delegate) { this.Delegate = @delegate; this.dummyBall = SCNNodeExtensions.LoadSCNAsset("projectiles_ball_8k"); // stri the geometry out of a low-lod model, assume it doesn't have an lod if (this.dummyBall.Geometry != null) { var lod = SCNNodeExtensions.LoadSCNAsset("projectiles_ball"); if (lod.Geometry != null) { lod.Geometry.Materials = this.dummyBall.Geometry.Materials; // this radius will be replaced by fixLevelsOfDetail // when the level is placed this.dummyBall.Geometry.LevelsOfDetail = new SCNLevelOfDetail[] { SCNLevelOfDetail.CreateWithScreenSpaceRadius(lod.Geometry, 100f) }; } } }