SceneKit nodesInsideFrustumWithPointOfView: unexpectedly returning empty - ios

I am using the following method to determine which SCNNodes are visible by the camera.
[self.scnView nodesInsideFrustumWithPointOfView:cameraNode];
However the returned array is always empty.
I set up the scene up as follows:
-(void)setupScene{
scene = [SCNScene scene];
cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 0, 0);
[scene.rootNode addChildNode:cameraNode];
self.scnView.scene = scene;
self.scnView.showsStatistics = YES;
self.scnView.backgroundColor = [UIColor clearColor];
}
At a random time, after the scene is created, I add a SCNNode to the scene:
testnode = [Testnode createNode];
testnode.position = SCNVector3Make(0, 0, -10);
[self.scnView.scene.rootNode addChildNode:testnode];
On my device, the node "testnode" is visible on my screen yet nodesInsideFrustumWithPointOfView: returns nothing.
EDIT: I tried changing the point of view to a spot light object and test whether "testnode" is inside its frustum. Here is what I see on screen: http://imgur.com/a/C3XGu Yet the array still returns empty. The testnode is the white cube.

Adding this for better visibility. Thank Crashalot for this.
Basically, if the SCNNode you are trying to detect using [scnView nodesInsideFrustumWithPointOfView:] and [scnView isNodeInsideFrustum: withPointOfView:] is the child of a node with empty geometry, it won't be detected.
In my case, I added a plane geometry and set the material to transparent:
SCNNode *emptynode = [SCNNode node];
node.geometry = [SCNPlane planeWithWidth:1.0f height:2.0f];
node.geometry.firstMaterial.transparency = 0.0f;

Related

Change SCNScene scale

