Making an entity rotate with the camera - Aframe - view

I'm attempting to make a rolodex menu in Aframe like this.
Here is the glitch so far https://glitch.com/edit/#!/fourth-kitten
I'm trying to get the entity to rotate into view with the camera
AFRAME.registerComponent('rotate-with-camera', {
tick: function (){
console.log(this)
if(this.el.sceneEl.camera){
const {rotation} = this.el.sceneEl.camera.parent
const containerRotation = this.el.getAttribute('rotation')
this.el.setAttribute('rotation', {...containerRotation, z: containerRotation.y -= rotation._y * 360})
}
}
})
However I can't get a nice smooth roll like this example and I'm confused as to the right maths to get it staying in front of the camera on roll up or roll down to make the next row animate into view.
Any ideas?

If you want to position it in front of the camera, then just use the parent - child DOM hierarchy:
<a-camera>
<a-entity position="0 0 -3></a-entity>
</a-camera>
This way your menu will be in front of the camera, and will be rotating with the camera.
To access outer elements, you can move the container a bit when the camera is looking right - left
Glitch here.

Related

Make children of imported Three.js group follow pointer when their canvas gets hovered

In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when they get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes in Three.js).
I think, firstly, I have to detect, where currently pointer is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far:
Hi everyone.
In Three.js, I have a group of meshes that is loaded from outside with help of FBX loader. The group has six meshes inside. My task is to make this meshes follow pointer when their canvas get hovered. More precisely, I'd like to have a sort of magnetic effect (just like navbar items in this pen, but with meshes of Three.js).
I think, firstly, I have to detect, where currently pointer on canvas is, i.e. get position of cursor in world coordinates system, and then translate meshes towards it. But when I try to get the position of cursor, it seems to be wrong.
Having said that, I have two questions:
How to get proper cursor's position relative to the world coordinates?
How to change position of each of the group's meshes so that they get translated against the cursor?
Here is what have I done so far. Function that translates meshes isn't written yet. Mousemove callback returns pretty big digits, though:
// Load object and play a third-party animation
loader.load("Object_001.fbx", (object) => {
mixer = new THREE.AnimationMixer(object);
const action = mixer.clipAction(object.animations[0]);
action.play();
object.traverse((child) => {
if (child.isMesh) {
child.material.map = texture;
child.material.needsUpdate = true;
}
});
scene.add(object);
});
// log coordinates of the pointer
const mouse = new THREE.Vector3();
const position = new THREE.Vector3();
function onMouseMove(event) {
mouse.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1,
0.5
);
mouse.unproject(camera);
mouse.sub(camera.position).normalize();
const distance = -camera.position.z / mouse.z;
position.copy(camera.position).add(mouse.multiplyScalar(distance));
console.log(position);
}
wrapperElement.addEventListener("mousemove", onMouseMove);
Thanks in advance.
Made a codepen here:
https://codepen.io/cdeep/pen/YzxPPZQ
The cursor only exists in the canvas dom element which is a rendering of the camera view frustum.
The easiest way to make the object follow a mouse is to get the point of intersection of the mouse with another object in the 3d scene and set the object position to the intersection point. The above codepen showcases that.
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects([ground]);
if(intersects.length) {
const { point } = intersects[0];
cube.position.copy(point.setY(0.5));
}
You could also position it at a fixed distance from the mouse but it looks odd in my opinion:
const distance = 10;
raycaster.setFromCamera( mouse, camera );
const { origin, direction } = raycaster.ray;
cube.position.copy(origin.clone().add(direction.multiplyScalar(distance)));
Documentation for raycaster:
https://threejs.org/docs/index.html?q=ray#api/en/core/Raycaster
Raycasting is used for mouse picking (working out what objects in the
3d space the mouse is over) amongst other things.

threejs - raycasting in AR with controller after repositioning

