SceneKit follow camera? - objective-c

I am trying to make a follow camera in scenekit. I have just started, so try to bear with me. I have a node (robotNode) and am trying to have the camera follow the robot. I have partially achived this by doing adding the camera as a child node of the robot:
cameraNode = [SCNNode node];
cameraNode.camera = [SCNCamera camera];
[robotNode addChildNode:cameraNode];
// place the camera
cameraNode.position = SCNVector3Make(0, 0, 10);
But the problem is when I start to rotate the camera, it doesn't follow the node anymore. See here:
How can I get it to continue to follow the node?

What you've written will create a camera node a fixed distance from the robot, but you've done nothing to control where the camera points.
Create an SCNLookAtConstraint whose target is the robot node. Attach that to the camera node.
// warning, written in browser, untested
SCNLookAtConstraint *robotStare = [SCNLookAtConstraint lookAtConstraintWithTarget:robotNode];
// and maybe also
robotStare.gimbalLockEnabled = YES;
cameraNode.constraints = #[robotStare];
// OP added this, to make camera follow node. I'm skeptical.
cameraNode.camera.usesOrthographicProjection = YES;

Related

Is there any way to add a regular ARAnchor on a detected image?

I'm trying to put a 3D object on top of the detected image, and it worked. But when I moved the camera around the image, the object didn't stay at the center of image. Is there any way to add a regular anchor at the center of image to help me fix the 3D object at the right position? The following code is what I've tried, but it not worked.
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
if ([anchor isKindOfClass:[ARImageAnchor class]]) {
ARAnchor *newAnchor = [[ARAnchor alloc] initWithTransform:anchor.transform];
[self.sceneView.session addAnchor:newAnchor];
}
}
I detect an image and put a plane on it, it looks center correctly
But when I move the camera to another position, it doesn't locate on the center of image
There's no need to create a new anchor because there's already one provided by ARKit. You should add your 3D content to the node provided by this method.
According to the renderer:didAddNode:forAnchor: documentation:
You can provide visual content for the anchor by attaching geometry (or other SceneKit features) to this node or by adding child nodes.
So, in this method:
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor
{
[node addChildNode: your3DObjectNode];
}
Then it should stay at the center of your image.

ExoPlayer in Android TV plays video in portrait mode instead of landscape

We are using ExoPlayer to play m3u8 files (stream) on Android TV. The streaming is working fine, but the video plays in portrait mode (even if the video is shot in landscape).
Looks like some issue with orientation of the android TV instead of aspect ratio.
private fun initializePlayer() {
if(mPlayer == null) {
playerView = activity!!.findViewById<SimpleExoPlayerView>(R.id.texture_view)
// playerView!!.setControllerVisibilityListener(this)
playerView!!.requestFocus()
val bandwidthMeter = DefaultBandwidthMeter()
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory(bandwidthMeter)
mTrackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
mPlayer = ExoPlayerFactory.newSimpleInstance(activity, mTrackSelector)
playerView!!.player= mPlayer
mPlayerAdapter = LeanbackPlayerAdapter(activity, mPlayer, UPDATE_DELAY)
mPlayerGlue = VideoPlayerGlue(activity!!, mPlayerAdapter!!)
mPlayerGlue!!.host = VideoSupportFragmentGlueHost(this)
mPlayerGlue!!.playWhenPrepared()
play(s1)
}
}
Commenting these lines :
mPlayerAdapter = LeanbackPlayerAdapter(activity, mPlayer, UPDATE_DELAY)
mPlayerGlue = VideoPlayerGlue(activity!!, mPlayerAdapter!!)
mPlayerGlue!!.host = VideoSupportFragmentGlueHost(this)
mPlayerGlue!!.playWhenPrepared()
Plays the video in landscape but the player controls are hidden and it only plays the lowest quality of the video. Please help us with this.
Metadata of the MP4 video contains a property called Rotation=90° but it's ignored by the ExoPlayer. To fix it you need to inject this Java function into your code:
void onVideoSizeChanged(int width,
int height,
int unappliedRotationDegrees, // 90° or 270°
float pixelWidthHeightRatio);
This allows an application using TextureView to easily apply the rotation by making the appropriate call to TextureView.setTransform. Note that on Lollypop+ unappliedRotationDegrees will always be equal to 0.
You can find this function on a line #74 at MediaCodecVideoTrackRenderer page of GitHub.
If the above-mentioned method doesn't work for you, you may find another remedy in Rotation Issue #91 post on a GitHub.
As far i know, exoplayer will generate its size based on texture view size. So try to programmatically resize your texture view by
playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL);
and also try to resize your player programmatically
mPlayer.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
Hope this will help.

Cesium: Having the camera in an entity's first person view

