threejs - Defect in rotation - THREE.OrbitControls - three.js

I use OrbitControls now but still i have strange bug. It is hard to explain. When i drag mouse down in the begin work normally and then in one moment whole scene begin to rotate in wrong direction and flip my whole scene.
I got warnings :
OrbitControls.js:1103 [Violation] Added non-passive event listener to
a scroll-blocking 'wheel' event. Consider marking event handler as
'passive' to make the page more responsive. See
https://www.chromestatus.com/feature/5745543795965952
Here is my code:
controls = new THREE.OrbitControls(camera, renderer.domElement);
//controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop)
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
controls.screenSpacePanning = true;
controls.minDistance = 14;
controls.maxDistance = 120;
controls.maxPolarAngle = Math.PI / 3;
controls.target.set(5, 4, -20);
I need to limit rotation , disable 360 rotating scene.
For example i wanna allow max angle of 45.

Try this, i had a familiar issue and applied it to my code and worked
camera.up = new THREE.Vector3( 0, 0, 1 );

Did you take a look at the documentation? It outlines four different properties to limit angles of rotation. These are the defaults:
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
controls.minPolarAngle = 0; // radians
controls.maxPolarAngle = Math.PI; // radians
// How far you can orbit horizontally, upper and lower limits.
// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
controls.minAzimuthAngle = - Infinity; // radians
controls.maxAzimuthAngle = Infinity; // radians
Edit:
The above solution is for OrbitControls, which is not what the original question asked. TrackballControls does not offer the ability to limit angles of rotation.

Related

ThreeJs: Add a Gridhelper which always face the perspective camera

I have a threejs scene view containing a mesh, a perspective camera, and in which I move the camera with OrbitControls.
I need to add a measurement grid on a threejs view which "face" my perspective camera
It works on "start up" with the following code by applying a xRotation of Pi/2 on the grid helper
window.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 300 );
window.camera.position.z = 150;
window.grid1 = new THREE.GridHelper(500, ~~(500 * 2))
window.grid1.material.transparent = true;
window.grid1.material.depthTest = false;
window.grid1.material.blending = THREE.NormalBlending;
window.grid1.renderOrder = 100;
window.grid1.rotation.x = Math.PI / 2;
window.scene.add(window.grid1);
window.controls = new OrbitControls(window.camera, window.renderer.domElement );
window.controls.target.set( 0, 0.5, 0 );
window.controls.update();
window.controls.enablePan = false;
window.controls.enableDamping = true;
But once i start moving with orbitcontrol the grid helper don't stay align with the camera.
I try to use on the renderLoop
window.grid1.quaternion.copy(window.camera.quaternion);
And
window.grid1.lookAt(window.camera.position)
Which seems to work partially, gridhelper is aligned on the "floor" but not facing the camera
How can I achieve that?
Be gentle I'm starting with threejs :)
This is a bit of a hack, but you could wrap your grid in a THREE.Group and rotate it instead:
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 300 );
camera.position.z = 150;
const grid1 = new THREE.GridHelper(500, ~~(500 * 2));
grid1.material.transparent = true;
grid1.material.depthTest = false;
grid1.material.blending = THREE.NormalBlending;
grid1.renderOrder = 100;
grid1.rotation.x = Math.PI / 2;
const gridGroup = new THREE.Group();
gridGroup.add(grid1);
scene.add(gridGroup);
// ...
And then, in your render loop, you make your group face to the camera (and not the grid):
gridGroup.lookAt(camera.position)
This works because it kind of simulates the behaviour of setting the normal in a THREE.Plane. The GridHelper is rotated to be perpendicular to the camera, and the it is wrapped in a group with no rotation. So by rotating the group, the grid will always be offset so that it is perpendicular to the camera.

three js zooming in where the cursor is using orbit controls

