Three.js - how to detect what shape was selected? after drag - javascript

I made a canvas with shapes in it...the shapes are draggable.
Everything seems to work fine...But now I'm trying to figure out
how can I detect what shape was selected/dragged?
this is my code: (Javascript)
var container, stats;
var camera, scene, projector, renderer;
var objects = [], plane;
var mouse = new THREE.Vector2(),
offset = new THREE.Vector3(),
INTERSECTED, SELECTED;
var basic_x_dist = 0;
var cameraX = 0,cameraY = 0,cameraZ = 100; // default-same as camera.position.z!
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 500 );
camera.position.z = 300;
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
var light = new THREE.SpotLight( 0xffffff, 1.5 );
light.position.set( 0, 500, 2000 );
light.castShadow = true;
scene.add( light );
var geometry = new THREE.CubeGeometry( 7, 7, 1, 3, 3, 1);
for ( var i = 0; i < 5; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: THREE.ImageUtils.loadTexture( 'red.png' ) } ) );
//object.material.ambient = object.material.color;
object.position.x = basic_x_dist;
basic_x_dist += 10;
//object.position.x = Math.random() * 100 - 50;
//object.position.y = Math.random() * 60 - 30;
//object.position.z = Math.random() * 80 - 40;
//object.rotation.x = ( Math.random() * 360 ) * Math.PI / 180;
//object.rotation.y = ( Math.random() * 360 ) * Math.PI / 180;
//object.rotation.z = ( Math.random() * 360 ) * Math.PI / 180;
//object.scale.x = Math.random() * 2 + 1;
//object.scale.y = Math.random() * 2 + 1;
//object.scale.z = Math.random() * 2 + 1;
//object.castShadow = true;
//object.receiveShadow = true;
scene.add( object );
objects.push( object );
}
plane = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) );
plane.lookAt( camera.position );
plane.visible = false;
scene.add( plane );
projector = new THREE.Projector();
renderer = new THREE.CanvasRenderer();
renderer.sortObjects = false;
renderer.setSize( window.innerWidth, window.innerHeight );
//renderer.shadowMapEnabled = true;
//renderer.shadowMapSoft = true;
//renderer.shadowCameraNear = 3;
//renderer.shadowCameraFar = camera.far;
//renderer.shadowCameraFov = 50;
//renderer.shadowMapBias = 0.0039;
//renderer.shadowMapDarkness = 0.5;
//renderer.shadowMapWidth = 1024;
//renderer.shadowMapHeight = 1024;
container.appendChild( renderer.domElement );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js webgl - draggable cubes';
container.appendChild( info );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
renderer.domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
renderer.domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
renderer.domElement.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.onkeypress=key_event;
}
function onDocumentMouseMove( event ) {
event.preventDefault();
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() );
if ( SELECTED ) {
var intersects = ray.intersectObject( plane );
SELECTED.position.copy( intersects[ 0 ].point.subSelf( offset ) );
return;
}
var intersects = ray.intersectObjects( objects );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
plane.position.copy( INTERSECTED.position );
}
container.style.cursor = 'pointer';
} else {
if ( INTERSECTED ) INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
container.style.cursor = 'auto';
}
}
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 );
container.style.cursor = 'move';
}
}
function onDocumentMouseUp( event ) {
event.preventDefault();
if ( INTERSECTED ) {
plane.position.copy( INTERSECTED.position );
SELECTED = null;
}
container.style.cursor = 'auto';
}
function rotateLeft(){
cameraX += 5;
}
function rotateRight(){
cameraX -= 5;
}
function rotateUp(){
cameraY += 5;
}
function rotateDown(){
cameraY -= 5;
}
function zoomIn(){
cameraZ += 5;
}
function zoomOut(){
cameraZ -= 5;
}
function showPositions(){
for(var i=0; i<5; i++){
alert(objects[i].position.x);
alert(objects[i].position.y);
alert(objects[i].position.z);
}
}
function key_event( event ) {
var unicode=event.keyCode? event.keyCode : event.charCode;
//alert(unicode); // find the char code
switch(unicode){
case 97: rotateLeft(); break;
case 100: rotateRight(); break;
case 119: rotateUp(); break;
case 120: rotateDown(); break;
case 122: zoomIn(); break;
case 99: zoomOut(); break;
case 115: showPositions(); break;
}
}
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
camera.position.x = cameraX; // updating the camera view-x scale after events
camera.position.y = cameraY; // updating the camera view-y scale after events
camera.position.z = cameraZ; // updating the camera view-z scale after events
camera.lookAt( scene.position );
renderer.render( scene, camera );
}

