cheap shadow in SceneKit - ios

Is it possible to cast a static shadow in SceneKit? Don't know if static is the right word. I would only like a smooth black circle underneath my object when it falls. The object moves in y- and x-direction. I know that I can use sampleRadius property but that has a significant impact on performance. I have seen such thing in other game engines and I am wondering if I can achieve it in SceneKit too.
EDIT:
I used this, but I only ge black scene with very little lightning. It looks like that floor is completely black. I have tried different gobo images, but no luck. What have I missed?
let spotNode = scene.rootNode.childNodeWithName("spot", recursively: true)
let spotlight = spotNode?.light
spotlight?.categoryBitMask = 1
spotlight!.shadowMode = SCNShadowMode.Modulated
spotlight?.gobo?.contents = UIImage(named: "goboImage")
floorNode?.categoryBitMask = 1
//Apple code:
// Use modulated mode
light.shadowMode = SCNShadowModeModulated;
// Configure the projected shadow
light.gobo.contents = aShadowImage;
// Use bit masks to specify receivers
light.categoryBitMask = kProjectorLightMask;
floor.categoryBitMask = kProjectorLightMask;

you'll want to use the SCNShadowModeModulated shadow mode. The different techniques for shadows are explained in depth in the Building a Game with SceneKit presentation from WWDC 2014.

General on this:
The categoryBitMask represents "categories".
So when you set a categoryBitMask on a light you are saying "This light will only hit objects in this category"
Ex:
static let WorldCategory: Int = 1 << 0 // Category for background objects
static let GameObjectsCat: Int = 1 << 1 // Category for monsters in the game :)
...
// When setting up the lights
spotlight.categoryBitMask = GameObjectsCat // Light will only affect game objects
spotlight.castsShadow = true
...
// Pretend this is a SCNNode representing a 3d fortress...
// Will not be affected the by spotlight or project it's shadows
fortress.categoryBitMask = WorldCategory
// Pretend this is a SCNNode representing a evil 3d monster...
// Affected by spotlight and projects shadows
orc.categoryBitMask = GameObjectsCat
orc.castsShadow = true
That was the general take on categoryBitMask.
In your case we want to:
Create a modulated light (SCNShadowMode.Modulated)
Set the gobo image
Give light and floor the same categoryBitMask but make sure it is not set for any other node (like game characters or whatever). Tip would be to create a new category like static let SimpleShadow: Int = 1 << 2
This light will not illuminate anything in the scene (not even the floor), it will only project your gobo where ever it is pointed. So a second light source is needed to see something (Ambient will be easiest).
I have not had the possibility to test this out, so I am writing from what I remember. :)
But please note. With proper use of categoryBitMask you could easily create a "special light" designed to project a real shadow of your main character against the floor node. This would be very cheap, as the shadow would only be calculated for one single node, projected against one single node.
Hope this will help.

Related

Smooth out SCNGeometry using subdivisionLevel

We are working on an app similar to iPhone's Animoji app. We have created our own 3d model, which looks quite nice, the only issue is that there are edges around the eyes or nose of the model which are not smooth. Model has been prepared in Maya 3D software.
See the screenshot
You can see the rough edges around eyes and around eye brows.
We are trying following code to smooth these edges:
let node = self.childNodes[0]
let geometry = node.geometry
let verticies = self.change(geometry: geometry!)
geometry?.edgeCreasesSource = SCNGeometrySource(vertices: verticies)
geometry?.subdivisionLevel = 0
guard let element = geometry?.elements.first else {
return
}
//print(element.debugDescription)
geometry?.edgeCreasesElement = SCNGeometryElement(data: element.data, primitiveType: .line, primitiveCount: element.primitiveCount, bytesPerIndex: element.bytesPerIndex)
We are trying to set subdivisionLevel so that edges can be smoothed out. Is this the right approach? Or we need to do something else to get this fixed programmatically. 3d Designer has very smooth edges when we see it in Maya.
Using subdivision level is the right approach to smooth out geometries. In the code snippet however, you're not setting any subdivision level that's higher than 0. If you're working with a small number of objects, it's a good idea to use SceneKit editor and set the subdivision level in the attributes inspector.

ARKit - SCNNode continuous movement

