three.js raycaster in a container - three.js

for my internship in need to make an aplication with three.js what needs to be in a container on a page but it needs an onclick function on the objects. the problem is i cannot find annything on raycasting only in a container and clicking now will not get objects i need
application
onMouseDown(event) {
let s = this;
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
s.mouse.x = ( event.clientX / s.renderer.domElement.clientWidth ) * 2 - 1;
s.mouse.y = - ( event.clientY / s.renderer.domElement.clientHeight ) * 2 + 1;
s.intersects = s.raycaster.intersectObjects(s.blocks, true);
for ( var i = 0; i < s.intersects.length;){
s.intersects[ i ].object.material.color.set( 0xff0000 );
console.log(i)
console.log(s.getScene().children)
console.log(s.intersects)
console.log("test 123")
}
if (s.intersects == 0){
console.log(s.mouse.x)
console.log(s.mouse.y)
}
}
edit: it is not the same as Detect clicked object in THREE.js he is not working in a container. plus he has a little problem with the margins for me everywhere i click on the screen it does not detect what i need and i need it to be working only on the container not the whole webpage plus there help is outdated and is not working annymore

If you are working with a canvas that is not at the top-left corner of the page, you need one more step to get to the normalized device coordinates. Note that the NDC in WebGL are relative to the canvas drawing-area, not the screen or document ([-1,-1] and [1,1] are the bottom-left and top-right corners of the canvas).
Ideally, you'd use ev.offsetX/ev.offsetY, but browser-support for that isn't there yet. Instead, you can do it like this:
const {top, left, width, height} = renderer.domElement.getBoundingClientRect();
mouse.x = -1 + 2 * (ev.clientX - left) / width;
mouse.y = 1 - 2 * (ev.clientY - top) / height;
See here for a working example: https://codepen.io/usefulthink/pen/PVjeJr
Another option is to statically compute the offset-position and size of the canvas on the page and compute the final values based on ev.pageX/ev.pageY. This has the benefit of being a bit more stable (as it is not scrolling-dependent) and would allow to cache the top/left/width/height values.

Related

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);

Does the point coordinate in three.js change if the camera moves?