I found pretty simple solution...but I guess that's the way it always works :-) All you have to do for the detection is 2 things: First of all add another variable: var thisObject; After that you have to go to the onMouseDown() function. Right after the SELECTED = intersects[0].object; you have to write this thing:
for(var i=0; i<objects.length; i++)
{
if(SELECTED.position.x == objects[i].position.x)
thisObject = i;
}
...Now thisObject holds the index of the current shape (that was selected/dragged) in objects array...yeah that simple :-)

Related

Mousemove event with three.js and gsap

I am trying to do a mousemove event where the mesh would scale when the mouse hovers over it and then it goes back to its original size when the mouse no longer hovers above it. So I've been looking at other examples and they don,t use gsap. The closest one I've seen is tween.js so maybe my syntax is wrong but I don't know how to rectify it.
Here is my function
function onMouseMove(event) {
//finding position of mouse
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse,camera);
// meshes included in mousemove
objects.push( mesh);
objects.push( mesh2 );
//including objects into intersects
var intersects = raycaster.intersectObjects(objects, true);
//if statement for intersection
if ( intersects.length > 0 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
if ( INTERSECTED )
//gsap animation
INTERSECTED.gsap.to(intersects[0].object.scale, {duration: .7, x: 1.2, y:1.2});
INTERSECTED = intersects[ 0 ].object;
}
} else {// there are no intersections
// restore previous intersection object to its original size
if ( INTERSECTED )
gsap.to(intersects[0].object.scale, {duration: .7, x: 1, y:1});
INTERSECTED = null;
}
}
With this I get an error:
Cannot read property 'object' of undefined
at onMouseMove
But when I previously did a for loop with undefined object, the code works, but I just need it to scale down again
Here is my for loop:
for(var i = 0; i < intersects.length; i++) {
gsap.to(intersects[i].object.scale, {duration: .7, x: 1.2, y:1.2});
};
EDIT:
created a fiddle, using the for loop but commented out the if statement:
let camera, scene, renderer, cube, cube1;
let raycaster;
let mouse = new THREE.Vector2(), INTERSECTED;
const objects = [];
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.z = 20;
scene = new THREE.Scene();
const geometry = new THREE.BoxBufferGeometry(3,3,3);
const material = new THREE. MeshBasicMaterial({ color: 0x00ff00 });
cube = new THREE.Mesh(geometry, material);
cube.position.y = 5;
scene.add(cube);
const geometry1 = new THREE.BoxBufferGeometry(3,3,3);
const material1 = new THREE. MeshBasicMaterial({ color: 0x00ff00 });
cube1 = new THREE.Mesh(geometry1, material1);
scene.add(cube1);
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.addEventListener('mousemove',onMouseMove, false);
}
// animation
function onMouseMove (event) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera(mouse,camera);
//included in mousemove
objects.push( cube );
objects.push( cube1 );
var intersects = raycaster.intersectObjects(objects, true);
//working for loop
for(var i = 0; i < intersects.length; i++) {
gsap.to(intersects[i].object.scale, {duration: .7, x: 1.2, y:1.2});
}
//not working if statement
/*
if ( intersects.length > 0 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
if ( INTERSECTED )
INTERSECTED.gsap.to(intersects[0].object.scale, {duration: .7, x: 1.2, y:1.2});
INTERSECTED = intersects[ 0 ].object;
}
} else {// there are no intersections
// restore previous intersection object (if it exists) to its original size
if ( INTERSECTED )
gsap.to(intersects[0].object.scale, {duration: .7, x: 1.2, y:1.2});
INTERSECTED = null;
}
*/
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.114/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap#3.2.4/dist/gsap.js"></script>
Try it with this updated code:
let camera, scene, renderer, cube, cube1;
let raycaster;
let mouse = new THREE.Vector2(), INTERSECTED = null;
const objects = [];
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.z = 20;
scene = new THREE.Scene();
const geometry = new THREE.BoxBufferGeometry( 3, 3, 3 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
cube.position.y = 5;
scene.add( cube );
const geometry1 = new THREE.BoxBufferGeometry( 3, 3, 3 );
const material1 = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube1 = new THREE.Mesh( geometry1, material1 );
scene.add( cube1 );
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
window.addEventListener( 'mousemove', onMouseMove, false );
}
function onMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
//included in mousemove
objects.push( cube );
objects.push( cube1 );
var intersects = raycaster.intersectObjects( objects, true );
if ( intersects.length > 0 ) {
var object = intersects[ 0 ].object;
if ( object !== INTERSECTED ) {
INTERSECTED = object;
gsap.to( INTERSECTED.scale, { duration: .7, x: 1.2, y: 1.2 } );
}
} else {
if ( INTERSECTED !== null ) {
gsap.to( INTERSECTED.scale, { duration: .7, x: 1, y: 1 } );
INTERSECTED = null;
}
}
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
body {
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.114/build/three.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap#3.2.4/dist/gsap.js"></script>

How to add texture to BufferGeometry faces.?

I have created a bufferGeometry , which consist of 5 planes (100x25) with two triangles each.
function createGeometry() {
var geometry = new THREE.PlaneGeometry(100, 25, 1);
return geometry;
}
function createScene() {
var bufferGeometry = new THREE.BufferGeometry();
var radius = 125;
var count = 5;
var positions = [];
var normals = [];
var colors = [];
var vector = new THREE.Vector3();
var color = new THREE.Color( 0xffffff );
var heartGeometry = createGeometry();
var geometry = new THREE.Geometry();
var step = 0;
for ( var i = 1, l = count; i <= l; i ++ ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
// color.setHSL( ( i / l ), 1.0, 0.7 );
geometry.faces.forEach( function ( face ) {
positions.push( geometry.vertices[ face.a ].x );
positions.push( geometry.vertices[ face.a ].y );
positions.push( geometry.vertices[ face.a ].z );
positions.push( geometry.vertices[ face.b ].x );
positions.push( geometry.vertices[ face.b ].y );
positions.push( geometry.vertices[ face.b ].z );
positions.push( geometry.vertices[ face.c ].x );
positions.push( geometry.vertices[ face.c ].y );
positions.push( geometry.vertices[ face.c ].z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
normals.push( face.normal.x );
normals.push( face.normal.y );
normals.push( face.normal.z );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
colors.push( color.r );
colors.push( color.g );
colors.push( color.b );
});
}
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide
});
var mesh = new THREE.Mesh( bufferGeometry, material );
scene.add( mesh );
}
Now instead of coloring each plane how can i add a text to each plane. Say i just want to display 1,2,3,4,5 at the center of each plane.
What I know is the following has to be done to add the texture.
Generate texture from canvas
Change the material to Shader Material with map:texture
Add uvs to bufferGeometry.
But what is the relation between uvs and texture.
I have the texture creating function
//not sure for each character or one texture as a single texture
function createTexture(ch){
var fontSize = 20;
var c = document.createElement('canvas');
c.width = 100;
c.height = 25;
var ctx = c.getContext('2d');
ctx.font = fontSize+'px Monospace';
ctx.fillText(ch, c.width/2, c.height/2);
var texture = new THREE.Texture(c);
texture.flipY = false;
texture.needsUpdate = true;
return texture;
}
Full DEMO Code
EDIT
I am considering performance as high priority for this experiment. We can add each mesh for each text, but that will increase the number of mesh in screen and reduce performance. I am looking for any idea with a single mesh as in my example.
The closest I found is this, but i didn't understood the exact technique they are using.
You've to copy the first uv channel of the .faceVertexUvs property form the THREE.Geometry, similar as you do it with the vertex coordinates and normal vectors:
for ( var i = 1, l = count; i <= l; i ++ ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
geometry.faces.forEach( function ( face ) {
let f = [face.a, face.b, face.c];
for (let i=0; i < 3; ++i) {
positions.push( ...geometry.vertices[f[i]].toArray() );
normals.push( ...face.normal.toArray() );
colors.push( ...color.toArray() );
}
} );
geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
for (let i=0; i < 3; ++i) {
uv.push( ...faceUvs[i].toArray() );
}
} );
}
If you want to define multiple textures for one geometry, then you've to use multiple THREE.Materials for one THREE.Mesh.
Define groups (see .addGroup) for the THREE.BufferGeometry. Each group associates a range of vertices to a material.
var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );
let materials = []
let v_per_group= positions.length / 3 / count;
for ( var i = 0; i < count; i ++ ) {
bufferGeometry.addGroup(i * v_per_group, v_per_group, i);
let material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide,
map : createTexture("button" + (i+1))
});
materials.push(material);
}
var mesh = new THREE.Mesh( bufferGeometry, materials );
scene.add( mesh );
var camera, scene, renderer, controls, stats;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45.0, window.innerWidth / window.innerHeight, 100, 1500.0 );
camera.position.z = 480.0;
scene.add( camera );
controls = new THREE.TrackballControls( camera, renderer.domElement );
controls.minDistance = 100.0;
controls.maxDistance = 800.0;
controls.dynamicDampingFactor = 0.1;
scene.add( new THREE.AmbientLight( 0xffffff, 1 ) );
createScene();
//stats = new Stats();
//document.body.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
}
function createGeometry() {
var geometry = new THREE.PlaneGeometry(100, 25, 1);
return geometry;
}
function createTexture(ch){
var fontSize = 20;
var c = document.createElement('canvas');
c.width = 128;
c.height = 32;
var ctx = c.getContext('2d');
ctx.beginPath();
ctx.rect(0, 0, 128, 32);
ctx.fillStyle = "white";
ctx.fill();
ctx.fillStyle = "black";
ctx.font = fontSize+'px Monospace';
ctx.fillText(ch, 20, 24);
var texture = new THREE.Texture(c);
texture.flipY = true;
texture.needsUpdate = true;
return texture;
}
function createScene() {
var radius = 125;
var count = 5;
var vector = new THREE.Vector3();
var color = new THREE.Color( 0xffffff );
var heartGeometry = createGeometry();
var geometry = new THREE.Geometry();
var positions = [];
var normals = [];
var colors = [];
var uv = [];
for ( var i = 1, l = count; i <= l; i ++ ) {
geometry.copy( heartGeometry );
const y = i * 30
geometry.translate(-100, y, 0);
geometry.faces.forEach( function ( face ) {
let f = [face.a, face.b, face.c];
for (let i=0; i < 3; ++i) {
positions.push( ...geometry.vertices[f[i]].toArray() );
normals.push( ...face.normal.toArray() );
colors.push( ...color.toArray() );
}
} );
geometry.faceVertexUvs[0].forEach( function ( faceUvs ) {
for (let i=0; i < 3; ++i) {
uv.push( ...faceUvs[i].toArray() );
}
} );
}
var bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
bufferGeometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
bufferGeometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
bufferGeometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( uv, 2 ) );
let materials = []
let v_per_group = positions.length / 3 / count;
for ( var i = 0; i < count; i ++ ) {
bufferGeometry.addGroup(i * v_per_group, v_per_group, i);
let material = new THREE.MeshBasicMaterial({
vertexColors: THREE.VertexColors,
side: THREE.FrontSide,
map : createTexture("button" + (i+1))
});
materials.push(material);
}
var mesh = new THREE.Mesh( bufferGeometry, materials );
scene.add( mesh );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
//stats.update();
renderer.render( scene, camera );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/102/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>

