I downloaded sample code from https://github.com/yshrkt/VuforiaSampleSwift to implement Vuforia with swift. It didn't work oob with the latest SDK, but I have successfully loaded xml/dat dataSet, and when I point to target method ViewController.createStonesScene(with view: VuforiaEAGLView) -> SCNScene is called. Unfortunately I don't see anything on camera preview. There should be a plane with background color, but nothing is added. Can someone help me resolve this? I wan't to display a simple plane for start. My project is at: https://www.dropbox.com/s/fk71oay1sopc1vp/test.zip?dl=1
Please update Vuforia License key in AppDelegate, I can't provide it as I'm not the owner of this application.
I get to the point, that I know, that scene is being displayed, but camera transformations must be wrong. Either _cameraNode.camera.projectionTransform or _cameraNode.transform (or both).
I added zNear = 0.01 to cameraNode, which was a little step. Now If I just apply cameraNode.transform and disable cameraNode.camera.projectionTransform I can see a node of correct size, but it's rotated 90 degrees, and also when I move camera it moves 90 degrees wrong (moving up/down moves it left/right). A little bit of code that does this is below:
- (void)setNeedsChangeSceneWithUserInfo: (NSDictionary*)userInfo {
SCNScene* scene = [self.sceneSource sceneForEAGLView:self userInfo:userInfo];
if (scene == nil) {
return;
}
SCNCamera* camera = [SCNCamera camera];
_cameraNode = [SCNNode node];
_cameraNode.camera = camera;
_cameraNode.camera.zNear = 0.01;
// _cameraNode.camera.projectionTransform = _projectionTransform;
[scene.rootNode addChildNode:_cameraNode];
_renderer.scene = scene;
_renderer.pointOfView = _cameraNode;
}
// Set camera node matrix
- (void)setCameraMatrix:(Vuforia::Matrix44F)matrix {
SCNMatrix4 extrinsic = [self SCNMatrix4FromVuforiaMatrix44:matrix];
SCNMatrix4 inverted = SCNMatrix4Invert(extrinsic);
_cameraNode.transform = inverted;
}
- (void)setProjectionMatrix:(Vuforia::Matrix44F)matrix {
_projectionTransform = [self SCNMatrix4FromVuforiaMatrix44:matrix];
// _cameraNode.camera.projectionTransform = _projectionTransform;
}
So now I need to rotate whole scene 90 degrees. I think it might have been in _cameraNode.camera.projectionTransform = _projectionTransform;, but when I enable this, I can't see anything anymore. How to apply 90" rotation to this scene?
Here is a video of what I mean: https://www.dropbox.com/s/z6pwaztlfyad8fx/ScreenRecording_09-03-2018%2015-01-17.MP4?dl=0
I think this can be in VuforiaManager.mm:
// Cache the projection matrix
const Vuforia::CameraCalibration& cameraCalibration = Vuforia::CameraDevice::getInstance().getCameraCalibration();
_projectionMatrix = Vuforia::Tool::getProjectionGL(cameraCalibration, 0.05f, 5000.0f);
[_eaglView setProjectionMatrix:_projectionMatrix];
Anyone know how to fix this?
Related
I have an interesting problem. I create an SKSpriteNode with associated physics body.
SKTexture *shipTexture = [SKTexture textureWithImageNamed:#"Spaceship"];
self.heroSpriteNode = [HeroSpriteNode spriteNodeWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody = [SKPhysicsBody bodyWithTexture:shipTexture size:CGSizeMake(40, 35.2)];
self.heroSpriteNode.physicsBody.dynamic = YES;
self.heroSpriteNode.physicsBody.mass = 1000.0;
self.heroSpriteNode.physicsBody.affectedByGravity = NO;
self.heroSpriteNode.physicsBody.allowsRotation = YES;
self.heroSpriteNode.physicsBody.angularDamping = 0.5;
self.heroSpriteNode.anchorPoint = CGPointMake(0.5,0.5);
self.heroSpriteNode.physicsBody.categoryBitMask = heroCategory;
self.heroSpriteNode.physicsBody.contactTestBitMask = groundCategory;
self.heroSpriteNode.physicsBody.collisionBitMask = groundCategory | edgeCategory;
The Scene itself has no gravity.
In the update routine I call applyTorque: to the physics body.
-(void) updateWithDeltaTime:(NSTimeInterval)seconds
{
CGFloat rotateTorque = self.rotateRate;
switch (self.rotateForceApplied)
{
case leftForceApplied:
break;
case rightForceApplied:
rotateTorque = -1.0 * rotateTorque;
break;
case zeroForceApplied: // separate from default in case behavior should change
rotateTorque = 0.0;
break;
default:
rotateTorque = 0.0;
break;
}
NSLog(#"before physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
[self.heroSpriteNode.physicsBody applyTorque:rotateTorque];
NSLog(#"after physics position: x %f, y %f", self.heroSpriteNode.position.x, self.heroSpriteNode.position.y);
}
The sprite rotates and appears to rotate in place. What it is actually doing is rotating around a center point (so no net lateral movement). This is seen by logging the sprite position before and after applying the torque. No other actions are being applied to the physics body. The movement in position is about 9 points in each direction at most.
Because the camera is pinned by constraints to the "hero" sprite, and the world moves with the camera (the centerOnNode: sample code), this causes the whole world to move in a circular pattern as the sprite spins. The world itself does not spin but moves at the same rate in a circular pattern with the spinning.
With the sprite anchorPoint being 0.5, 0.5 I would think it should rotate around a center point which should not change the position of the sprite.
What would I being doing wrong with this?
If it matters this is on iOS9, Xcode7, running on device not in the simulator. (The iOS9 SpriteKit documentation is publicly available on Apple's website, as is a public beta of iOS9 itself so this should not be breaking any NDA, and I don't think anything here is iOS9 specific anyway)
Sprite Kit is a physics engine that attempts to mimic characteristics of objects in the real world, so you should expect an object with a physics body in the simulation to behave as they would in nature. In this case, you are rotating a non-uniform physics body that may rotate non-uniformly and, possibly, move over time (unless the body is rotating about its center of mass). If you change the physics body to a circle, the sprite should rotate uniformly and should remain at a fixed location.
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 am working on an iOS camera based app, in which I have to capture a first point and then I need to draw the line to the current focus point to the first captured point. MagicPlan works this way.
Here is an image:
I have tried to fix a point for first point using accelerometer values and the tilted angle of the device. But, no luck so far. And how would i draw the line to the second point from the first point?
This is the code that i have tried so far:
if (self.motionManager.deviceMotionAvailable)
{
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler: ^(CMDeviceMotion *motion, NSError *error) {
CATransform3D transform;
transform = CATransform3DMakeRotation(motion.attitude.pitch, 1, 0, 0);
transform = CATransform3DRotate(transform,motion.attitude.roll, 0, 1, 0);
transform = CATransform3DRotate(transform,motion.attitude.yaw, 0, 0, 1);
self.viewObject.layer.transform = transform;
}];
}
if (self.motionManager.deviceMotionActive)
{
/**
* Pulling gravity values from deviceMotion sensor
*/
CGFloat x = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.x];
CGFloat y = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.y];
CGFloat z = [self convertRadianToDegree:self.motionManager.deviceMotion.gravity.z];
CGFloat r = sqrtf(x*x + y*y + z*z);
/**
* Calculating device forward/backward title angle in degrees
*/
CGFloat tiltForwardBackward = acosf(z/r) * 180.0f / M_PI - 90.0f;
[self.lblTilForwardBackward setText:[#(tiltForwardBackward) stringValue]];
}
You have a lot of issues to resolve here. It isn't just a matter of adjusting for camera orientation as the height that the camera is being held at and position of the camera in the room are also changing. Even in MagicPlan, when the person turns around, the camera moves (rotates about the axis going through the person's head down to his feet).
There is quite a lot of algebra and rotation/translation matrix operations to work out. No one is going to do this for you. You'll have to figure it all out and derive it yourself (or look it up from old graphics text books).
I suggest doing something as straight forward and multi-step as possible (so you can debug each step along the way). Assume flat ground (indoor environment).
Get camera position/orientation/focal length from the first snapshot.
Figure out the touch point in real world Cartesian coordinates(start with video coordinates and translate via roll/pitch/yaw and ray traced projection to ground plane(using camera height).
From the focal length you can figure out the field of view and depth to center of field of view and using camera orientation and click distance from center of screen determine xyz offset from some origin (your feet maybe).
Determine and track camera position and orientation relative to that origin.
On second snapshot (or motion awake), figure out (center or touched point) distance from origin and exact xyz (as above).
Once you have those two points in xyz you can plot the line by taking the standard orthogonal projection onto the view plane. Clipping as needed in case original point is out of the FOV.
Let's say we have a SCNNode and we want to rotate, change flight direction and move in space with no gravity. The node currently is just a camera. actually it should react to gyroscope accelerometer data. i think scene update can be done in - (void)renderer:(id <SCNSceneRenderer>)aRenderer didSimulatePhysicsAtTime:(NSTimeInterval)time.
The problem I'm facing is how to utilise the gyroscope & accelerometer data for calculating flight direction, and how to calculate the rotation direction & movement. I refreshed my memory about 3D rotation matrix for each axis(x,y,z) but still missing above mentioned part to solve this problem. May be thinking very complicated and the solution is very easy.
- (void)viewDidLoad
{
SCNView *scnView = (SCNView *) self.view;
SCNScene *scene = [SCNScene scene];
scnView.scene = scene;
_cameraNode = [[SCNNode alloc] init];
_cameraNode.camera = [SCNCamera camera];
_cameraNode.camera.zFar = 500;
_cameraNode.position = SCNVector3Make(0, 60, 50);
_cameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI_4*0.75);
[scene.rootNode addChildNode:_cameraNode];
scnView.pointOfView = _cameraNode;
[self setupAccelerometer];
}
- (void)setupAccelerometer
{
_motionManager = [[CMMotionManager alloc] init];
GameViewController * __weak weakSelf = self;
if ([[GCController controllers] count] == 0 && [_motionManager isAccelerometerAvailable] == YES) {
[_motionManager setAccelerometerUpdateInterval:1/60.0];
[_motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) {
[weakSelf accelerometerDidChange:accelerometerData.acceleration];
}];
}
}
- (void)accelerometerDidChange:(CMAcceleration)acceleration
{
//acceleration.x
//acceleration.y
//acceleration.z
// now we have the data and saved somewhere
}
If you want to combine gyroscope and accelerometer data to get an orientation, you'd probably be better off letting CoreMotion do that for you. The methods listed under Managing Device Motion Updates in the docs get you CMDeviceMotion objects; from one of those you can get a CMAttitude that represents an orientation, and from that you can get a rotation matrix or quaternion that you can apply to a node in your scene. (Possibly after further transforming it so that a neutral orientation fits into your scene the way you want.)
After you've oriented the node the way you want, getting a flight direction from that should be pretty simple:
Choose a flight direction in terms of the local coordinate space of the node and make a vector that points in that direction; e.g. a camera looks in the -z direction of its node, so if you want the camera node to look along its flight path you'll use a vector like {0, 0, -1}.
Convert that vector to scene space with convertPosition:toNode: and the scene's rootNode. This gets you the flight direction in terms of the scene, taking the node's orientation into account.
Use the converted vector to move the node, either by assigning it as the velocity for the node's physics body or by using it to come up with a new position and moving the node there with an action or animation.
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.