I am trying to downscale my object using SCNNode here is my code :
SCNScene * scene = [SCNScene sceneNamed:#"ship.dae"];
SCNNode *node = [SCNNode node];
//This line doesn't do anything !
node.scale = SCNVector3Make(11, 11, 11);
[scene.rootNode addChildNode:node];
// set the scene to the view
_myView.scene = scene;
_myView.allowsCameraControl = YES;
_myView.autoenablesDefaultLighting = YES;
_myView.backgroundColor = [UIColor whiteColor];
But the problem is object's scale doesn't change at all ! what is wrong ?
The answer of Jörg Kirchhof is right. Look at the ship in the SceneKit Editor. The top node of the scene graph is the node you want. In the case of Apple's sample code it is "ship".
Your second line SCNNode *node = [SCNNode node]; does not make any sense it that case because it is just an empty node. Replace that line with the line below.
SCNNode *node = [scene.rootNode childNodeWithName:#"ship" recursively:YES];
With [SCNNode node] you are creating a new node that is unrelated to the scene and the ship. In the next line you scale the (non-existent) geometry of that node.
Scale the rootNode of your scene instead.
If your .dae contains more than only the ship you intended to scale, search for the node containing the ship using [scene.rootNode childNodeWithName:shipNodeName recursively:YES].

How to prevent the camera from entering a SCNNode - Scenekit iOS

I have a problem whit the camera in SceneKit, by default i can move, rotate, zoom my camera whit this line :
myView.allowsCameraControl = YES;
But my camera can pass through walls and floor (which allows me to see the underside of the stage ) .
My first request: is it possible to apply constraints to the camera (position, rotation) ?
My second request: I thought making a cube encompassing stage and do my collision detection between my cube and my camera, but it does not work ...
code for viewDidLoad:
SCNView *myView = (SCNView *)self.view3D;
myView.scene = [SCNScene sceneNamed:#"art.scnassets/Pointe Marrin 3 def 3 def 2.dae"];
myView.scene.physicsWorld.contactDelegate = self;
myView.scene.physicsWorld.gravity = SCNVector3Make(0, 0, 0);
cubeLimite = [myView.scene.rootNode childNodeWithName:#"Cube" recursively:YES];
camera = [myView.scene.rootNode childNodeWithName:#"Caméra" recursively:YES];
cubeLimite.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:nil];
cubeLimite.physicsBody.mass = 0;
cubeLimite.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
cubeLimite.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
//test camera
CGFloat boxSide = 0.001;
SCNBox *box = [SCNBox boxWithWidth:boxSide
height:boxSide
length:boxSide
chamferRadius:0];
SCNNode *boxNode = [SCNNode nodeWithGeometry:box];
SCNPhysicsShape *shape = [SCNPhysicsShape shapeWithGeometry:boxNode.geometry options:nil];
camera.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:shape];
camera.physicsBody.mass = 0;
camera.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
camera.physicsBody.collisionBitMask = SCNPhysicsCollisionCategoryAll;
Code for delegate :
- (void)physicsWorld:(SCNPhysicsWorld *)world
didBeginContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact debut");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world
didUpdateContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact milieu");
}
- (void)physicsWorld:(SCNPhysicsWorld *)world didEndContact:(SCNPhysicsContact *)contact{
NSLog(#"Contact fin");
}
The delegate :
SCNPhysicsContactDelegate
is declared in the .h file
Ideas ?
thank you beforehand
It's not possible, as far as I know, to constrain the default camera when allowsCameraControl is enabled.
The answer to Rotate SCNCamera node looking at an object around an imaginary sphere outlines an approach that might work for you, depending on what constraints you need to build.

How to setup camera to point at object

In my app I load models from different files (format is the same) and they have different geometry: big, small, wide, etc. I have object and camera position hard coded and for some cases I don't see anything because camera not point to object.
Maybe there is a way to normalise model before adding it to scene.
Update.
With Moustach answer I came up to following solution:
// import object from file
SCNNode *object = [importer load:path];
object.position = SCNVector3Make(0, 0, 0);
[scene.rootNode addChildNode:object];
// create and add a camera to the scene
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
// to avoid view clipping
cameraNode.camera.automaticallyAdjustsZRange = YES;
// set camera position to front of object
SCNVector3 sphereCenter;
CGFloat sphereRadius;
[object getBoundingSphereCenter:&sphereCenter radius:&sphereRadius];
cameraNode.position = SCNVector3Make(sphereCenter.x, sphereCenter.y, sphereCenter.z + 2 * sphereRadius);
[scene.rootNode addChildNode:cameraNode];
Works well for me.
You can calculate the bounding box of your mesh, and scale it based on the number you get to make it the same size as other objects.

Physics forces not affecting dynamic body Scenekit

My scene contains;
A static body at the root of the scene
_firstNode = [[SCNNode alloc] init];
_firstNode.position = SCNVector3Make(0, 0, 0);
_firstNode.geometry = [SCNSphere sphereWithRadius:950];
_firstNode.geometry.firstMaterial.diffuse.contents = [UIColor greenColor];
_firstNode.physicsBody = [SCNPhysicsBody staticBody];
[[scene rootNode] addChildNode:_firstNode];
A dynamic body with it's pivot property at (0,-1000,0) with a SCNAction that permanently rotates it around the static body.
_secondNode = [[SCNNode alloc] init];
_secondNode.position = SCNVector3Make(0, 0, 0);
_secondNode.pivot = SCNMatrix4MakeTranslation(0, -1200, 0);
_SecondNode.physicsBody = [SCNPhysicsBody dynamicBody];
_secondNode.geometry = [SCNSphere sphereWithRadius:2];
_secondNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:-1 y:0 z:0 duration:10.0]]];
[[scene rootNode] addChildNode:_secondNode];
When I try apply;
[_secondNode.physicsBody applyTorque:SCNVector4Make(0, 1, 0, 1) impulse:YES];
There is no effect on the node as if it was declared as a static body.
If I am reading what you want correctly, set your second node as so...
_secondNode.position = SCNVector3Make(0,-1200,0);
This will off set it from the firstNode
Then set its pivot point like so...
_secondNode.pivot = _firstNode.pivot;
This sets the pivot to the same spot as your firstNode and allows it to rotate around that point. This will only work as long as your firstNode pivot property has not been changed.
Lastly set your action like so...
SCNAction rotateByX:M_PI*2 y:0 z:0 duration:10.0
Noticed I changed your x setting to M_PI*2 which is equal to 360 degrees. You can use "-M_PI" to rotate the opposite way.
For physics to simulate the node needs a physicsShape assigned, which is achieved with;
_secondNode.physicsBody.physicsShape = [SCNPhysicsShape shapeWithGeometry:[SCNSphere sphereWithRadius:2] options:nil];