I would like to have my camera follow the first-person view of a moving entity. I do not believe that trackedEntity will work for this use case because I don't want to look at the entity, but I want to look out from it. I would also like the user to be able to use the mouse to turn the camera with respect to the moving entity (for example, to look out the left window of a moving plane).
In a traditional game engine, I would do this by attaching the camera to the entity, so it would move with it, but retain its own local transform with respect to the entity so that it was free to move with respect to the entity.
The only way I can think of right now is to keep track of the "user-controlled" transform separately and multiply it with the entity transform at every clock tick. Is there a better way?
Have a look at Cesium's Cardboard sandcastle example. Here you are on board of a hot-air balloon and perceive the world from there. After scrolling out, you can pan with the mouse to look around. Since the calculations are quite complicated, I cannot give any details how it works, but it seems that the camera view is aligned to the moving direction of the entity. The essential part of the script is:
// Set initial camera position and orientation to be when in the model's reference frame.
var camera = viewer.camera;
camera.position = new Cesium.Cartesian3(0.25, 0.0, 0.0);
camera.direction = new Cesium.Cartesian3(1.0, 0.0, 0.0);
camera.up = new Cesium.Cartesian3(0.0, 0.0, 1.0);
camera.right = new Cesium.Cartesian3(0.0, -1.0, 0.0);
viewer.scene.postUpdate.addEventListener(function (scene, time) {
var position = entity.position.getValue(time);
if (!Cesium.defined(position)) {
return;
}
var transform;
if (!Cesium.defined(entity.orientation)) {
transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);
} else {
var orientation = entity.orientation.getValue(time);
if (!Cesium.defined(orientation)) {
return;
}
transform = Cesium.Matrix4.fromRotationTranslation(
Cesium.Matrix3.fromQuaternion(orientation),
position
);
}
// Save camera state
var offset = Cesium.Cartesian3.clone(camera.position);
var direction = Cesium.Cartesian3.clone(camera.direction);
var up = Cesium.Cartesian3.clone(camera.up);
// Set camera to be in model's reference frame.
camera.lookAtTransform(transform);
// Reset the camera state to the saved state so it appears fixed in the model's frame.
Cesium.Cartesian3.clone(offset, camera.position);
Cesium.Cartesian3.clone(direction, camera.direction);
Cesium.Cartesian3.clone(up, camera.up);
Cesium.Cartesian3.cross(direction, up, camera.right);
});
Maybe you can try to modify the camera vectors or multiply the transform with another rotation matrix to simulate turning one's head (to look left/right/back) while being in the initial perspective. For instance, you can try to combine the example above with code from a repository called Cesium First Person Camera Controller.
Had to figure this out myself as well.
Camera.setView and self-defined utility functions are your friend.
E.g. Here is a naive implementation of rotation (does not work well when the pitch of the camera is too "birds-eye-view" like):
Cesium.Camera.prototype.rotateView = function(rotation) {
let { heading, pitch, roll } = rotation;
heading = this.heading + (heading || 0);
pitch = this.pitch + (pitch || 0);
roll = this.roll + (roll || 0);
const destination = this.position;
this.setView({
destination,
orientation: {
heading,
pitch,
roll
}
});
};
Similarly you can update the position with the position of the entity by providing destination.

How to get the real position of a sub node in SceneKit after rotation?

I am developing a scene with SceneKit. I have a main node with a subnode:
// Main node
SCNNode* planet = [SCNNode node];
planet.geometry = [SCNSphere sphereWithRadius:2];
planet.position = SCNVector3Make(0, -3, 5);
// sub-node
SCNNode* satellite = [SCNNode node];
satellite.geometry = [SCNSphere sphereWithRadius:0.4];
satellite.position = SCNVector3Make(4, 0, 0);
[planet addChildNode:satellite];
[scene.rootNode addChildNode:planet];
I use a NSTimer to make some actions and some animations. In the timer event I do that:
planetRotation += 0.1;
planet.rotation = SCNVector4Make(0,1,0, planetRotation);
But if i try to get the position of the satellite node I always get the same value.
I tried to get the positionnode to know the real position of the satellite node but nothing changes.
How can I get the real position of a sub-node when I change the rotation of the parent node?
Thanks in advance
the position of a node is expressed in its parent coordinate system. Just like for views in UIKIt/AppKit. If you change the frame of a view, the frame of its subviews does not change.
What you want is what we call the world transform of the subnode (i.e. its transform expressed in the coordinate system of the scene's root node).
You can have a look at worldTransform and -convertPosition:toNode:.
The node's position won't change due to physics, that is why you aren't seeing it change. You need to call the node's presentationNode to get the position of the node as it is presented onscreen:
node.presentationNode.position

Camera as child of Object3D positioning issues

Setup a simple scene here:
http://jsfiddle.net/majman/Sps3c/
I was initially trying to demonstrate a problem I was having with rotating a parent container while having the camera maintain it's relative offset position, but when setting up this example I couldn't even adjust the camera's initial position.
Current Problem:
// camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 5, 150);
camera.position.z = 50; // this doesn't work?
// object to contain camera & helper
cameraContainer = new THREE.Object3D();
cameraContainer.rotation.order = "YXZ"; // maybe not necessary
// add to container
cameraContainer.add(camera);
scene.add(cameraContainer);
Now when rotating the cameraContainer, the camera's rotation follows - but I'd like the camera's position to be offset from the cameraContainer. I'm unable to modify any position properties for some reason.
Your code is working fine. You are confused because the CameraHelper is not displaying the camera in its actual position. You need to add the CameraHelper as a child of the scene.
// camera helper
cameraHelper = new THREE.CameraHelper( camera2 );
scene.add( cameraHelper );
updated fiddle: http://jsfiddle.net/Sps3c/1/
Tip: I added an OrbitController to your demo for a better view of the situation.
three.js r.66