I'm using the raycaster function to get the coordinates of portions of a texture as a preliminary to creating areas that will link to other portions of my website. The model I'm using is hollow and I'm raycasting to the intersection with the skin of the model from a point on the interior. I've used the standard technique suggested here and elsewhere to determine the coordinates in 3d space from mouse position:
//1. sets the mouse position with a coordinate system where the center
// of the screen is the origin
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
console.log("mouse position: (" + mouse.x + ", "+ mouse.y + ")");
//2. set the picking ray from the camera position and mouse coordinates
raycaster.setFromCamera( mouse, camera );
//3. compute intersections
var intersects = raycaster.intersectObjects( scene.children, true );
var intersect = null;
var point = null;
//console.log(intersects);
for ( var i = 0; i < intersects.length; i++ ) {
console.log(intersects[i]);
if (i = intersects.length - 1) {
intersect = intersects[ i ];
point = intersect[ "point" ];
}
This works, but I'm getting inconsistent results if the camera position changes. My assumption right now is that this is because the mouse coordinates are generated from the center of the screen and that center has changed since I've moved the camera position. I know that getWorldPosition should stay consistent regardless of camera movement, but trying to call point.getWorldPosition returns "undefined" as a result. Is my thinking about why my results are inconsistent correct, and if so and I'm right that getWorldPosition is what I'm looking for how do I go about calling it so I can get the proper xyz coordinates for my intersect?
EDITED TO ADD:
When I target what should be the same point (or close to) on the screen I get very different results.
For example, this is my model (and forgive the janky code under the hood -- I'm still working on it):
http://www.minorworksoflydgate.net/Model/three/examples/clopton_chapel_dev.html
Hitting the upper left corner of the first panel of writing on the opposite wall (so the spot marked with the x in the picture) gets these results (you can capture them within that model by hitting C, escaping out of the pointerlock, and viewing in the console) with the camera at 0,0,0:
x: -0.1947601252025508,
​
y: 0.15833788110908806,
​
z: -0.1643094916216681
If I move in the space (so with a camera position of x: -6.140427450769398, y: 1.9021520960972597e-14, z: -0.30737391540643844) I get the following results for that same spot (as shown in the second picture):
x: -6.229400824609087,
​
y: 0.20157559303778091,
​
z: -0.5109691487471469
My understanding is that if these are the world coordinates for the intersect point they should stay relatively similar, but that x coordinate is much different. Which makes sense since that's the axis the camera moves on, but shouldn't it not make a difference for the point of intersection?
My comment will not be related to the camera but I had also an issue about the raycaster and calculating the position of the mouse is more accurate with the following way.
const rect = renderer.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = - ((event.clientY - rect.top) / rect.height) * 2 + 1;
So the trick to this when there's no mouse available due to a pointer lock is to use the direction of the ray created by the object controls. It's actually pretty simple, but not really out there.
var ray_direction = new THREE.Vector3();
var ray = new THREE.Raycaster(); // create once and reuse
controls.getDirection( ray_direction );
ray.set( controls.getObject().position, ray_direction );

Rotate Three.js scene on mouse move

I have a scene redered just fine and need to make it rotate horizontally and/or vertically as the mouse moves over the DOM. I was able to do the horizontal rotation by changind the scene rotation.y property:
const step = 0.005;
let halfWindowWidth = window.innerWidth / 2;
let target = (Math.PI / halfWindowWidth * mouseX / 2) - 0.785398;
if (target < y) {
if (y - (step * 2) > target) {
target = y - (step * 2);
}
} else if (target > y) {
if (y + (step * 2) < target) {
target = y + (step * 2);
}
}
scene.rotation.y = target;
I know that maybe I will need to work on the x and z axis to make it rotate vertically, but I don't know what calculations should I do.
The rotation must take place as the mouse goes further from the center of the DOM, from -90˚ to 90˚ (180˚ in total). The step constant I use to have the rotation be animated and not simply jump when the mouse moves too quick.
Take a look at the following examples
http://threejs.org/examples/#misc_controls_orbit
http://threejs.org/examples/#misc_controls_trackball
there are other examples for different mouse controls, but both of these allow the camera to rotate around a point and zoom in and out with the mouse wheel, the main difference is OrbitControls enforces the camera up direction, and TrackballControls allows the camera to rotate upside-down.
All you have to do is include the controls in your html document
<script src="js/OrbitControls.js"></script>
and include this line in your source
controls = new THREE.OrbitControls( camera, renderer.domElement );

Three.js StereoEffect displays meshes across 2 eyes

I have a THREE.js scene using StereoEffect renderer. However, when I add new meshes to the scene, they are displayed across the two eyes instead of being duplicated for each eye. I believe THREE.js is supposed to do it automatically and I don't have to duplicate them myself ? (I tried duplicating them but it is a lot of annoying calculation and I could not manage it)
My meshes are actually transparent planes, and I add a DOM element on the top of them to have a flat display.
Illustrating Example
OK I finally found it ! I tried putting back on a texture (not invisible) on my meshes and I discovered the problem.
When we use StereoEffect and we see that our mesh is duplicated on both views, it is actually an illusion : THREE.JS puts an image there, but the actual object is invisible, put exactly at the middle of the two images !
See image here : explanation
If you use raycaster for instance, it will tell you there's not an instersection where you see the mesh, but at the center of the line from left image to right image ! Same for mesh.position.
So what I did was keep an invisible texture, and create two div tags that I placed symmetrically around the mesh position :
var middleX = window.offsetWidth/2;
//left div
if(this.element.id.indexOf("-2") == -1){
var posL = coords2d.x - middleX/2;
this.element.style.left = posL + 'px';
//Hide if gets on the right part of the screen
if(posL > middleX) this.element.style.display = 'none';
}
//right div
else{
var posR = coords2d.x + middleX/2;
this.element.style.left = posR + 'px';
//Hide if gets on the left part of the screen
if(posR < middleX) this.element.style.display = 'none';
}
That gives the illusion that my mesh is there, put it is just empty divs.
Then, to check if someone clicks on my mesh, I do the opposite : I go back to the real position of the mesh before sending it to raycaster !
function hasClicked(e) {
e.preventDefault();
var clientX,clientY;
//GET REAL POSITION OF OBJECT
var middleX = window.offsetWidth/2;
//Right screen
if(e.clientX>middleX){
clientX = e.clientX - middleX/2;
}
//Left screen
else {
clientX = e.clientX + middleX/2;
}
clientY = e.clientY; //Keep same Y coordinate
//TRANSFORM THESE COORDS ON SCREEN INTO THREE.JS COORDS
var mouse = new THREE.Vector2();
mouse.x = (clientX / window.innerWidth) * 2 - 1;
mouse.y = -(clientY / window.innerHeight) * 2 + 1;
//USE RAYCASTER
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(arrowManager.storage);
if (intersects.length > 0) {
//It works !!!
}
}
And it works perfectly fine !! Hope this can help someone else, I was getting really desperate here ;)

draw sprite on where mouse clicked

I am noobie to three.js, after reading https://threejs.org/examples/?q=inter#canvas_interactive_cubes , I want to add a function draw points where i clicked to my project(load and display stl files), I used the formula in that example,
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
but I found when the sprite is not drawn on where i clicked, but a little lower, you can see at http://static.medi-tool.cn/share/ds1/index.html (first click on the model will draw a sprite, second will draw a sprite and calculate the distance between two point, then two sprite will be removed in 2 seconds). can anyone tell me why? thanks a lot.
I found out it is because I have a div up to the canvas, when using the formula, i have to minus the height of that div, in other words, when calculate mouse.x, mouse.y, 'event.clientX' & 'event.clientY' should be relative to the canvas, not viewport.
The test page in the Question is OK now.

Resources