Z-Axis Coordination in WebGL and Three.js - javascript

I'm experimenting with WebGL and Three.js,
But how to calculate the Z-Axis coordination while moving mouse?
I use this for X and Y:
function getCoord(e) {
X = e.layerX - canvas.offsetLeft;
Y = e.layerY - canvas.offsetTop;
}
how to get Z coordination?
Edit: I find this example, but can't figure it out myself...
https://github.com/mrdoob/three.js/blob/master/examples/webgl_interactive_voxelpainter.html
Thanks

Here is the answer...
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse2D.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse2D.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function render() {
mouse3D = projector.unprojectVector( mouse2D.clone(), camera );
...

Related

Three.js raycasting when its not full screen

I've been having issues with three.js raycasting recently.
I have a div element for which the element is not the full screen and it should not be the full screen and I think this is causing issues with the positioning of the raycast which I don't know how to fix.
let mouseVector = new THREE.Vector3()
let raycaster = new THREE.Raycaster()
Object.assign(mouseVector, {
x: 2 * (e.clientX / canvas.width) - 1,
y: 1 - 2 * (e.clientY / canvas.height)
})
raycaster.setFromCamera(mouseVector, camera)
let intersects = raycaster.intersectObject(scene.children)
for(const intersection of intersects) {
console.log(intersection)
}
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObjects( scene.children, true );
console.log(intersects);
for(const ray of intersects) {
if(ray.object.name !== "Grid") {
if(smallestDist == undefined){
smallestDist1 = ray;
}else if(smallestDist > ray.distance){
smallestDist1 = ray
}
}
}
mouse.x = ( e.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( e.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
if(intersects[i].object.name == "Grid"){
}else{
if(smallestDist == undefined){
smallestDist = intersects[i];
}else if(smallestDist > intersects[i].distance){
smallestDist = intersects[i]
}
}
}
console.log(smallestDist1)
if(smallestDist1){
const geometry = new THREE.SphereGeometry( 0.5, 15, 15 );
const material = new THREE.MeshBasicMaterial( {color: 0xFF0000} );
const sphere = new THREE.Mesh( geometry, material );
sphere.position.set(smallestDist1.point.x, smallestDist1.point.y, smallestDist1.point.z)
sphere.name = "TestSphere"
scene.add( sphere );
}
Above is the code i've tried however as the element with the 3D object isn't full window sized I think it's bugging out. Any help is appreciated.
Try to compute mouse like so:
const rect = renderer.domElement.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
mouse.x = ( x / canvas.clientWidth ) * 2 - 1;
mouse.y = ( y / canvas.clientHeight) * - 2 + 1
The top-left point of the browser window is the origin of coordinate (0, 0) for MouseEvent, while the exact center point of canvas for threejs.
Click here to see coordinate details.
As shown the image above, four corner points of the canvas is equivalent to (-1, 1), (1, 1), (1, -1), (-1, -1) under threejs context.
Otherwise, the canvas might have offsets to the browser (0, 0), which can be accessed with canvas.offsetTop and cavas.offsetLeft.
With all above considered, any MouseEvent client coordinate (event.clientX, event.clientY) should be mapped to threejs coordinate as below:
mouse.x = ( (event.clientX - canvas.offsetLeft) / canvas.clientWidth ) * 2 - 1;
mouse.y = ( (event.clientY - canvas.offsetTop) / canvas.clientHeight ) * -2 + 1;

How to apply a radian to multiple resize drag handlers

Consider the following:
You have a resize box with 8 handles, each consisting of a function to resizer its parent box size. Everything is bells and whistle, but upon applying CSS transform to rotate x degrees all hell breaks loose.
How would one apply a radian or such, so that each handler calculates the correct value while dragging a handler?
This is a demo of the full working code with credit to its author.
This is what one is trying to achieve
Resizer handler illustration
---- Code Sample ----
/**
* Drag Resize Bottom Right Corner
**/
drag_br: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x,
y: self._startPos.y,
w: self._startPos.w + ( e.clientX - self._startPos.clientX ),
h: self._startPos.h + ( e.clientY - self._startPos.clientY )
});
self._repaint();
},
/**
* Drag Resize Top Right Corner
**/
drag_tr: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x ,
y: self._startPos.y + ( e.clientY - self._startPos.clientY ),
w: self._startPos.w + ( e.clientX - self._startPos.clientX ),
h: self._startPos.h - ( e.clientY - self._startPos.clientY )
});
self._repaint();
},
/**
* Drag Resize Top Left Corner
**/
drag_tl: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x + ( e.clientX - self._startPos.clientX ),
y: self._startPos.y + ( e.clientY - self._startPos.clientY ),
w: self._startPos.w - ( e.clientX - self._startPos.clientX ),
h: self._startPos.h - ( e.clientY - self._startPos.clientY )
});
self._repaint();
},
/**
* Drag Resize Bottom Left Corner
**/
drag_bl: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x + ( e.clientX - self._startPos.clientX ),
y: self._startPos.y,
w: self._startPos.w - ( e.clientX - self._startPos.clientX ),
h: self._startPos.h + ( e.clientY - self._startPos.clientY )
});
self._repaint();
},
/**
* Drag Resize Top Edge
**/
drag_t: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x,
y: self._startPos.y + ( e.clientY - self._startPos.clientY ),
w: self._startPos.w,
h: self._startPos.h - ( 2 * ( e.clientY - self._startPos.clientY ) )
});
self._repaint();
},
/**
* Drag Resize Left Edge
**/
drag_l: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x + ( e.clientX - self._startPos.clientX ),
y: self._startPos.y,
w: self._startPos.w - ( 2 * ( e.clientX - self._startPos.clientX ) ),
h: self._startPos.h
});
self._repaint();
},
/**
* Drag Resize Bottom Edge
**/
drag_b: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x,
y: self._startPos.y - ( e.clientY - self._startPos.clientY ),
w: self._startPos.w,
h: self._startPos.h + ( 2 * ( e.clientY - self._startPos.clientY ) )
});
self._repaint();
},
/**
* Drag Resize Right Edge
**/
drag_r: function( e ) {
var self = this;
self.layoutRect( {
x: self._startPos.x - ( e.clientX - self._startPos.clientX ),
y: self._startPos.y,
w: self._startPos.w + ( 2 * ( e.clientX - self._startPos.clientX ) ),
h: self._startPos.h
});
self._repaint();
},
I've already searched for a possible solution but each result with (use a plugin/jQuery) or an incomplete solution.
What's the issue?
Currently, the demo has a transform rotate(45deg) when click-dragging the wrong calculation is applied. If no transform rotate is applied it works as expected. This can be seemed by removing the transform: rotate(45deg) from the CSS panel.
My bad, contrary to what I said in comment, the trigonometry is not that basic, mostly because it raises little coordinates problems. Forgive my drawing, hope it's clear:
You want to know X which is what you ant to add to the width of your rectangle
B is your angle (45°), which in JS and in radians gives Math.PI / 4
You know dX and dY which are the coordinates of how much you dragged
A is the angle of the dragging, Z is the distance
Now, these are the useful rules to know: Pythagore's theorem and basic trigonometry:
Pythagore says because of the square triangle, you have Z² = dX² + dY², so in JS:
var Z = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
Trigonometry says that cos(A) = dX / Z, that gives in JS (cosa is the inverse of cos):
var A = Math.acos(dX / Z);
For the same reason you have cos(A + B) = X / Z, it means that:
var X = Z * cos(A + B);
Last thing to know is that to get the height instead of width, you will use sin instead of cos..
ADDITIONAL PROBLEMS:
The coordinate system is upside down for the Y axis: in trigonometry, when you go up, it's positive, on a screen, this is (usually) the contrary -> needs to negate dY to have the right angles
When the vertical distance has been calculated with the right angles, you need to negate the result when going up (because you actually subtract height to the rectangle)
In the picture the mouse could be below the horizontal line, in which case you need to subtract the angles instead of adding them (I recommend you draw it) and use A - B instead of A + B!!!
I won't go into full details for this one, first thought would make one use Math.abs(A-B) to get the angle below the horizontal line, but actually you need to use A-B so that the angle is negative when the mouse angle is "inside" B (between horizontal line and your angle)
Now for the final code for the right bottom corner, hope you'll manage the rest (X in the example is dW here):
drag_br: function( e ) {
var self = this;
var dX = e.clientX - self._startPos.clientX,
dY = self._startPos.clientY - e.clientY, // same as -(e.clientY - self._startPos.clientY)
Z = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2)),
B = Math.PI / 4,
A, dW, dH;
if(Z === 0){ //no division by zero please
dW = 0;
dH = 0;
}else{
A = Math.acos(dX / Z);
if(dY >= 0){ //dragged up from horizontal line
dW = Z * Math.cos(A + B);
dH = -Z * Math.sin(A + B);
}else{ //dragged down from horizontal line
dW = Z * Math.cos(A - B);
dH = Z * Math.sin(A - B);
}
}
self.layoutRect( {
x: self._startPos.x,
y: self._startPos.y,
w: self._startPos.w + dW,
h: self._startPos.h + dH
});
self._repaint();
},