Clickable Three JS Convex Objects (once clicked reveals image)

I adjusted an example from the three js website.
I'm looking for making the small floating objects have a click event.
The click event would trigger an image or video revealed on the larger convex shape in the center
Concept + Images
http://kevinwitkowski.tumblr.com/post/109592122645/workshop-update
Working Sample
Here is my current code.
var container;
var camera, scene, renderer;
var mesh;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
// array of functions for the rendering loop
var onRenderFcts= [];
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xd6e3e8, 0.0030 );
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 1000);
camera.position.z = 0;
controls = new THREE.OrbitControls(camera)
var light, object, materials;
light = new THREE.DirectionalLight( 0xe8dbd6 );
light.position.set( -50, -80, -10 );
scene.add( light );
light = new THREE.DirectionalLight( 0xd6dae8 );
light.position.set( 20, 120, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0xd6e8e4 );
light.position.set( 0, 1, 30 );
scene.add( light );
var map = THREE.ImageUtils.loadTexture( 'textures/1.jpeg' );
map.wrapS = map.wrapT =
THREE.RepeatWrapping;
map.anisotropy = 16;
var materials = [
new THREE.MeshLambertMaterial( { color: 0xffffff, shading: THREE.FlatShading, vertexColors: THREE.VertexColors } )
//new THREE.MeshBasicMaterial( { color: 0x00000, shading: THREE.FlatShading, wireframe: true, transparent: false, opacity: 0.5} )
];
// random convex 1
points = [];
for ( var i = 0; i < 30; i ++ ) {
points.push( randomPointInSphere( 50 ) );
}
object = THREE.SceneUtils.createMultiMaterialObject( new THREE.ConvexGeometry( points ), materials );
object.position.set( 0, 0, 0);
scene.add( object );
// random convex 2
points = [];
for ( var i = 0; i < 30; i ++ ) {
points.push( randomPointInSphere( 15 ) );
}
object = THREE.SceneUtils.createMultiMaterialObject( new THREE.ConvexGeometry( points ), materials );
object.position.set( 15, 50, -60 );
scene.add( object );
// random convex 3
points = [];
for ( var i = 0; i < 30; i ++ ) {
points.push( randomPointInSphere( 15 ) );
}
object = THREE.SceneUtils.createMultiMaterialObject( new THREE.ConvexGeometry( points ), materials );
object.position.set( 30, 10, 80 );
scene.add( object );
// random convex 4
points = [];
for ( var i = 0; i < 30; i ++ ) {
points.push( randomPointInSphere( 8 ) );
}
object = THREE.SceneUtils.createMultiMaterialObject( new THREE.ConvexGeometry( points ), materials );
object.position.set( -80, -50, 20 );
scene.add( object );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setClearColor( 0xf5f5f5 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, true );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
}
//
function randomPointInSphere( radius ) {
return new THREE.Vector3(
( Math.random() - 0.5 ) * 1 * radius,
( Math.random() - 0.5 ) * 2 * radius,
( Math.random() - 0.5 ) * 2 * radius
);
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
var timer = Date.now() * 0.00005;
camera.position.x = Math.cos( timer ) * 300;
camera.position.z = Math.sin( timer ) * 300;
camera.lookAt( scene.position );
for ( var i = 0, l = scene.children.length; i < l; i ++ ) {
var object = scene.children[ i ];
object.rotation.x = timer * 1;
object.rotation.y = timer * 3;
}
// handle window resize
window.addEventListener('resize', function(){
renderer.setSize( window.innerWidth, window.innerHeight )
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
}, true)
renderer.render( scene, camera );
}
var lastTimeMsec= null
requestAnimationFrame(function animate(nowMsec){
// keep looping
requestAnimationFrame( animate );
// measure time
lastTimeMsec = lastTimeMsec || nowMsec-1000/60
var deltaMsec = Math.min(200, nowMsec - lastTimeMsec)
lastTimeMsec = nowMsec
// call each update function
onRenderFcts.forEach(function(onRenderFct){
onRenderFct(deltaMsec/1000, nowMsec/1000)
})
})
The normal way of doing this is using a THREE.Raycaster and THREE.Projector to cast a ray from the camera through space, then finding if an object intersects with this ray.
See this example: http://soledadpenades.com/articles/three-js-tutorials/object-picking/
Thankfully, others have implemented libraries such as ObjectControls: https://github.com/cabbibo/ObjectControls
This allows you to directly attach hover or select events to meshes and it will just work.
CreateMultiMaterialObject method creates an object3D, so when you click, it is necessary to specify the second parameter (recursion) = true:
var intersects = raycaster.intersectObjects( objects, true );
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
}

