Three.js - move custom geometry to origin - rotation

I have some custom geometries obtained from a STEP file conversion and I use the mouse to rotate them. They rotate around the origin of the scene, but since they are far from it, they seem rotating on a virtual sphere. How can I move them to the origin so that they don't seem "floating" around (I mean that I'd like to reduce to zero the radius of the virtual sphere). This is the example I'd like to move. I've tried setting their position to (0, 0, 0) doing:
object.position.x = 0;
object.position.y = 0;
object.position.z = 0;
but it didin't work.

The typical solution to this problem is to translate the geometry right after it is created. You do that by applying a translation matrix to the geometry like so:
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( distX, distY, distZ ) );
EDIT: You can simply do this, instead:
geometry.translate( distX, distY, distZ ); // three.js r.72
The function geometry.computeBoundingBox() may be of help to you in determining an amount to translate.
However, I see in your case, you have multiple geometries, so it it a bit more complicated, but doable. You will need to translate each geometry by the same amount.
EDIT
Tip: Instead of adding each object to the scene, create a parent object, add it to the scene, and then add the objects to the parent.
var parent;
parent = new THREE.Object3D();
scene.add( parent );
parent.add( object1 );
parent.add( object2 );
// and so on...
Then in your render function, just rotate the parent, not the individual objects.

Related

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.

Three.js: Object3D added in scene, attached to another object3D doesn't update position on translation

The logic of my code is such as the object3D which are included in the scene, on double click get added with a Line (with BufferGeometry).
I am getting the object3D by using Raycaster intersect.
The way I am adding it is:
scene.add( newLine );
newLine.updateMatrixWorld();
THREE.SceneUtils.attach( newLine, scene, intersects[0].object );
The following is my mousemove code which helps me moving the object3D in XZ plane.
function onDocumentMouseMove( event ) {
var mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
var mouseY = - ( event.clientY / window.innerHeight ) * 2 + 1;
var mouse = new THREE.Vector2( mouseX, mouseY );
raycaster.setFromCamera( mouse, camera);
if (selection) {
var intersects = raycaster.intersectObject( plane );
selection.position.copy( intersects[0].point.sub( offset ));
}
}
Nothing complicated. Simple code. And the movement is happening well. I can easily move the object3D around.
When I am checking the console for change in position of object3D on grabbing and moving it, it is changing which is what should happen. But I do not see any change at in the position of the Line, i.e., newLine as in my code. The issue is unless I am calling .updateWorldMatrix() as well which as per THREE docs, should automatically be called in each render cycle. Still I am calling that. Why am not able to get the position of my newLine when clearly its position is moving along with the object3D when I am dragging object3D around?
Why is it needed? Unless the position of the line can show as changing, I can't update an HTML element, which I am attaching to the end of that line. Hence, the position change is imperative. Gif attached which shows, when the cube/ sphere/ cone is moved, render(..) gives me changing position log of that. However, when logging the same for the Line it doesn't change. If any can help me with the issue, it will be amazing. Thanks much.
EDIT
When I am attaching the HTMLElement directly to parent object3D it shows expected result. It moves when I move object3D. This is because as said its position is being updated continuously in the render cycle when I move it.
Gif:
The line's .position attribute is not changing because its local position remains the same. Since the line is attached to the parent, its relative position to the parent doesn't change, only the global position does. To get the global position of line, you can use the .getWorldPosition() method:
// Declare var to store world position
var worldPos = new THREE.Vector3();
// Get world position of line
line.getWorldPosition(worldPos);
// Now global position is stored in Vec3
console.log(worldPos);

Three.js - How to prevent a light from shining on an object

