In this article I would like to give you a short introduction to the SceneKit topic. SceneKit is a powerful graphic engine supplied by Apple as a framework and using the Metal Engine for 3D Graphics. SceneKit is available for Xojo under MacOS with the MBS Xojo Mac64bit Plugin.
Geometrical objects can be composed of basic forms. The following basic forms are available: sphere, cylinder, capsule, truncated cone, tube, torus, box, infinity plane, plane, and the pyramid.
Each of these basic forms has its own class with its own properties. Now we will looking at the constructors of the individual forms.
Here you can see the basic forms. The red bar passes through the origins of the forms)
From an article previously published on the blog, in which we modeled together our solar system, some of you already know the basic form sphere that uses the class SCNSphereMBS. In the constructor the value for the radius is passed.
In the constructor of the class SCNCylinderMBS class we define the radius and the height of the cylinder
For the creation of a capsule we use the SCNCapsuleMBS class. The capsule is a basic form, although it could also be assembled from two spheres and a cylinder. So it is not surprising that for the creation of this basic form, the radius of the capsule tip and the height of the capsule is needed.
Cone and truncate cone
For modeling a cone or a truncate cone we need the class SCNConeMBS. We define the radius of the upper surface and the lower surface, as well as the height. This results in a truncated cone. If we define the upper radius as 0, we get a perfect cone.
Tube and torus
We have also become familiar with the basic tube shape in the solar system project. In the last project we use the object of the class SCNTubeMBS to modeled the ring of Saturn. The tube is defined by an inner radius, an outer radius and height. The inner cylinder is subtracted by the outside cylinder.
This basic shape is easily confused with the torus basic shape, because both are round and have an opening in the middle. But if you cut a torus, the cut surface is a circle. For the tube, the cut surface is rectangular. The objects of the class SCNTorusMBS are defined by its ringRadius and pipeRadius.
For the box we have the normal parameters width, height and length. Additionally we have an another parameter where we can specify how much our corners should be rounded. Extremely sharp edges gives the animation an unnatural look. Look at a dice for example, its corners are rounded too.
Plane and Floor
The objects of the SCNPlaneMBS class are defined by width and height. Besides the geometry of the normal plane, we also have geometry of an infinite plane. The geometry can be created as an object of the SCNFloorMBS class. The class is called Floor, because it is often used to model a floor or ceiling. It has a property that allows you to set the intensity of the reflection, that ensures that objects are reflected in this floor.
Last but not least we have pyramids. In the constructor of the SCNPyramidMBS class we defined the width, height and length of the pyramid. Our pyramid has a rectangular base. In the middle of this base area is also the center of gravity. This is a special thing about the pyramids. For all other basic shapes it is in the middle of the object. This information is especially important for the positioning in a scene. We will see the positioning of objects in a scene later
Besides the basic forms you can also use text in your scenes. For this we define a string and the text depth in the constructor of an object of the SCNTextMBS class. Then you can set additional properties for the text, such as chamfering for the edges.
How to use it in a Scene
Now I would like to show you how to integrate the basic forms and text into your scene.
Here we see the code to add a capsule to the scene.
First we create an object with the appropriate geometry. This geometry is passed to a node which we add to our scene.
The nodes in a scene
Our scene consists of a node system. But what is a node? Our objects are nodes in a tree. We have a root node in our scene that is the first node of the tree, on which all other nodes are connected in different levels. The root node exist exactly once in a scene! The other nodes are children of the root node. A node can be a parent of another node. That means that, these two nodes are related. If you move a parent node, you automatically move the child node too. But if you move the child node the parent node doesn't move. If you move a parent node with more than one further level, all nodes of this branch will move too. You can also assemble your own shapes from the basic forms. We can, for example, assemble a cow. The legs of this cow should move when the trunk moves. So the node of the trunk should be a parent node. The legs are children of the trunk. So when we move the trunk of the cow, the legs move with it. But if we want to move the legs separately, this is no problem, because you can move the child nodes separately from the parent nodes.
You can also determine the position of a node. The position of a node in the scene is determined by an object of the SCNVector3MBS class. We have the axis x, y, and z.
The x coordinate defines the position on the high axis, y the axis that gives us left and right, and the z axis gives us the for and back position. In the coordinate system we have a point with (0,0,0). This is the origin of the coordinate system and the default insert value for a node. All axes can have positive and negativ values. For example we have a sphere in default position, the origin. If we want to move left and right we change the x values. If we go to the right we have positive values and if we go to the left we got negativ values.
A sphere in the standard position in the coordinate system
But we not only want to display the objects in our scene, we also want to move them. We only need to know two basic movements: the transposition and the rotation. In the transposition, we move a node from one location in the coordinate system to another location. We have two different methods to do that. One is the moveBy method, that defines a movement depending on the current position. We move some units in x, y, or z direction. Additionally we set the speed in the parameters. That is the time, that this movement need in the animation.
The second method is moveTo. In the parameters we defines a coordinate to which our object is moved. This method is independent of the current location.
At next we come to the rotations. Again, we have to distinguish between two rotations: The rotation around its own axis and the rotation around an other object.
For the rotation around its own axis we use the rotateBy method that gets the angel of the rotation and the duration as parameters.The rotation around an other object is more difficult, because we don't have a direct method for that. But we can still achieve this effect by positioning an additional node at the position where the object is located. This auxiliary node is a parent node of the rotated object. If we want to make this parent node invisible, we can simply assign it a transparent color.
Rotation and transposition are the only two movements we must be able to animate. All other movements are combinations or modifications of this movements.
Now we want to bring a little movement into the scene. In this example we want to move a sphere up and down along the y axis.
First we define an up and down movement. We use the moveby method and pass the values.
Because we want to execute this movements one after the other, we build a sequence. To this sequence we pass an array of the movements.
We would like to play these movements several times. In this case there should be no limit for the number of repetitions. So we use the method repeatActionForever and set our sequence into the parameter. Then we start our animation with runAction.
In the example we animate a non physical movement.
In plugin version 20.2, which will be released in May and is already available in the pre-release version, there will be functionalities for physics under SceneKit. With physics you can model physical movements, e.g. the repulsion between two pyramids. In the property physicsBody of each node we can specify a physical group whose elements influence each other. With the method applyForce we can specify the movement of the pyramids as a three-dimensional vector. The movement is physical and forms a curve when we set a value on the y coordinate. If two pyramids meet in their flight path, they influence theirs fall. You can change the properties of the pyramids e.g. the mass and observe and visualize the effects.
Also new in the next plugin version will be the Hit Test for nodes. With the Hit Test for example you can test, whether you have clicked with the mouse on an object or whether an object crosses a given boundary. You can set the background color of the scene to the color of an object you have clicked.
Here you can see the code of the MouseDown event:
In this case we get the coordinates of the click in the MouseDown event.
Dim p As New CGPointMBS(x, y)
Dim results() As SCNHitTestResultMBS = myview.hitTest(p)
If results <> Nil And results.Ubound >= 0 Then
Dim result As SCNHitTestResultMBS = results(0)
background = result.node.Geometry.firstMaterial.diffuse.contents
MyView.backgroundColor = Background
With this coordinates we create a new instance of the class CGPointMBS and check, whether we hit an object, with the hitTest method of the scene in this point. We get the result as an array. Because we only need one color we need the first element in the array. We take the color from this node and set the color to the background property of the Scene. The color change.
The appearance of the objects is also essential for an animation. This appearance in color and structure is defined by the material. It is a big different between the look of a sparkling metal ball and a rough stone ball, also they have the same shape and the same size. We start with the color of an object. You have variants of colors you can choose. The most easiest way is to take some of the default colors in the NSColorMBS class.
Here you see how to define a color in the first material property of the geometry node. We defined in this example a black color. But you can also choose the default colors blue, brown, cyan, green, dark gray, Gray, light gray, magenta, orange, purple, red, white, and yellow. Additionally you can use the system colors, which have a bit different color tone like the others. We call it for example with
NodeOneGeometry.firstMaterial.diffuse.contents = NSColorMBS.blackColor
We can also use HSV and RGB color spaces.
NodeOneGeometry.firstMaterial.diffuse.contents = NSColorMBS.systemOrangeColor
In the same way you can map a picture to your geometry.
PlaneGeometry.firstMaterial.diffuse.contents = pic
Here you see a plane on which we taped a picture.
Not only color influences the appearance of a material but also other properties.
For example the specular reflection point. The property specular shows whether an object reflects or absorbs light. So we can imagine that a foam ball absorbs the light, but a plastic ball reflects the light. The color we use in specular reflection depends on the material of the ball, but also on the light that hits the object. The intensity of the light spot also depends on the same factors.
With the property shininess we determine the size of the reflection. If you take a look to rough vs a plain Christmas bulb you can see the difference this property makes. We see the specular reflection of the plain bulb. It is small and have hard shape edges. On the rough material the specular reflection is weaker but greater and have very smooth edges. Because that you can represent a material as rough or plain with this property.
The specular refection on a plain and on a rough Christmas bulb
Another very important property is displacement. With this property we can give a surface a structure. The structure to be embossed into the material is given by a grayscale image. The black parts of this image look imprinted on the object. Therefore we can modulate, for example, scratches and stone material.
The properties you can set in your code with the following lines:
Geometry.firstMaterial.specular.intensity = 0.9
Geometry.firstMaterial.shininess = 0.5
Geometry.firstMaterial.displacement.contents = pic
Geometry.firstMaterial.displacement.intensity = 2
Another very important thing that determines the look and impact of a scene is the lighting. We can choose a default lighting system in the scene by setting the property
But we can use different light sources to create our own light set.
myView.autoenablesDefaultLighting = True
We have five different types of lights: ambient light, directional light, IES light, omni light and spot light.
The ambient light illuminates a scene evenly in all points. Therefore no shadows or reflections are created. As a result the depth in the scene is missing. This light typ can be used for cartoon animations or together with other light sources to brighten the scene in general.
The directional light illuminates all objects from a given direction. We can imagine this light source as an infinitely large plane. So the light comes from one direction, but shines evenly from that direction. The side that is illuminated can be seen well. The side that is turned away from the light source seems very dark. If you want to illuminate this side as well, you can add another light source.
The IES Light type emits a pear-shaped light cone. This is reminiscent of a street lamp and can be used in scenes e.g. for ceiling or free-standing lamps.
With the Omni light type the light comes from one point and shines in all directions equally. We can compare this light type with the sun.
As the name of the spotlight type suggests, it is a cone of light that is directed outwards and comes close to the light of a spotlight.
A light source is also a node in the scene. If we want to include a light source in a scene, we first initialize a new node. In the property light we have to initialize a new light. After that we define the light type, light color and so on. Then we add the node to our scene. Because it is a normal node in our scene, we can determine its position as usual.
The code for the new light you can see here:
I hope you enjoyed my introduction to the topic SceneKit. If you have any questions please contact me. I wish you much fun with SceneKit.
Dim directional As New SCNNodeMBS
directional.light = New SCNLightMBS
directional.Light.Type = SCNLightMBS.SCNLightTypeDirectional
directional.light.Color = NSColorMBS.colorWithWhite(0.75, 1)
By Stefanie Juchmes