I'm trying to reset the position of my latest Object in three.js.
First of all I have this:
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
controls.enabled = false;
SELECTED = intersects[ 0 ].object;
container.style.cursor = 'move';
//mouse Click goes here
buildMenu();
}
}
In my buildMenu Function I position my Object like this:
function buildMenu(){
camera.position.set(0,60,250);
//HERE I POSITION MY SELECTED OBJECT
SELECTED.position.x = 0;
SELECTED.position.y = 50;
SELECTED.position.z = 200;
$(document).bind('click',function(){
$(document).click(function(){
scene.remove(overlayBG);
**//HERE I TRY TO RESET EVERYTHING BUT NOTHING WORKS : (**
});
});
//$('#bg, #menu').fadeIn(100)
}
I thought I can easily reset my Object by mesh.position.set(0,0,0) inside the $(document).click Function but that doesn't work. How can I detect the latest (Re-Positioned) Object and how can I reposition it.
I'm not sure if I understood correctly.
Made a simple example, You don't need to bind any click events for this. Example is here
//last selected object
if ( SELECTED ){
SELECTED.position.set(0,0,0);
}
//new selected object
SELECTED = intersects[ 0 ].object;
Related
I am having the following function to get the selected object, so my function is here
function onMouseDown(event) {
console.log(event);
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var pLocal = new THREE.Vector3(0, 0, -1);
var pWorld = pLocal.applyMatrix4(camera.matrixWorld);
var ray = new THREE.Raycaster(pWorld, vector.sub(pWorld).normalize());
ray.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = ray.intersectObjects( scene.children );
//console.log(intersects);
console.log(scene.children);
if ( intersects.length > 0 ) {
var clickedObject = intersects[0].object;
console.log('objects '+intersects[ 0 ].object);
console.log('objects id'+intersects[ 0 ].object.id);
console.log('objects name'+intersects[ 0 ].object.name);
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
console.log('INTERSECTED '+INTERSECTED);
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
}
but am getting empty array on intersects , i tried my level best but i can't find any solution here, if i console the scene.children i can see all the attached objects of the scene
on before this am adding the object into scene as like here
var loader = new THREE.JSONLoader();
loader.load("uploads/accessories/3d/code/3dfile_"+file+".js",
function(geometry, object_material)
{
var object = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(object_material));
model = new THREE.Object3D();
model.id="Myid"+file;
model.name="Myname"+file;
model.userData ={ URL: "http://myurl.com" };
model.add(object);
model.position.set(x,y,z);
model.scale.set(obj_width,obj_height,obj_rotation);
model.opacity =2;
model.rotation.y = 600;
model.duration = 12000;
model.mirroredLoop = true;
model.castShadow = true;
model.receiveShadow = true;
console.log(model);
var smodel=model;
scene.add(smodel);
}
);
now i want to get the added model by new THREE.Object3D(); in onMouseDown but i can't, any one have the same issue ?
You need to pass in the recursive flag to Raycaster.intersectObjects().
intersects = ray.intersectObjects( scene.children, true );
Also, do not overwrite Object3D.id. Set Object3D.userData.id instead.
three.js r.68
You are using a MeshFaceMaterial which doesn't have a hex to grab. You have to get the 'materials array' from the 'material' like so:
for(var p =0; p < INTERSECTED.material.materials.length; p++){
INTERSECTED.currentHex = INTERSECTED.material.materials[p].emissive.getHex();
}
This way you are not selecting the MeshFaceMaterial but the MeshLambertMaterial or MeshBasicMaterial that you are using for each face. If you are using images as your textures I would suggest checking out Three.js material texture and color for a quick answer to that question.
Hope this was helpful!
Raycaster is little bit tricky I have added explanation
A typical setup need to be done for raycaster to work as follows:
first initialize a variable to hold mouse position from which we need to emit a ray
var mouse = {
x: 0,
y: 0
};
//object intersected
var INTERSECTED;
// pool of objects which can be selected
var objects = [];
// ray caster initialization
var raycaster = new THREE.Raycaster();
We need to create a Object3D parent to hold all child meshes
let mainGroup = new THREE.Object3D();
// add into object pool
objects.push(mainGroup);
scene.add(mainGroup);
// add mesh to be select by ray caster in Object3D parent
mainGroup.add(meshToSelect);
Add an event listener to get mouse X,Y position
document.addEventListener("mousemove", this.onDocumentMouseMove, false);
onDocumentMouseMove = event => {
event.preventDefault();
if (event && typeof event !== undefined) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
};
Now use raycaster like this:
renderScene = () => {
// Emit ray
raycaster.setFromCamera(mouse, this.camera);
// all intersects
var intersects = raycaster.intersectObjects(objects, true);
if (intersects.length > 0) {
if (INTERSECTED != intersects[0].object) {
//if new item selected swap color of new item
//& revert color of previous item
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
INTERSECTED = intersects[0].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
INTERSECTED.material.color.setHex(0xff0000);
}
} else {
// None selected then revert color of previous item
if (INTERSECTED)
INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
INTERSECTED = null;
}
if (this.renderer) this.renderer.render(scene, this.camera);
};
Try to make through 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 jsonLoader = new THREE.JSONLoader();
jsonLoader.load( "models/Tux.js", addModelToScene );
function addModelToScene( geometry, materials ) {
var material = new THREE.MeshFaceMaterial( materials );
model = new THREE.Mesh( geometry, material );
model.scale.set( 10, 10, 10 ); model.name = 'Tux';
model.rotation.x = -Math.PI/2;
model.position.set( 175, 45, 125 );
scene.add( model );
EventsControls.attach( model );
}
I've got an exported animation from blender loaded into my scene. What I’m trying to achieve is to click the object, the object animated 50% the way through it's keyframes and stops, then if I click the object again the remaining 50% of keyframes are iterated over.
So far I have the object animating to 50%. The issue is that no intersection is found in the click eventhandler.
function mousedown( event )
{
event.preventDefault();
// update mouse object
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
// create a reay with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
var ray = projector.pickingRay( vector.clone(), camera );
//Check for intersections
var intersects = ray.intersectObjects( targetList , true);
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
console.log(intersects);
//Start animating - animation is a global var in this scene triggering
//the animation.
animation = true;
}
else
{
console.log("No intersections");
}
}
This works when the object is first instantiated. It doesn't work after the object has 'been animated' though. After some debugging I can see that even after the object has moved (the first 50% of the animation has played through) the object's geometry's position = (0, 0, 0).
Which seems to make sense as to why the mousedown event isn't picking up an intersection, as in the 'world' the object isn't actually positioned where it seems to be on the screen.
Here's the code for loading the object:
jsonLoader.load("assets/models/cheese01.js", function(geometry){
geometry.computeMorphNormals();
geometry.computeVertexNormals();
// geometry.computeBoundingSphere();
// geometry.normalsNeedUpdate = true;
// verticesNeedUpdate = true;
var material = new THREE.MeshLambertMaterial({
color: 0xaa1100,
morphTargets: true,
morphNormals: true,
wrapAround: true
});
var mesh = new THREE.MorphAnimMesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.duration = 3000;
mesh.time = 0;
mesh.scale.set( 1, 1, 1 );
mesh.rotation.x = degToRad(175);
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
geometry.normalsNeedUpdate = true;
mesh.parseAnimations();
scene.add(mesh);
//Objects subject to intersection checks (from mouse click)
targetList.push(mesh);
//Objects to animate
morphs.push(mesh);
});
Cheers in advance for any help!
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.
I'm trying to detect when the user has clicked on a specific cube in my 3d scene
I've seen a few similar questions but none seem to have quite the same problem as me.
I have a 3D Array of cubes which populates and displays fine but when my mouse down function is called, the intersect array is always empty - I can't see what's wrong and would appreciate any help.
My renderer is set up like so:
function setupRenderer()
{
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColorHex( 0xEEEEEE, 1 );
renderer.domElement.addEventListener( 'mousedown', onDocumentMouseMove, false );
$('body').append(renderer.domElement);
}
and the event handler is:
function onDocumentMouseDown(event)
{
console.log("mouse clicked!");
event.preventDefault();
if(event.target == renderer.domElement)
{
var mouseX = (event.clientX / window.innerWidth)*2-1;
var mouseY = -(event.clientY /window.innerHeight)*2+1;
var vector = new THREE.Vector3(mouseX, mouseY, 0.5);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.subSelf(camera.position).normalize());
var intersects = raycaster.intersectObjects(cubes);
console.log("intersects.length: " + intersects.length);
if ( intersects.length > 0 ) {
console.log("intersected objects");
/* do stuff */
}
}
}
You can see the current project in action at http://kev-adsett.co.uk/experiments/three.js/experiment1/
You need to pass in a single array of objects into
var intersects = raycaster.intersectObjects( objects );
If the objects array is hierarchal (i.e., one of the objects has a child), then you need to specify it this way:
var intersects = raycaster.intersectObjects( objects, true );
You can also pass in scene.children.
This function will not work with your "cubes" data structure.
three.js r.54
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;