I'm trying to create an application in which the user stacks different geometric shapes. In a .scn file, which is loaded inside the ARSCNView, I insert a static plane and then at each tap of the user, the app inserts a dynamic SCNNode.
The first node is being inserted a few inches above the plane, to replicate a falling object. And then, each other node is being dropped on top of another.
This is the main idea of the application; the problem appears after adding 3 or 4 nodes, they appear to slide of each other, almost jiggle, and the whole structure collapses.
This is my node I'm inserting:
let dimension: CGFloat = 0.075
let cube = SCNBox(width: dimension, height: dimension, length: dimension, chamferRadius: 0.0)
let node = SCNNode(geometry: cube)
node.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.dynamic, shape: nil)
node.physicsBody?.mass = 2.0
node.physicsBody?.friction = 1.0
node.physicsBody?.restitution = 0.01
node.physicsBody?.damping = 0.0
node.physicsBody?.angularDamping = 0.0
node.physicsBody?.rollingFriction = 1.0
node.physicsBody?.allowsResting = true
let insertionYOffset = 0.3
node.position = SCNVector3(hitResult.worldCoordinates.x, hitResult.worldCoordinates.y + Float(insertionYOffset), hitResult.worldCoordinates.z)
I've tried to play with the values and these are the best ones, but they aren't enough to create a stable structure of blocks.
As a requirement, I need to keep the blocks dynamic, they need to be affected by gravity, wind, etc.
The problem most likely has something to do with a factor called Dynamic which will allow it to continuously move or may have to do with the objects hitting each other to fix that problem all you have to do is change the collision mask to two different numbers.
I see two points that may be the flaws of your simulation:
Your objects are stacked. When two objects collides there's a small amount of error added in order to keep the physics in a stable phase and prevent weird behaviors. So when you stack objects you add error amounts and the simulation becomes inaccurate. I suggest making objects static over time or depending of the position, or reduce gravity by a bit.
Most of the physics engines works better with objects sized with the unit size (most of the cases, it's 1 like in Box2D or PhysX). Objects that are very small or very big tend to act less natural. 0.075 sounds a bit small for a simulation. I don't see a obvious solution, maybe look for a way to change the reference size.

Most efficient way to change color of part of a skspritenode

So I know how to change the color of the entire sprite node but is there a way to say change the color of only the arms of a character or is the only way to do this through assigning the sprite a new image with the appropriate color? Thanks for the help in advance.
The only way to change the color of only a part of a SKSpriteNode can only be done with other nodes involved. But I only recommend this if the area you are changing color of is simple, such as a rect or circle. To do this, I also recommend you use a SKNode to wrap its child nodes.
For example:
var character = SKNode()
var characterImage = SKSpriteNode(imageNamed:"yourImage.png")
//Set sizes, anchor points, what not
character.addChild(characterImage)
var colorChanger = SKSpriteNode(color: yourColor, size: size of arms or whatever)
//Set position -> (colorChange.position)
colorChanger.name = "colorizer"
character.addChild(colorChanger )
With this, you can also change the color of the arms whenever you want, using childNode(withName:) to access colorChanger.
But, if the arms are to complicated, you do have to use separate images. Using a SKNode to then wrap these images is recommended as well.
PS: If you're new to Swift and Spritekit, the reason it's better to use SKNodes as wrappers is because it makes it much easier to move them, rotate them, etc, because you just need to change the location, rotation, etc, of one node instead of many.
I'm not aware of any way to change the color property of a specific part of the same SKSpriteNode. Something you could do is to make textures of the colors you need the sprite to be and change the texture property instead of the color.

Back face culling in SceneKit

I am currently trying to set up a rotating ball in scene kit. I have created the ball and applied a texture to it.
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.doubleSided = true
ballGeometry.materials = [ballMaterial]
The current ballTexture is a semi-transparent texture as I am hoping to see the back face roll around.
However I get some strange culling where only half of the back facing polygons are shown even though the doubleSided property is set to true.
Any help would be appreciated, thanks.
This happens because the effects of transparency are draw-order dependent. SceneKit doesn't know to draw the back-facing polygons of the sphere before the front-facing ones. (In fact, it can't really do that without reorganizing the vertex buffers on the GPU for every frame, which would be a huge drag on render performance.)
The vertex layout for an SCNSphere has it set up like the lat/long grid on a globe: the triangles render in order along the meridians from 0° to 360°, so depending on how the sphere is oriented with respect to the camera, some of the faces on the far side of the sphere will render before the nearer ones.
To fix this, you need to force the rendering order — either directly, or through the depth buffer. Here's one way to do that, using a separate material for the inside surface to illustrate the difference.
// add two balls, one a child of the other
let node = SCNNode(geometry: SCNSphere(radius: 1))
let node2 = SCNNode(geometry: SCNSphere(radius: 1))
scene.rootNode.addChildNode(node)
node.addChildNode(node2)
// cull back-facing polygons on the first ball
// so we only see the outside
let mat1 = node.geometry!.firstMaterial!
mat1.cullMode = .Back
mat1.transparent.contents = bwCheckers
// my "bwCheckers" uses black for transparent, white for opaque
mat1.transparencyMode = .RGBZero
// cull front-facing polygons on the second ball
// so we only see the inside
let mat2 = node2.geometry!.firstMaterial!
mat2.cullMode = .Front
mat2.diffuse.contents = rgCheckers
// sphere normals face outward, so to make the inside respond
// to lighting, we need to invert them
let shader = "_geometry.normal *= -1.0;"
mat2.shaderModifiers = [SCNShaderModifierEntryPointGeometry: shader]
(The shader modifier bit at the end isn't required — it just makes the inside material get diffuse shading. You could just as well use a material property that doesn't involve normals or lighting, like emission, depending on the look you want.)
You can also do this using a single node with a double-sided material by disabling writesToDepthBuffer, but that could also lead to undesirable interactions with the rest of your scene content — you might also need to mess with renderingOrder in that case.
macOS 10.13 and iOS 11 added SCNTransparencyMode.dualLayer which as far as I can tell doesn't even require setting isDoubleSided to true (the documentation doesn't provide any information at all). So a simple solution that's working for me would be:
ballMaterial.diffuse.contents = UIImage(named: ballTexture)
ballMaterial.transparencyMode = .dualLayer
ballGeometry.materials = [ballMaterial]

