Essentially in my scene I have an entity with laser controls and a raycaster in the following structure:
<a-scene>
<a-entity laser-controls raycaster ...>
<!--<a-entity id="inventory" ...> Could be inserted here-->
</a-entity>
<!--<a-entity id="inventory" ...> Could be inserted here-->
</a-scene>
My objective is to summon the inventory at the current x,y position of the laser line, I already have access to the point corresponding to the end of the laser line. I don't want the inventory to move from that position again. If I set the inventory to be the child of the raycaster it always moves with the line. If I set the inventory to be the child of the scene whilst setting it's position to the world coordinates of the point where it's supposed to be, it just does not work.
Approaches I've tried and failed:
Starting the inventory as raycaster child and changing parent to the scene and applying the world matrix of the raycaster
Keeping inventory as raycaster child, capturing it's initial world matrix and setting that same world matrix on every tick
And finally, my current approach (with code) that still fails
1.Convering the local coordinates of the line end to world coordinates
2.Appending inventory to scene
3.Converting the world coordinates from line end into local coordinates of the scene
4.Applying the position in 3 to the inventory
let v = this.emptyVec
v.copy(this.raycaster.components.line.data.end)
this.raycaster.object3D.localToWorld(v);
this.el.appendChild(inventoryNode) //this.el is the scene
this.el.object3D.worldToLocal(v);
const {x,y} = v
inventoryNode.object3D.position.set(x,y,inventoryZDistance)
TLDR: How do I set an entity to the position of the raycaster line end at the point in time I add it to the scene and have it remain in that position forever
Ended up finding a solution. This is running on the event listener for a controller click event (point being it doesn't need to run on every tick, it's run only once)
Created a dummy node that is a child of the raycaster and set it's position to the x,y coordinates of the line end and whatever z coordinate I want
Appended the proper inventory node to the scene
Set the position of the inventory from the world matrix of the dummy node with the setFromMatrixPosition() method
let dummyNode = document.createElement("a-entity")
dummyNode.setAttribute("id", "dummyinventory")
dummyNode.setAttribute("visible", false) //maybe unnecessary
const {x,y} = this.raycaster.components.line.data.end
dummyNode.object3D.position.set(x,y,inventoryZDistance)
this.raycaster.appendChild(dummyNode)
inventoryNode.setAttribute('look-at', "[camera]") //if you want inventory to face the camera
this.el.appendChild(inventoryNode)
setTimeout(()=>{
let newMatrix = dummyNode.object3D.matrixWorld
inventoryNode.object3D.position.setFromMatrixPosition(newMatrix)
},50) //give the world matrix of dummyNode some time to update, with no timeout sometimes fails
The same thought process could be applied if one wanted to spawn some entity where the user is looking by having the dummy node be a child of the camera instead of the raycaster
Related
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.
I have a 3D scene with a bunch of CSS object that I want to rotate so that they are all pointing towards a point in the space.
My CSS objects are simple rectangles that are a lot wider than they are high:
var element = document.createElement('div');
element.innerHTML = "test";
element.style.width = "75px";
element.style.height = "10px";
var object = new THREE.CSS3DObject(element);
object.position.x = x;
object.position.y = y;
object.position.z = z;
Per default, the created objects are defined as if they are "facing" the z-axis. This means that if I use the lookAt() function, the objects will rotate so that the "test" text face the point.
My problem is that I would rather rotate so that the "right edge" of the div is pointing towards the desired point. I've tried fiddling with the up-vector, but I feel like that wont work because I still want the up-vector to point up. I also tried rotating the object Math.PI/2 along the y axis first, but lookAt() seems to ignore any prior set rotation.
It seems like I need to redefine the objects local z-vector instead, so that it runs along with the global x-vector. That way the objects "looking at"-direction would be to the right in the scene, and then lookAt() would orient it properly.
Sorry for probably mangling terminology, newbie 3D programmer here.
Object.lookAt( point ) will orient the object so that the object's internal positive z-axis points in the direction of the desired point.
If you want the object's internal positive x-axis to point in the direction of the desired point, you can use this pattern:
object.lookAt( point );
object.rotateY( - Math.PI / 2 );
three.js r.84
This question already has an answer here:
Three.js add an object to a group but keep global position/rotation/scale as it was
(1 answer)
Closed 6 years ago.
When I add an Object3D to another Object3D ("add" method) the child changes its transform depending on parent's one.
Is there a way to keep position/rotation/scale of the child after adding it to a parent?
Fiddle: https://jsfiddle.net/pqfzd8a2/1/
mesh = new THREE.Mesh(geometry, material);
mesh.position.y = 25;
var mesh2 = mesh.clone();
mesh2.position.y = 80;
var mesh3 = mesh2.clone();
mesh.rotation.z = Math.PI*0.25;
scene.add(mesh);
mesh.add(mesh2);
scene.add(mesh3);
What was done?
mesh is rotated.
mesh2 and mesh3 are clones of each other with same position.
mesh2 is added to mesh, while mesh3 was added to scene.
The difference between mesh2 and mesh3 is that they were added (parented) to different parents (to mesh and directly to the scene, correspondingly). Before being added (parented) to something, they were in same position. The point is to keep the object where it was before being added (parented).
Expected result: mesh2 and mesh3 should be in the same positions.
Actual result: mesh2 changes position/rotation/scale after being added.
How to make objects keep their global transform after being parented?
Well that is the defined behaviour and at least the reason why those hierarchical structures exist. If you do not want to apply the parent's transform on the child, there is no reason to parent those items.
With this approach it is much easier to handle complex Objects, which are composed of smaller objects, like a car with wheels. Instead of moving the car and then each wheel, one makes the wheels childs of the car and they will move/rotate/scale with it all the time. That is how the transformation of the parent is »inherited« by all its children. So their transform is relative their respective parent. Like the moon rotates around the earth, which rotated around the sun, which rotates around whetever…
If you really need to cancel out the parents transform within a child, then you need to apply the inverse of parents transform to the child's transform, but as I said, that does not make much sense, because you just do not need to parent them and your done.
No they shouldn't be in the same position, because you are changing position and rotation of both mesh and mesh2. If you are adding a mesh to another you are adding it to the parent context.
mesh.add(mesh2); // <-- mesh is translated and rotated, mesh2 will inherit the same context
In this fiddle you see that the rotation on mesh also applies to mesh2: https://jsfiddle.net/r57opdgn/
Within my THREE scene i have an Object that rotates. The child object is positioned at (92,92,92) within the rotating Object, so it orbits the centre position (0,0,0) is a spherical path with my mouse movement. How can i find the global position of the child object in respect to the scene as it rotates. I'm fairly new to THREE.js and thankful for any support in advance.
My code:
ObjectParent = new THREE.Object3D();
scene.add( ObjectParent );
ObjectChild = new THREE.Object3D();
ObjectParent.add( ObjectChild );
ObjectChild.position.set(92,92,92);
Render:
ObjectParent.rotation.y -= (target.y + ObjectParent.rotation.y) * 0.08;
ObjectParent.rotation.x += (target.x - ObjectParent.rotation.x) * 0.07;
ObjectParent.updateMatrixWorld();
scene.updateMatrixWorld();
First of all, you usually do not need to call
ObjectParent.updateMatrixWorld();
scene.updateMatrixWorld();
inside your render loop. The renderer will do that for you.
To find the world position of a child object, you can use this pattern:
var vector = new THREE.Vector3();
...
vector.setFromMatrixPosition( child.matrixWorld );
The renderer should have previously updated the scene matrices for you. If you have changed a parent's position since the last rendering, you will have to first call
parent.updateMatrixWorld();
to update the relevant matrices.
It is a good idea to look at the three.js source code so you understand what these functions do.
three.js r.67
I've just started learning THREE and have been messing about with the three.js example of controllable MD2 characters to try and fashion it into a 3rd person shooter kind of game. I've been trying to write a simple algorithm for the enemy characters and I'm pretty sure that ray-casting would be ideal.The whole idea is that the enemies should stop rotating once they're facing the player. But Here's the problem that's giving me sleepless nights! :
Let's say, the enemy object is the origin for the ray caster ray. No matter what direction I set for the direction of that ray ( even, for example (1,0,0) - the positive x-axis), the ray's direction is always pointing towards the center of the scene!!!
Please help! haven't been able to find any Example online for this kind of use for the ray caster (apart from collision detection which I really don't need at the moment).
If all you want is for enemies to stop rotating when they are looking at the player, I would consider just checking the direction between them, as it's a lot faster than casting a ray to see if it intersects:
// Assuming `enemy` is a THREE.Mesh
var targetDir = enemy.position.clone().sub(player.position).normalize();
var currentDir = (new THREE.Vector3()).applyMatrix4(enemy.matrixWorld).sub(enemy.position).normalize();
var amountToRotate = currentDir.sub(targetDir);
var offset = amountToRotate.length();
Then rotate each axis no more than the value for that axis in amountToRotate if offset is greater than some threshold.
That said, here is how you use a Raycaster, given the variables above:
var raycaster = new THREE.Raycaster(enemy.position, targetDir);
var intersections = raycaster.intersectObject(player);
Note that if you are running any of the above code in an animation loop, it will create a lot of garbage collection churn because you are constantly creating a bunch of new objects and then immediately throwing them away. A better pattern, which is used a lot in the library itself, is to initialize objects once, copy values to them if you need to, and then use those copies for computation. For example, you could create a function to do your raycasting for you like this:
var isEnemyLookingAtPlayer = (function() {
var raycaster = new THREE.Raycaster();
var pos = new THREE.Vector3();
return function(enemy) {
raycaster.ray.origin.copy(enemy.position);
raycaster.ray.direction.copy(pos.copy(enemy.position).sub(player.position).normalize());
return !!raycaster.intersectObject(player).length;
};
})();