Is there a way to prevent a directional light from illuminating a specific object? I guess this would also apply to a spotlight. The reason for this is I would like to use two directional lights, but with one light shining on an object to give it 'self shadowing', and do not want that light it to interfere with another object.
Currently I have a single directional light declared as such :
function addpointlight()
{
var SHADOW_MAP_WIDTH = 4096, SHADOW_MAP_HEIGHT = 2048;
//Enabling this this light just creates serious artifacts on the obj I am trying to shadow.
//var newlight=new THREE.DirectionalLight(0xeeeeee,0.7);
//newlight.position.set( 0, 100, 300 );
//newlight.castShadow=false;
//scene.add(newlight);
// create a directional light
pointLight = new THREE.DirectionalLight(0xeeeeee,0.80);
//pointLight.onlyShadow=true;
pointLight.position.set( 0, 100, 300 );
pointLight.shadowCameraVisible=true;
pointLight.shadowCameraNear = 10;
pointLight.shadowCameraFar = 1500;
pointLight.shadowCameraFov = 90;
pointLight.castShadow=true;
var d = 4;
pointLight.shadowCameraLeft = -d;
pointLight.shadowCameraRight = d;
pointLight.shadowCameraTop = -d;
pointLight.shadowCameraBottom = d;
pointLight.shadowBias = 0.00;
pointLight.shadowDarkness = 0.7;
pointLight.shadowMapWidth = SHADOW_MAP_WIDTH;
pointLight.shadowMapHeight = SHADOW_MAP_HEIGHT;
// add to the scene
scene.add(pointLight);
}
This light wraps nicely around the object I want to self shadow, eliminating shadow artifacts. It moves with a moving object that it is creating a shadow on using this :-
pointLight.position.set(obj.position.x+40,obj.position.y+5,obj.position.z+300);
pointLight.target=obj;
So I'd like to create a second directional light that only affects the other objects, not this one, tnd this one's light must not affect other objects.
I'd create a fiddle, but the models I am testing with together with the textures make it a rather large fiddle in terms of bandwidth.
The three.js version in r70.
You want to limit the objects that a light affects. Until the time at which three.js supports "layers", where a light will only affect objects in its own layer(s), you may be able to achieve what you want with a work-around: two separate scenes and two render passes.
renderer.autoClear = false;
...
renderer.clear();
renderer.render( scene1, camera );
renderer.render( scene2, camera );
If you have transparent objects, they will have to be in the second scene. Also, an object can only be in one scene, so you will have to duplicate a light if you want it in both.
three.js r.70

How to find the scene position of an object, within a rotating parent object

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

Three.js - Collision Detection - Obj Wavefront

I am trying to build a virtual tour inside a building (the whole building is an obj model) using three.js. Everything loads fine and the library is pretty straightforward. My most critical issue is that I can't implement collision detection with the camera, I tried using rays but I couldn't find a suitable example for my case.
My model load:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener( 'load', function ( event ) {
var newModel = event.content;
newModel.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
scene.add( newModel );
objects.push( newModel );
});
loader.load( 'model/model.obj', 'model/model.mtl' );
The camera creation (I don't know if it is relevant to the issue)
camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
1,
10000
);
camera.position.set( 0, 25, 0 );
camera.lookAt( 0, 0, 0 );
NOTE: The camera moves inside the model, I don't want to detect collision between two separate obj models, I want to detect collision (and stop the camera from passing through walls) inside one single model.
Any help will be greatly appreciated
Looking at the documentation for Raycaster in Three.js at http://threejs.org/docs/58/#Reference/Core/Raycaster, you can create a ray like Raycaster( origin, direction, near, far ). Perhaps for you this would look something like
var ray = new THREE.Raycaster(camera.position, cameraForwardDirection, camera.position, collisionDistance);
Where cameraForwardDirection is the direction in front of you camera. I think you can get this by doing something like:
var cameraForwardDirection = new THREE.Vector3(0,0,-1).applyMatrix4(camera.matrixWorld);
This should work because the camera points in the negative Z direction (hence the 0,0,-1) and we want to apply the orientation of the camera to this vector. This assumes you are only moving forward. If you wanted to check for collisions in other directions, you could cast rays in other directions.
collisionDistance would be the minimum distance for a collision. You can experiment with this to find what works with respect to the scale of things in your scene.
Once you have cast this ray, you will need to check for intersections. You can use the ray.intersectObject( object, recursive ) method. Since it seems like you just have that one model, it might look something like:
var intersects = ray.intersectObject(newModel, true);
if(intersects.length>0){
// stop the camera from moving farther into the wall
}

Resources