Three.js: global rotation - javascript

I want to rotate all objects in a scene around the origin 0,0,0 along the x axis. However, setting obj.rotation.x += 0.1; doesn't rotate along the origin, but along the the center of the object instead. How can I achieve the desired rotation of the object around the origin? I feel that there should be an easy way, but could not find any in the official docs or online.

Instead of adding the objects to the scene, add them to a THREE.Group() object:
var group = new THREE.Group();
scene.add(group);
...
var mesh1 = new THREE.Mesh(...);
group.add(mesh1);
var mesh2 = new THREE.Mesh(...);
group.add(mesh2);
//and so on with meshes
and then in the render loop:
group.rotation.x += 0.1;
Threejs.r84

You can use THREE.Object3D()
Add all your meshes to an object with myObject.add(myMesh)
And then use myObject.rotateX(angle)
where the angle is in radians, myObject is an Object3D object and myMesh is the mesh to be added.
This will rotate the object around x-axis in local space.
More can be found in the documentation: Object3D

Related

Three.js: Bounding sphere of a scaled object

I have a set of 3D shapes (pyramid, cube, octahedron, prism etc.) and I need to build described sphere around each of them. It is easy to do so using geometry.boundingSphere as it has radius of the described sphere. But if I scale an object the bounding sphere is not being updated. Is it possible to update bounding sphere relatively to the scale?
Using Three.js 0.129.
const { position } = entity.object3D;
const mesh = entity.getObject3D('mesh') as THREE.Mesh;
mesh.geometry.computeBoundingSphere();
const { radius } = mesh.geometry.boundingSphere;
createSphere(radius, position);
The geometry.boundingSphere property represents the geometry. You could technically have two meshes with different scales share the same geometry, so you would want to maintain the geometry's original bounding sphere, and then compute a new one for each mesh, individually.
One problem with scaling the bounding sphere is that you can scale your mesh in x, y, and z separately, and even invert vertex position values given negative scaling values. unequal scale values would lead to it being less of a sphere, and more of a spheroid, which would not help you in math.
What you can do is recompute a bounding sphere for your mesh, given its updated world transformation matrix. I suggest using the world matrix because other ancestors of your mesh could also influence scale in unpredictable ways.
// Given:
// (THREE.Mesh) yourMesh
// Copy the geometry
let geoClone = yourMesh.geometry.clone() // really bad for memory, but it's simple/easy
// Apply the world transformation to the copy (updates vertex positions)
geoClone.applyMatrix4( yourMesh.matrixWorld )
// Convert the vertices into Vector3s (also bad for memeory)
let vertices = []
let pos = geoClone.attributes.position.array
for( let i = 0, l = pos.length; i < l; i += 3 ){
vertices.push( new THREE.Vector3( pos[i], pos[i+1], pos[i+2] ) )
}
// Create and set your mesh's bounding sphere
yourMesh.userData.boundingSphereWorld = new THREE.Sphere()
yourMesh.userData.boundingSphereWorld.setFromPoints( vertices )
This will create a world-aligned bounding sphere for your mesh. If you want one based on local transformations, you can follow the same idea using the local yourMesh.matrix matrix instead. Just know that your sphere's center will then be based on your mesh's local transformation/rotation, not just its scale.

Rotate ThreeJS Object3D on a tilted Y axis

I've tried many ways to tilt my object then rotate it on this tilted axis, but no matter what I try it always rotates as though the Y axis is straight up, it doesn't seem to rotate on this new tilted axis, WHY NOT?
this.axis = new Vector3(0, 0, 0.5).normalize()
this.mesh = new Mesh(this.geometry, this.material)
this.mesh.rotateOnAxis(this.axis, Math.degToRad(25))
I've tried using rotateOnWorldAxis, also tried makeRotationAxis on Matrix4 and applying directly to geometry.
Example here:
codesandbox.io
There are a few ways to achieve this; one solution would be to add additional hierarchy to your scene by introducing a group node. Here the group is added as a child to the scene, and your mesh is added as a child to that group:
this.mesh = new Mesh(this.geometry, this.material);
/* New group */
this.group = new THREE.Group();
/* Tilt group */
this.group.rotateOnAxis(this.axis, Math.degToRad(25));
/* Add mesh to group */
this.group.add( this.mesh );
/* Add group to scene */
this.scene.add( group );
With this added hierarchy, the "axis tilt" is applied to the group instead of to the mesh. In threejs, doing this causes any children (ie mesh) of the group to inherit that tilt as well.
This method provides an intuitive way of combing the tilt with locally applied rotation (ie as you are doing via rotation.y += 0.005) to achieve the desired axis based rotation.
Here's a working codesandbox. Hope that helps!
Alternative method
Alternatively, you could use the rotateY() method to apply a rotation transform around the mesh object's local frame:
this.mesh.rotateY(0.005);
// this.mesh.rotation.y += 0.005 <-- not needed
Here's a working example to demonstrate this approach

