Three.js: Unable to Set Maximum and Minimum Zoom Level - javascript

I am using this example for my WebGL panorama cube:
https://threejs.org/examples/?q=pano#webgl_panorama_equirectangular
I want to zoom in and out within limits, i.e. set maximum and minimum zoom level, but not infinitely as code provides by default. For infinite zoom, which later reverses the view if scrolled too much, the function from example above looks like this:
function onDocumentMouseWheel( event ) {
camera.fov += event.deltaY * 0.05;
camera.updateProjectionMatrix();
}
To address the issue I tried to update FOV when inside my allowed range:
function onDocumentMouseWheel( event ) {
var fovMAX = 95;
var fovMIN = 5;
var newFov = camera.fov + event.deltaY * 0.05;
if (newFov > fovMIN && newFov < fovMAX) {
camera.fov = newFov;
camera.updateProjectionMatrix();
};
}
Please note my FOV is 90:
camera = new THREE.PerspectiveCamera( 90, window.innerWidth / window.innerHeight), 0.1, 100 );
This seems to have perfectly worked for maximum zoomed-in level - it stops when FOV is 5 and does not zoom in further. However, as I zoom out to FOV 95, it would stop, but if I continue zooming out with mouse more, I would zoom infinitely again, even though FOV remains 95.
How to stop/control infinite zoom out?

Your code works as expected in the minimal example below. (I did make a small change in that I'm using >=/<= rather than >/<.)
var renderer, scene, camera, controls, stats;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 35,
NEAR = 1,
FAR = 1000;
var fovMax = 95,
fovMin = 5,
fovTmp = 0;
function zoom(event) {
event.preventDefault();
fovTmp = camera.fov + (event.deltaY * 0.05);
if (fovTmp >= fovMin && fovTmp <= fovMax) {
camera.fov = fovTmp;
camera.updateProjectionMatrix();
}
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 50;
scene.add(camera);
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
// POPULATE EXAMPLE
var cubeGeo = new THREE.BoxBufferGeometry(1, 1, 1),
cubeMat = new THREE.MeshPhongMaterial({
color: "red"
});
var mesh = new THREE.Mesh(cubeGeo, cubeMat);
scene.add(mesh);
window.addEventListener("wheel", zoom);
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})();
Something else is changing your camera's data. Are you sure you're not updating camera.zoom anywhere?
For what it's worth, the only other (core) three.js method that modifies the FOV is PerspectiveCamera.setFocalLength
Edit based on new information
Ah, there's your problem. I didn't know you were using an external mouse controller. OrbitControls doesn't adjust the FOV to create its zoom effect. Instead, it literally moves the camera nearer to/further from the control's target. Your zoom-in likely (by chance) corresponded with it reaching 0 distance. But because it can zoom out to infinity, that's why it continued to zoom out.
Disabling zoom like in your comment should resolve the issue.
three.js r87

Related

How to make a parallax effect using Three.js

