Setting lighting in ARKit framework - ios

Ok, I'm new to SceneKit and ARKit here and I just want to set any models I add to my scene to have a certain, bright lighting. I have tried all different configurations of the automatically update lighting settings with ARSceneView, however the only thing that really creates a discernible difference is autoenablesDefaultLighting:
func setup() {
antialiasingMode = .multisampling4X
//autoenablesDefaultLighting = true
preferredFramesPerSecond = 60
contentScaleFactor = 1.3
if let camera = pointOfView?.camera {
camera.wantsHDR = true
camera.wantsExposureAdaptation = true
camera.exposureOffset = -1
camera.minimumExposure = -1
camera.maximumExposure = 3
}
}
Regardless of the lighting obtained from the camera (as I know ArKit is able to do), I just want to set 1 lighting setting always. I want my scene contents to be lit like this:
Is this possible? What would I set sceneView.scene.lightingEnvironment equal to in order to achieve this effect?

According to the docs, you should be able to create a SCNNode in a position and then add a SCNLight to it:
https://developer.apple.com/documentation/scenekit/scnnode
https://developer.apple.com/documentation/scenekit/scnlight

Related

How to set category mask in sprite kit?

let dart = SKSpriteNode(imageNamed: "Dart")
dart.physicsBody?.isDynamic = true
dart.physicsBody?.categoryBitMask = 3
dart.physicsBody?.collisionBitMask = 2
when I print the darts category masks it returns nil. I have set other sprites category masks through the scene editor and they work fine. Thanks for any help.
You haven't actually made a physics body.
dart.physicsBody = SKPhysicsBody(...)
dart.physicsBody?.isDynamic = true
// etc.
The ... depends on the type of physics body you want (circle, rectangle, polygon, etc.) See examples here:
https://developer.apple.com/documentation/spritekit/sknode/getting_started_with_physics_bodies

Make rotation stop realisticly in Swift

I have a square = SKSpriteNode() that turns that rotates 360° when you touch it. I want to stop the spinning when you touch it again.
Now I would make square rotate through the SKAction.rotate but how can I stop the spinning realisticly meaning that I want the sprite to spin slower and slower until it stands still.
Did you try and ease out curve for the timingMode? If you don't like this effect you can provide your own custom timingFunction. There are several websites where you can explore animation curves online. I like this one.
You may want to consider using a physicsbody and applying an angular force.
let square = SKSpriteNode(color:.white,size:CGSize(10,10))
if let physicsBody = SKPhysicsBody(rectangleOf:square.frame.size)
{
physicsBody.isDynamic = true
physicsBody.allowsRotation = true
physicsBody.affectedByGravity = false
physicsBody.angularDamping = 0.1 //Adjust this to speed up or slow down the resistance of the spin
square.physicsBody = physicsBody
}
...
When you need to spin:
square.physicsBody!.angularImpulse(1) //Adjust this to change the amount of force applied to the spin

How to create a User Generated Animation in SceneKit with .dae (COLLADA) file from Blender

I’m trying to animate specific parts of a SCNScene object in SceneKit (in my case I want to animate fingers of a hand). I import the .dae (COLLADA) file easily from Blender with the respective bones to generate articulation on the model.
override func viewDidLoad() {
super.viewDidLoad()
var scene = SCNScene(named: "hand.dae")!
sceneView.scene = scene
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.lightGrayColor()
}
My goal is to animate those bones on iOS with user generated values between 0 and 1. Imagine a UISlider where you scroll back and forth and see the specific finger move depending on the value of the slider.
This is needed animation screenshot
Image with the animation pretended
I’ve tried animate the model by calling an animation file like the Apple’s Fox example:
private var indexFingerAnimation: CAAnimation!
indexFingerAnimation = CAAnimation.animationWithSceneNamed(“move_index_finger.dae”)
indexFingerAnimation = false
indexFingerAnimation = 0.3
indexFingerAnimation = 0.3
indexFingerAnimation = Float.infinity
The problem is that’s a Global animation instead of just the index finger. Besides it’s always a ‘pre-defined’ animation instead of an animation controlled by user input. Ultimately I want to mix animations (e.g. move index finger and thumb at the same time revealing gestures)
Is this possible? I’m struggling because I can’t figure out how to manipulate specific parts of the mesh. I’m starting to study MetalKit but it’s not clear to me that’s the solution.
Any help would be really appreciated.
I have never tried two animations at the same time
but I can rotate SCNNode in dae file with two or more animate
You must set pivot point and group them together