How to click and slide into three js cube?

i have this code which works nice and i want to add a situation that when i click on the red cube all the page "jump" closer to the cube. (maybe the camera ?).
i do not have any idea and i will hope you can help me.
In general, i want to learn how to click in one object in three js and move into a second object in my page.
this is my code :
<html>
<head>
<script src="js/three.js"></script>
</head>
<body>
<script>
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(document.body.clientWidth, document.body.clientHeight);
document.body.appendChild(renderer.domElement);
renderer.setClearColorHex(0xEEEEEE, 1.0);
renderer.clear();
renderer.shadowCameraFov = 50;
renderer.shadowMapWidth = 1024;;
renderer.shadowMapHeight = 1024;
var fov = 45; // camera field-of-view in degrees
var width = renderer.domElement.width;
var height = renderer.domElement.height;
var aspect = width / height; // view aspect ratio
var near = 1; // near clip plane
var far = 10000; // far clip plane
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = -400;
camera.position.x = 200;
camera.position.y = 350;
var scene = new THREE.Scene();
var cube = new THREE.Mesh(
new THREE.CubeGeometry(50, 50, 50),
new THREE.MeshLambertMaterial({ color: 0xff0000 })
);
scene.add(cube);
cube.castShadow = true;
cube.receiveShadow = true;
var plane = new THREE.Mesh(
new THREE.PlaneGeometry(400, 200, 10, 10),
new THREE.MeshLambertMaterial({ color: 0xffffff }));
plane.rotation.x = -Math.PI / 2;
plane.position.y = -25.1;
plane.receiveShadow = true;
scene.add(plane);
var light = new THREE.SpotLight();
light.castShadow = true;
light.position.set(170, 330, -160);
scene.add(light);
var litCube = new THREE.Mesh(
new THREE.CubeGeometry(50, 50, 50),
new THREE.MeshLambertMaterial({ color: 0xffffff }));
litCube.position.y = 50;
litCube.castShadow = true;
scene.add(litCube);
renderer.shadowMapEnabled = true;
renderer.render(scene, camera);
var paused = false;
var last = new Date().getTime();
var down = false;
var sx = 0, sy = 0;
window.onmousedown = function (ev) {
down = true; sx = ev.clientX; sy = ev.clientY;
};
window.onmouseup = function () { down = false; };
window.onmousemove = function (ev) {
if (down) {
var dx = ev.clientX - sx;
var dy = ev.clientY - sy;
camera.position.x += dx;
camera.position.y += dy;
sx += dx;
sy += dy;
}
}
function animate(t) {
if (!paused) {
last = t;
litCube.position.y = 60 - Math.sin(t / 900) * 25;
litCube.position.x = Math.cos(t / 600) * 85;
litCube.position.z = Math.sin(t / 600) * 85;
litCube.rotation.x = t / 500;
litCube.rotation.y = t / 800;
renderer.clear();
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
window.requestAnimationFrame(animate, renderer.domElement);
};
animate(new Date().getTime());
onmessage = function (ev) {
paused = (ev.data == 'pause');
};
</script>
</body>
</html>
waiting for your replay,
thanks :)
You need to implement different and separated parts to do this:
Selecting an object can be done by using a Raycaster, you'll find many examples here on SO and in the three.js examples such as this one
Orienting the camera - see camera.lookAt( target.position ) - and zooming can be done in many ways, but you might want to use a kind of Control to ease the camera placement process, such as one of these. The TrackballControls for example seems appropriate.
One last bit, as your title says "sliding", is how the "camera jump" is done. If you want a smooth zoom, you'll need a kind of easing function. Have a look on Tween.js for this.
vincent wrote an excellent answer. I just want to add an example to help to understand.
Jsfiddle
<script>
var container, stats;
var camera, scene, projector, raycaster, renderer, selected;
var target, zoom=false;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100, theta = 0;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
var light = new THREE.DirectionalLight( 0xffffff, 2 );
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( -1, -1, -1 ).normalize();
scene.add( light );
var geometry = new THREE.CubeGeometry( 20, 20, 20 );
var cube = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: '#F3B557' } ) );
cube.rotation = new THREE.Euler(0,Math.PI/4,0);
cube.position = new THREE.Vector3(-20,0,0);
scene.add(cube);
cube = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: '#F05B47' } ) );
cube.rotation = new THREE.Euler(0,Math.PI/4,0);
cube.position = new THREE.Vector3(20,0,0);
scene.add(cube);
projector = new THREE.Projector();
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
renderer.domElement.addEventListener( 'mousedown', onCanvasMouseDown, false);
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
// set lookAt position according to target position
if(target){
camera.lookAt( target.position );
}else{
camera.lookAt(new THREE.Vector3(0,0,0));
}
//zoom in and out
if(zoom && camera.fov>10){
camera.fov-=1;
camera.updateProjectionMatrix();
}else if(!zoom && camera.fov<70){
camera.fov+=1;
camera.updateProjectionMatrix();
}
camera.position = new THREE.Vector3(0,100,100);
// find intersections
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
renderer.render( scene, camera );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
//detect selected cube
function onCanvasMouseDown( event ){
if(INTERSECTED){
target = INTERSECTED;
zoom = true;
}else{
zoom = false;
}
}
</script>