I want to make a parallax effect on mouse move using three.js.
Basically I want to generate bunch of clouds to canvas and want them to move on x axis when mouse is moved.
So I have already tried to add clouds to the scene as images. How could I link mouse move even to these clouds? Or should I add clouds to the scene differently?
How I added clouds:
var imgCloud = new THREE.MeshBasicMaterial({
map:THREE.ImageUtils.loadTexture('img/cloud.jpg')
});
imgCloud.map.needsUpdate = true;
// Cloud
var cloud = new THREE.Mesh(new THREE.PlaneGeometry(200, 200), imgCloud);
cloud.overdraw = true;
scene.add(cloud);
You need to add an event listener to the window to get the mouse position. Then you can use that to modify the position of all clouds individually or just the position of the scene.
window.addEventListener('mousemove', onMouseMove, false);
function onMouseMove(event) {
scene.position.x = (event.clientX - window.innerWidth / 2);
}
You can simply catch the mouse position and then move the camera along an axis relative to the new mouse position.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 150;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Add cloud
for (let i = 0; i <= 200; i++) {
const geometry = new THREE.SphereGeometry(5, 32, 32);
const material = new THREE.MeshBasicMaterial({ color: 0x3399ff, transparent: true, opacity: 0.8});
const sphere = new THREE.Mesh(geometry, material);
sphere.position.x += Math.round(Math.random() * 500) - 250;
sphere.position.y += Math.round(Math.random() * 500) - 250;
sphere.position.z += Math.round(Math.random() * 200);
scene.add(sphere);
}
// Control
var mouseTolerance = 0.1;
document.onmousemove = function (e) {
var centerX = window.innerWidth * 0.5;
var centerY = window.innerHeight * 0.5;
camera.position.x = (e.clientX - centerX) * mouseTolerance;
camera.position.y = (e.clientY - centerY) * mouseTolerance;
};
//Render loop
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
render();
body{
margin: 0;
overflow: hidden;
height: 400px;
width: 600px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
The mouse position can be obtained withdocument.onmousemove as usual. Camera can be repositioned with camera.position.[axis]. You can play with different axes and see how it looks. Note that some objects must be closer to the camera than others otherwise it will not look like Parallax

Set 3d cube rotation origin

I have a simple 3d cube (BoxGeometry of 100, 100, 100) and I am trying to rotate it. If we call all 100x100x100 a tile - when I rotate it I can see it's overlapping the below tile.
(by changing color, now I totally understand the behaviour).
tl.to(this.cube4.rotation, 0.5, {z: -45* Math.PI/180});
[
What if I want to rotate it based on an anchor point of right bottom? So instead of overflowing inside the below tile, it will overflow that portion to above tile.
So it will look like the green example and not the red example:
The red example here is achieved by
tl.to(this.cube4.rotation, 0.5, {z: -45* Math.PI/180});
tl.to(this.cube4.position, 0.5, {x: 50 }, 0.5);
I am very new to three.js so if any terminology is wrong, please warn me
Add the ("red") cube to a THREE.Group, in that way that the rotation axis (the edge) is in the origin of the group. This means the cube has to be shifted by the half side length.
If you rotate the group object, then the cube (which is inside the group) will rotate around the edge and not around its center.
e.g.
var bbox = new THREE.Box3().setFromObject(cube);
cube.position.set(bbox.min.x, bbox.max.y, 0);
var pivot = new THREE.Group();
pivot.add(cube);
scene.add(pivot);
See also the answer to How to center a group of objects?, which uses this solution to rotate a group of objects.
(function onLoad() {
var camera, scene, renderer, orbitControls, pivot;
var rot = 0.02;
init();
animate();
function init() {
container = document.getElementById('container');
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
camera.position.set(4, 1, 2);
//camera.lookAt( -1, 0, 0 );
loader = new THREE.TextureLoader();
loader.setCrossOrigin("");
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
scene.add(camera);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
}
orbitControls = new THREE.OrbitControls(camera, container);
var ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set(1,2,-1.5);
scene.add( directionalLight );
addGridHelper();
createModel();
}
function createModel() {
var material = new THREE.MeshPhongMaterial({color:'#80f080'});
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var cube1 = new THREE.Mesh(geometry, material);
cube1.position.set(0,-0.5,-0.5);
var cube2 = new THREE.Mesh(geometry, material);
cube2.position.set(0,0.5,-0.5);
var cube3 = new THREE.Mesh(geometry, material);
cube3.position.set(0,-0.5,0.5);
var material2 = new THREE.MeshPhongMaterial({color:'#f08080'});
var cube4 = new THREE.Mesh(geometry, material2);
var bbox = new THREE.Box3().setFromObject(cube4);
cube4.position.set(bbox.min.x, bbox.max.y, 0);
pivot = new THREE.Group();
pivot.add(cube4);
pivot.position.set(-bbox.min.x, 0.5-bbox.max.y, 0.5);
scene.add(cube1);
scene.add(cube2);
scene.add(cube3);
scene.add(pivot);
}
function addGridHelper() {
var helper = new THREE.GridHelper(100, 100);
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add(helper);
var axis = new THREE.AxesHelper(1000);
scene.add(axis);
}
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
pivot.rotation.z += rot;
if (pivot.rotation.z > 0.0 || pivot.rotation.z < -Math.PI/2) rot *= -1;
render();
}
function render() {
renderer.render(scene, camera);
}
})();
<!--script src="https://threejs.org/build/three.js"></!--script-->
<script src="https://rawcdn.githack.com/mrdoob/three.js/r124/build/three.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/r124/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.js"></script>
<div id="container"></div>
From the first image, it appears that the pivot of your red tile is at its center.
For the rotation you want, you would ideally change the pivot to the lower right of the cube. This is impossible without modifying the geometry of the cube.
BUT a simple trick is to create an empty node at that pivot point, parent your cube to that empty, and apply your rotation to the empty. (Don't forget to remove your translation, you don't need it anymore)
Here is some pseudo code, assuming your red box is centered at (0,0,0) and has a width and height of 100:
// create an empty node at desired rotation pivot
var empty = new Object3D or group
empty.position = (50, -50, 0)
// parent your cube to the empty
var cube = your box
empty.add(cube)
// you may need to change the local position of your cube to bring it back to its global position of (0,0,0)
cube.position = (-50, 50, 0)
rotate empty by 45°
I think you can get the bounds of the rotated object like this:
bounds = new THREE.Box3().setFromObject( theRedObject )
Then reposition the object.y based on its bounds.min.y
let scene, camera, controls, ambient, point, loader, renderer, container, stats;
const targetRotation = 0;
const targetRotationOnMouseDown = 0;
const mouseX = 0;
const mouseXOnMouseDown = 0;
const windowHalfX = window.innerWidth / 2;
const windowHalfY = window.innerHeight / 2;
init();
animate();
var box, b1, b2, b3;
function init() {
// Create a scene which will hold all our meshes to be rendered
scene = new THREE.Scene();
// Create and position a camera
camera = new THREE.PerspectiveCamera(
60, // Field of view
window.innerWidth / window.innerHeight, // Aspect ratio
/*window.innerWidth / -8,
window.innerWidth / 8,
window.innerHeight / 8,
window.innerHeight / -8,
*/
0.1, // Near clipping pane
1000 // Far clipping pane
);
scene.add(camera)
// Reposition the camera
camera.position.set(0, 5, 10);
// Point the camera at a given coordinate
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add orbit control
controls = new THREE.OrbitControls(camera);
controls.target.set(0, -0.5, 0);
controls.update();
// Add an ambient lights
ambient = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambient);
// Add a point light that will cast shadows
point = new THREE.PointLight(0xffffff, 1);
point.position.set(25, 50, 25);
point.castShadow = true;
point.shadow.mapSize.width = 1024;
point.shadow.mapSize.height = 1024;
scene.add(point);
group = new THREE.Group();
group.position.y = 0;
scene.add(group);
rotationAnchor = new THREE.Object3D()
group.add(rotationAnchor);
box = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshStandardMaterial({
color: 'grey'
}))
b1 = box.clone();
b2 = box.clone();
b3 = box.clone();
b3.material = b3.material.clone()
b3.material.color.set('red')
group.add(box);
group.add(b1);
b1.position.y += 1
group.add(b2);
b2.position.z += 1
rotationAnchor.add(b3);
rotationAnchor.position.set(0.5, 0.5, 1.5)
b3.position.set(-.5, -.5, -.5)
// Create a renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
// Set size
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// Set color
renderer.setClearColor(0xf8a5c2);
renderer.gammaOutput = true;
// Enable shadow mapping
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Append to the document
container = document.createElement("div");
document.body.appendChild(container);
document.body.appendChild(renderer.domElement);
// Add resize listener
window.addEventListener("resize", onWindowResize, false);
// Enable FPS stats
stats = new Stats();
container.appendChild(stats.dom);
var gui = new dat.GUI({
height: 5 * 32 - 1
});
let params = {
'test': 4,
'bevelThickness': 1,
'bevelSize': 1.5,
'bevelSegments': 3
}
gui.add(params, 'test', 0, 10).onChange(val => {
test = val
})
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
rotationAnchor.rotation.z = (Math.cos(performance.now() * 0.001) * Math.PI * 0.25) + (Math.PI * 1.25)
requestAnimationFrame(animate);
// Re-render scene
renderer.render(scene, camera);
// Update stats
stats.update();
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.2/dat.gui.min.js"></script>