SceneKit: How to detect contact without collision

I'm looking for the best way (performace-wise) to detect contact between two objects that do not collide (not bounce off each other) in a SceneKit physics world.
I saw that SpriteKit has a contactTestBitMask and a collisionBitMask for physics bodies while SceneKit only has the latter. So there must to be another preferred way to get notified when objects have contact in SceneKit. I guess that calling contactTestBetweenBody:andBody:options: in each frame for each object is not the best way to do it?
UPDATE
With iOS 9.0, Apple has added contactTestBitMask to SCNPhysicsBody. So this question will become obsolete soon.
This question deserves a full tutorial. In short, each physics body has a categoryBitMask and collisionBitMask.
categoryBitMask
This is the bit that represents the body.
collisionBitMask
This is the bit that represents which bodies collide with it.
By default (when assigned a physicsBody) all bodies collide with the exception of a kinematicBody, which act special. Objects don't collide with it. But don't get confused, it can collide with other objects when manually moved (such as by dragging it with a finger).
How to handle!!!
Assign your bodies whether it be static or dynamic. Assign there category and collision mask. If the mask match up they collide. How about an example!
These bodies collide with each other
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 8;
bodyB.physicsBody.collisionBitMask = 4;
These don't collide
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;
Noticed I used categories by power of two. This is because the contactDelegate uses bitwise AND to compare. Read up on bitwise if you don't understand. It will really help.
These bodies collide without physics
EXAMPLE: You have a hero that runs through a ghost and you want to know that it happened without either one being effected. Such as bouncing off each other.
bodyA.physicsBody.categoryBitMask = 4;
bodyB.physicsBody.categoryBitMask = 8;
bodyA.physicsBody.collisionBitMask = 0;
bodyB.physicsBody.collisionBitMask = 0;
So the following code is designed so that bodyA(your hero) runs through bodyB(ghost).
bodyB.physicsField.categoryBitMask = 4
Noticed that the above is a physicsField.catagoryBitMask. It is assigned a 4 so that it matches up with bodyA.
let collisionField = SCNPhysicsField.customFieldWithEvaluationBlock(){
(_, _, _, _, _) in
WHAT U WANT TO DO ON CONTACT
}
bodyB.physicsField = collisionField
The physicsField is not the shape of your geometry unfortunately. Its default is the shape of its bounding box or sphere/elliptical if you set the physicsField property usesEllipsoidalExtent to "true/yes"
The area of effect can be changed using the physicsFields halfExtent property like so...
bodyB.physicsField.halfExtent = SCNVector3Make(.5, .5, .5)
The above will extend the field of a box the size of (2, 2, 2) to (2.5, 2.5, 2.5).
EDIT: If they still collide, try removing the physicsbody from bodyB (this will remove all physics simulation from the node). I don't think it will be a problem as long as the catagory and collision bitmask are assigned properly.
This is the best method I could find. All coding is from my head, with the support of Apple docs, so may not be exactly right but I hope this helps!
I have found the best way is to just set the mass so small on a kinematic body that the other one will pass through it.
sensorBox.physicsBody.mass = 0.00000000001; // super tiny mass makes it a "sensor"
Don't forget your scene's physics world can call a "contactTestBetweenBody: andBody: withOptions... this works great.
Usage
[myScene.physicsWorld contactTestBetweenBody:(SCNPhysicsBody *) andBody:(SCNPhysicsBody *) options:(NSDictionary *)];

Resources