Node moving when pivot is inside the geometry of another node - ios

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,-1200,0)
_secondNode = [[SCNNode alloc] init];
_secondNode.position = SCNVector3Make(0, 0, 0);
_secondNode.pivot = SCNMatrix4MakeTranslation(0, -1200, 0);
_secondNode.physicsBody = [SCNPhysicsBody dynamicBody];
_secondNode.physicsBody.physicsShape = [SCNPhysicsShape shapeWithGeometry:[SCNSphere sphereWithRadius:2] options:nil];
[[scene rootNode] addChildNode:_secondNode];
Since both nodes are children of the rootNode the pivot is located in the centre of the static node. The idea is to run a SCNAction that rotates _secondNode around the _firstNode about it's X axis and applyTorque as needed to rotate about it's Y axis.
That code is fine for the moment, the problem is that while _firstNode remains static, it is pushing _secondNode.pivot to the surface of _firstNode's geometry. However simply taking around _firstNode's physicsBody away doesn't solve the problem either.

Sounds like it is getting too much friction as it is trying to spin. Try setting your angularDamping down to 0. Its default is 0.1. The angularDamping simulates the amount of friction as an object spins. A value of 0 simulates no friction (spin for ever). A value of 1.0 simulates 100% friction or static basically.
Set it like so...
yourNode.physicsBody.angularDamping = 0

Related

ios sprite kit bodyWithBodies not working

SKSpriteNode * test = [[SKSpriteNode alloc] initWithColor:[UIColor redColor] size:CGSizeMake(100, 100)];
[self addChild:test];
[test setPosition:CGPointMake(300, 300)];
// bottom
CGSize textureSize = CGSizeMake(100, 100);
SKPhysicsBody *physicsBottom = [SKPhysicsBody bodyWithRectangleOfSize:textureSize];
physicsBottom.categoryBitMask = ColliderBottom;
physicsBottom.contactTestBitMask = ColliderBottom | ColliderObstacle;
// working...
// test.physicsBody = physicsBottom;
// not working... this line is problem.
test.physicsBody = [SKPhysicsBody bodyWithBodies:#[physicsBottom]];
didEndContact not working by this code.
test.physicsBody = [SKPhysicsBody bodyWithBodies:#[physicsBottom]];
what's problem????
typedef enum {
ColliderObstacle = 1 << 0,
ColliderCharacter = 1 << 2,
ColliderLeft = 1 << 3,
ColliderRight = 1 << 4,
ColliderBottom = 1 << 5,}ColliderType;
// not wokring... this line... I don't understand.
Why didEndContact event not working???????
I tried the way to set the physicsBody in my project and couldn't reproduce your problem.
However,by compared the code between yours and mine, I think your problem is that you create ONE physicsBody and set it to EVERY "collider" you create, which is related to the copying issues.
Perhaps this can be fixed by adding copy method:
renderComponent.node.physicsBody = [SKPhysicsBody bodyWithBodies:#[[physicsComponentBottom.physicsBody copy]
]];
I read the documentation later and find the restriction of the method,that it only support volume-based physicsBody.This might be your problem.
Sprite Kit supports two kinds of physics bodies, volume-based bodies and edge-based bodies. When you create a physics body, its kind, size, and shape are determined by the constructor method you call. An edge-based body does not have mass or volume and is unaffected by forces or impulses in the system. Edge-based bodies are used to represent volume-less boundaries or hollow spaces in your physics simulation. In contrast, volume-based bodies are used to represent objects with mass and volume.

Camera Rotation in SceneKit

I have posted a similar question to this
here , but this is different in that it deals with eular angles.
Given the setup that I had in the other post. A simple board that is on the screen, and a camera that is looking at it, I want to rotate the camera. For simplicity I am doing the entire camera in code. For context, per the other question and answer I have established that the board runs long on the Z axis, Shorter on the X axis, and the height is the Y axis.
Adding this code to the scene we can see my board running on the Z axis. I have raised the camera up on the Y axis a little to get a better view. My end goal is to get the board running longways accross the camera.
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
cameraNode.camera.zFar = 200;
cameraNode.camera.zNear = 0.1;
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 5, 0);
cameraNode.eulerAngles = SCNVector3Make(0, 0, 0);
Gives
Great start. Then I try to rotate the camera so it is looking from the top, down on the board. My understanding it that I would need to rotate around the X-axis to accomplish this. I did this by trying the following.
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
cameraNode.camera.zFar = 200;
cameraNode.camera.zNear = 0.1;
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 50, 0);
cameraNode.eulerAngles = SCNVector3Make(0, 0, -M_PI/2);
The iOS documentation states that the rotation in eulars is set with (z, y, x). This did not work, however, and only gave a blank screen. I then started experimenting and found that rotation around the Z axis would get me in the right direction.
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
cameraNode.camera.zFar = 200;
cameraNode.camera.zNear = 0.1;
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 50, 0);
cameraNode.eulerAngles = SCNVector3Make(-M_PI/2, 0, 0);
This rendered this screen.
This didn't make sense but I went ahead with it and eventually found that by also Rotating around the Y axis I could get my desired screen.
SCNNode *cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
cameraNode.camera.zFar = 200;
cameraNode.camera.zNear = 0.1;
[scene.rootNode addChildNode:cameraNode];
cameraNode.position = SCNVector3Make(0, 50, 0);
cameraNode.eulerAngles = SCNVector3Make(-M_PI/2, -M_PI/2, 0);
The y rotation was also a little baffling because I would have expected to have to rotate around the Z axis.
While this code works I have a similar question to my other post. Why do I have to rotate around the Z axis instead of the X axis for my first rotation. I think I might just not understand eular angles that well.
Thanks in advance
Edit:
as #mnuages pointed out, the documentation online states that this vector is in (x,y,z). This is contrary to the header documentation which I would normally use as can be seen here
It would appear that this is just a bug/typo in Apple's code. Thanks for clearing it up.
[Comment does not fit in a comment]
Please note that the documentation states that
The order of components in this vector matches the axes of rotation:
Pitch (the x component) is the rotation about the node’s x-axis.
Yaw (the y component) is the rotation about the node’s y-axis.
Roll (the z component) is the rotation about the node’s z-axis.

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