Zoom in to the object based on mouse pointer

Here I'm trying to zoom in the object(say cube) based on the on mouse pointer position. Here I googled and got a idea that it can be achieved in D3 JS and also it can achieved by capturing the mouse pointer 3D point and passing it in the mouse wheel function.
Here's the fiddle https://jsfiddle.net/fpt9hswo/
var scene, renderer, camera;
var cube;
var controls;
var containerWidth = window.innerWidth,
containerHeight = window.innerHeight;
init();
animate();
function init() {
configureRenderer();
scene = new THREE.Scene();
configureCube();
configureCamera();
configureLight();
configureControls();
fitAll();
}
function configureRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(containerWidth, containerHeight);
document.body.appendChild(renderer.domElement);
}
function configureCube() {
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(50, 0, 0);
scene.add(cube);
}
function configureCamera() {
camera = new THREE.PerspectiveCamera(45, containerWidth / containerHeight, 1, 1000);
camera.position.set(0, 160, 400);
camera.lookAt(scene);
}
function configureLight() {
pointLight = new THREE.PointLight(0xffffff, 1.0, 100000);
pointLight.position.set(0, 300, 200);
scene.add(pointLight);
}
function configureControls() {
controls = new THREE.TrackballControls(camera, renderer.domElement);
// configuration of controls
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5.0;
controls.panSpeed = 2.0;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0;
}
function fitAll() {
// Calculate bounding box of the whole scene
var boundingBoxOfNode = new THREE.Box3().setFromObject(scene),
centerOfGravity = boundingBoxOfNode.getCenter();
/************* CAMERA *************************/
camera.position.addVectors(camera.position, centerOfGravity);
camera.lookAt(centerOfGravity);
//new camera positions will be set here
//Eg: camera.position.set(newCamera.x,newCamera.y,newCamera.z);
//Similarly new camera rotation and quaternion coordinates will be set
//Eg: camera.rotation.set(newCamera.rotatex,newCamera.rotatey,newCamera.rotatez);
//Eg: camera.quaternion.set(newCamera.qw,newCamera.qx,newCamera.qy,newCamera.qz);
/************* CONTROLS *************************/
controls.target.set(centerOfGravity.x, centerOfGravity.y, centerOfGravity.z);
//new controls.target values will be set here
//Eg: controls.target.set(newCamera.targetx,newCamera.targety,newCamera.targetz);
}
function animate() {
controls.update();
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
Thanks in advance
To do what you want you've to implement your own mouse wheel event and you've to disable the default zoom of THREE.TrackballControls:
controls = new THREE.TrackballControls(camera, renderer.domElement);
//...
controls.zoomSpeed = 0.0;
Add a wheel event:
e.g.
renderer.domElement.addEventListener("wheel", event => {
const delta = event.deltaY;
// [...]
}
Zooming in perspective projection can be achieved, by shifting the camera position in the depth of the world.
If you want to zoom to a point, this can be achieved by moving the camera along a ray, from the camera position through the cursor (mouse) position and to move the target position in parallel.
A point on the ray from the camera to the cursor in normalized device space can be found with ease. In normalized device space all coordinates are in range [-1, 1] and all points with the same x and y coordinate are at the same ray. If the z coordinate is -1, the the point is on the near plane and if z is 1 then the point is on the far plane.
e.g. NDC point on the far plane and on a ray through the cursor:
let x = 2 * event.clientX / window.innerWidth - 1;
let y = 1 - 2 * event.clientY / window.innerHeight;
let cursorpos = new THREE.Vector3(x, y, 1);
This point in normalized device space can be transformed to a point in world space, by THREE.Vector3.unproject(). The parameter to the function has to be the THREE.Camera which defines the view and projection:
cursorpos.unproject(camera);
The direction for the movement of the camera is the normalized direction from the camera position to the cursor position in world space:
let dir = new THREE.Vector3().copy(cursorpos).sub(camera.position).normalize();
Calculate the movement dependent on the direction and the mouse wheel delta and update the camera and THREE.TrackballControls:
let shift = new THREE.Vector3().copy(dir).multiplyScalar(delta * 0.1);
camera.position.add(shift);
controls.position0.add(shift);
controls.target.add(shift);
See the example, where I applied the suggestions to the code of the question:
var scene, renderer, camera;
var cube;
var controls;
var containerWidth = window.innerWidth,
containerHeight = window.innerHeight;
init();
animate();
function init() {
configureRenderer();
scene = new THREE.Scene();
configureCube();
configureCamera();
configureLight();
configureControls();
}
function configureRenderer() {
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(containerWidth, containerHeight);
document.body.appendChild(renderer.domElement);
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
if (controls)
controls.handleResize();
}
renderer.domElement.addEventListener("wheel", event => {
const delta = event.deltaY;
let x = 2 * event.clientX / window.innerWidth - 1;
let y = 1 - 2 * event.clientY / window.innerHeight;
let cursorpos = new THREE.Vector3(x, y, 1);
cursorpos.unproject(camera);
let dir = new THREE.Vector3().copy(cursorpos).sub(camera.position).normalize();
let shift = new THREE.Vector3().copy(dir).multiplyScalar(delta * 0.1);
camera.position.add(shift);
controls.position0.add(shift);
controls.target.add(shift);
});
}
function configureCube() {
var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: 0xff0000
});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.set(50, 0, 0);
scene.add(cube);
}
function configureCamera() {
camera = new THREE.PerspectiveCamera(45, containerWidth / containerHeight, 1, 1000);
camera.position.set(0, 160, 400);
camera.lookAt(scene);
}
function configureLight() {
pointLight = new THREE.PointLight(0xffffff, 1.0, 100000);
pointLight.position.set(0, 300, 200);
scene.add(pointLight);
}
function configureControls() {
controls = new THREE.TrackballControls(camera, renderer.domElement);
// configuration of controls
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 0.0;
controls.panSpeed = 2.0;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0;
}
function animate() {
controls.update();
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>

THREE.js Random multidirectional orbits around point using trig

I'm trying to rotate multiple objects around the same Vector3 point but in different directions - so they effectively 'swarm' around the point.
I'm using the trig approach rather simply wrapping each object in a Container and applying some random rotations as I'm projecting their 3D vector to 2D positions to attach label DIVs above the canvas, and the container approach messes with with project class.
Here's my current code which makes all objects rotate around the point along the same orbit path:
for(var i = 0; i<objectsArr.length; i++){
var obj = objectsArr[i];
var radius = obj.angle * (Math.PI / 180);
obj.position.x = obj.radius * Math.cos(radius);
obj.position.y = obj.radius * Math.sin(radius);
obj.angle += obj.orbitSpeed;
}
Does anyone know how I can make them orbit in random directions along X, Y, Z axis?
First, see this answer about rotating objects about a point.
From your code, it looks like you're storing each object's orbit speed, and their current angle of rotation (which represents the vector to the position). Rather than storing a 2D angle, store it as a Vector3 which represents the normal of (perpendicular to) the object's orbital plane. (This will let you really get a "swarm" going later, when you can define different orbital planes.) I also recommend storing the orbit speed in radians per step, so you don't have to perform the conversion every pass.
The rest of problem actually becomes very simple with the Vector3.applyAxisAngle method.
Some pseudo-code:
Subtract the rotation point position from the object's position.
Use the object's orbit speed, and angle to update the temp position.
Add the rotation point position back to the object's position.
To see it in your code:
var obj;
for(var i = 0; i< objectsArr.length; i++){
obj = objectsArr[i];
obj.position.sub(rotationPoint); // rotationPoint is a Vector3
obj.position.applyAxisAngle(obj.angle, obj.orbitSpeed);
obj.add(rotationPoint);
}
And here's a live demo of a few objects orbiting randomly about a "nucleus" at (10, 10, 10).
var renderer, scene, camera, controls, stats, nucleus;
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight,
FOV = 60,
NEAR = 1,
FAR = 1000;
var electrons = [],
numElectrons = 100; // more electrons = slower updating
function populateScene() {
var geo = new THREE.SphereBufferGeometry(10, 16, 16);
var mat = new THREE.MeshPhongMaterial({color:"blue"});
nucleus = new THREE.Mesh(geo, mat);
nucleus.position.set(10, 10, 10); // you can change these values
scene.add(nucleus);
var electron = null,
plane = new THREE.Plane(),
point = new THREE.Vector3();
geo = new THREE.SphereBufferGeometry(1, 16, 16);
mat = new THREE.MeshPhongMaterial({color:"red"});
for(var i = 0; i < numElectrons; ++i){
electron = new THREE.Mesh(geo, mat);
electrons.push(electron);
electron.angle = new THREE.Vector3(
Math.random(),
Math.random(),
Math.random()
).normalize();
electron.orbitSpeed = (Math.random() * 0.05) + 0.05;
if(Math.random() > 0.5) electron.orbitSpeed *= -1;
plane.normal.copy(electron.angle);
point.set(Math.random(), Math.random(), Math.random());
plane.projectPoint(point, electron.position);
electron.position.setLength(Math.floor(Math.random() * 20) + 15);
electron.position.applyAxisAngle(electron.angle, Math.random() / 10);
electron.position.add(nucleus.position);
scene.add(electron);
}
}
function updateElectrons(){
var obj = null;
for(var i = 0; i < numElectrons; ++i){
obj = electrons[i]
obj.position.sub(nucleus.position);
obj.position.applyAxisAngle(obj.angle, obj.orbitSpeed);
obj.position.add(nucleus.position);
}
}
function init() {
document.body.style.backgroundColor = "slateGray";
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 100;
scene.add(camera);
controls = new THREE.TrackballControls(camera, renderer.domElement);
controls.dynamicDampingFactor = 0.5;
controls.rotateSpeed = 3;
var light = new THREE.PointLight(0xffffff, 1, Infinity);
camera.add(light);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0';
document.body.appendChild(stats.domElement);
resize();
window.onresize = resize;
populateScene();
animate();
}
function resize() {
WIDTH = window.innerWidth;
HEIGHT = window.innerHeight;
if (renderer && camera && controls) {
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
controls.handleResize();
}
}
function render() {
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
updateElectrons();
render();
controls.update();
stats.update();
}
function threeReady() {
init();
}
(function() {
function addScript(url, callback) {
callback = callback || function() {};
var script = document.createElement("script");
script.addEventListener("load", callback);
script.setAttribute("src", url);
document.head.appendChild(script);
}
addScript("https://threejs.org/build/three.js", function() {
addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
threeReady();
})
})
})
})();
three.js r86