I'm rather new to threejs, so what I'm doing might not be the most efficient way.
I have an object in AR on a mobile device and I want to know if I intersect with it when touching on the screen.
I use the following code to generate the raycast, and it works initally.
const tempMatrix = new THREE.Matrix4();
tempMatrix.identity().extractRotation(this.controller.matrixWorld);
this.raycaster.ray.origin.setFromMatrixPosition(this.controller.matrixWorld);
this.raycaster.ray.direction.set(0, 0, -1).applyMatrix4(tempMatrix);
However, I have the ability to reposition the object (i.e. reset the position so the object is in front, relative to the current camera direction and position) by moving and rotating the whole scene.
After the repositioning, the raycasting is completely offset and is not casting rays anywhere near where I touch the screen.
Repositioning is done like this (while it works, if there's a better way, let me know!) :
public handleReposition(): void {
const xRotation = Math.abs(this.camera.rotation.x) > Math.PI / 2 ? -Math.PI : 0;
const yRotation = this.camera.rotation.y;
this.scene.rotation.set(xRotation, yRotation, xRotation);
this.scene.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z);
}
How can I achieve to raycast to the correct new location?
Thanks!
Assuming this.scene is actually the main threejs Scene, it's usually a bad idea to change its rotation or position, since it will affect everything inside the scene, including the controller. I'd suggest moving your object instead, or add your object(s) to a Group and move that.

How to make a cube to always appear in front of the XR(phone) camera?

I'm trying to make a cube mesh to be always positioned in front of the XR camera.
No matter how I move my phone camera, the cube should appear right in front of the camera showing only one side of the cube.
Firstly, I added a cube mesh to the scene in the beginning:
material = new THREE.MeshLambertMaterial({ color: 0x9797CE });
box = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), material);
box.position.set(0, 0, -3);
scene.add(box);
And then tried to draw the box in front of the XR camera:
function animate() {
let xrCamera = renderer.xr.getCamera(camera);
box.position.set(xrCamera.position.x, xrCamera.position.y, xrCamera.position.z - 3);
box.rotation.set(xrCamera.rotation.x, xrCamera.rotation.y, xrCamera.rotation.z);
renderer.render(scene, camera);
}
When I run the code, the cube appears in front of my phone camera.
But when I rotate my phone, the cube rotates itself in the same position not following the camera.
I also tried xrCamera.add(box) but it doesn't seem to work.
How can I correctly make the cube always appear still in front of the XR camera?
It's important to know that currently (r115) the transformation properties position, rotation and scale as well as the local matrix of the XR camera are not updated.
So instead of adding the box to xrCamera, add it to camera. Besides, keep in mind that WebXRManager.getCamera() is intended for internal use only and no part of the public API.
I have been solving a similar problem. I needed to get a point in front of camera using aframe API. But the challege was when the experience were on VR mode(fullscreen) and playing on movile or headset. In this context the management of the current camera is absolutely controlled by WebXR. With WebXR THREE applies headset pose to the object3D internally.
You only can use the matrixWorld of the three camera to access the camera world reference data, other properties or methods are not correct. In the case of aframe you must access to the object3D of the aframe camera entity and manage its matrixWorld. It is the only method to get correct information of the position/rotation/scale of the camera that it is move by the sensors of a movile or of a AR/VR goggles when the play is on VR/AR mode.
I use to get the in front of Camera Position With WebXR Headset Pose:
const distanceFromCamera = 250; // the depth in the screen, what ever you need
const inFrontOfCameraPosition = new AFRAME.THREE.Vector3( 0, 0, -distanceFromCamera );
const threeSceneCamera = <THREE.PerspectiveCamera>AFRAME.scenes[0].camera;
inFrontOfCameraPosition.applyMatrix4( threeSceneCamera.matrixWorld );
return { x: inFrontOfCameraPosition.x, y: inFrontOfCameraPosition.y, z: inFrontOfCameraPosition.z };

aframe/threejs camera manual rotation