How can I make my text labels face the camera at all times? Perhaps using sprites?

I'm looking at two examples, one is canvas interactive objects and the other is mouse tooltip. I tried combining the two to generate text labels on each individual cube and here's what I have so far.
However, the text moves with the rotating cubes and the text appears backwards or sideways at times.
How can I make the text fixed in a sprite like in the mouse tooltip (http://stemkoski.github.io/Three.js/Mouse-Tooltip.html) example? I tried to incorporate the sprite but I kept getting errors. I'm not sure how to do it. Could you explain how I can go by it?
Thanks.
Here's my code so far:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - interactive - cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script src="js/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, projector, renderer;
var projector, mouse = { x: 0, y: 0 }, INTERSECTED;
var particleMaterial;
var currentLabel = null;
var objects = [];
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'three.js - clickable objects';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 0, 300, 500 );
scene = new THREE.Scene();
var geometry = new THREE.CubeGeometry( 100, 100, 100 );
for ( var i = 0; i < 10; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.label = "Object " + i;
scene.add( object );
objects.push( object );
}
var PI2 = Math.PI * 2;
particleMaterial = new THREE.ParticleCanvasMaterial( {
color: 0x000000,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 1, 0, PI2, true );
context.closePath();
context.fill();
}
} );
projector = new THREE.Projector();
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 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 ) {
if ( intersects[ 0 ].object != INTERSECTED )
{
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED ) {
INTERSECTED.material.color.setHex( INTERSECTED.currentHex ); }
// store reference to closest object as current intersection object
INTERSECTED = intersects[ 0 ].object;
// store color of closest object (for later restoration)
INTERSECTED.currentHex = INTERSECTED.material.color.getHex();
// set a new color for closest object
INTERSECTED.material.color.setHex( 0xffff00 );
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 40px Arial";
context1.fillStyle = "rgba(255,0,0,0.95)";
context1.fillText(INTERSECTED.label, 0, 50);
// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;
var material1 = new THREE.MeshBasicMaterial( {map: texture1, side:THREE.DoubleSide } );
material1.transparent = true;
var mesh1 = new THREE.Mesh(
new THREE.PlaneGeometry(canvas1.width, canvas1.height),
material1
);
mesh1.position = intersects[0].point;
if (currentLabel)
scene.remove(currentLabel);
scene.add( mesh1 );
currentLabel = mesh1;
}
else // there are no intersections
{
// restore previous intersection object (if it exists) to its original color
if ( INTERSECTED ) {
console.log("hello");
INTERSECTED.material.color.setHex( INTERSECTED.currentHex );
}
// remove previous intersection object reference
// by setting current intersection object to "nothing"
INTERSECTED = null;
mesh1 = null;
mesh1.position = intersects[0].point;
scene.add( mesh1 );
}
//var particle = new THREE.Particle( particleMaterial );
//particle.position = intersects[ 0 ].point;
//particle.scale.x = particle.scale.y = 8;
//scene.add( particle );
}
/*
// Parse all the faces
for ( var i in intersects ) {
intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
}
*/
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
var radius = 600;
var theta = 0;
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
Billboarding is easy. All you have to do, in your case, is add this inside your render loop:
if ( currentLabel ) currentLabel.lookAt( camera.position );

Categories

Resources