How to get an object's world position (Three.js)

I want to place my already placed object at a new location, but it moves from the local position and not global.
this._scene.updateMatrixWorld();
this._scene.add(mesh);
var v1 = new THREE.Vector3();
v1.setFromMatrixPosition( mesh.matrixWorld );
mesh.position.set(v1.x +2, v1.y + 2, v1.z + 2);
What I did to solve my problem:
Every mesh got a geometry attribute. That is why you can call mesh.geometry.
From this point i created a BoundingSphere around my mesh.
mesh.geometry.computeBoundingSphere();
Now it is possible to get the world position of my boundingSphere, which simultaneously is the world position of my mesh.
var vector = mesh.geometry.boundingSphere.center;
Congratulations! 'vector' got the center world position in (x,y,z) of your mesh.
Just to clarify, 'mesh' is a THREE.Mesh object.
Wouldn't you just then move the object itself by the increment? I'm not sure why you need the matrix involved?
mesh.position.set(mesh.position.x +2, mesh.position.y + 2, mesh.position.z + 2);
edit... if you need to use matrix, you need to set the matrixWorld. Look at http://threejs.org/docs/#Reference/Core/Object3D.matrixWorld but using the position setter will do the heavy lifting for you.

Collision detection with boundingSphere

For each mesh (THREE.Object3D) Three.js provide a very handy properties - boundingSphere and boundingSphere that have intersectsSphere and isIntersectionBox methods.
With all this I thought I can use it for simple collision detection but when I try it appears that collision happens all the time because (I tried boundingSphere) boundingSphere.center is always in (0, 0, 0); So If I want to check collisions between 2 meshes I should for each object - clone boundingSphere object and then get it world coordinates and only then to use intersectsSphere.
something like this:
var bs = component.object.geometry.boundingSphere.clone();
bs.center.setFromMatrixPosition(component.object.matrixWorld);
...
if (_bs.intersectsSphere(bs)){
is this how it suppose to be used or am I missing something and there are more convenient way of doing collisions detection based on boundingBox/boundingSphere?
If you want to do collision detection with bounding boxes you need the boxes in the world coordinate system. The bounding volumes in the intersectsSphere and isIntersectionBox properties of the mesh are in the local coordinate system of the object.
You can do like you did: clone the volumes and move them to the correct position in the world coordinate system, that is a good solution.
Otherwise you can also set a new box from your meshes and do collision using those boxes. Let's say you have a THREE.Mesh called mesh then you can do:
sphere = new THREE.Sphere.setFromPoints( mesh.vertices );
box = new THREE.Box3.setFromObject( mesh );
A little tip. During development it can be nice to see the bounding boxes in your scene, for this you can use the THREE.BoundingBoxHelper:
var helper = new THREE.BoundingBoxHelper( mesh );
scene.add( helper );

Check if object is looking at position

I'm trying to check if an object (this.target) is looking at a specific position (newPosition). What I have right now is the following:
new THREE.Matrix4().lookAt( newPosition, this.target.position, this.target.up ) == this.target.matrix
But for some reason, this won't work. How are you meant to check if an Object3D is looking at a specific position?
That won't work because the matrix for your target is not in the same place, and can be scaled differently and all sorts of stuff.
A better technique is to raycast from your eye object, and see if it hits the target object.
http://threejs.org/examples/#webgl_geometry_terrain_raycast
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera( mouse, camera );
// See if the ray from the camera into the world hits one of our meshes
var intersects = raycaster.intersectObject( mesh );

Categories