how to detect click events on three.js sprite

I want to detect a click event on a three.js sprite here's the code:
function bindEvents(state) {
let intersected;
function onDocumentMouseDown(event) {
event.preventDefault();
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = - (event.clientY / window.innerHeight) * 2 + 1;
const camera = state.threeD.elements.camera;
const raycaster = state.threeD.raycaster;
raycaster.setFromCamera(new THREE.Vector2(mouseX, mouseY), camera);
var intersects = raycaster.intersectObjects(state.clickables?state.clickables:[]);
if (intersects.length > 0) {
if (intersected) intersected.material.map = intersected.currentMap;
intersected = intersects[0].object;
intersected.currentMap = intersected.material.map;
intersected.material.map = selectCityTexture(128, '#cccccc', 'cccccc');
}
}
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('touchstart', onDocumentMouseDown, false);
}
This almost works, the mouse position is offset by some amount. Probably some calculation error, I have no idea how the above code works please help.
const mouseX = (event.clientX / window.innerWidth) * 2 - 1;
const mouseY = - (event.clientY / window.innerHeight) * 2 + 1;
these are changed with
const mouseX = (event.clientX / canvas.width) * 2 - 1;
const mouseY = - (event.clientY / canvas.height) * 2 + 1;

Three.js Raycaster works with mouse not with touch