I'm very new to three js and is currently trying to implement a feature where the user can zoom in where the cursor is. The plan is to use a raycaster to get the point of intersection and then and use it to update the vector of the orbit controls every time the cursor moves.
the orbit control is initialized like so
this.controls = new OrbitControls( this.camera_, this.threejs_.domElement );
this.controls.listenToKeyEvents( window );
this.controls.screenSpacePanning = false;
this.controls.minDistance = 30;
this.controls.maxDistance = 500;
this.controls.maxPolarAngle = Math.PI / 2;
this is the eventlistener
document.addEventListener('pointermove', (e) => this.onPointerMove(e), false);
and the onPointerMove function looks like this
onPointerMove(event){
const pointer = {
x: (event.clientX / window.innerWidth) * 2 - 1,
y: -(event.clientY / window.innerHeight) * 2 + 1,
}
this.rayCaster.setFromCamera( pointer, this.camera_);
const intersects = this.rayCaster.intersectObjects( this.scene_.children, false);
if ( intersects.length > 0 ) {
this.controls.target(intersects[0].point);
this.controls.update();
}
}
so far, intersects[0].point seems to be getting the intersect coordinate correctly but the orbit control is simply not getting updated. I have also tried changing the camera's position using
this.camera_.position.set(intersects[0].point.x+20,intersects[0].point.y+20,intersects[0].point.z+20);
this.controls.update();
however that just moves my camera everywhere i point.
Edit:
this doesnt work either
const newTarget = new Vector3(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);
this.controls.target.copy(newTarget);
found the answer here.
Apparently you need to use either copy or set to change the target of the orbit controls. Without calling update().
like so
this.controls.target.set(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z);

How can i zoom in to a raycaster selection?

I wonder how can i zoom in to mouse selection with ThreeJs, i'm using raycaster for selecting the model areas but something isn't working when i click on areas - as you can see in My Example (thanks to Mugen87) , my goal is to zoom into faces when i click on them... i've added the following function :
function onClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObject( mesh, false, intersects );
if(mesh.type!= 'Mesh' || !intersects.length){
return
}
var n = intersects[ 0 ].face.normal.clone();
n.multiplyScalar(30);
n.add(intersects[ 0 ].point);
var p = intersects[ 0 ].point;
camera.position.copy(n);
camera.lookAt(p);
}
When i click outside the model - it's not keeping the position and it's spinning ...
That's because you're using OrbitControls, which pretty much takes over your camera and assigns its position and rotation once per frame. When you try to set its position manually, OrbitControls overrides anything you did.
The solution is to make your controls variable global. Then when you're ready to take over the camera position, set controls.enabled = false; in the onClick() function. Then when you're ready to return to the regular controls, set controls.enabled = true;
https://threejs.org/docs/#examples/en/controls/OrbitControls.enabled

Set camera left/right

I have a three.js animation of a person running. I have embedded this in an iFrame on my site however the character runs off the screen.
I am very happy with the positioning and the camera angle, I just need to move it right so that the character is centred in the iFrame.
Below is the code I am using.
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 4000);
camera.position.set(0, 150, 50);
camera.position.z = cz;
camera.zoom = 3.5;
camera.updateProjectionMatrix();
scene.add(camera);
You could use the camera.lookAt() method, which will point the camera towards the desired position.
// You could set a constant vector
var targetPos = new THREE.Vector3(50, 25, 0);
camera.lookAt(targetPos);
// You could also do it in the animation loop
// if the position will change on each frame
update() {
person.position.x += 0.5;
camera.lookAt(person.position);
renderer.render(scene, camera);
}
I feel like the lookAt() method wouldn't work. It will just rotate the camera, and you specified you like the camera placement/angle.
If you want to move the camera to the right along with you model, set the camera's position.x equal to model.x for every frame(assuming left/right is still the X axis).
person.position.x += 0.5;
camera.position.x = person.position.x;
Alternatively, you could keep the object and camera static and move the ground plane. Or even have a rotating cylinder with a big enough radius flipped on its side.

Nearby culling in Three.js despite camera not being near face

