public NSDictionary?ToDictionary() { var n = 0; if (ShapeType.HasValue) { n++; } if (KeepAsCompound.HasValue) { n++; } if (Scale.HasValue) { n++; } if (n == 0) { return(null); } var i = 0; var keys = new NSString [n]; var values = new NSObject [n]; if (ShapeType.HasValue) { keys [i] = SCNPhysicsShapeOptionsKeys.Type; switch (ShapeType.Value) { case SCNPhysicsShapeType.BoundingBox: values [i] = SCNPhysicsShapeOptionsTypes.BoundingBox; break; case SCNPhysicsShapeType.ConcavePolyhedron: values [i] = SCNPhysicsShapeOptionsTypes.ConcavePolyhedron; break; case SCNPhysicsShapeType.ConvexHull: default: values [i] = SCNPhysicsShapeOptionsTypes.ConvexHull; break; } } if (KeepAsCompound.HasValue) { keys [i] = SCNPhysicsShapeOptionsKeys.KeepAsCompound; values [i] = new NSNumber(KeepAsCompound.Value); } if (Scale.HasValue) { keys [i] = SCNPhysicsShapeOptionsKeys.Scale; values [i] = NSValue.FromVector(Scale.Value); } return(NSDictionary.FromObjectsAndKeys(values, keys)); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { // Animate by default SCNTransaction.Begin(); switch (index) { case 0: // Disable animations for first step SCNTransaction.AnimationDuration = 0; // Initially dim the torus AnimatedNode.Opacity = 0.0f; TextManager.HighlightCodeChunks(null); break; case 1: TextManager.HighlightCodeChunks(new int[] { 0 }); break; case 2: TextManager.HighlightCodeChunks(new int[] { 1, 2, 3 }); break; case 3: TextManager.HighlightCodeChunks(new int[] { 4, 5 }); break; case 4: SCNTransaction.AnimationDuration = 1.0f; // Show the torus AnimatedNode.Opacity = 1.0f; // Animate explicitly var animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 2.0f; animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); animation.RepeatCount = float.MaxValue; AnimatedNode.AddAnimation(animation, new NSString("myAnimation")); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Dim the text TextManager.TextNode.Opacity = 0.75f; presentationViewController.CameraHandle.Position = presentationViewController.CameraHandle.ConvertPositionToNode(new SCNVector3(9, 8, 15), presentationViewController.CameraHandle.ParentNode); presentationViewController.CameraPitch.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 10)); SCNTransaction.Commit(); break; } SCNTransaction.Commit(); }
public override void SetupSlide(PresentationViewController presentationViewController) { // Create a node for Earth and another node to display clouds // Use the 'pivot' property to tilt Earth because we don't want to see the north pole. EarthNode = SCNNode.Create(); EarthNode.Pivot = SCNMatrix4.CreateFromAxisAngle(new SCNVector3(1, 0, 0), (float)(Math.PI * 0.1f)); EarthNode.Position = new SCNVector3(6, 7.2f, -2); EarthNode.Geometry = SCNSphere.Create(7.2f); CloudsNode = SCNNode.Create(); CloudsNode.Geometry = SCNSphere.Create(7.9f); GroundNode.AddChildNode(EarthNode); EarthNode.AddChildNode(CloudsNode); // Initially hide everything EarthNode.Opacity = 0.0f; CloudsNode.Opacity = 0.0f; EarthNode.Geometry.FirstMaterial.Ambient.Intensity = 0; EarthNode.Geometry.FirstMaterial.Normal.Intensity = 0; EarthNode.Geometry.FirstMaterial.Reflective.Intensity = 0; EarthNode.Geometry.FirstMaterial.Emission.Intensity = 0; // Use a shader modifier to display an environment map independently of the lighting model used EarthNode.Geometry.ShaderModifiers = new SCNShaderModifiers { EntryPointFragment = " _output.color.rgb -= _surface.reflective.rgb * _lightingContribution.diffuse;" + "_output.color.rgb += _surface.reflective.rgb;" }; // Add animations var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 40.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); EarthNode.AddAnimation(rotationAnimation, new NSString("earthNodeAnimation")); rotationAnimation.Duration = 100.0f; CloudsNode.AddAnimation(rotationAnimation, new NSString("cloudsNodeAnimation")); }
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)); }
public static SCNPhysicsShape Create(SCNPhysicsShape [] shapes, SCNVector3 [] transforms) { if (shapes == null) { ObjCRuntime.ThrowHelper.ThrowArgumentException(nameof(shapes)); } if (transforms == null) { ObjCRuntime.ThrowHelper.ThrowArgumentException(nameof(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)); }
// Updates the secondary image of the floor if needed private void UpdateFloorImage(NSImage image, Slide slide) { // We don't want to animate if we replace the secondary image by a new one // Otherwise we want to translate the secondary image to the new location var disableAction = false; if (FloorImage != image) { FloorImage = image; disableAction = true; if (image != null) { // Set a new material property with this image to the "floorMap" custom property of the floor var property = SCNMaterialProperty.Create(image); property.WrapS = SCNWrapMode.Repeat; property.WrapT = SCNWrapMode.Repeat; property.MipFilter = SCNFilterMode.Linear; Floor.FirstMaterial.SetValueForKey(property, new NSString("floorMap")); } } if (image != null) { var slidePosition = slide.GroundNode.ConvertPositionToNode(new SCNVector3(0, 0, 10), null); if (disableAction) { SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0; Floor.FirstMaterial.SetValueForKey(NSValue.FromVector(slidePosition), new NSString("floorImageNamePosition")); SCNTransaction.Commit(); } else { Floor.FirstMaterial.SetValueForKey(NSValue.FromVector(slidePosition), new NSString("floorImageNamePosition")); } } }
public override void SetupSlide(PresentationViewController presentationViewController) { TextManager.SetTitle("Extending Scene Kit with OpenGL"); TextManager.SetSubtitle("Material custom program"); TextManager.AddBulletAtLevel("Custom GLSL code per material", 0); TextManager.AddBulletAtLevel("Overrides Scene Kit’s rendering", 0); TextManager.AddBulletAtLevel("Geometry attributes are provided", 0); TextManager.AddBulletAtLevel("Transform uniforms are also provided", 0); // Add a torus and animate it TorusNode = Utils.SCAddChildNode(GroundNode, "torus", "Scenes/torus/torus", 10); TorusNode.Position = new SCNVector3(8, 8, 4); TorusNode.Name = "object"; var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 10.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); TorusNode.AddAnimation(rotationAnimation, new NSString("torusRotation")); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { switch (index) { case 0: // Set the slide's title and subtitle and add some text TextManager.SetTitle("Constraints"); TextManager.SetSubtitle("SCNConstraint"); TextManager.AddBulletAtLevel("Applied sequentially at render time", 0); TextManager.AddBulletAtLevel("Only affect presentation values", 0); TextManager.AddCode("#aNode.#Constraints# = new SCNConstraint[] { aConstraint, anotherConstraint, ... };#"); // Tweak the near clipping plane of the spot light to get a precise shadow map presentationViewController.SpotLight.Light.SetAttribute(new NSNumber(10), SCNLightAttribute.ShadowNearClippingKey); break; case 1: // Remove previous text TextManager.FlipOutText(SlideTextManager.TextType.Subtitle); TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.FlipOutText(SlideTextManager.TextType.Code); // Add new text TextManager.SetSubtitle("SCNLookAtConstraint"); TextManager.AddBulletAtLevel("Makes a node to look at another node", 0); TextManager.AddCode("#nodeA.Constraints = new SCNConstraint[] { #SCNLookAtConstraint.Create# (nodeB) };#"); TextManager.FlipInText(SlideTextManager.TextType.Subtitle); TextManager.FlipInText(SlideTextManager.TextType.Bullet); TextManager.FlipInText(SlideTextManager.TextType.Code); break; case 2: // Setup the scene SetupLookAtScene(); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Dim the text and move back a little bit TextManager.TextNode.Opacity = 0.5f; presentationViewController.CameraHandle.Position = presentationViewController.CameraNode.ConvertPositionToNode(new SCNVector3(0, 0, 5.0f), presentationViewController.CameraHandle.ParentNode); SCNTransaction.Commit(); break; case 3: // Add constraints to the arrows var container = ContentNode.FindChildNode("arrowContainer", true); // "Look at" constraint var constraint = SCNLookAtConstraint.Create(BallNode); var i = 0; foreach (var arrow in container.ChildNodes) { var delayInSeconds = 0.1 * i++; var popTime = new DispatchTime(DispatchTime.Now, (long)(delayInSeconds * Utils.NSEC_PER_SEC)); DispatchQueue.MainQueue.DispatchAfter(popTime, () => { SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Animate to the result of applying the constraint ((SCNNode)arrow.ChildNodes [0]).Rotation = new SCNVector4(0, 1, 0, (float)(Math.PI / 2)); arrow.Constraints = new SCNConstraint[] { constraint }; SCNTransaction.Commit(); }); } break; case 4: // Create a keyframe animation to move the ball var animation = CAKeyFrameAnimation.FromKeyPath("position"); animation.KeyTimes = new NSNumber[] { 0.0f, (1.0f / 8.0f), (2.0f / 8.0f), (3.0f / 8.0f), (4.0f / 8.0f), (5.0f / 8.0f), (6.0f / 8.0f), (7.0f / 8.0f), 1.0f }; animation.Values = new NSObject[] { NSValue.FromVector(new SCNVector3(0, 0.0f, 0)), NSValue.FromVector(new SCNVector3(20.0f, 0.0f, 20.0f)), NSValue.FromVector(new SCNVector3(40.0f, 0.0f, 0)), NSValue.FromVector(new SCNVector3(20.0f, 0.0f, -20.0f)), NSValue.FromVector(new SCNVector3(0, 0.0f, 0)), NSValue.FromVector(new SCNVector3(-20.0f, 0.0f, 20.0f)), NSValue.FromVector(new SCNVector3(-40.0f, 0.0f, 0)), NSValue.FromVector(new SCNVector3(-20.0f, 0.0f, -20.0f)), NSValue.FromVector(new SCNVector3(0, 0.0f, 0)) }; animation.CalculationMode = CAAnimation.AnimationCubicPaced; // smooth the movement between keyframes animation.RepeatCount = float.MaxValue; animation.Duration = 10.0f; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.Linear); BallNode.AddAnimation(animation, new NSString("ballNodeAnimation")); // Rotate the ball to give the illusion of a rolling ball // We need two animations to do that: // - one rotation to orient the ball in the right direction // - one rotation to spin the ball animation = CAKeyFrameAnimation.FromKeyPath("rotation"); animation.KeyTimes = new NSNumber[] { 0.0f, (0.7f / 8.0f), (1.0f / 8.0f), (2.0f / 8.0f), (3.0f / 8.0f), (3.3f / 8.0f), (4.7f / 8.0f), (5.0f / 8.0f), (6.0f / 8.0f), (7.0f / 8.0f), (7.3f / 8.0f), 1.0f }; animation.Values = new NSObject[] { NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI / 4))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI / 4))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI / 2))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI + Math.PI / 2))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2 - Math.PI / 4))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2 - Math.PI / 4))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2 - Math.PI / 2))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI - Math.PI / 2))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI / 4))), NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI / 4))) }; animation.RepeatCount = float.MaxValue; animation.Duration = 10.0f; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.Linear); BallNode.AddAnimation(animation, new NSString("ballNodeAnimation2")); var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 1.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(1, 0, 0, (float)(Math.PI * 2))); BallNode.ChildNodes [1].AddAnimation(rotationAnimation, new NSString("ballNodeRotation")); break; case 5: // Add a constraint to the camera SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; constraint = SCNLookAtConstraint.Create(BallNode); presentationViewController.CameraNode.Constraints = new SCNConstraint[] { constraint }; SCNTransaction.Commit(); break; case 6: // Add a constraint to the light SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; var cameraTarget = ContentNode.FindChildNode("cameraTarget", true); constraint = SCNLookAtConstraint.Create(cameraTarget); presentationViewController.SpotLight.Constraints = new SCNConstraint[] { constraint }; SCNTransaction.Commit(); break; } }
public override void PresentStep(int switchIndex, PresentationViewController presentationViewController) { switch (switchIndex) { case 0: // Set the slide's title and subtitle and add some text TextManager.SetTitle("Core Image"); TextManager.SetSubtitle("CI Filters"); TextManager.AddBulletAtLevel("Screen-space effects", 0); TextManager.AddBulletAtLevel("Applies to a node hierarchy", 0); TextManager.AddBulletAtLevel("Filter parameters are animatable", 0); TextManager.AddCode("#aNode.#Filters# = new CIFilter[] { filter1, filter2 };#"); break; case 1: SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0f; // Dim the text and move back a little TextManager.TextNode.Opacity = 0.0f; presentationViewController.CameraHandle.Position = presentationViewController.CameraNode.ConvertPositionToNode(new SCNVector3(0, 0, 5.0f), presentationViewController.CameraHandle.ParentNode); SCNTransaction.Commit(); // Reveal the grid GroupNode.Opacity = 1; break; case 2: SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // Highlight an item HighlightContact(13, presentationViewController); SCNTransaction.Commit(); break; case 3: var index = 13; var subStep = 0; // Successively select items for (var i = 0; i < 5; ++i) { var popTime = new DispatchTime(DispatchTime.Now, (long)(i * 0.2 * Utils.NSEC_PER_SEC)); DispatchQueue.MainQueue.DispatchAfter(popTime, () => { SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.2f; UnhighlightContact(index); if (subStep++ == 3) { index += ColumnCount; } else { index++; } HighlightContact(index, presentationViewController); SCNTransaction.Commit(); }); } break; case 4: // BLUR+DESATURATE in the background, GLOW in the foreground // Here we will change the node hierarchy in order to group all the nodes in the background under a single node. // This way we can use a single Core Image filter and apply it on the whole grid, and have another CI filter for the node in the foreground. var selectionParent = HeroNode.ParentNode; SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0; // Stop the animations of the selected node HeroNode.Transform = HeroNode.PresentationNode.Transform; // set the current rotation to the current presentation value HeroNode.RemoveAllAnimations(); // Re-parent the node by preserving its world tranform var wantedWorldTransform = selectionParent.WorldTransform; GroupNode.ParentNode.AddChildNode(selectionParent); selectionParent.Transform = selectionParent.ParentNode.ConvertTransformFromNode(wantedWorldTransform, null); SCNTransaction.Commit(); // Add CIFilters SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; // A negative 'centerX' value means no scaling. //TODO HeroNode.Filters [0].SetValueForKey (new NSNumber (-1), new NSString ("centerX")); // Move the selection to the foreground selectionParent.Rotation = new SCNVector4(0, 1, 0, 0); HeroNode.Transform = ContentNode.ConvertTransformToNode(SCNMatrix4.CreateTranslation(0, Altitude, 29), selectionParent); HeroNode.Scale = new SCNVector3(1, 1, 1); HeroNode.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 4) * 0.25f); // Upon completion, rotate the selection forever SCNTransaction.SetCompletionBlock(() => { var animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 4.0f; animation.From = NSValue.FromVector(new SCNVector4(0, 1, 0, 0)); animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, NMath.PI * 2)); animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.RepeatCount = float.MaxValue; HeroNode.ChildNodes [0].AddAnimation(animation, new NSString("heroNodeAnimation")); }); // Add the filters var blurFilter = CIFilter.FromName("CIGaussianBlur"); blurFilter.SetDefaults(); blurFilter.Name = "blur"; blurFilter.SetValueForKey(new NSNumber(0), CIFilterInputKey.Radius); var desaturateFilter = CIFilter.FromName("CIColorControls"); desaturateFilter.SetDefaults(); desaturateFilter.Name = "desaturate"; GroupNode.Filters = new CIFilter[] { blurFilter, desaturateFilter }; SCNTransaction.Commit(); // Increate the blur radius and desaturate progressively SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 2; GroupNode.SetValueForKey(new NSNumber(10), new NSString("filters.blur.inputRadius")); GroupNode.SetValueForKey(new NSNumber(0.1), new NSString("filters.desaturate.inputSaturation")); SCNTransaction.Commit(); break; } }
private void BuildImageGrid() { // Create a root node for the grid GroupNode = SCNNode.Create(); // Retrieve the template node to replicate var scene = SCNScene.FromFile("Contacts/contact"); var templateNode = scene.RootNode.FindChildNode("people", true); for (int k = 0, j = 0; j < RowCount; j++) { for (var i = 0; i < ColumnCount; i++, k++) { // Hierarchy : __groupNode > container > node var container = SCNNode.Create(); var node = templateNode.Clone(); node.Name = "contact" + k; GroupNode.AddChildNode(container); container.AddChildNode(node); if (k == 28) { HeroNode = node; } // Curved layout var angle = 0.12f * ((ColumnCount - 1) / 2.0f - i); var x = NMath.Cos(angle + (float)(Math.PI / 2)) * 500.0f; var z = NMath.Sin(angle + (float)(Math.PI / 2)) * 500.0f; container.Position = new SCNVector3(x, j * 60, -z + 400); container.Rotation = new SCNVector4(0, 1, 0, angle); // We want a different image on each elemement and to do that we need to // unshare the geometry first and then unshare the material var geometryNode = node.ChildNodes [0]; geometryNode.Geometry = (SCNGeometry)geometryNode.Geometry.Copy(); var materialCopy = (SCNMaterial)geometryNode.Geometry.Materials [1].Copy(); materialCopy.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Contacts/contact" + (k % ContactImageCount), "jpg")); geometryNode.Geometry.ReplaceMaterial(1, materialCopy); // Animate (rotate forever) 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 tf = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.TimingFunctions = new CAMediaTimingFunction[] { tf, tf, tf }; animation.RepeatCount = float.MaxValue; animation.BeginTime = CAAnimation.CurrentMediaTime() + 1.0f + j * 0.1f + i * 0.05f; // desynchronize the animations node.AddAnimation(animation, new NSString("animation")); } } // Add the group to the scene GroupNode.Scale = new SCNVector3(0.03f, 0.03f, 0.03f); GroupNode.Position = new SCNVector3(0, Altitude - 2.8f, 18); GroupNode.Opacity = 0.0f; GroundNode.AddChildNode(GroupNode); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { switch (index) { case 0: // Set the slide's title and subtitle TextManager.SetTitle("Scene Graph"); TextManager.SetSubtitle("Summary"); break; case 1: // A node that will help visualize the position of the stars WireframeBoxNode = SCNNode.Create(); WireframeBoxNode.Rotation = new SCNVector4(0, 1, 0, (float)(Math.PI / 4)); WireframeBoxNode.Geometry = SCNBox.Create(1, 1, 1, 0); WireframeBoxNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/box_wireframe", "png")); WireframeBoxNode.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant; // no lighting WireframeBoxNode.Geometry.FirstMaterial.DoubleSided = true; // double sided // Sun SunNode = SCNNode.Create(); SunNode.Position = new SCNVector3(0, 30, 0); ContentNode.AddChildNode(SunNode); SunNode.AddChildNode((SCNNode)WireframeBoxNode.Copy()); // Earth-rotation (center of rotation of the Earth around the Sun) var earthRotationNode = SCNNode.Create(); SunNode.AddChildNode(earthRotationNode); // Earth-group (will contain the Earth, and the Moon) EarthGroupNode = SCNNode.Create(); EarthGroupNode.Position = new SCNVector3(15, 0, 0); earthRotationNode.AddChildNode(EarthGroupNode); // Earth EarthNode = (SCNNode)WireframeBoxNode.Copy(); EarthNode.Position = new SCNVector3(0, 0, 0); EarthGroupNode.AddChildNode(EarthNode); // Rotate the Earth around the Sun var animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 10.0f; animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); animation.RepeatCount = float.MaxValue; earthRotationNode.AddAnimation(animation, new NSString("earth rotation around sun")); // Rotate the Earth animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 1.0f; animation.From = NSValue.FromVector(new SCNVector4(0, 1, 0, 0)); animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); animation.RepeatCount = float.MaxValue; EarthNode.AddAnimation(animation, new NSString("earth rotation")); break; case 2: // Moon-rotation (center of rotation of the Moon around the Earth) var moonRotationNode = SCNNode.Create(); EarthGroupNode.AddChildNode(moonRotationNode); // Moon MoonNode = (SCNNode)WireframeBoxNode.Copy(); MoonNode.Position = new SCNVector3(5, 0, 0); moonRotationNode.AddChildNode(MoonNode); // Rotate the moon around the Earth animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 1.5f; animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); animation.RepeatCount = float.MaxValue; moonRotationNode.AddAnimation(animation, new NSString("moon rotation around earth")); // Rotate the moon animation = CABasicAnimation.FromKeyPath("rotation"); animation.Duration = 1.5f; animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); animation.RepeatCount = float.MaxValue; MoonNode.AddAnimation(animation, new NSString("moon rotation")); break; case 3: // Add geometries (spheres) to represent the stars SunNode.Geometry = SCNSphere.Create(2.5f); EarthNode.Geometry = SCNSphere.Create(1.5f); MoonNode.Geometry = SCNSphere.Create(0.75f); // Add a textured plane to represent Earth's orbit var earthOrbit = SCNNode.Create(); earthOrbit.Opacity = 0.4f; earthOrbit.Geometry = SCNPlane.Create(31, 31); earthOrbit.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/orbit", "png")); earthOrbit.Geometry.FirstMaterial.Diffuse.MipFilter = SCNFilterMode.Linear; earthOrbit.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 2)); earthOrbit.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant; // no lighting SunNode.AddChildNode(earthOrbit); break; case 4: // Add a halo to the Sun (a simple textured plane that does not write to depth) SunHaloNode = SCNNode.Create(); SunHaloNode.Geometry = SCNPlane.Create(30, 30); SunHaloNode.Rotation = new SCNVector4(1, 0, 0, Pitch * (float)(Math.PI / 180.0f)); SunHaloNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/sun-halo", "png")); SunHaloNode.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant; // no lighting SunHaloNode.Geometry.FirstMaterial.WritesToDepthBuffer = false; // do not write to depth SunHaloNode.Opacity = 0.2f; SunNode.AddChildNode(SunHaloNode); // Add materials to the stars EarthNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/earth-diffuse-mini", "jpg")); EarthNode.Geometry.FirstMaterial.Emission.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/earth-emissive-mini", "jpg")); EarthNode.Geometry.FirstMaterial.Specular.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/earth-specular-mini", "jpg")); MoonNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/moon", "jpg")); SunNode.Geometry.FirstMaterial.Multiply.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/sun", "jpg")); SunNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes/earth/sun", "jpg")); SunNode.Geometry.FirstMaterial.Multiply.Intensity = 0.5f; SunNode.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant; SunNode.Geometry.FirstMaterial.Multiply.WrapS = SunNode.Geometry.FirstMaterial.Diffuse.WrapS = SunNode.Geometry.FirstMaterial.Multiply.WrapT = SunNode.Geometry.FirstMaterial.Diffuse.WrapT = SCNWrapMode.Repeat; EarthNode.Geometry.FirstMaterial.LocksAmbientWithDiffuse = MoonNode.Geometry.FirstMaterial.LocksAmbientWithDiffuse = SunNode.Geometry.FirstMaterial.LocksAmbientWithDiffuse = true; EarthNode.Geometry.FirstMaterial.Shininess = 0.1f; EarthNode.Geometry.FirstMaterial.Specular.Intensity = 0.5f; MoonNode.Geometry.FirstMaterial.Specular.Contents = NSColor.Gray; // Achieve a lava effect by animating textures animation = CABasicAnimation.FromKeyPath("contentsTransform"); animation.Duration = 10.0f; var animationTransform1 = CATransform3D.MakeTranslation(0, 0, 0); animationTransform1 = animationTransform1.Concat(CATransform3D.MakeScale(3, 3, 3)); var animationTransform2 = CATransform3D.MakeTranslation(1, 0, 0); animationTransform2 = animationTransform1.Concat(CATransform3D.MakeScale(3, 3, 3)); animation.From = NSValue.FromCATransform3D(animationTransform1); animation.To = NSValue.FromCATransform3D(animationTransform2); animation.RepeatCount = float.MaxValue; SunNode.Geometry.FirstMaterial.Diffuse.AddAnimation(animation, new NSString("sun-texture")); animation = CABasicAnimation.FromKeyPath("contentsTransform"); animation.Duration = 30.0f; animationTransform1 = CATransform3D.MakeTranslation(0, 0, 0); animationTransform1 = animationTransform1.Concat(CATransform3D.MakeScale(5, 5, 5)); animationTransform2 = CATransform3D.MakeTranslation(1, 0, 0); animationTransform2 = animationTransform1.Concat(CATransform3D.MakeScale(5, 5, 5)); animation.From = NSValue.FromCATransform3D(animationTransform1); animation.To = NSValue.FromCATransform3D(animationTransform2); animation.RepeatCount = float.MaxValue; SunNode.Geometry.FirstMaterial.Multiply.AddAnimation(animation, new NSString("sun-texture2")); break; case 5: // We will turn off all the lights in the scene and add a new light // to give the impression that the Sun lights the scene var lightNode = SCNNode.Create(); lightNode.Light = SCNLight.Create(); lightNode.Light.Color = NSColor.Black; // initially switched off lightNode.Light.LightType = SCNLightType.Omni; SunNode.AddChildNode(lightNode); // Configure attenuation distances because we don't want to light the floor lightNode.Light.SetAttribute(new NSNumber(20), SCNLightAttribute.AttenuationEndKey); lightNode.Light.SetAttribute(new NSNumber(19.5), SCNLightAttribute.AttenuationStartKey); // Animation SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; lightNode.Light.Color = NSColor.White; // switch on presentationViewController.UpdateLightingWithIntensities(new float[] { 0.0f }); //switch off all the other lights SunHaloNode.Opacity = 0.5f; // make the halo stronger 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 override void SetupSlide(PresentationViewController presentationViewController) { // Set the slide's title and subtile and add some text TextManager.SetTitle("Node Attributes"); TextManager.SetSubtitle("SCNGeometry"); TextManager.AddBulletAtLevel("Triangles", 0); TextManager.AddBulletAtLevel("Vertices", 0); TextManager.AddBulletAtLevel("Normals", 0); TextManager.AddBulletAtLevel("UVs", 0); TextManager.AddBulletAtLevel("Materials", 0); // We create a container for several versions of the teapot model // - one teapot to show positions and normals // - one teapot to show texture coordinates // - one teapot to show materials var allTeapotsNode = SCNNode.Create(); allTeapotsNode.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 2)); GroundNode.AddChildNode(allTeapotsNode); TeapotNodeForPositionsAndNormals = Utils.SCAddChildNode(allTeapotsNode, "TeapotLowRes", "Scenes.scnassets/teapots/teapotLowRes", 17); TeapotNodeForUVs = Utils.SCAddChildNode(allTeapotsNode, "Teapot", "Scenes.scnassets/teapots/teapot", 17); TeapotNodeForMaterials = Utils.SCAddChildNode(allTeapotsNode, "teapotMaterials", "Scenes.scnassets/teapots/teapotMaterial", 17); TeapotNodeForPositionsAndNormals.Position = new SCNVector3(4, 0, 0); TeapotNodeForUVs.Position = new SCNVector3(4, 0, 0); TeapotNodeForMaterials.Position = new SCNVector3(4, 0, 0); foreach (var child in TeapotNodeForMaterials.ChildNodes) { foreach (var material in child.Geometry.Materials) { material.Multiply.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/teapots/UVs", "png")); material.Multiply.WrapS = SCNWrapMode.Repeat; material.Multiply.WrapT = SCNWrapMode.Repeat; //material.Reflective.Contents = NSColor.White; //material.Reflective.Intensity = 3.0f; //material.FresnelExponent = 3.0f; } } // Animate the teapots (rotate forever) var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 40.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 0, 1, (float)(Math.PI * 2))); TeapotNodeForPositionsAndNormals.AddAnimation(rotationAnimation, new NSString("teapotNodeForPositionsAndNormalsAnimation")); TeapotNodeForUVs.AddAnimation(rotationAnimation, new NSString("teapotNodeForUVsAnimation")); TeapotNodeForMaterials.AddAnimation(rotationAnimation, new NSString("teapotNodeForMaterialsAnimation")); // Load the "explode" shader modifier and add it to the geometry //var explodeShaderPath = NSBundle.MainBundle.PathForResource ("Shaders/explode", "shader"); //var explodeShaderSource = System.IO.File.ReadAllText (explodeShaderPath); // TODO TeapotNodeForPositionsAndNormals.Geometry.ShaderModifiers = new SCNShaderModifiers { EntryPointGeometry = explodeShaderSource }; PositionsVisualizationNode = SCNNode.Create(); NormalsVisualizationNode = SCNNode.Create(); // Build nodes that will help visualize the vertices (position and normal) BuildVisualizationsOfNode(TeapotNodeForPositionsAndNormals, ref PositionsVisualizationNode, ref NormalsVisualizationNode); NormalsVisualizationNode.CastsShadow = false; TeapotNodeForMaterials.AddChildNode(PositionsVisualizationNode); TeapotNodeForMaterials.AddChildNode(NormalsVisualizationNode); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { SCNTransaction.Begin(); switch (index) { case 1: TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.SetSubtitle("API"); TextManager.AddEmptyLine(); TextManager.AddCode("#aMaterial.#ShaderModifiers# = new SCNShaderModifiers {\n" + " <Entry Point> = <GLSL Code>\n" + "};#"); TextManager.FlipInText(SlideTextManager.TextType.Code); TextManager.FlipInText(SlideTextManager.TextType.Subtitle); break; case 2: TextManager.FlipOutText(SlideTextManager.TextType.Code); TextManager.AddEmptyLine(); TextManager.AddCode("#aMaterial.#ShaderModifiers# = new SCNShaderModifiers { \n" + " EntryCGPointragment = \n" + " new Vector3 (1.0f) - #output#.Color.GetRgb () \n" + "};#"); TextManager.FlipInText(SlideTextManager.TextType.Code); break; case 3: TextManager.FlipOutText(SlideTextManager.TextType.Code); TextManager.FlipOutText(SlideTextManager.TextType.Subtitle); TextManager.SetSubtitle("Entry points"); TextManager.AddBulletAtLevel("Geometry", 0); TextManager.AddBulletAtLevel("Surface", 0); TextManager.AddBulletAtLevel("Lighting", 0); TextManager.AddBulletAtLevel("Fragment", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); TextManager.FlipInText(SlideTextManager.TextType.Subtitle); break; case 4: SCNTransaction.AnimationDuration = 1; TextManager.HighlightBullet(0); // Create a (very) tesselated plane var plane = SCNPlane.Create(10, 10); plane.WidthSegmentCount = 200; plane.HeightSegmentCount = 200; // Setup the material (same as the floor) plane.FirstMaterial.Diffuse.WrapS = SCNWrapMode.Mirror; plane.FirstMaterial.Diffuse.WrapT = SCNWrapMode.Mirror; plane.FirstMaterial.Diffuse.Contents = new NSImage("/Library/Desktop Pictures/Circles.jpg"); plane.FirstMaterial.Diffuse.ContentsTransform = SCNMatrix4.CreateFromAxisAngle(new SCNVector3(0, 0, 1), NMath.PI / 4); plane.FirstMaterial.Specular.Contents = NSColor.White; plane.FirstMaterial.Reflective.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/envmap", "jpg")); plane.FirstMaterial.Reflective.Intensity = 0.0f; // Create a node to hold that plane PlaneNode = SCNNode.Create(); PlaneNode.Position = new SCNVector3(0, 0.1f, 0); PlaneNode.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 2)); PlaneNode.Scale = new SCNVector3(5, 5, 1); PlaneNode.Geometry = plane; ContentNode.AddChildNode(PlaneNode); // Attach the "wave" shader modifier, and set an initial intensity value of 0 var shaderFile = NSBundle.MainBundle.PathForResource("Shaders/wave", "shader"); var geometryModifier = File.ReadAllText(shaderFile); PlaneNode.Geometry.ShaderModifiers = new SCNShaderModifiers { EntryPointGeometry = geometryModifier }; PlaneNode.Geometry.SetValueForKey(new NSNumber(0.0f), new NSString("intensity")); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0; // Show the pseudo code for the deformation var textNode = TextManager.AddCode("#float len = #geometry#.Position.Xy.Length;\n" + "aMaterial.ShaderModifiers = new SCNShaderModifiers { \n" + " #EntryPointGeometry# = geometry.Position.Y \n" + "};#"); textNode.Position = new SCNVector3(8.5f, 7, 0); SCNTransaction.Commit(); break; case 5: // Progressively increase the intensity SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 2; PlaneNode.Geometry.SetValueForKey(new NSNumber(1.0f), new NSString("intensity")); PlaneNode.Geometry.FirstMaterial.Reflective.Intensity = 0.3f; SCNTransaction.Commit(); // Redraw forever ((SCNView)presentationViewController.View).Playing = true; ((SCNView)presentationViewController.View).Loops = true; break; case 6: SCNTransaction.AnimationDuration = 1; TextManager.FadeOutText(SlideTextManager.TextType.Code); // Hide the plane used for the previous modifier PlaneNode.Geometry.SetValueForKey(new NSNumber(0.0f), new NSString("intensity")); PlaneNode.Geometry.FirstMaterial.Reflective.Intensity = 0.0f; PlaneNode.Opacity = 0.0f; // Create a sphere to illustrate the "car paint" modifier var sphere = SCNSphere.Create(6); sphere.SegmentCount = 100; sphere.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/noise", "png")); sphere.FirstMaterial.Diffuse.WrapS = SCNWrapMode.Repeat; sphere.FirstMaterial.Diffuse.WrapT = SCNWrapMode.Repeat; sphere.FirstMaterial.Reflective.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/envmap3", "jpg")); sphere.FirstMaterial.FresnelExponent = 1.3f; SphereNode = SCNNode.FromGeometry(sphere); SphereNode.Position = new SCNVector3(5, 6, 0); GroundNode.AddChildNode(SphereNode); // Attach the "car paint" shader modifier shaderFile = NSBundle.MainBundle.PathForResource("Shaders/carPaint", "shader"); var surfaceModifier = File.ReadAllText(shaderFile); sphere.FirstMaterial.ShaderModifiers = new SCNShaderModifiers { EntryPointSurface = surfaceModifier }; var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 15.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.By = NSValue.FromVector(new SCNVector4(0, 1, 0, -(float)(Math.PI * 2))); SphereNode.AddAnimation(rotationAnimation, new NSString("sphereNodeAnimation")); TextManager.HighlightBullet(1); break; case 7: SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.5f; SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); // Move the camera closer presentationViewController.CameraNode.Position = new SCNVector3(5, -0.5f, -17); SCNTransaction.Commit(); break; case 8: SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0f; SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); // Move back presentationViewController.CameraNode.Position = new SCNVector3(0, 0, 0); SCNTransaction.Commit(); break; case 9: SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0f; // Hide the sphere used for the previous modifier SphereNode.Opacity = 0.0f; SphereNode.Position = new SCNVector3(6, 4, -8); SCNTransaction.Commit(); SCNTransaction.AnimationDuration = 0; TextManager.HighlightBullet(2); // Load the model, animate var intermediateNode = SCNNode.Create(); intermediateNode.Position = new SCNVector3(4, 0.1f, 10); TorusNode = Utils.SCAddChildNode(intermediateNode, "torus", "Scenes/torus/torus", 11); rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 10.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); TorusNode.AddAnimation(rotationAnimation, new NSString("torusNodeAnimation")); GroundNode.AddChildNode(intermediateNode); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0f; intermediateNode.Position = new SCNVector3(4, 0.1f, 0); SCNTransaction.Commit(); break; case 10: // Attach the shader modifier shaderFile = NSBundle.MainBundle.PathForResource("Shaders/toon", "shader"); var lightingModifier = File.ReadAllText(shaderFile); TorusNode.Geometry.ShaderModifiers = new SCNShaderModifiers { EntryPointLightingModel = lightingModifier }; break; case 11: SCNTransaction.AnimationDuration = 1.0f; // Hide the torus used for the previous modifier TorusNode.Position = new SCNVector3(TorusNode.Position.X, TorusNode.Position.Y, TorusNode.Position.Z - 10); TorusNode.Opacity = 0.0f; // Load the model, animate intermediateNode = SCNNode.Create(); intermediateNode.Position = new SCNVector3(4, -2.6f, 14); intermediateNode.Scale = new SCNVector3(70, 70, 70); XRayNode = Utils.SCAddChildNode(intermediateNode, "node", "Scenes/bunny", 12); XRayNode.Position = new SCNVector3(0, 0, 0); XRayNode.Opacity = 0.0f; GroundNode.AddChildNode(intermediateNode); rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 10.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.From = NSValue.FromVector(new SCNVector4(0, 1, 0, 0)); rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); intermediateNode.AddAnimation(rotationAnimation, new NSString("bunnyNodeAnimation")); TextManager.HighlightBullet(3); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1.0f; XRayNode.Opacity = 1.0f; intermediateNode.Position = new SCNVector3(4, -2.6f, -2); SCNTransaction.Commit(); break; case 12: // Attach the "x ray" modifier shaderFile = NSBundle.MainBundle.PathForResource("Shaders/xRay", "shader"); var fragmentModifier = File.ReadAllText(shaderFile); XRayNode.Geometry.ShaderModifiers = new SCNShaderModifiers { EntryPointFragment = fragmentModifier }; XRayNode.Geometry.FirstMaterial.ReadsFromDepthBuffer = false; break; case 13: // Highlight everything TextManager.HighlightBullet(-1); // Hide the node used for the previous modifier XRayNode.Opacity = 0.0f; XRayNode.ParentNode.Position = new SCNVector3(4, -2.6f, -5); // Create the model sphere = SCNSphere.Create(5); sphere.SegmentCount = 150; // tesselate a lot VirusNode = SCNNode.FromGeometry(sphere); VirusNode.Position = new SCNVector3(3, 6, 0); VirusNode.Rotation = new SCNVector4(1, 0, 0, Pitch * (float)(Math.PI / 180.0f)); GroundNode.AddChildNode(VirusNode); // Set the shader modifiers var geomFile = NSBundle.MainBundle.PathForResource("Shaders/sm_geom", "shader"); var surfFile = NSBundle.MainBundle.PathForResource("Shaders/sm_surf", "shader"); var lightFile = NSBundle.MainBundle.PathForResource("Shaders/sm_light", "shader"); var fragFile = NSBundle.MainBundle.PathForResource("Shaders/sm_frag", "shader"); geometryModifier = File.ReadAllText(geomFile); surfaceModifier = File.ReadAllText(surfFile); lightingModifier = File.ReadAllText(lightFile); fragmentModifier = File.ReadAllText(fragFile); VirusNode.Geometry.FirstMaterial.ShaderModifiers = new SCNShaderModifiers { EntryPointGeometry = geometryModifier, EntryPointSurface = surfaceModifier, EntryPointLightingModel = lightingModifier, EntryPointFragment = fragmentModifier }; break; case 14: SCNTransaction.AnimationDuration = 1.0f; // Hide the node used for the previous modifier VirusNode.Opacity = 0.0f; VirusNode.Position = new SCNVector3(3, 6, -10); // Change the text TextManager.FadeOutText(SlideTextManager.TextType.Code); TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.FlipOutText(SlideTextManager.TextType.Subtitle); TextManager.SetSubtitle("SCNShadable"); TextManager.AddBulletAtLevel("Protocol adopted by SCNMaterial and SCNGeometry", 0); TextManager.AddBulletAtLevel("Shaders parameters are animatable", 0); TextManager.AddBulletAtLevel("Texture samplers are bound to a SCNMaterialProperty", 0); TextManager.AddCode("#var aProperty = SCNMaterialProperty.#Create# (anImage);\n" + "aMaterial.#SetValueForKey# (aProperty, #new NSString# (\"aSampler\"));#"); TextManager.FlipInText(SlideTextManager.TextType.Subtitle); TextManager.FlipInText(SlideTextManager.TextType.Bullet); TextManager.FlipInText(SlideTextManager.TextType.Code); break; } SCNTransaction.Commit(); }
// Create a carousel of 3D primitives private void PresentPrimitives() { // Create the carousel node. It will host all the primitives as child nodes. CarouselNode = SCNNode.Create(); CarouselNode.Position = new SCNVector3(0, 0.1f, -5); CarouselNode.Scale = new SCNVector3(0, 0, 0); // start infinitely small ContentNode.AddChildNode(CarouselNode); // Animate the scale to achieve a "grow" effect SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; CarouselNode.Scale = new SCNVector3(1, 1, 1); SCNTransaction.Commit(); // Rotate the carousel forever var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 40.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)Math.PI * 2)); CarouselNode.AddAnimation(rotationAnimation, new NSString("rotationAnimation")); // A material shared by all the primitives var sharedMaterial = SCNMaterial.Create(); sharedMaterial.Reflective.Contents = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/envmap", "jpg")); sharedMaterial.Reflective.Intensity = 0.2f; sharedMaterial.DoubleSided = true; PrimitiveIndex = 0; // SCNBox var box = SCNBox.Create(5.0f, 5.0f, 5.0f, 5.0f * 0.05f); box.WidthSegmentCount = 4; box.HeightSegmentCount = 4; box.LengthSegmentCount = 4; box.ChamferSegmentCount = 4; AddPrimitive(box, 5.0f / 2, rotationAnimation, sharedMaterial); // SCNPyramid var pyramid = SCNPyramid.Create(5.0f * 0.8f, 5.0f, 5.0f * 0.8f); pyramid.WidthSegmentCount = 4; pyramid.HeightSegmentCount = 10; pyramid.LengthSegmentCount = 4; AddPrimitive(pyramid, 0, rotationAnimation, sharedMaterial); // SCNCone var cone = SCNCone.Create(0, 5.0f / 2, 5.0f); cone.RadialSegmentCount = 20; cone.HeightSegmentCount = 4; AddPrimitive(cone, 5.0f / 2, rotationAnimation, sharedMaterial); // SCNTube var tube = SCNTube.Create(5.0f * 0.25f, 5.0f * 0.5f, 5.0f); tube.HeightSegmentCount = 5; tube.RadialSegmentCount = 40; AddPrimitive(tube, 5.0f / 2, rotationAnimation, sharedMaterial); // SCNCapsule var capsule = SCNCapsule.Create(5.0f * 0.4f, 5.0f * 1.4f); capsule.HeightSegmentCount = 5; capsule.RadialSegmentCount = 20; AddPrimitive(capsule, 5.0f * 0.7f, rotationAnimation, sharedMaterial); // SCNCylinder var cylinder = SCNCylinder.Create(5.0f * 0.5f, 5.0f); cylinder.HeightSegmentCount = 5; cylinder.RadialSegmentCount = 40; AddPrimitive(cylinder, 5.0f / 2, rotationAnimation, sharedMaterial); // SCNSphere var sphere = SCNSphere.Create(5.0f * 0.5f); sphere.SegmentCount = 20; AddPrimitive(sphere, 5.0f / 2, rotationAnimation, sharedMaterial); // SCNTorus var torus = SCNTorus.Create(5.0f * 0.5f, 5.0f * 0.25f); torus.RingSegmentCount = 40; torus.PipeSegmentCount = 20; AddPrimitive(torus, 5.0f / 4, rotationAnimation, sharedMaterial); // SCNPlane var plane = SCNPlane.Create(5.0f, 5.0f); plane.WidthSegmentCount = 5; plane.HeightSegmentCount = 5; plane.CornerRadius = 5.0f * 0.1f; AddPrimitive(plane, 5.0f / 2, rotationAnimation, sharedMaterial); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { switch (index) { case (int)ParticleSteps.Fire: TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddEmptyLine(); TextManager.AddBulletAtLevel("Particle Image", 0); TextManager.AddBulletAtLevel("Color over life duration", 0); TextManager.AddBulletAtLevel("Size over life duration", 0); TextManager.AddBulletAtLevel("Several blend modes", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); var hole = SCNNode.Create(); hole.Geometry = SCNTube.Create(1.7f, 1.9f, 1.5f); hole.Position = new SCNVector3(0, 0, HOLE_Z); hole.Scale = new SCNVector3(1, 0, 1); GroundNode.AddChildNode(hole); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.5f; hole.Scale = new SCNVector3(1, 1, 1); SCNTransaction.Commit(); var ps = SCNParticleSystem.Create("fire", "Particles"); hole.AddParticleSystem(ps); Hole = hole; break; case (int)ParticleSteps.FireScreen: ps = Hole.ParticleSystems [0]; ps.BlendMode = SCNParticleBlendMode.Screen; break; case (int)ParticleSteps.Local: TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Local vs Global", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); Hole.RemoveAllParticleSystems(); Hole2 = Hole.Clone(); Hole2.Geometry = (SCNGeometry)Hole.Geometry.Copy(); Hole2.Position = new SCNVector3(0, -2, HOLE_Z - 4); GroundNode.AddChildNode(Hole2); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.5; Hole2.Position = new SCNVector3(0, 0, HOLE_Z - 4); SCNTransaction.Commit(); ps = SCNParticleSystem.Create("reactor", "Particles"); ps.ParticleColorVariation = new SCNVector4(0, 0, 0.5f, 0); Hole.AddParticleSystem(ps); var localPs = (SCNParticleSystem)ps.Copy(); localPs.ParticleImage = ps.ParticleImage; localPs.Local = true; Hole2.AddParticleSystem(localPs); var animation = CABasicAnimation.FromKeyPath("position"); animation.From = NSValue.FromVector(new SCNVector3(7, 0, HOLE_Z)); animation.To = NSValue.FromVector(new SCNVector3(-7, 0, HOLE_Z)); animation.BeginTime = CAAnimation.CurrentMediaTime() + 0.75; animation.Duration = 8; animation.AutoReverses = true; animation.RepeatCount = float.MaxValue; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.TimeOffset = animation.Duration / 2; Hole.AddAnimation(animation, new NSString("animateHole")); animation = CABasicAnimation.FromKeyPath("position"); animation.From = NSValue.FromVector(new SCNVector3(-7, 0, HOLE_Z - 4)); animation.To = NSValue.FromVector(new SCNVector3(7, 0, HOLE_Z - 4)); animation.BeginTime = CAAnimation.CurrentMediaTime() + 0.75; animation.Duration = 8; animation.AutoReverses = true; animation.RepeatCount = float.MaxValue; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.TimeOffset = animation.Duration / 2; Hole2.AddAnimation(animation, new NSString("animateHole")); break; case (int)ParticleSteps.Gravity: TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Affected by gravity", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); Hole2.RemoveAllParticleSystems(); Hole2.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.ScaleTo(0, 0.5), SCNAction.RemoveFromParentNode() })); Hole.RemoveAllParticleSystems(); Hole.RemoveAnimation(new NSString("animateHole"), 0.5f); SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.5; var tube = (SCNTube)Hole.Geometry; tube.InnerRadius = 0.3f; tube.OuterRadius = 0.4f; tube.Height = 1.0f; SCNTransaction.Commit(); ps = SCNParticleSystem.Create("sparks", "Particles"); Hole.RemoveAllParticleSystems(); Hole.AddParticleSystem(ps); foreach (var child in ((SCNView)presentationViewController.View).Scene.RootNode.ChildNodes) { if (child.Geometry != null) { if (child.Geometry.GetType() == typeof(SCNFloor)) { FloorNode = child; } } } /*FloorNode = ((SCNView)presentationViewController.View).Scene.RootNode.FindNodes ((SCNNode child, out bool stop) => { * stop = false; * if (child.Geometry != null) * stop = (child.Geometry.GetType () == typeof(SCNFloor)); * return stop; * });*/ /*FloorNode = [presentationViewController.view.scene.rootNode childNodesPassingTest:^BOOL(SCNNode *child, BOOL *stop) { * return [child.geometry isKindOfClass:[SCNFloor class]]; * }][0];*/ ps.ColliderNodes = new SCNNode[] { FloorNode }; break; case (int)ParticleSteps.Collider: TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Affected by colliders", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); var boxNode = SCNNode.Create(); boxNode.Geometry = SCNBox.Create(5, 0.2f, 5, 0); boxNode.Position = new SCNVector3(0, 7, HOLE_Z); boxNode.Geometry.FirstMaterial.Emission.Contents = NSColor.DarkGray; GroundNode.AddChildNode(boxNode); ps = Hole.ParticleSystems [0]; ps.ColliderNodes = new SCNNode[] { FloorNode, boxNode }; animation = CABasicAnimation.FromKeyPath("eulerAngles"); animation.From = NSValue.FromVector(new SCNVector3(0, 0, NMath.PI / 4 * 1.7f)); animation.To = NSValue.FromVector(new SCNVector3(0, 0, -NMath.PI / 4 * 1.7f)); animation.BeginTime = CAAnimation.CurrentMediaTime() + 0.5; animation.Duration = 2; animation.AutoReverses = true; animation.RepeatCount = float.MaxValue; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.TimeOffset = animation.Duration / 2; boxNode.AddAnimation(animation, new NSString("animateHole")); BoxNode = boxNode; break; case (int)ParticleSteps.Fields: Hole.RemoveAllParticleSystems(); Hole.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.ScaleTo(0, 0.75), SCNAction.RemoveFromParentNode() })); BoxNode.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.MoveBy(0, 15, 0, 1.0), SCNAction.RemoveFromParentNode() })); var particleHolder = SCNNode.Create(); particleHolder.Position = new SCNVector3(0, 20, HOLE_Z); GroundNode.AddChildNode(particleHolder); ParticleHolder = particleHolder; TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Affected by physics fields", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); ps = SCNParticleSystem.Create("snow", "Particles"); ps.AffectedByPhysicsFields = true; ParticleHolder.AddParticleSystem(ps); Snow = ps; //physics field var field = SCNPhysicsField.CreateTurbulenceField(50, 1); field.HalfExtent = new SCNVector3(20, 20, 20); field.Strength = 4.0f; var fieldOwner = SCNNode.Create(); fieldOwner.Position = new SCNVector3(0, 5, HOLE_Z); GroundNode.AddChildNode(fieldOwner); fieldOwner.PhysicsField = field; FieldOwner = fieldOwner; ps.ColliderNodes = new SCNNode[] { FloorNode }; break; case (int)ParticleSteps.FieldsVortex: VortexFieldOwner = SCNNode.Create(); VortexFieldOwner.Position = new SCNVector3(0, 5, HOLE_Z); GroundNode.AddChildNode(VortexFieldOwner); //tornado var worldOrigin = new SCNVector3(FieldOwner.WorldTransform.M41, FieldOwner.WorldTransform.M42, FieldOwner.WorldTransform.M43); var worldAxis = new SCNVector3(0, 1, 0); var vortex = SCNPhysicsField.CustomField((SCNVector3 position, SCNVector3 velocity, float mass, float charge, double timeInSeconds) => { var l = new SCNVector3(); l.X = worldOrigin.X - position.X; l.Z = worldOrigin.Z - position.Z; SCNVector3 t = Cross(worldAxis, l); var d2 = (l.X * l.X + l.Z * l.Z); var vs = (nfloat)(VS / Math.Sqrt(d2)); var fy = (nfloat)(1.0 - (Math.Min(1.0, (position.Y / 15.0)))); return(new SCNVector3(t.X * vs + l.X * (nfloat)VW * fy, 0, t.Z * vs + l.Z * (nfloat)VW * fy)); }); vortex.HalfExtent = new SCNVector3(100, 100, 100); VortexFieldOwner.PhysicsField = vortex; break; case (int)ParticleSteps.SubSystems: FieldOwner.RemoveFromParentNode(); ParticleHolder.RemoveAllParticleSystems(); Snow.DampingFactor = -1; TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Sub-particle system on collision", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); ps = SCNParticleSystem.Create("rain", "Particles"); var pss = SCNParticleSystem.Create("plok", "Particles"); pss.IdleDuration = 0; pss.Loops = false; ps.SystemSpawnedOnCollision = pss; ParticleHolder.AddParticleSystem(ps); ps.ColliderNodes = new SCNNode[] { FloorNode }; break; case (int)ParticleSteps.Confetti: ParticleHolder.RemoveAllParticleSystems(); TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Custom blocks", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); ps = SCNParticleSystem.Create(); ps.EmitterShape = SCNBox.Create(20, 9, 5, 0); ps.BirthRate = 100; ps.ParticleLifeSpan = 10; ps.ParticleLifeSpanVariation = 0; ps.SpreadingAngle = 20; ps.ParticleSize = 0.25f; ps.ParticleVelocity = 10; ps.ParticleVelocityVariation = 19; ps.BirthDirection = SCNParticleBirthDirection.Constant; ps.EmittingDirection = new SCNVector3(0, -1, 0); ps.BirthLocation = SCNParticleBirthLocation.Volume; ps.ParticleImage = new NSImage(NSBundle.MainBundle.PathForResource("Particles/confetti", "png")); ps.LightingEnabled = true; ps.OrientationMode = SCNParticleOrientationMode.Free; ps.SortingMode = SCNParticleSortingMode.Distance; ps.ParticleAngleVariation = 180; ps.ParticleAngularVelocity = 200; ps.ParticleAngularVelocityVariation = 400; ps.ParticleColor = NSColor.Green; ps.ParticleColorVariation = new SCNVector4(0.2f, 0.1f, 0.1f, 0); ps.ParticleBounce = 0; ps.ParticleFriction = 0.6f; ps.ColliderNodes = new SCNNode[] { FloorNode }; ps.BlendMode = SCNParticleBlendMode.Alpha; var floatAnimation = CAKeyFrameAnimation.FromKeyPath(""); floatAnimation.Values = new NSNumber[] { 1, 1, 0 }; floatAnimation.KeyTimes = new NSNumber[] { 0, 0.9f, 1 }; floatAnimation.Duration = 1.0f; floatAnimation.Additive = false; //ps.PropertyControllers = @{ SCNParticlePropertyOpacity: [SCNParticlePropertyController controllerWithAnimation:floatAnimation] }; //ps.HandleEvent (SCNParticleEvent.Birth, /*[ps handleEvent:SCNParticleEventBirth forProperties:@[SCNParticlePropertyColor] withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) { * * for (int i = 0; i < count; ++i) { * var col = (float *)((char *)data[0] + dataStride[0] * i); * if (rand() & 0x1) { // swith green for red * col[0] = col[1]; * col[1] = 0; * } * * } * }];*/ /*[ps handleEvent:SCNParticleEventCollision forProperties:@[SCNParticlePropertyAngle, SCNParticlePropertyRotationAxis, SCNParticlePropertyAngularVelocity, SCNParticlePropertyVelocity, SCNParticlePropertyContactNormal] withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) { * * for (NSInteger i = 0; i < count; ++i) { * // fix orientation * float *angle = (float *)((char *)data[0] + dataStride[0] * indices[i]); * float *axis = (float *)((char *)data[1] + dataStride[1] * indices[i]); * * float *colNrm = (float *)((char *)data[4] + dataStride[4] * indices[i]); * SCNVector3 collisionNormal = {colNrm[0], colNrm[1], colNrm[2]}; * SCNVector3 cp = SCNVector3CrossProduct(collisionNormal, SCNVector3Make(0, 0, 1)); * CGFloat cpLen = SCNVector3Length(cp); * angle[0] = asin(cpLen); * * axis[0] = cp.x / cpLen; * axis[1] = cp.y / cpLen; * axis[2] = cp.z / cpLen; * * // kill angular rotation * float *angVel = (float *)((char *)data[2] + dataStride[2] * indices[i]); * angVel[0] = 0; * * if (colNrm[1] > 0.4) { * float *vel = (float *)((char *)data[3] + dataStride[3] * indices[i]); * vel[0] = 0; * vel[1] = 0; * vel[2] = 0; * } * } * }];*/ ParticleHolder.AddParticleSystem(ps); break; case (int)ParticleSteps.EmitterCube: ParticleHolder.RemoveAllParticleSystems(); TextManager.FlipOutText(SlideTextManager.TextType.Bullet); TextManager.AddBulletAtLevel("Emitter shape", 0); TextManager.FlipInText(SlideTextManager.TextType.Bullet); ParticleHolder.RemoveFromParentNode(); ps = SCNParticleSystem.Create("emitters", "Particles"); ps.Local = true; ParticleHolder.AddParticleSystem(ps); var node = SCNNode.Create(); node.Position = new SCNVector3(3, 6, HOLE_Z); node.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(NMath.PI * 2, new SCNVector3(0.3f, 1, 0), 8))); GroundNode.AddChildNode(node); Bokeh = ps; node.AddParticleSystem(ps); break; case (int)ParticleSteps.EmitterSphere: Bokeh.EmitterShape = SCNSphere.Create(5); break; case (int)ParticleSteps.EmitterTorus: Bokeh.EmitterShape = SCNTorus.Create(5, 1); break; } }
public override void SetupSlide(PresentationViewController presentationViewController) { // Set the slide's title and add some code TextManager.SetTitle("Materials"); TextManager.AddBulletAtLevel("Diffuse", 0); TextManager.AddBulletAtLevel("Ambient", 0); TextManager.AddBulletAtLevel("Specular", 0); TextManager.AddBulletAtLevel("Normal", 0); TextManager.AddBulletAtLevel("Reflective", 0); TextManager.AddBulletAtLevel("Emission", 0); TextManager.AddBulletAtLevel("Transparent", 0); TextManager.AddBulletAtLevel("Multiply", 0); // Create a node for Earth and another node to display clouds // Use the 'pivot' property to tilt Earth because we don't want to see the north pole. EarthNode = SCNNode.Create(); EarthNode.Pivot = SCNMatrix4.CreateFromAxisAngle(new SCNVector3(1, 0, 0), (float)(Math.PI * 0.1f)); EarthNode.Position = new SCNVector3(6, 7.2f, -2); EarthNode.Geometry = SCNSphere.Create(7.2f); CloudsNode = SCNNode.Create(); CloudsNode.Geometry = SCNSphere.Create(7.9f); GroundNode.AddChildNode(EarthNode); EarthNode.AddChildNode(CloudsNode); // Initially hide everything EarthNode.Opacity = 1.0f; CloudsNode.Opacity = 0.5f; EarthNode.Geometry.FirstMaterial.Ambient.Intensity = 1; EarthNode.Geometry.FirstMaterial.Normal.Intensity = 1; EarthNode.Geometry.FirstMaterial.Reflective.Intensity = 0.2f; EarthNode.Geometry.FirstMaterial.Reflective.Contents = NSColor.White; EarthNode.Geometry.FirstMaterial.FresnelExponent = 3; EarthNode.Geometry.FirstMaterial.Emission.Intensity = 1; EarthNode.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/earth/earth-diffuse", "jpg")); EarthNode.Geometry.FirstMaterial.Shininess = 0.1f; EarthNode.Geometry.FirstMaterial.Specular.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/earth/earth-specular", "jpg")); EarthNode.Geometry.FirstMaterial.Specular.Intensity = 0.8f; EarthNode.Geometry.FirstMaterial.Normal.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/earth/earth-bump", "png")); EarthNode.Geometry.FirstMaterial.Normal.Intensity = 1.3f; EarthNode.Geometry.FirstMaterial.Emission.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/earth/earth-emissive", "jpg")); //EarthNode.Geometry.FirstMaterial.Reflective.Intensity = 1.0f; // This effect can also be achieved with an image with some transparency set as the contents of the 'diffuse' property CloudsNode.Geometry.FirstMaterial.Transparent.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Scenes.scnassets/earth/cloudsTransparency", "png")); CloudsNode.Geometry.FirstMaterial.TransparencyMode = SCNTransparencyMode.RgbZero; // Use a shader modifier to display an environment map independently of the lighting model used /*EarthNode.Geometry.ShaderModifiers = new SCNShaderModifiers { * EntryCGPointragment = " _output.color.rgb -= _surface.reflective.rgb * _lightingContribution.diffuse;" + "_output.color.rgb += _surface.reflective.rgb;" + };*/ // Add animations var rotationAnimation = CABasicAnimation.FromKeyPath("rotation"); rotationAnimation.Duration = 40.0f; rotationAnimation.RepeatCount = float.MaxValue; rotationAnimation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, (float)(Math.PI * 2))); EarthNode.AddAnimation(rotationAnimation, new NSString("earthNodeAnimation")); rotationAnimation.Duration = 100.0f; CloudsNode.AddAnimation(rotationAnimation, new NSString("cloudsNodeAnimation")); //animate light var lightHandleNode = SCNNode.Create(); var lightNode = SCNNode.Create(); lightNode.Light = SCNLight.Create(); lightNode.Light.LightType = SCNLightType.Directional; lightNode.Light.CastsShadow = true; lightHandleNode.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(0, -NMath.PI * 2, 0, 12))); lightHandleNode.AddChildNode(lightNode); EarthNode.AddChildNode(lightHandleNode); }
public static void SetFloat4(this SCNGeometry geometry, string uniform, SCNVector4 value) { geometry.SetValueForKey(NSValue.FromVector(value), new NSString(uniform)); }
void Setup() { // create a new scene var scene = SCNScene.FromFile("art.scnassets/ship"); // create and add a camera to the scene var cameraNode = SCNNode.Create(); cameraNode.Camera = SCNCamera.Create(); scene.RootNode.AddChildNode(cameraNode); // place the camera cameraNode.Position = new SCNVector3(0, 0, 15); // create and add a light to the scene var lightNode = SCNNode.Create(); lightNode.Light = SCNLight.Create(); lightNode.Light.LightType = SCNLightType.Omni; lightNode.Position = new SCNVector3(0, 10, 10); scene.RootNode.AddChildNode(lightNode); // create and add an ambient light to the scene var ambientLightNode = SCNNode.Create(); ambientLightNode.Light = SCNLight.Create(); ambientLightNode.Light.LightType = SCNLightType.Ambient; ambientLightNode.Light.Color = SKColor.DarkGray; scene.RootNode.AddChildNode(ambientLightNode); // retrieve the ship node var ship = scene.RootNode.FindChildNode("ship", true); // animate the 3d object #if __IOS__ ship.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(0, 2, 0, 1))); #else var animation = CABasicAnimation.FromKeyPath("rotation"); animation.To = NSValue.FromVector(new SCNVector4(0, 1, 0, NMath.PI * 2)); animation.Duration = 3; animation.RepeatCount = float.MaxValue; //repeat forever ship.AddAnimation(animation, null); #endif // retrieve the SCNView var scnView = (SCNView)View; // set the scene to the view scnView.Scene = scene; // allows the user to manipulate the camera scnView.AllowsCameraControl = true; // show statistics such as fps and timing information scnView.ShowsStatistics = true; // configure the view scnView.BackgroundColor = SKColor.Black; #if __IOS__ // add a tap gesture recognizer var tapGesture = new UITapGestureRecognizer(HandleTap); var gestureRecognizers = new List <UIGestureRecognizer> (); gestureRecognizers.Add(tapGesture); gestureRecognizers.AddRange(scnView.GestureRecognizers); scnView.GestureRecognizers = gestureRecognizers.ToArray(); #endif }
// 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); }
public override void PresentStep(int index, PresentationViewController presentationViewController) { switch (index) { case 1: // Load the scene var intermediateNode = SCNNode.Create(); intermediateNode.Position = new SCNVector3(0.0f, 0.1f, -24.5f); intermediateNode.Scale = new SCNVector3(2.3f, 1.0f, 1.0f); intermediateNode.Opacity = 0.0f; RoomNode = Utils.SCAddChildNode(intermediateNode, "Mesh", "Scenes/cornell-box/cornell-box", 15); ContentNode.AddChildNode(intermediateNode); // Hide the light maps for now foreach (var material in RoomNode.Geometry.Materials) { material.Multiply.Intensity = 0.0f; material.LightingModelName = SCNLightingModel.Blinn; } // Animate the point of view with an implicit animation. // On completion add to move the camera from right to left and back and forth. SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 0.75f; SCNTransaction.SetCompletionBlock(() => { SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 2; SCNTransaction.SetCompletionBlock(() => { var animation = CABasicAnimation.FromKeyPath("position"); animation.Duration = 10.0f; animation.Additive = true; animation.To = NSValue.FromVector(new SCNVector3(-5, 0, 0)); animation.From = NSValue.FromVector(new SCNVector3(5, 0, 0)); animation.TimeOffset = -animation.Duration / 2; animation.AutoReverses = true; animation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); animation.RepeatCount = float.MaxValue; presentationViewController.CameraNode.AddAnimation(animation, new NSString("myAnim")); }); presentationViewController.CameraHandle.Position = presentationViewController.CameraHandle.ConvertPositionToNode(new SCNVector3(0, +5, -30), presentationViewController.CameraHandle.ParentNode); presentationViewController.CameraPitch.Rotation = new SCNVector4(1, 0, 0, -(float)(Math.PI / 4) * 0.2f); SCNTransaction.Commit(); }); intermediateNode.Opacity = 1.0f; SCNTransaction.Commit(); break; case 2: // Remove the lighting by using a constant lighing model (no lighting) foreach (var material in RoomNode.Geometry.Materials) { material.LightingModelName = SCNLightingModel.Constant; } break; case 3: // Activate the light maps smoothly SCNTransaction.Begin(); SCNTransaction.AnimationDuration = 1; foreach (var material in RoomNode.Geometry.Materials) { material.Multiply.Intensity = 1.0f; } SCNTransaction.Commit(); break; } }