Physics Issue When Loading SCNScene from .dae

I am attempting to load a scene that is currently just 3 walls, a ceiling and a floor. I am loading the scene that I created in blender and load it in fine. However, a SCNNode with a geometry of a SCNBox just falls right through. The box has a dynamic physics body attached to it and the I am manually setting the walls/floor to be static nodes. Below is the code I am using to set up the scene and add the box, I can also post my .dae if needed. Anyone have any ideas as to what might be going on?
//Load the scene from file
SCNScene *scene = [SCNScene sceneNamed:#"mainScene.dar"];
//Get each node in the scene, and give it a static physics bodt
for (SCNNode *node in [[scene rootNode] childNodes]) {
SCNPhysicsBody *staticBody = [SCNPhysicsBody staticBody];
staticBody.restitution = 1.0;
node.presentationNode.physicsBody = staticBody;
NSLog(#"node.name %#",node.name);
}
//Create box
SCNNode *block = [SCNNode node];
block.position = SCNVector3Make(0, 0, 3);
//Set up the geometry
block.geometry = [SCNBox boxWithWidth:.8 height:.8 length:.8 chamferRadius:0.05];
block.geometry.firstMaterial.diffuse.mipFilter = SCNFilterModeLinear;
block.castsShadow = YES;
//Make it blue
for (SCNMaterial *mat in block.geometry.materials) {
mat.emission.contents = [UIColor blueColor];
}
//Add physics body
SCNPhysicsBody *body = [SCNPhysicsBody staticBody];
body.mass = 5;
body.restitution = .7;
body.friction = 0.5;
block.physicsBody = body;
//Add the node to the scene
[[scene rootNode] addChildNode:block];
In response to ricksters answer I tried to create custom geometry for each new node, but my box still falls through. Here is the code that I am using for the custom geometry. This replaces the for-in in the original code.
//Get each node in the scene, and give it a static physics bodt
for (SCNNode *node in [[scene rootNode] childNodes]) {
SCNGeometry *geometry = [SCNBox boxWithWidth:node.scale.x height:node.scale.y length:node.scale.z chamferRadius:0.0];
SCNPhysicsShape *physicsShape = [SCNPhysicsShape shapeWithGeometry:geometry options:nil];
SCNPhysicsBody *staticBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeStatic shape:physicsShape];
staticBody.restitution = 1.0;
node.physicsBody = staticBody;
}
So I had a similar issue. I found that the issue stemmed from how the file was created in the 3d modeling software. I was using Blender to test this; I made a plane with a box, added a physics body to the box and the plane and the box fell through. I realized this had to do with the scale. In Blender, I applied the object transformation which reset the scale to 1.0 1.0 1.0 by pressing CTRL A and selecting the scale option. So ultimately what seems to be happening is that SceneKit uses the base geometry ignoring the transform of the geometry. What you are seeing on the screen is the base geometry with the node's transform applied. Set the transform to identity before exporting the Collada file and you should be set.
Depending on how the geometry for those nodes is built in the DAE, SceneKit may not be able to automatically construct a physics shape that does what you want.
Instead, use bodyWithType:shape: to create exactly the collision shape you want, separate from the node's visible geometry. Creating physics shapes from parametric geometry (e.g. SCNBox, SCNSphere) works best.

Resources