I've run into an issue after switching to a logarithmic depth buffer in Three.js. Everything runs nicely except for nearby culling of the ground as described in the following photos:
As you can see, the camera is elevated above the ground significantly. The character box that is shown is about 2 units above the ground, and my camera is set up as such:
var WIDTH = window.innerWidth
, HEIGHT = window.innerHeight;
var VIEW_ANGLE = 70
, ASPECT = WIDTH / HEIGHT
, NEAR = 1e-6
, FAR = 9000;
var aspect = WIDTH / HEIGHT;
var camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.rotation.order = 'YXZ';
So my NEAR parameter is nowhere near 2, the distance between the camera and the ground. You can see in the second image that I even move up the camera with my PointerLockControls and still run into the issue.
Can anyone diagnose my issue?
I also tested my issue by seeing if this bug occurred with a static camera as well. It does.
Additionally, this problem only happens with the logarithmic depth buffer, as it doesn't happen with the default depth buffer.
I have my camera as a child to a controls object, which is defined as follows:
controls = new THREE.PointerLockControls(camera);
controls.getObject().position.set(strtx, 50, strtz);
scene.add(controls.getObject());
camera.position.z += 2;
camera.position.y += .1;
Here's the relevant code for PointerLockControls:
var pitchObject, yawObject;
var v = new THREE.Vector3(0, 0, -1);
THREE.PointerLockControls = function(camera){
var scope = this;
camera.rotation.set(0, 0, 0);
pitchObject = new THREE.Object3D();
pitchObject.rotation.x -= 0.3;
pitchObject.add(camera);
yawObject = new THREE.Object3D();
yawObject.position.y = 10;
yawObject.add(pitchObject);
var PI_2 = Math.PI / 2;
var onMouseMove = function(event){
if (scope.enabled === false) return;
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
yawObject.rotation.y -= movementX * 0.002;
pitchObject.rotation.x -= movementY * 0.002;
pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
};
this.dispose = function() {
document.removeEventListener( 'mousemove', onMouseMove, false );
};
document.addEventListener( 'mousemove', onMouseMove, false );
this.enabled = false;
this.getObject = function () {
return yawObject;
};
this.getDirection = function() {
// assumes the camera itself is not rotated
var rotation = new THREE.Euler(0, 0, 0, "YXZ");
var direction = new THREE.Vector3(0, 0, -1);
return function() {
rotation.set(pitchObject.rotation.x, yawObject.rotation.y, 0);
v.copy(direction).applyEuler(rotation);
return v;
};
}();
};
You'll also notice that it's only the ground that is being culled, not other objects
Edit:
I've whipped up an isolated environment that shows the larger issue. In the first image, I have a flat PlaneBufferGeometry that has 400 segments for both width and height, defined by var g = new THREE.PlaneBufferGeometry(380, 380, 400, 400);. Even getting very close to the surface, no clipping is present:
However, if I provide only 1 segment, var g = new THREE.PlaneBufferGeometry(380, 380, 1, 1);, the clipping is present
I'm not sure if this intended in Three.js/WebGL, but it seems that I'll need to do something to work around it.
I don't think this is a bug, I think this is a feature of how the depthbuffer in the different settings works. Look at this example. On the right, the depthbuffer can't make up its mind between the letters in "microscopic" and the sphere. This is because it has lower precision at very small scales and starts doing rounding that oscilates between one object and another, and favoring draw order over z-depth.
It's always a tradeoff. If you want to forgo this issue, you can try raising the scale of your scene overall, so that the 'near' of the camera will never be so close to something that it can round it off - so just work in a number range that won't be rounded in the exponential model of the logarithmic z-buffer.
Also another question - how is the blue defined, because maybe what you're seeing is not clipping from being too close, but confusion between whether blue or the ground is closer. If it's just a blue box encompassing everything, you could try making it bigger and more distant from the ground.
EDIT:
Okay, this looks like it should work. so I would start looking for edge cases. What can you do to change the scene so that it does work? What can you do to make other things start breaking?
try moving the landscape far down/ far up (does the issue persist when looking up instead of down at it, does it persist even when it's unquestionably far away?)
try rotating the landscape
try changing the camera FOV
try changing the camera far plane
try changing the camera near plane from 1e-x notation to .000001, .0001,.01,.1, etc. see what effect it has.
console.log the camera object in your render function, and make sure that the fov, near, far etc, is as you set on setup and that it's not being overwritten and reset to default. check what it prints out in chrome's developer tools, you can browse the whole object, check position, parent name, all that stuff.
basically i don't see a blatant mistake, so I would guess it's something hard to spot, or it's working exactly as it should. Figure out what you can do to improve the effect/ make it worse, and that will clarify a direction to go.
A good rule of thumb for debugging is to try and just take things to an extreme, without trying to fix it, or keep the code true to its purpose, and just see in what way it breaks further/changes. report back when you find something.

Resources