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
}
Related
I have model far away from the origin, and I want a directional light to hit the model like sunlight would do.
I set a position and a target for my DirectionalLight:
export const dirLight = getDirectional();
function getDirectional() {
const dirLight = new DirectionalLight( 0xffffff, 1 );
dirLight.position.set( 585000 + 10000, 6135000 + 10000, -500 + 5000);
return dirLight;
};
const helper = new THREE.DirectionalLightHelper( dirLight, 1000 );
let t = new THREE.Object3D();
t.translateX(585000);
t.translateY(6135000);
t.translateZ(1000);
dirLight.target = t;
scene.add(dirLight);
scene.add(dirLight.target);
scene.add(t);
helper.update();
scene.add( helper );
I would expect the light direction now to be parallel to vector between light position and light target, but apparently the light direction is still towards the origin of the scene. What am I doing wrong ?
A running example can be seen here
The documentation states that the target needs to be added to the scene so that the world coordinates are calculated. However, that does not seem to work.
So, instead I tried manually updating the world coordinates, and that worked. Probably that will only work with a static target.
In your case that would be adding
dirLight.target.updateMatrixWorld();
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
I'm writing a Three.js prototype for interacting with objects using the Leap Motion. Each frame (or regularly anyway), I want to check if the representation of the user's finger is above or beneath an object in the scene.
I've done this with the code below, but the intersectObject call is taking about 200 milliseconds, even though it's just testing one object. This is causing the animation to slow down and become very jerky (I've tried doing it e.g. once every 20 frames instead of every frame, but then it still jerks every 20 frames).
Is there a way to do this quicker? Am I doing something wrong? How do other people deal with this?
Thanks!
Code:
...
var filepath = '../models/Scissors.js';
loader.load(filepath, function(geometry, materials) {
scissors = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(materials) );
scene.add( scissors );
});
...
function update() {
...
// NB. Sphere1 has been positioned to represent the user's index finger
// in 3D space
var vector = sphere1.position.subSelf( camera.position );
var ray = new THREE.Raycaster( camera.position, vector.clone().normalize() );
var start = new Date().getTime();
var collisions = ray.intersectObjects( [scissors] );
// Takes about 200ms
console.log('Took ' + (new Date().getTime() - start) + ' ms' );
if( collisions.length > 0 ) {
console.log('HIT!');
}
...
requestAnimFrame(update);
}
Silly me, of course the reason it's slow is because the scissors object is a non-trivial model. Now I'm containing it within an invisible cube and testing against that instead. And it's super fast now (0-1 milliseconds) :-)
I'm attempting to modify TrackballControls.js so that its rotation is like that of OrbitControls.js, where the horizon stays flat, but maintain the ability to rotate over and around a scene (specifically, a collada building model). I've been trying to figure this out for the better part of a day now, but I'm a designer, not a programmer. :-) I'm not even sure if I should be focusing on this.rotateCamera and/or this.update.
(BTW, I would just use OrbitControls.js, but it doesn't support panning, which is necessary when looking at large collada building models.)
Any help would be much appreciated.
It's been awhile since this question was asked, but I ran into the same problem and didn't find much discussion online, so I thought I'd post my solution.
If you must use TrackballControls and you want to a flat horizon, you can simply edit the TrackballControls.js library by adding the following line to the end of the 'this.rotateCamera' method
this.object.up = new THREE.Vector3(0,1,0);
This locks the camera up direction in the (0,1,0) direction (i.e in the y direction). The entire modified method function would then read:
this.rotateCamera = function () {
var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
if ( angle ) {
var axis = ( new THREE.Vector3() ).crossVectors( _rotateStart, _rotateEnd ).normalize();
quaternion = new THREE.Quaternion();
angle *= _this.rotateSpeed;
quaternion.setFromAxisAngle( axis, -angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_rotateEnd.applyQuaternion( quaternion );
if ( _this.staticMoving ) {
_rotateStart.copy( _rotateEnd );
} else {
quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
_rotateStart.applyQuaternion( quaternion );
}
}
// Lock the camera up direction
this.object.up = new THREE.Vector3(0,1,0);
};
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.