How to detect collision between MOVING boxes

I followed this article on MDN to detect collision between boxes and tried it in this JSFiddle.
I want to be able to move the red box and when they're not colliding anymore I want the logs to stop appearing... However, I don't know how to keep the Box3 (the bounding box of my box) updated to the newer position of my main box.
I currently move the box around with keyboard keys like so:
checking if key is pressed
switch (e.key) {
case `z`:
moveForward = true;
break;
case `s`:
moveBackward = true;
break;
case `q`:
moveLeft = true;
break;
case `d`:
moveRight = true;
break;
}
in my animate function:
if (moveForward) redBox.position.z -= .05;
if (moveBackward) redBox.position.z += .05;
if (moveLeft) redBox.position.x -= .05;
if (moveRight) redBox.position.x += .05;
I would’ve guessed the easy solution would be to update the position of the Box3, but it seems like I’m not allowed to add the Box3 to my scene… So I don’t know how to update it.
var scene, renderer, camera;
var redBox, blueBox, redBbox, blueBbox;
var controls;
init();
animate();
function init()
{
renderer = new THREE.WebGLRenderer( {antialias:true} );
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize (width, height);
document.body.appendChild (renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
camera.position.y = 50;
camera.position.z = 50;
camera.position.x = 50;
camera.lookAt (new THREE.Vector3(0,0,0));
controls = new THREE.OrbitControls (camera, renderer.domElement);
redBox = new THREE.Mesh(
new THREE.BoxGeometry(3, 3, 3),
new THREE.MeshBasicMaterial({color: 0xff00000})//RED box
);
redBox.position.set(3, 3, 3);
redBbox = new THREE.Box3(new THREE.Vector3(), new THREE.Vector3());
redBbox.setFromObject(redBox);
const redBoxHelper = new THREE.BoxHelper(redBox, 0xFFFFFF);
scene.add(redBox, redBoxHelper);
blueBox = new THREE.Mesh(
new THREE.BoxGeometry(3, 2, 5),
new THREE.MeshBasicMaterial({color: 0x00000ff})//BLUE box
);
blueBox.position.set(3, 3, 3);
blueBbox = new THREE.Box3(new THREE.Vector3(), new THREE.Vector3());
blueBbox.setFromObject(blueBox);
const blueBoxHelper = new THREE.BoxHelper(blueBox, 0xFFFFFF);
scene.add(blueBox, blueBoxHelper);
window.addEventListener ('resize', onWindowResize, false);
}
function onWindowResize ()
{
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize (window.innerWidth, window.innerHeight);
}
function animate()
{
//controls.update();
requestAnimationFrame ( animate );
if(redBbox.intersectsBox(blueBbox)){
console.log('intersection');
}
renderer.render (scene, camera);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>
<script src="https://dl.dropboxusercontent.com/u/3587259/Code/Threejs/OrbitControls.js">
</script>
In according to the MDN article's example, add next two lines into your animation() function before checking of intersection:
redBbox.setFromObject(redBox);
blueBbox.setFromObject(blueBox);
jsfiddle example.

Categories