SKEffectNode - CIFilter Blur Size Limit - Big Black Box

I am trying to blur multiple SKNode objects. I do this by having a parent SKEffectNode with a CIFilter set to #"CIGaussianBlur". Like so:
- (SKEffectNode *)createBlurNode
{
SKEffectNode *blurNode = [[SKEffectNode alloc] init];
blurNode.shouldRasterize = YES;
[blurNode setShouldEnableEffects:NO];
[blurNode setFilter:[CIFilter filterWithName:#"CIGaussianBlur"
keysAndValues:#"inputRadius", #10.0f, nil]];
return blurNode;
}
This works fine for a bunch of nodes currently onscreen. But when I space these notes far away from each other (about 3000 pixels), the blurring no longer happens and I get a big black box. This happens regardless of whether the SKNodes I'm blurring are SKShapeNodes or SKSpriteNodes. Here's a sample project with this issue: Sample Project. (By the way, thanks to BobMoff for the initial version found here):
Here's happy blur (when nodes are less than 3000 pixels away from each other):
Sad blur (when nodes are more than 3000 pixels away from each other):
UPDATE
This behavior occurs whenever an SKEffectNode is the parent. It doesn't matter if it's enabling effects, blurring, etc. If the parent node is an SKNode, it's fine. i.e. Even if the parent blur node is created like it is below, you will get the blackness:
- (SKEffectNode *)createBlurNode
{
SKEffectNode *blurNode = [[SKEffectNode alloc] init];
// blurNode.shouldRasterize = YES;
// [blurNode setShouldEnableEffects:NO];
// [blurNode setFilter:[CIFilter filterWithName:#"CIGaussianBlur"
// keysAndValues:#"inputRadius", #10.0f, nil]];
return blurNode;
}
I had a similar problem, with a very wide, panning scene that I wanted to blur.
To get the blur effect to work, I removed any nodes that were sticking out too far past the edges of the scene:
// Property declarations, elsewhere in the class:
var blurNode: SKEffectNode
var mainScene: SKScene
var exParents: [SKNode : SKNode] = [:]
/**
* Remove outlying nodes from the scene and activate the SKEffectNode
*/
func blurScene() {
let FILTER_MARGIN: CGFloat = 100
let widthMax: CGFloat = mainScene.size.width + FILTER_MARGIN
let heightMax: CGFloat = mainScene.size.height + FILTER_MARGIN
// Recursively iterate through all blurNode's children
blurNode.enumerateChildNodesWithName(".//*", usingBlock: {
[unowned self]
node, stop in
if node.parent != nil && node.scene != nil { // Ignore nodes we already removed
if let sprite = node as? SKSpriteNode {
// Calculate sprite node position in scene coordinates
let sceneOrig = sprite.scene!.convertPoint(sprite.position, fromNode: sprite.parent!)
// Find left, right, bottom and top edges of sprite
let l = sceneOrig.x - sprite.size.width*sprite.anchorPoint.x
let r = l + sprite.size.width
let b = sceneOrig.y - sprite.size.height*sprite.anchorPoint.y
let t = b + sprite.size.height
if l < -FILTER_MARGIN || r > widthMax || b < -FILTER_MARGIN || t > heightMax {
self.exParents[sprite] = sprite.parent!
sprite.removeFromParent()
}
}
}
})
blurNode.shouldEnableEffects = true
}
/**
* Disable blur and reparent nodes we removed earlier
*/
func removeBlur() {
self.blurNode.shouldEnableEffects = false
for (kid, parent) in exParents {
parent.addChild(kid)
}
exParents = [:]
}
NOTES:
This does remove content from your effect node, so extremely wide nodes won't show up in the final result:
You can see the mountain highlighted in red stuck out too far and was removed from the resulting blur.
This code only considers SKSpriteNodes. Empty SKNodes don't seem to break the effect node, but if you're using other visible nodes like SKShapeNodes or SKLabelNodes, you'll have to modify this code to include them.
If you have ignoreSiblingOrder = false, this code might mess up your z-ordering since you can't guarantee what order the nodes are added back to the scene.
Stuff I tried that didn't work
Simply saying node.hidden = true instead of using removeFromParent() doesn't work. That would be WAY too easy ;)
Using an SKCropNode to crop out outlying content didn't work for me. I tried having the SKEffectNode parent the SKCropNode and the other way around, but the black square appeared no matter how small I made the cropped area. This might still be worth looking into if you're desperate for a cleaner solution.
As noted here, SKScenes are secretly SKEffectNodes and you can set their filter just like our blurNode above. SKScenes don't show a black screen when their content is too big. Unfortunately, they seem to just silently disable the filter instead. Again, I might have missed something, so you could explore this option further if you're trying to apply an effect across the entire scene.
Alternate Solutions
You can capture an image of the whole screen and apply a filter to that, as suggested here. I ended up going with an even simpler solution; I took a generic screenshot of the stuff I wanted to blur, then applied a very heavy blur so you can't see the precise details. I used that as the blurred background and you can hardly tell it's not the real thing ;) This also saves a healthy chunk of memory and avoids a small UI hiccup.
Musings
This is a pretty nasty bug, and I hope Apple comes up with a solution soon. You can click this cute picture of a camera to get a GPU trace and some insight on what's happening:
The device seems to be discarding the framebuffer for the effect node because it takes up too much memory. This is affirmed by the fact that when there's more memory pressure on the device, it's easier to get the 'black square' on smaller content in the SKEffectNode.
I used a method that worked for my game but it requires the blurred area to be static without movement.
On iOS 10 using Swift 3 I used SKSpriteNode, SKView, SKEffectNode, CIFilter. I created a sprite from a texture returned from the SKView method "texture from node" and passed the current scene as the parameter because it inherits from SKNode. So essentially I was taking a "screenshot" of the scene and creating a sprite from it. I then put it in an SKEffectNode with a blur filter. (set "should rasterize" to true for better performance as I only needed to blur once). Finally I added the new sprite to the scene. From there you could add sprites to the scene and place them above the new blurred node.
let blurFilter = CIFilter(name: "CIGaussianBlur")!
let blurAmount = 15.0
blurFilter.setValue(blurAmount, forKey: kCIInputRadiusKey)
let blurEffect = SKEffectNode()
blurEffect.shouldRasterize = true
let screenshotNode = SKSpriteNode(texture: gameScene.view!.texture(from: gameScene))
blurEffect.addChild(screenshotNode)
blurEffect.filter = blurFilter
gameScene.addChild(blurEffect)
Possible workaround for the bug:
Use a camera, zoom WAY out, so you can see most everything of your background, take a screenshot style rendering of this image. Crop it to your needs, and then blur it. Then rasterise this.
Then scale this image back up, and slice it up if needs be, and place accordingly.
SKEffectNode renders into a texture. In most iOS systems the maximum size for a texture is 2048x2048. If an SKEffectNode is trying to render content larger than that, it will just use a 2048x2048 texture and anything outside of it will just not appear in the texture. It won't give you any error or warning about this happening; it simply does it silently.
And no, there is no way to tell SKEffectNode to use a texture of a specific size, and pan&clamp the content into it. It always uses a texture that will cover all the child nodes, and if the texture would be too large, it just silently uses that 2048x2048 texture.

Background for different screen sizes

I am quite new in the SpriteKit development and I am trying to develop my first game.
I have implemented some edges (to avoid the ball going out of the screen) using the visual Scene editor (GameScene.sks) and I had to specify the size of the Scene (640x960).
Now, using the code, I would like to change the background according to the device width/height (because I cannot limit to 640x960).
So, I need to use:
scene.scaleMode = SKSceneScaleMode.Fill
In order to stretch the edges of the scene for different devices
BUT
I would like the backgrounds to be in scale mode 1
Is that possible at all?
This is the code I am using to set up a different background for each device:
if(skView.bounds.width == 768.0) {
var backgroundTexture = SKTexture(imageNamed: "bg_768.jpg")
let background = SKSpriteNode(texture: backgroundTexture)
background.size.width = skView.bounds.width
background.size.height = skView.bounds.height
background.position = CGPointMake(CGRectGetMidX(skView.frame), CGRectGetMidY(skView.frame))
scene.addChild(background)
} else {
//set others backgrounds
}
But if the scene get stretched they get stretched too..!
Is there a way to avoid this?
Thank you!

Resources