Thanks again guys for the previous help, but again, I need your help.
Now I have markers on my map, just like I wanted. But, now these markers need to be clickable. In fact, they need to show another div when clicked (so jquery needed).
I have tried it like this:
First, I started with putting my canvas in a div with an id:
container = document.createElement( 'div' );
container.setAttribute("id", "driedkaart");
document.body.appendChild( container );
Then, by adding the markers like this:
// ADD SPRITES FOR MARKERS
var pin = THREE.ImageUtils.loadTexture( 'pin.png' );
var marker = new THREE.SpriteMaterial( { map: pin } );
var sprite = new THREE.Sprite( marker );
sprite.position.set( 1.3, 1.9, -1.7 );
sprite.scale.set(0.2, 0.6, 0.5 );
var marker = new THREE.SpriteMaterial( { map: pin } );
var sprite1 = new THREE.Sprite( marker );
sprite1.position.set( -2.3, 1.9, -1.9 );
sprite1.scale.set(0.2, 0.6, 0.5 );
//Group the markers to set them relative to the map
group = new THREE.Object3D;
group.add(sprite);
group.add(sprite1);
scene.add(group);
And now, I want to make them clickable (jquery is linked correctly):
sprite = document.createElement('img');
sprite.setAttribute("id", "Eersteslagomieper");
sprite.src = 'pin.png';
document.body.appendChild(sprite);
This creates the img after the canvas. But I need the sprite (on the map) to be clickable.
ps: The marker image isn't set on point just yet, but don't worry about that :)
Thanks again
live code: http://www.bensjitestsite.site50.net
The keyword for clickable objects in three.js is RayCaster.
// Projector to generate the 3D coordinates from the click
var projector = new THREE.Projector();
// push all the clickable objects into this array
var clickableObjects = [];
clickableObjects.push(sprite,sprite1);
// Bind the mousedown handler
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
function onDocumentMouseDown( event ) {
event.preventDefault();
// transforms the 2D click coordinates into a THREE.Vector3 for 3D coordinates
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
// casts a ray from the camera position directly to the calculated vector
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// iterates through the clickable objects and checks if they intersect with the ray
var intersects = raycaster.intersectObjects( clickableObjects );
if ( intersects.length > 0 ) {
// The intersects array contains all the elements,
// that intersect with the casted ray. intersects[0] is the nearest object.
console.log('Intersects:',intersects)
}
}
After casting a ray from the camera position to the normalized vector that is calculated with the mouse down event values, you'll get an array of objects that intersect with the casted ray. You can now identify the clicked object and display the needed div.
Related
I'd like to be able to catch the faces of an object in the radius of a circular cursor (like in painting/photoshop).
I'll show you what is it for https://jsfiddle.net/Shaggisu/w7ufmutr/9/
I'd want be able to selct not only the single face that in the moment intersects with the mouse point but all faces that might be in the circular radius, I tried to uplod some image for that cursor but cant really make it work with external files in jsfiddle.
My question is, if is there some standard method of achieving multiple selection/intersection in a specified radius or should I devised some code that would for example reiterate on suroundin faces around the mouse point in specific moment.
I'm still quite new to three.js so I would ask for some directions to go with it, and especialy if there are some solid ways to achieve, any tip would be helpful too.
var brushTexture = THREE.ImageUtils.loadTexture( '/cur_circle.png' );
var brushMaterial = new THREE.SpriteMaterial( { map: brushTexture, useScreenCoordinates: true, alignment: THREE.SpriteAlignment.center } );
brushSprite = new THREE.Sprite( brushMaterial );
brushSprite.scale.set( 32, 32, 1.0 );
brushSprite.position.set( 50, 50, 0 );
scene.add( brushSprite );
//////////////////////////////////////////////////////////////////////
// initialize object to perform world/screen calculations
projector = new THREE.Projector();
// when the mouse moves, call the given function
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( event )
{
// the following line would stop any other event handler from firing
// (such as the mouse's TrackballControls)
event.preventDefault();
console.log("Click.");
// update the mouse variable
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( targetList );
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
controls.enabled = false; // stops camera rotation
console.log("Hit # " + toString( intersects[0].point ) );
// change the color of the closest face.
intersects[ 0 ].face.color.setRGB( 0.8 * Math.random() + 0.2, 0, 0 );
intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}
}
function onDocumentMouseMove( event){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
brushSprite.position.set( event.clientX, event.clientY, 0);
// find intersections
// create a Ray with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// create an array containing all objects in the scene with which the ray intersects
var intersects = ray.intersectObjects( targetList );
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
console.log("Hit # " + toString( intersects[0].point ) );
// change the color of the closest face.
intersects[ 0 ].face.color.setRGB( 0.8 * Math.random() + 0.2, 0, 0 );
intersects[ 0 ].object.geometry.colorsNeedUpdate = true;
}
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
}
function onDocumentMouseUp( event){
event.preventDefault();
document.removeEventListener( "mousemove", onDocumentMouseMove);
controls.enabled = true;
}
The code is modified version of the stemkoskis github that I used for practice.
I have already extended it somewhat for camera management in intersection events and continous selection, but the selection of multiple faces in a radius is what interests me now the most.
You can do this in javascript, modifying vertex color, like you do it in your sample but you will be quickly limited by the number of polygon.
That said, consider your brush like a cone, which start from the Ray.origin and extend in Ray.direction. The radius of the cone is driven by the radius of your brush.
Iterate over each vertices.
For each, get the minimum distance between the vertex to the Ray line.
Get the radius of the brush/cone based on the distance between this vertex and the Ray.origin
If the minimum distance is inferior to the cone radius, your vertex is "in". You can also handle the distance to create a smooth brush.
It should looks like this, it kind of pseudo code, you may need to adapt to ThreeJs Math lib:
// Important, Ray origin and direction must be defined in the same space a vertices positions
// You may need to transform ray origin and direction in object local space.
// get the length of Ray.direction
// may be useless if 'direction' is normalized
var rayDirLenSq = ray.direction.length();
rayDirLenSq *= rayDirLenSq;
var brushRadius = 10.0;
for( var i=0;i< vertices.length;i++){
// get the vertex
var v = vertices[i];
var vdir = v.sub( ray.origin );
var dot = vdir.dot( ray.direction ) / rayDirLenSq;
if( dot < 0 ){
// handle vertices behind the camera if needed
}
// v2 : projection of the vertex onto ray line
var v2 = ray.direction.clone().multiplyScalar( dot );
// v3 : projection -> vertex
var v3 = vdir.subtract( v2 )
// dist is the distance between the vertex and the ray line
var dist = v3.length()
// 0 when vertex is at the brush border
// 1 when vertex is in the brush center
var paintingFactor = Math.max(0.0, 1.0 - dist/brushRadius )
}
Depending of what you want, you can store the painting factor of each vertices to get a mean factor per faces. Or you can modify vertex color of each vertices independantly to get gradients on your faces...
I didn't test the code, it may contain some mistakes :)
A more advanced method
You could also use a texture to paint on. You will get rid of vertex (and javascript) limitations. You will be able to paint with textured brushes, and have detail inside a triangle (no more vertex color).
The principle :
You need UVs datas and a texture + FBO for each of your meshes.
In a prepass, for each mesh, render it to it's Fbo in it's uvs coords
gl_Position = vec4( UVs*2.0-1.0, 0.0, 1.0 );
Provide the worldSpace vertex position to fragment shader, so you can access the world space position of each pixel of the object texture.
vVertexPosition = modelMatrix * aPosition;
With vVertexPosition in your fragment shader, you can then use the same method as the javascript one to get the brushFactor of each pixels of your mesh.
You can even project this world space pixel position in a custom projection matrix based on the Ray to get the uvs coordinate of the pixel in a brush texture, and paint with textured brush.
I used STLLoader to load an stl onto a threeJS scene returning a BufferGeometry.
I then used
myMesh.position.set( x,y,z )
myMesh.rotation.setFromQuaternion ( quaternion , 'XYZ');
to translate the geometry. This effectively changes the
myMesh.position
myMesh.quaternion
Translation is happening in the scene and all works well.
I expected that the
myMesh.geometry.attributes.position.array
would be different before and after the translation - but it remained identical. I want to extract the new veritces from the buffergeometry after translation.
I tried to call
myMesh.geometry.dynamic = true;
myMesh.geometry.attributes.position.needsUpdate = true;
in the render loop but no luck as I haven't updated the vertices explicity.
You want to get the world position of a mesh's geometry, taking into consideration the mesh's transform matrix, mesh.matrix. Also, your mesh geometry is THREE.BufferGeometry.
Here is the pattern to follow:
mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 10, 10, 10 );
mesh.rotation.set( - Math.PI / 2, 0, 0 );
mesh.scale.set( 1, 1, 1 );
scene.add( mesh );
mesh.updateMatrix(); // make sure the mesh's matrix is updated
var vec = new THREE.Vector3();
var attribute = mesh.geometry.attributes.position; // we want the position data
var index = 1; // index is zero-based, so this the the 2nd vertex
vec.fromAttribute( attribute, index ); // extract the x,y,z coordinates
vec.applyMatrix4( mesh.matrix ); // apply the mesh's matrix transform
three.js r.71
Im currently working on an small web-application which is using threejs. I ran into the following issue:
I build a prototype which contains my threejs content and everything works well here (The canvas is in the prototype window.innerWidth and window.innerHeight => so has the same size as my Browser window. Selecting works well but I want to use the canvas on my web page application and picking of 3d surfaces needs to work as well there.
I discovered as soon as I change the margin or top via CSS of the canvas it doesn't work anymore. The web-application is based on a scroll page and the threejs canvas is inside a div container which can only be seen by scrolling through the page.
For picking I use the following logic/code -> this one works well in the "fullscreen prototype" but not in the web application page
self.renderer.domElement.addEventListener( 'click', function(event){
event.preventDefault();
//CONVERT MOUSE POSITION TO CORRECT VECTOR
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
//TRANSLATES A 2D POINT FROM Normalized Device Coordinates TO RAYCASTER THAT CAN BE USED FOR PICKING
self.projector.unprojectVector( vector, self.camera );
//RAYCASTER IS NEEDED TO DETECT INTERACTION WITH CUBE SURFACE
var raycaster = new THREE.Raycaster( self.camera.position, vector.sub( self.camera.position ).normalize() );
var intersects = raycaster.intersectObjects( self.scene.children );
//CHANGE COLOR BASED ON INTERSECTION WITH ELEMENT
if ( intersects.length > 0 ) {
//SELECTED OBJECT
}
}, false );
I think that the calculation is wrong for the var vector but I just can't figure it out how to do it correctly.
Any help would be appreciated
Thank you
best reards
200% way
var x = event.offsetX == undefined ? event.layerX : event.offsetX;
var y = event.offsetY == undefined ? event.layerY : event.offsetY;
var vector = new THREE.Vector3();
vector.set( ( x / renderer.domElement.width ) * 2 - 1, - ( y / renderer.domElement.height ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
Or see this example. Look at messages in the console.
<script src="js/controls/EventsControls.js"></script>
EventsControls = new EventsControls( camera, renderer.domElement );
EventsControls.draggable = false;
EventsControls.onclick = function() {
console.log( this.focused.name );
}
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
EventsControls.attach( mesh );
//
function render() {
EventsControls.update();
controls.update();
renderer.render(scene, camera);
}
If you want to use it in your webpage, you probably need to calculate the vector with the width and height from your canvas element instead of the window which is your whole browser window.
I've already searched, but didn't find anything that helps:
I got an vector and a CylinderGeometry Mesh. I want to acchieve, that the cylinder is facing the point where the vector is showing. As input I got a position (c) and a direction (m) (like a line equation: y = mx + c):
function draw (m,c, _color) {
//... create the geometry and mesh
// set the position
line.position.x = c.x;
line.position.y = c.y;
line.position.z = c.z;
// i've tried something like this:
line.lookAt(c.add(m));
//.. and add to scene
}
But it looks like the direction is the direct opposite of what I want to acchieve.
I've also tried stuff like translation:
geometry.applyMatrix( new THREE.Matrix4().makeTranslation(0, length/2, 0));
and tried to get the rotation manually like line.rotation.x = direction.angleTo(vec3(1,0,0))* 180 / Math.PI;. But none of them worked like I needed.
This works for me:
// Make the geometry (of "distance" length)
var geometry = new THREE.CylinderGeometry( 0.6, 0.6, distance, 8, 1, true );
// shift it so one end rests on the origin
geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, distance / 2, 0 ) );
// rotate it the right way for lookAt to work
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( THREE.Math.degToRad( 90 ) ) );
// Make a mesh with the geometry
var mesh = new THREE.Mesh( geometry, material );
// Position it where we want
mesh.position.copy( from.sceneObject.position );
// And make it point to where we want
mesh.lookAt( to.sceneObject.position );
I have building project with three.js... canvas where you can drag object
and play with the camera view as well... there is a famous example- "Draggable Cubes",
well my project is pretty similar.
On my project there are 3 main events: mouseup /mousedown/ mousemove...
Well everything was ok....but now I'm trying to run this code on iPhone,changing my events
with touchstart / touchmove / touchend...
The moving object function seems to work fine, but when I'm trying to select the object by clicking him,
it's always the same object that been selected... and not the one I'm pointing on....
I guess the problem is with this function:
function onDocumentMouseDown( event ) {
event.preventDefault();
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.intersectObjects( objects );
if ( intersects.length > 0 ) {
SELECTED = intersects[ 0 ].object;
var intersects = ray.intersectObject( plane );
offset.copy( intersects[ 0 ].point ).subSelf( plane.position );
}
}
Is anybody have an idea what is the problem?
in this line :
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
You use the mouse Vector2 object but you don't initialize it.
Something like this should work :
mouse.x = +(event.targetTouches[0].pageX / window.innerwidth) * 2 +-1;
mouse.y = -(event.targetTouches[0].pageY / window.innerHeight) * 2 + 1;