I have an annoying problem where my raycasting is fine with the mouse but not with touch.
I setup events as such
$(document).bind('mousedown', onDocumentMouseDown);
$(document).bind('mousewheel', onDocumentMouseWheel);
$(document).bind('touchstart', onDocumentTouchStart);
$(document).bind('touchmove', onDocumentTouchMove);
My mouse event
function onDocumentMouseDown(e) {
e.preventDefault();
detectHotspotClick(e.pageX,e.pageY);
}
My Touch event
function onDocumentTouchStart(e) {
e.preventDefault();
var event = e.originalEvent;
if (event.touches.length == 1) {
var t=event.touches[0];
draggingY = t.pageY;
detectHotspotClick(t.pageX,t.pageY);
}
if (event.touches.length == 2) {
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
}
}
My detection
function detectHotspotClick(x,y){
console.log('detectHotspotClick('+ x + "," + y+ ")" );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = ( x / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( y / renderer.domElement.height ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( targetList, true );
if ( intersects.length > 0)
hotspotClick(intersects[ 0 ].object);
}
This works fine with mouse, but I notice on Touch, the mouse.x value always seems to be negetative and I'm not sure that's right.
Is there any other way of doing this I should consider?
Not sure why.. but it works with detectHotspotClick(t.pageX*2,t.pageY*2) on the touchevent, which must report different figures to the mouse event.

three.js rotate object on mouse down and move

I'm trying to get a good mouse movement in my scene so I can rotate around the object.
I have two problems, I can figure out how to limit the movement so that it never rotates below 0 degrees on the Y axis. (I dont want to see the object from below, only above)
And the second thing I can't figure out is how to make the movement smooth. Now with what I have achieved in the jsfiddle is that the camera moves back to its starting position before starting to rotate.
My try: http://jsfiddle.net/phacer/FHD8W/4/
This is the part I dont get:
var spdy = (HEIGHT_S / 2 - mouseY) / 100;
var spdx = (WIDTH / 2 - mouseX) / 100;
root.rotation.x += -(spdy/10);
root.rotation.y += -(spdx/10);
What I want without using an extra library: http://www.mrdoob.com/projects/voxels/#A/afeYl
You can rotate you scene with this code,
To ensure to not rotate under 0, simule rotation of a vector (0,0,1) and check if y of vector is negative
var mouseDown = false,
mouseX = 0,
mouseY = 0;
function onMouseMove(evt) {
if (!mouseDown) {
return;
}
evt.preventDefault();
var deltaX = evt.clientX - mouseX,
deltaY = evt.clientY - mouseY;
mouseX = evt.clientX;
mouseY = evt.clientY;
rotateScene(deltaX, deltaY);
}
function onMouseDown(evt) {
evt.preventDefault();
mouseDown = true;
mouseX = evt.clientX;
mouseY = evt.clientY;
}
function onMouseUp(evt) {
evt.preventDefault();
mouseDown = false;
}
function addMouseHandler(canvas) {
canvas.addEventListener('mousemove', function (e) {
onMouseMove(e);
}, false);
canvas.addEventListener('mousedown', function (e) {
onMouseDown(e);
}, false);
canvas.addEventListener('mouseup', function (e) {
onMouseUp(e);
}, false);
}
function rotateScene(deltaX, deltaY) {
root.rotation.y += deltaX / 100;
root.rotation.x += deltaY / 100;
}

Categories