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].
Related
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;
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.
I'm trying to use categoryBitMask in SceneKit to make it so certain SCNLight lights only affect certain object nodes.
The documentation seems simple enough, but I can't get things to work properly.
For example, here's my light:
SCNLight *lightFront = [SCNLight light];
lightFront.color = [UIColor colorWithWhite:0.90f alpha:1.0f];
lightFront.type = SCNLightTypeOmni;
lightFront.castsShadow = YES;
lightFront.categoryBitMask = 2;
And here's my object node:
characterNode = [SCNNode node];
SCNScene *characterScene = [SCNScene sceneWithURL:[[NSBundle mainBundle] URLForResource:#"character_idle" withExtension:#"dae"] options:nil error:nil];
for( SCNNode *node in characterScene.rootNode.childNodes ) {
node.categoryBitMask = 2;
[characterNode addChildNode:node];
}
characterNode.categoryBitMask = 2;
The categoryBitMask in both cases match. But! lightFront does not affect the characterNode.
If I change the light's categoryBitMask to 3, though, the node does show the light:
lightFront.categoryBitMask = 3;
This should work the opposite, right?
Other object nodes in the scene -- even those with different categoryBitMasks -- all seem to get the same lighting effects. Meaning, lightFront above will be visible or invisible on all object nodes in my scene, regardless of categoryBitMask. As far as I can tell.
Help!
I want to create a metal ball on the screen, I had the material and run animation done. But When the ball was rolling that I hope the texture shouldn't roating. Because that made rolling ball not look like metal ball...
There is anyways or not? I could make SKSpriteNode don't rotate the ball.
SKSpriteNode *node = [self spriteNodeWithImageNamed:#"ball.png"];
node.name = #"ball";
node.size = CGSizeMake(sideSize, sideSize);
node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius: sideSize/2];
node.physicsBody.restitution = 0.2;
node.physicsBody.friction = 0.01;
node.physicsBody.angularDamping = 0.5;
node.physicsBody.categoryBitMask = GMCategoryBall;
node.position = CGPointMake(wood.position.x,wood..position.y +wood..size.height);
node.physicsBody.contactTestBitMask =.....
node.physicsBody.collisionBitMask =.....
Here are some suggestions:
Add another SKSpriteNode without a physicsBody that is just there for looks. Have it always centered on your ball.
Add another SKSpriteNode as a sub-node of your ball, and set its rotation to the opposite of your ball's rotation.
Add another SKSpriteNode, set its physicsBody to a bit mask value that won't interact with anything else in the scene (0 is a good choice) and set a joint that keeps it locked to the ball. Turn off rotation for that Sprite.
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.