Change value on mouse move, three.js - three.js

I have this sphere
var geometry = new THREE.SphereGeometry( 50, 64, 32 );
And I want to create a fonction like
function onMouseMove(event){
mouseX = (event.clientX - window.innerWidth/2) / window.innerWidth/2;
mouseY = (event.clientY - window.innerHeight/2) / window.innerHeight/2;
}
To get SphereGeometry( 50, MouseX*64, MouseY*32 )
But I can't find the right way to write this..
Can somebody help me, thanks.

Change the position of your mesh, and rerender it.
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
You can use events to bind your mousemove:
canvas.addEventListener('mousemove', onMouseMove, true);

Related

Tween camera target in three.js

I have this code that works well:
function onMouseMove( event ) {
window.onmousedown = function() {
var canvasPosition = renderer.domElement.getBoundingClientRect();
var mouseX = event.clientX - canvasPosition.left;
var mouseY = event.clientY - canvasPosition.top;
var mouseVector = new THREE.Vector3 (
2 * (mouseX / window.innerWidth) - 1,
1 - 2 * (mouseY / window.innerHeight), 1);
mouseVector.unproject( camera );
var dir = mouseVector.sub( camera.position ).normalize();
var distance = - camera.position.z / dir.z;
var pos = camera.position.clone().add( dir.multiplyScalar( distance ) );
camera.getWorldDirection();
camera.lookAt( pos );
// camera.updateMatrixWorld(true);
console.log(mouseVector);
console.log(mouseX);
console.log(mouseY);
// render();
}
}
But I would like to smooth the movement. So I found the following code from the tween example, but not sure how to use it. In the above code, I get current camera lookat from one place, one format, and put the new camera look at in camera.lookat in a different format - neither of which seem to be standard x,y,z.
In the below code, the tween would have me change an properties (x,y,z) on a single item. which the unprojecting and normalizing of the camera do not accommodate:
new TWEEN.Tween( intersects[ 0 ].object.position )
.to( {
x: Math.random() * 800 - 400,
y: Math.random() * 800 - 400,
z: Math.random() * 800 - 400
}, 2000 )
.easing( TWEEN.Easing.Elastic.Out)
.start();
If there is a breakdown or something I can read, or actually work out problems to understand, I'd be grateful. I've read camera tutorials and matrix tutorials over and over for years, but my brain just can't comprehend it.
I've been digging around here quite a bit, but nothing addresses a camera tween - at least for a valid version of threejs
Thank you!
I recommend you get acquainted with linear interpolation, or more commonly known as "lerp". The THREE.Vector3 class has a lerp function that you could use to interpolate between a starting point and an ending point:
var camPos = new THREE.Vector3(0, 0, 0); // Holds current camera position
var targetPos = new THREE.Vector3(10, 10, -10);// Target position
var origin = new THREE.Vector3(0, 0, 0); // Optional origin
function animate(){
// Interpolate camPos toward targetPos
camPos.lerp(targetPos, 0.05);
// Apply new camPos to your camera
camera.position.copy(camPos);
// (Optional) have camera look at the origin after it's been moved
camera.lookAt(origin);
// render();
}
In the above example, your animate() function is called once per frame, and the camera will travel 5% towards targetPos per frame.
If you change targetPos, the camera will animate towards its new target value.
I recommend you first get acquainted with lerping before you start bringing in third-party libraries like TWEEN.js or others.
just for smoothing the movement, this might already help you:
// keep this outside of the event-handler
var lookAtPosition = new THREE.Vector3();
var lookAtTween = new TWEEN.Tween(lookAtPosition);
// as lookAt is not a property we can assign to we need to
// call it every time the tween was updated:
lookAtTween.onUpdate(function() {
camera.lookAt(lookAtPosition);
});
window.onmousedown = function() {
// do your thing to compute pos
// instead of `camera.lookAt(pos)`, do this:
lookAtTween
.stop() // just in case it's still animating
.to(pos, 500) // set destination and duration
.start(); // start the tween
};

Three.js: not accurate to detect faceIndex when clicking the 2*2*2 geometry cube