I am trying to manually tween camera rotation to 0,0:
camera.object3D.matrixAutoUpdate=false;
var orgrot = { x : camera.object3D.rotation.x, y: camera.object3D.rotation.y };
var target = { x : 0, y: 0 };
var tween = new TWEEN.Tween(orgrot).to(target, 500);
tween.onUpdate(function(){
camera.object3D.rotation.x= this.x;
camera.object3D.rotation.y= this.y;
camera.object3D.updateMatrix();
});
The tween works as expected, but I am loosing mouse control over camera.
To get it back I am setting matrixAutoUpdate to true
tween.onComplete(function(){
document.querySelector('#camera').object3D.matrixAutoUpdate=true
});
But after that, the camera rotation changes back to original position (before tween ) and I would like to keep 0,0. What I am missing ? thanks
UPDATE Below is a version using aframe only without going into threejs objects
I think the problem is camera look-control component
When enabled - I can not animate camera rotation or even setAttribute.
I have to disable it first - than fire animation and than enable it again.
But the problem is, when I enable it again:
camera.setAttribute ('look-controls-enabled',"true")
camera goes back to original rotation ( state before reset animation ).
Similar problem when using matrixAutoUpdate=false/true
Here is my pen http://codepen.io/anon/pen/dMjrWd
If you rotate left 30deg, it will fire resetCamera animation - it works as intended.
But only when look component is disabled. If I enable it again in "animationend" event - the rotation will go back to original state and trigger resetCamera again and again
You really should not have to mess with matrixAutoUpdate.
But given that you are, try the following to force the position, quaternion, and scale to match the matrix:
object.matrix.decompose( object.position, object.quaternion, object.scale );
three.js r.76
I see several problems. If you are using aframe you should not access the object3D directly. You should do
cameraEl.setAttribute('position', {x: 0, y: 0; z;0 })
You can apply a tween to any entity using the declarative API
If you want to modify the camera independently of the running tween you can wrap your camera in another entity
<a-entity rotation="0 360 0"><a-entity id="camera" camera><a-animation ...></a-animation></a-entity></a-entity>
try this codeblock, it works for me on aframe 0.8.2
static faceCameraToCoords(camera, coords){
camera.setAttribute("look-controls",{enabled:false})
camera.setAttribute("rotation", coords);
var newX = camera.object3D.rotation.x
var newY = camera.object3D.rotation.y
camera.components['look-controls'].pitchObject.rotation.x = newX
camera.components['look-controls'].yawObject.rotation.y = newY
camera.setAttribute("look-controls",{enabled:true})
}
You can no longer set a-camera rotation after version 0.8.0.
Updated solution for Aframe 0.9.2:
Given the following camera setup:
<a-entity
id="rig"
position="0 1.6 0">
<a-camera position="0 0 0"></a-camera>
</a-entity>
You can directly alter the camera rotation thus:
const cameraEl = document.querySelector('a-camera');
cameraEl.components['look-controls'].pitchObject.rotation.x = newRotation.x;
cameraEl.components['look-controls'].yawObject.rotation.y = newRotation.y;
Make sure the newRotation values are in radians. For example, use THREE.Math.degToRad(newRotation.x).

How can I click on coordinates on globe?

I am using chrome experiment's globe http://globe.chromeexperiments.com and I would like to click on any color coordinates to show popup but event listener is not being called. can somebody assist me how to do it?
You could try casting a ray from the camera into the scenery and then getting the intercepts. See the docs for rayCaster.
What you would do is create a new rayCaster object, then get the mouse coordinates of the user's screen. You would need to create some kind of "onclick" event on the parent element of the canvas.
Once the event occurs, the rayCaster object has a property which handles our situation, "setFromCamera". However, you need to provide it a list of all of the objects that you are interested in intersecting, that is all of the rectangles.
So you code would look something like this, note that I used jQuery for this:
var rayCaster = new THREE.Raycaster();
var onClickEventHandler = function(e) {
//Save the mouse data as a vector
//width and height pertain to the size of the canvas.
var mousePosition = new THREE.Vector3((e.offsetX / width) * 2 - 1,
-(e.offsetY / height) * 2 + 1,
0.5);
rayCaster.setFromCamera(mousePosition, camera);
//"objects" is the array of meshes that you care about the user intersecting
var intersects = rayCaster.intersectObjects(objects);
}
What is contained inside the "intersects" array is all of the items from the "objects" array which ere intersected by the ray in the order of intersection. You are likely interested in the first element of this array.
I don't know if that globe utility provides a better way of doing it, what I showed is the generic way of selecting an object in the scenery.

Resources