I have created a 2*2*2 geometry cube via Three.js. Now I want to detect the click event when clicking the faces (24 faces in total).
Please check my current implementation at https://jsfiddle.net/agongdai/pdwg3myr/17/. When clicking on the faces, I want to console.log the current face index. But the index is not always accurate. For example, clicking on the top-left gray cell should show 0, but actually clicking the bottom part of it shows 2.
Please help me to check the mouse click event handler:
var raycaster = new THREE.Raycaster();
function onDocumentMouseDown( event ) {
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.setFromCamera( vector, camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObject( cube );
if ( intersects.length > 0 ) {
var index = Math.floor( intersects[0].faceIndex / 2 );
console.log(index);
}
}
Could anybody please help?
Em, after googling a lot, I found this page and applied the approach. It's working properly https://jsfiddle.net/agongdai/pdwg3myr/19/:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onDocumentMouseDown(event) {
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -(event.clientY / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObject(cube);
if (intersects.length > 0) {
var index = Math.floor(intersects[0].faceIndex / 2);
console.log(index);
}
}
Update
To adjust to the left/top shift and scrolling, update it to https://jsfiddle.net/agongdai/pdwg3myr/24/:
function onDocumentMouseDown(event) {
const holder = renderer.domElement;
const rect = holder.getBoundingClientRect();
mouse.x = ((event.pageX - rect.left - window.scrollX) / holder.clientWidth) * 2 - 1;
mouse.y = -((event.pageY - rect.top - window.scrollY) / holder.clientHeight) * 2 + 1;
...
}

Three.JS - Particles orbiting a point in random directions forming a sphere

I have a particle system where all the particles are positioned at the same coordinates and one after another, in random directions, they (should) start orbiting the center of the scene forming a sphere.
What I managed to achieve until now is a group of Vector3 objects (the particles) that one after another start orbiting the center along the Z axis simply calculating their sine and cosine based on the current angle.
I'm not that good at math and I don't even know what to look for precisely.
Here's what I wrote:
var scene = new THREE.Scene();
let container = document.getElementById('container'),
loader = new THREE.TextureLoader(),
renderer,
camera,
maxParticles = 5000,
particlesDelay = 50,
radius = 50,
sphereGeometry,
sphere;
loader.crossOrigin = true;
function init() {
let vw = window.innerWidth,
vh = window.innerHeight;
renderer = new THREE.WebGLRenderer();
renderer.setSize(vw, vh);
renderer.setPixelRatio(window.devicePixelRatio);
camera = new THREE.PerspectiveCamera(45, vw / vh, 1, 1000);
camera.position.z = 200;
camera.position.x = 30;
camera.position.y = 30;
camera.lookAt(scene.position);
scene.add(camera);
let controls = new THREE.OrbitControls(camera, renderer.domElement);
let axisHelper = new THREE.AxisHelper(50);
scene.add(axisHelper);
container.appendChild(renderer.domElement);
window.addEventListener('resize', onResize, false);
}
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function draw() {
sphereGeometry = new THREE.Geometry();
sphereGeometry.dynamic = true;
let particleTexture = loader.load('https://threejs.org/examples/textures/particle2.png'),
material = new THREE.PointsMaterial({
color: 0xffffff,
size: 3,
transparent: true,
blending: THREE.AdditiveBlending,
map: particleTexture,
depthWrite: false
});
for ( let i = 0; i < maxParticles; i++ ) {
let vertex = new THREE.Vector3(radius, 0, 0);
vertex.delay = Date.now() + (particlesDelay * i);
vertex.angle = 0;
sphereGeometry.vertices.push(vertex);
}
sphere = new THREE.Points(sphereGeometry, material);
scene.add(sphere);
}
function update() {
for ( let i = 0; i < maxParticles; i++ ) {
let particle = sphereGeometry.vertices[i];
if ( Date.now() > particle.delay ) {
let angle = particle.angle += 0.01;
particle.x = radius * Math.cos(angle);
if ( i % 2 === 0 ) {
particle.y = radius * Math.sin(angle);
} else {
particle.y = -radius * Math.sin(angle);
}
}
}
sphere.geometry.verticesNeedUpdate = true;
}
function render() {
update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
init();
draw();
render();
And here's the JSFiddle if you want to see it live:
https://jsfiddle.net/kekkorider/qs6s0wv2/
EDIT: Working example
Can someone please give me a hand?
Thanks in advance!
You want each particle to rotate around a specific random axis. You can either let them follow a parametric equation of a circle in 3D space, or you can make use of THREE.js rotation matrices.
Right now all your particles are rotating round the vector (0, 0, 1). Since your particles start off on the x-axis, you want them all to rotate around a random vector in the y-z plane (0, y, z). This can be defined during the creation of the vertices:
vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
vertex.rotationAxis.normalize();
now you can just call the THREE.Vector3.applyAxisAngle(axis, angle) method on each of your particles with the random rotation axis you created each update:
particle.applyAxisAngle(particle.rotationAxis, 0.01);
To sum up, this is how it should look like:
draw():
...
for ( let i = 0; i < maxParticles; i++ ) {
let vertex = new THREE.Vector3(radius, 0, 0);
vertex.delay = Date.now() + (particlesDelay * i);
vertex.rotationAxis = new THREE.Vector3(0, Math.random() * 2 - 1, Math.random() * 2 - 1);
vertex.rotationAxis.normalize();
sphereGeometry.vertices.push(vertex);
}
...
update():
...
for ( let i = 0; i < maxParticles; i++ ) {
let particle = sphereGeometry.vertices[i];
if ( Date.now() > particle.delay ) {
particle.applyAxisAngle(particle.rotationAxis, 0.01);
}
}
...

Translate mouse click positon to 3D in Three.js

I'm learning Three.js by doing sphere panorama in Three.js (http://mrdoob.github.io/three.js/examples/webgl_panorama_equirectangular.html). I need to place a marker into panorama when I click. However, I have no idea to translate 2D position's mouse click to 3D world. I tried ray picking, but it didn't work(the intersets variable was always returned []).
And this is my code:
//I create sphere panorama:
var geometry = new THREE.SphereGeometry( 500, 60, 40 );
geometry.applyMatrix( new THREE.Matrix4().makeScale( -1, 1, 1 ) );var material = new THREE.MeshBasicMaterial( {
map: THREE.ImageUtils.loadTexture( 'textures/2294472375_24a3b8ef46_o.jpg' )
} );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
//I catched event mouse down:
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5);
projector.unprojectVector(vector, camera);
var ray = new THREE.Ray(camera.position, vector.subSelf(camera.position).normalize());
var intersects = ray.intersectObject(mesh);
if (intersects.length > 0) {
//code to add marker
} else {
//....
}
Please give me way to solve this problem.
Thanks all.

How do I put limits on OrbitControl?

Is there a way to put limits on the OrbitControls.js? Imagine I'm creating something above the ground, I wouldn't like the camera to go below the ground, know what I mean?! The same things goes for zoom in and zoom out. Is there a way to set some variables to limit that because I don't want the camera getting to close or too far away?
OrbitControls source
Zoom in / zoom out
this.minDistance = 0;
this.maxDistance = Infinity;
Where to stop rotation :
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
Don't let to go below the ground
controls.maxPolarAngle = Math.PI/2;
Just in case someone needs a a more robust answer with ground altitude and camera target adjustment:
You find the angle relative to the controls target and the ground position of the camera (regardless of altitude) and assign the maxPolarAngle. Adjust for your up axis, mine was Y. Inside the controls change event:
var centerPosition = controls.target.clone();
centerPosition.y = 0;
var groundPosition = camera.position.clone();
groundPosition.y = 0;
var d = (centerPosition.distanceTo(groundPosition));
var origin = new THREE.Vector2(controls.target.y,0);
var remote = new THREE.Vector2(0,d); // replace 0 with raycasted ground altitude
var angleRadians = Math.atan2(remote.y - origin.y, remote.x - origin.x);
controls.maxPolarAngle = angleRadians;
If you want more control over Orbit:
const controls = new OrbitControls(camera, this.renderer.domElement);
controls.enableDamping = true; //damping
controls.dampingFactor = 0.25; //damping inertia
controls.enableZoom = true; //Zooming
controls.autoRotate = true; // enable rotation
controls.maxPolarAngle = Math.PI / 2; // Limit angle of visibility
controls.keys = {
LEFT: 37, //left arrow
UP: 38, // up arrow
RIGHT: 39, // right arrow
BOTTOM: 40 // down arrow
};
controls.addEventListener("change", () => {
if (this.renderer) this.renderer.render(this.scene, camera);
});

Resources