I've got a double click function to allow the user to double click on a car model and it displays which objects have been intersected; e.g. wipers, grille, tyres and so on, and this function displays them in a list with the number of items the double click intersected with.
However, I am now trying to get it so that when a certain part of the car is clicked, for example, the tyres, it will display a paragraph with information on them. I can see how this is just a case of checking the name of the intersecting object and then displaying the relevant text if it intersects it, but every time I go to do what I think is right, it just breaks the already existing function to the point where the whole thing won't run.
I'm not exactly a JavaScript or Three.js pro at all, but trying to progress my function further is proving to be rather difficult.
Any suggestions? I've included the entire double click function, however it's when it's checking if there has been intersections near the bottom that is where the alterations need to be.
// On Mouse double click event function
function onDoubleClick(event) {
// Set the mouse down flag to false
mouseDown = false;
// Canvas x (left) and y (top) position
var canvasLeft = 0;
var canvasTop = 0;
// "event.clientX" is the mouse x position. "event.clientY" is the mouse y position
var tempX = event.clientX - canvasLeft;
var tempY = event.clientY - canvasTop;
// Create a normalised vector in 2d space
var vector = new THREE.Vector3((tempX / window.innerWidth) * 2 - 1, - (tempY / innerHeight) * 2 + 1, 0.5);
// Unproject a 2D point into the 3D word
// Use the camera projection matrix to transform the vector to the 3D world space
vector.unproject(camera);
// Send a ray in the direction the user has clicked from the cameras position
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
// Check if the ray has intersected with any objects and get intersections
var intersects = raycaster.intersectObjects(objects, true);
// Check if intersected with objects
if (intersects.length > 0) {
var tempStr = "Number of items: " + intersects.length + " ";
// List the items that were hit
for(var i=0; i < intersects.length; i++){
if(intersects[i].object.name != ""){
// The mesh name set above
tempStr += " | Name: " + intersects[i].object.name;
} else {
// The names inside the model
tempStr += " | Name: " + intersects[i].object.parent.name;
}
}
//Debug information
document.getElementById("debugInfo").innerHTML = tempStr + ".<br>";
//END
}
}
EDIT:
This is the entire code for the javascript file, as altering elements of the double click function seems to stop the page from loading.
window.onload = init;
// declare variables
var scene,camera,renderer, container;
var controls, guiControls, datGUI;
var grid, color;
var cube, cubeGeometry, cubeMaterial;
var plane, planeGeometry, planeMaterial;
var skyBoxMesh, texture_placeholder;
var spotLight;
var stats;
// Handles the mouse events.
var mouseOverCanvas;
var mouseDown;
// An array of objects that can be clicked on
var objects = [];
//DAE models
var showroom ,carOld, carNew;
var daeObject;
var animations;
var kfAnimations = [];
var kfAnimationsLength = 0;
var lastFrameCurrentTime = [];
var clock = new THREE.Clock();
var mouseOverCanvas, mouseDown;
var objectsClick=[];
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
//creates empty scene
scene = new THREE.Scene();
//camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, .1, 500);
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
//renderer
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor(0xe6f2ff);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMapSoft = true;
container.appendChild( renderer.domElement );
// Add an event to set if the mouse is over our canvas
renderer.domElement.onmouseover=function(e){ mouseOverCanvas = true; }
renderer.domElement.onmousemove=function(e){ mouseOverCanvas = true; }
renderer.domElement.onmouseout=function(e){ mouseOverCanvas = false; }
renderer.domElement.onmousedown=function(e){ mouseDown = true; }
renderer.domElement.onmouseup=function(e){ mouseDown = false; }
// Double Click Event. Set a function called "onDoubleClick"
renderer.domElement.ondblclick=onDoubleClick;
// stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//adds controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render);
var ambient = new THREE.AmbientLight( 0xadad85 );
scene.add( ambient );
//---------- creates grid ---------------
grid = new THREE.GridHelper(50,5);
color= new THREE.Color("rgb(255,0,0)");
grid.setColors( 0x000000);
scene.add(grid);
//----------- creates cube --------------
cubeGeometry = new THREE.BoxGeometry(5,5,5);
cubeMaterial = new THREE.MeshPhongMaterial({color: 0xff3300});
cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.position.x = 0;
cube.position.y = 6;
cube.position.z = 2.5;
cube.castShadow = true;
scene.add(cube);
//----------- creates plane ---------------
planeGeomenty= new THREE.PlaneGeometry(100,100,100);
planeMaterial = new THREE.MeshLambertMaterial({color: 0x00cc00});
plane = new THREE.Mesh(planeGeomenty, planeMaterial);
//position the add objects to the scene
plane.rotation.x = -.5*Math.PI;
plane.receiveShadow = true;
scene.add(plane);
//------------- skyBox --------------
texture_placeholder = document.createElement('canvas');
texture_placeholder.width = 128;
texture_placeholder.height = 128;
var context = texture_placeholder.getContext('2d');
context.fillStyle = 'rgb(200,200, 200)';
context.fillRect(0, 0,texture_placeholder.width, texture_placeholder.height);
var materials = [
loadTexture('images/skybox/posX.jpg'),
loadTexture('images/skybox/negX.jpg'),
loadTexture('images/skybox/posY.jpg'),
loadTexture('images/skybox/negY.jpg'),
loadTexture('images/skybox/posZ.jpg'),
loadTexture('images/skybox/negZ.jpg')
];
skyBoxMesh = new THREE.Mesh(new THREE.BoxGeometry(500,500,500,7,7,7),
new THREE.MeshFaceMaterial(materials));
skyBoxMesh.scale.x = -1;
scene.add(skyBoxMesh);
//---------- loads collada files -----------
loadCollada();
daeObject = cube;
// initialise datGUI controls values
guiControls = new function() {
this.rotationY = 0.0;
this.positionX = 0.0;
this.positionY = 0.0;
this.positionZ = -10;
this.lightX = 20;
this.lightY = 35;
this.lightZ = 40;
this.intensity = 1;
this.distance = 0;
this.angle = 1.570;
this.target = cube;
}
//add spotLight with starting parameters
spotLight = new THREE.SpotLight(0xffffff);
spotLight.castShadow = true;
spotLight.position.set(20,35,40);
spotLight.intensity = guiControls.intensity;
spotLight.distance = guiControls.distance;
spotLight.angle = guiControls.angle;
scene.add(spotLight);
//adds controls on the scene
datGUI = new dat.GUI();
// datGUI.add(guiControls, 'positionZ', 0, 1);
datGUI.add(guiControls, 'positionZ', -10, 25, 0.5). name("Move the car");
datGUI.add(guiControls, 'rotationY', 0, 1).name('Rotate the car');
datGUI.add(guiControls, 'lightX', -60, 180);
datGUI.add(guiControls, 'lightY', 0, 180);
datGUI.add(guiControls, 'lightZ', -60, 180);
datGUI.add(guiControls, 'target',[ 'cube','Modern Mini', 'Classic Mini']).onChange(function() {
if(guiControls.target == 'cube'){
spotLight.target = cube;
daeObject = cube;
}
else if(guiControls.target == 'Classic Mini'){
spotLight.target = carOld;
daeObject = carOld;
}
else if(guiControls.target = 'Modern Mini'){
spotLight.target = carNew;
daeObject = carNew;
}
});
datGUI.add(guiControls, 'intensity', 0.01, 5).onChange(function (value){
spotLight.intensity = value;
});
datGUI.add(guiControls, 'distance', 0, 1000).onChange(function (value){
spotLight.distance = value;
});
datGUI.add(guiControls, 'angle', 0.001, 1.570).onChange(function (value){
spotLight.angle = value;
});
datGUI.close();
container.appendChild(renderer.domElement);
window.addEventListener( 'resize', onWindowResize, false );
}
//------------------------- END INIT() ----------------------------
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function loadCollada() {
//--- Loads the Classic Mini ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/ClassicMini.dae', function ( collada ) {
carOld = collada.scene; // stores dae file to a global variable
carOld.position.set( 14.5, 1.8, -10 );
carOld.scale.set( 0.04, 0.04, 0.04 );
carOld.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
carOld.updateMatrix();
carOld.name = "Classic";
scene.add( carOld );
objects.push( carOld );
} );
//--- loads Modern Mini ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/ModernMini.dae', function ( collada ) {
carNew = collada.scene;
carNew.position.set( -14.5, 6.3, -10 );
carNew.scale.set( 0.06, 0.06, 0.06 );
// creates shadow
carNew.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
carNew.updateMatrix();
carNew.name = "Modern";
scene.add( carNew );
objects.push( carNew );
} );
//--- loads the Showroom ---
colladaLoader = new THREE.ColladaLoader();
colladaLoader.options.convertUpAxis = true;
colladaLoader.load( 'dae_files/roomAnim2.dae', function collada( collada ) {
showroom = collada.scene;
animations = collada.animations;
kfAnimationsLength = animations.length;
// Initialise last frame current time.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
lastFrameCurrentTime[i] = 0;
}
// Get all the key frame animations.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
var anim = animations[ i ];
var keyFrameAnim = new THREE.KeyFrameAnimation( anim );
keyFrameAnim.timeScale = 1;
keyFrameAnim.loop = false;
kfAnimations.push( keyFrameAnim );
anim = kfAnimations[i];
anim.play();
}
showroom.position.set(0, 0, -20);
showroom.scale.set(0.06, 0.06, 0.06);
showroom.traverse(function (child) {
child.castShadow = true;
child.receiveShadow = true;
});
showroom.updateMatrix();
scene.add( showroom );
animate();
} );
}
// On Mouse double click event function
function onDoubleClick(event) {
// Set the mouse down flag to false
mouseDown = false;
// Canvas x (left) and y (top) position
var canvasLeft = 0;
var canvasTop = 0;
// "event.clientX" is the mouse x position. "event.clientY" is the mouse y position
var tempX = event.clientX - canvasLeft;
var tempY = event.clientY - canvasTop;
// Create a normalised vector in 2d space
var vector = new THREE.Vector3((tempX / window.innerWidth) * 2 - 1, - (tempY / innerHeight) * 2 + 1, 0.5);
// Unproject a 2D point into the 3D word
// Use the camera projection matrix to transform the vector to the 3D world space
vector.unproject(camera);
// Send a ray in the direction the user has clicked from the cameras position
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
// Check if the ray has intersected with any objects and get intersections
var intersects = raycaster.intersectObjects(objects, true);
// Check if intersected with objects
if (intersects.length > 0) {
var tempStr = "Number of items: " + intersects.length + " ";
// List the items that were hit
for(var i=0; i < intersects.length; i++){
if(intersects[i].object.name != ""){
// The mesh name set above
tempStr += " | Name: " + intersects[i].object.name;
} else {
// The names inside the model
tempStr += " | Name: " + intersects[i].object.parent.name;
}
}
//Debug information
document.getElementById("debugInfo").innerHTML = tempStr + ".<br>";
//END
}
}
function loopAnimations(){
// Loop through all animations
for ( var i = 0; i < kfAnimationsLength; i++ ) {
// Check if the animation is player and not paused.
if(kfAnimations[i].isPlaying && !kfAnimations[i].isPaused){
if(kfAnimations[i].currentTime == lastFrameCurrentTime[i]) {
kfAnimations[i].stop();
//kfAnimations[i].play();
lastFrameCurrentTime[i] = 0;
}
}
}
}
function play_pauseAnim() {
//checks is there animation and is it paused
if(kfAnimationsLength > 0) {
if(kfAnimations[0].isPlaying) {
for(i = 0; i < kfAnimationsLength; i++){
kfAnimations[i].stop();
}
}else {
for(i = 0; i < kfAnimationsLength; i++) {
lastFrameCurrentTime[i] = 0;
//kfAnimations[i].play(kfAnimations[i].currentTime);
kfAnimations[i].play(0);
}
}
}
}
function checkTime(){
if(kfAnimationsLength > 0) {
if(kfAnimations[0].isPlaying) {
if(kfAnimations[0].currentTime > 3){
play_pauseAnim();
}
}
}
}
// create a render loop to draw the scene 60 times per second
function render() {
//checkTime();
daeObject.rotation.y += guiControls.rotationY;
//if (daeObject.position.z < 25) {
daeObject.position.z = guiControls.positionZ;
//}
spotLight.rotation.x += guiControls.rotationX;
spotLight.rotation.y += guiControls.rotationY;
spotLight.rotation.z += guiControls.rotationZ;
stats.update();
}
function animate () {
var deltaTime = clock.getDelta();
for ( var i = 0; i < kfAnimationsLength; i++ ) {
// Get a key frame animation.
var anim = kfAnimations[i];
anim.update( deltaTime );
}
loopAnimations();
requestAnimationFrame(animate);
// Update last frame current time.
for ( var i = 0; i < kfAnimationsLength; i++ ) {
lastFrameCurrentTime[i] = kfAnimations[i].currentTime;
}
render();
renderer.render(scene, camera);
}
// Loads skybox texture
function loadTexture(path) {
var texture = new THREE.Texture(texture_placeholder);
var material = new THREE.MeshBasicMaterial({
map: texture,
overdraw: 0.5
});
var image = new Image();
image.onload = function() {
texture.image = this;
texture.needsUpdate = true;
};
image.src = path;
return material;
}
Macast,
Please, check if you haven't forgotten in your code:
var objets = [];
var raycaster = new THREE.Raycaster();
And for each part of the car this line :
objects.push( mesh );
Ex:
var geometry = new THREE.RingGeometry( 1, 5, 32 );
var material = new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide } );
var simpleTire = new THREE.Mesh( geometry, material );
simpleTire.name = 'tire';
objects.push( simpleTire );
scene.add( simpleTire );
Then, it's simple :
if ( intersects.length > 0 ) {
switch(intersects[0].object.name){
case 'tire':
console.log('A pretty red tire');
break;
case 'motor':
console.log('An electric motor');
break;
}
}
Related
three.js beginner here! I'm working through several tutorials to learn how to animate 3D objects with three.js, so parts of the code here will seem familiar.
What I'd like to do: find a way to change the scale of each object over a period of time (3 to 5 seconds) in a loop. Ideally, these objects will change in size (and, possibly, shape).
What I have:
var Decoration = function() {
// Run the Group constructor with the given arguments
THREE.Group.apply(this, arguments);
this.rotationSpeed = Math.random() * 0.01 + .001 + .001;
this.rotationPosition = Math.random() * 0.01 + .001 + .001;
this.scale = Math.random() * 0.21 + .051 + .021;
this.rotation.x += 0.01;
this.rotation.y += 0.02;
this.scale.x += 1;
this.scale.y += 1;
this.scale.z = 1;
// A random color assignment
var colors = ['#ff0051', '#f56762','#a53c6c','#f19fa0','#72bdbf','#47689b'];
// The main bauble is an Octahedron
var bauble = new THREE.Mesh(
addNoise(new THREE.OctahedronGeometry(15,0), 0),
new THREE.MeshStandardMaterial( {
color: colors[Math.floor(Math.random()*colors.length)],
shading: THREE.FlatShading ,
metalness: 0,
roughness: 0.8,
refractionRatio: 0.25
} )
);
bauble.castShadow = true;
bauble.receiveShadow = true;
bauble.rotateZ(Math.random()*Math.PI*2);
bauble.rotateY(Math.random()*Math.PI*2);
this.add(bauble);
};
Decoration.prototype = Object.create(THREE.Group.prototype);
Decoration.prototype.constructor = Decoration;
Decoration.prototype.updatePosition = function() {
this.rotationPosition += this.rotationSpeed;
this.rotation.y = (Math.sin(this.rotationPosition));
};
var clock = new THREE.Clock();
// Create a scene which will hold all our meshes to be rendered
var scene = new THREE.Scene();
// Create and position a camera
var camera = new THREE.PerspectiveCamera(
60, // Field of view
window.innerWidth/window.innerHeight, // Aspect ratio
0.1, // Near clipping pane
1000 // Far clipping pane
);
// Reposition the camera
camera.position.set(0,30,50);
// Point the camera at a given coordinate
camera.lookAt(new THREE.Vector3(0,15,0))
// Create a renderer
var renderer = new THREE.WebGLRenderer({ antialias: true });
// Size should be the same as the window
renderer.setSize( window.innerWidth, window.innerHeight );
// Set a near white clear color (default is black)
renderer.setClearColor( 0xfff6e6 );
// Append to the document
document.body.appendChild( renderer.domElement );
var decorations = [];
// Add some new instances of our decoration
var decoration1 = new Decoration();
decoration1.position.y += 10;
scene.add(decoration1);
decorations.push(decoration1);
var decoration2 = new Decoration();
decoration2.position.set(20,15,-10);
decoration2.scale.set(.8,.8,.8);
scene.add(decoration2);
decorations.push(decoration2);
var decoration3 = new Decoration();
decoration3.position.set(20,10,-10);
scene.add(decoration3);
decorations.push(decoration3);
var decoration4 = new Decoration();
decoration3.position.set(20,10,-10);
scene.add(decoration4);
decorations.push(decoration4);
// Render the scene/camera combnation
renderer.render(scene, camera);
requestAnimationFrame(render);
function render() {
controls.update();
var t = clock.getElapsedTime();
if (t >= 3.0)
{
clock = new THREE.Clock();
this.scale.set(1,1,1);
}
else
{
this.scale.x = 1-(t/3.0);
this.scale.y = 1-(t/3.0);
this.scale.z = 1-(t/3.0);
}
// Update the decoration positions
for(var d = 0; d < decorations.length; d++) {
decorations[d].updatePosition();
}
// Render the scene/camera combnation
renderer.render(scene, camera);
// Repeat
requestAnimationFrame(render);
}
Any help here would be greatly appreciated!! Thank you :)
The animation/render for any decoration element you have set up happens in the render function. By adding your sought after transitions to the decoration elements in your render function, you can apply it to multiple variables.
Try this:
First, declare your clock variable:
var clock = new THREE.Clock();
Then, update your render function to:
function render() {
for(var d = 0; d < decorations.length; d++) {
decorations[d].rotation.x += 0.01;
decorations[d].rotation.y += 0.01;
}
var t = clock.getElapsedTime();
if (t >= 3.0)
{
clock = new THREE.Clock();
// Update the decoration positions
for(var d = 0; d < decorations.length; d++) {
decorations[d].scale.set(1,1,1);
}
}
else
{
// Update the decoration positions
for(var d = 0; d < decorations.length; d++) {
decorations[d].scale.x = 1-(t/3.0);
decorations[d].scale.y = 1-(t/3.0);
decorations[d].scale.z = 1-(t/3.0);
}
}
// Render the scene/camera combnation
renderer.render(scene, camera);
// Repeat
requestAnimationFrame(render);
}
Hello I have one doubt:
I have studied:
https://threejs.org/docs/#api/materials/PointsMaterial
And I have adapted the example to work with my existing code.
The aim is to render points on top of the model which we have loaded on click position.
Here we have the code, the important part is onDocumentMouseDown(), the file, logic.js:
if (!Detector.webgl) Detector.addGetWebGLMessage();
// global variables for this scripts
let OriginalImg,
SegmentImg;
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
var mousePressed = false;
var clickCount = 0;
init();
animate();
// initilize the page
function init() {
let filename = "models/nrrd/columna01.nrrd"; // change your nrrd file
let idDiv = 'original';
OriginalImg = new InitCanvas(idDiv, filename);
OriginalImg.init();
console.log(OriginalImg);
filename = "models/nrrd/columnasegmentado01.nrrd"; // change your nrrd file
idDiv = 'segment';
SegmentImg = new InitCanvas(idDiv, filename);
SegmentImg.init();
}
let originalCanvas = document.getElementById('original');
originalCanvas.addEventListener('mousedown', onDocumentMouseDown, false);
originalCanvas.addEventListener('mouseup', onDocumentMouseUp, false);
function onDocumentMouseDown(event) {
mousePressed = true;
clickCount++;
mouse.x = ( ( event.clientX - OriginalImg.renderer.domElement.offsetLeft ) / OriginalImg.renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = -( ( event.clientY - OriginalImg.renderer.domElement.offsetTop ) / OriginalImg.renderer.domElement.clientHeight ) * 2 + 1
console.log('Mouse x position is: ', mouse.x, 'the click number was: ', clickCount);
console.log('Mouse Y position is: ', mouse.y);
raycaster.setFromCamera(mouse.clone(), OriginalImg.camera);
var objects = raycaster.intersectObjects(OriginalImg.scene.children);
var pointGeometry = new THREE.Geometry();
var position = new THREE.Vector3();
position.x = objects[0].point.x;
position.y = objects[0].point.y;
position.z = objects[0].point.z;
pointGeometry.vertices.push(position);
var pointMaterial = new THREE.PointsMaterial({color: 0x888888});
var point = new THREE.Points(pointGeometry, pointMaterial);
OriginalImg.scene.add(point);
console.log(objects);
}
function onDocumentMouseUp(event) {
mousePressed = false
}
function animate() {
requestAnimationFrame(animate);
OriginalImg.animate();
SegmentImg.animate();
}
And we do add the points to the scene, but in fact they do not render, they do not show, and I wonder why?:
As you could see in the image we see that the raycaster intercepts those new created points, however the do not get drawn.
I wonder if they are too small, or just the color hides them with the background.
Could you help me please?.
Additional code:
// this class handles the load and the canva for a nrrd
// Using programming based on prototype: https://javascript.info/class
// This class should be improved:
// - Canvas Width and height
InitCanvas = function (IdDiv, Filename) {
this.IdDiv = IdDiv;
this.Filename = Filename
}
InitCanvas.prototype = {
constructor: InitCanvas,
init: function () {
this.container = document.getElementById(this.IdDiv);
// this should be changed.
debugger;
this.container.innerHeight = 600;
this.container.innerWidth = 800;
//These statenments should be changed to improve the image position
this.camera = new THREE.PerspectiveCamera(60, this.container.innerWidth / this.container.innerHeight, 0.01, 1e10);
this.camera.position.z = 300;
let scene = new THREE.Scene();
scene.add(this.camera);
// light
let dirLight = new THREE.DirectionalLight(0xffffff);
dirLight.position.set(200, 200, 1000).normalize();
this.camera.add(dirLight);
this.camera.add(dirLight.target);
// read file
let loader = new THREE.NRRDLoader();
loader.load(this.Filename, function (volume) {
//z plane
let sliceZ = volume.extractSlice('z', Math.floor(volume.RASDimensions[2] / 4));
debugger;
this.container.innerWidth = sliceZ.iLength;
this.container.innerHeight = sliceZ.jLength;
sliceZ.mesh.material.color.setRGB(0,1,1);
console.log('Our slice is: ', sliceZ);
scene.add(sliceZ.mesh);
}.bind(this));
this.scene = scene;
// renderer
this.renderer = new THREE.WebGLRenderer({alpha: true});
this.renderer.setPixelRatio(this.container.devicePixelRatio);
debugger;
this.renderer.setSize(this.container.innerWidth, this.container.innerHeight);
// add canvas in container
this.container.appendChild(this.renderer.domElement);
},
animate: function () {
this.renderer.render(this.scene, this.camera);
}
}
I wonder about the point size because if we see this example they are made with 0.05 of size:
https://github.com/mrdoob/three.js/blob/master/examples/webgl_interactive_raycasting_points.html
And in the example we see the camera being quite far away from the points being generated and they are visible:
https://threejs.org/examples/webgl_interactive_raycasting_points.html
What do you think?
You can use THREE.BufferGeometry() with .setDrawRange():
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(1, 5, 5);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
var mesh = new THREE.Mesh(new THREE.SphereBufferGeometry(3, 32, 24), new THREE.MeshBasicMaterial({
wireframe: true,
color: "red"
}));
scene.add(mesh);
var idx = 0;
var maxIdx = 10;
var points = [];
for (let i = 0; i < maxIdx; i++) {
points.push(new THREE.Vector3());
}
var geometry = new THREE.BufferGeometry().setFromPoints(points);
geometry.setDrawRange(0, idx);
var points = new THREE.Points(geometry, new THREE.PointsMaterial({
size: 0.125,
color: "yellow"
}));
scene.add(points);
window.addEventListener("mousemove", onMouseMove, false);
window.addEventListener("mousedown", onMouseDown, false);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var intersects = [];
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function onMouseDown(event) {
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObject(mesh);
if (intersects.length === 0) return;
if (idx == maxIdx) return;
let p = intersects[0].point;
geometry.attributes.position.setXYZ(idx, p.x, p.y, p.z);
geometry.attributes.position.needsUpdate = true;
idx++;
geometry.setDrawRange(0, idx);
}
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/91/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I have the following scene visable here: http://gravient.thomashoek.com/drag_example/
In the initial mode the text looks at the mouse. but when you make sertain mouse movements the text seems to flip the direct of looking at the mouse. As far as i figured out this happens when the mouse crosses the middle of the canvas.
A quick in depth explanation of the problem: So when the mouse is at the top of the screen the mesh looks at the bottom of the screen. And this is the same for all directions.
But when you move the mouse again in a certain undefined manner the problem fixes it self.
So my question is: How can i prevent this from happening?
My code to do the lookAt is as following:
var data = {
text : ["THE NEXT","POINT","OF VIEW"],
size : 5,
height : 2,
curveSegments : 12,
line_height : 1.7
};
function generateTextGeometry(mesh) {
var y_start = (0 - data.text.length * data.size / 2 - data.size / 2 - (data.text.length - 1) * data.line_height)/2;
var loader = new THREE.FontLoader();
loader.load( '../data/suisse_2.json', function ( font ) {
for (var i = data.text.length - 1; i >= 0; i--) {
var geometry = new THREE.TextGeometry( data.text[i], {
font: font,
size: data.size,
height: data.height,
curveSegments: data.curveSegments
});
geometry.center();
mesh.children[i].geometry = geometry;
mesh.children[i].position.y = y_start;
if (i < data.text.length) {
y_start += (data.size + data.line_height);
}else{
y_start += data.size;
}
}
});
}
//global vars for al the desired options all are false because we dont know if it will work.
var current_interaction_mode = null;
var webcam = false;
var gyroscope = false;
var followMouse = false;
var dragMouse = true;
var rotation_vals = {
x: -0.51,
y: 0.51,
range: 0.2
}
check_user_hardware();
//check if all the wanted options are available
function check_user_hardware(){
window.addEventListener("devicemotion", function(event){
if(event.rotationRate.alpha || event.rotationRate.beta || event.rotationRate.gamma){
if (!gyroscope) {
gyroscope = true;
current_interaction_mode = 'gyroscope_option';
set_user_ui_elements();
}
}else{
followMouse = true;
current_interaction_mode = 'followMouse_option';
set_user_ui_elements();
window.addEventListener('mousemove', get_user_mouse_pos);
}
calculate_rotation_mesh_pos(event.rotationRate.beta, event.rotationRate.gamma);
});
}
function get_user_mouse_pos(event){
rotation_vals.x = ( event.clientX / window.innerWidth ) * 2 - 1;
rotation_vals.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function calculate_rotation_mesh_pos(x_angle, y_angle){
// rotation_vals.x = some calculation that needs to be done here
// rotation_vals.y = some calculation that needs to be done here
}
function set_user_ui_elements(){
if (followMouse) {
$('#followMouse_option').show();
}
if (gyroscope) {
$('#gyroscope_option').show();
}
if (dragMouse) {
$('#dragMouse_option').show();
}
if (current_interaction_mode == 'followMouse_option') $('#followMouse_option').css('color', '#ff0000');
if (current_interaction_mode == 'gyroscope_option') $('#gyroscope_option').css('color', '#ff0000');
}
var scene = new THREE.Scene();
var camera = new THREE.OrthographicCamera( window.innerWidth / - 40, window.innerWidth / 40, window.innerHeight / 40, window.innerHeight / - 40, 0, 2000 );
camera.position.z = 1000;
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0x000000, 1 );
document.getElementById('interactive_container').appendChild( renderer.domElement );
//set up interaction parameters
var fakeMouseLoc = new THREE.Vector3();
var orbit = new THREE.OrbitControls( camera, renderer.domElement );
orbit.enableZoom = false;
orbit.enableKeys = false;
orbit.enablePan = false;
orbit.enabled = false;
//generate the text mesh
var mesh = new THREE.Object3D();
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.add( new THREE.Mesh(
new THREE.Geometry(),
new THREE.MeshBasicMaterial({
color: 0xFFFFFF
})
));
}
var options = generateTextGeometry(mesh);
scene.add( mesh );
var render = function(){
if (current_interaction_mode == 'followMouse_option' || current_interaction_mode == 'gyroscope_option') {
fakeMouseLoc.set(rotation_vals.x, rotation_vals.y, rotation_vals.range);
mesh.lookAt(fakeMouseLoc);
}
renderer.render( scene, camera );
requestAnimationFrame( render );
};
render();
window.addEventListener( 'resize', function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}, false );
function add_geometry_to_container(container){
container.add( new THREE.Mesh(
new THREE.Geometry(),
new THREE.MeshBasicMaterial({
color: 0xFFFFFF
})
));
}
function clear_geometry_of_container(container, empty){
if (!empty) {
container.children[0].geometry.dispose();
container.children[0].material.dispose();
container.remove(container.children[0]);
}else{
for (var i = container.children.length - 1; i >= 0; i--) {
container.children[i].geometry.dispose();
container.children[i].material.dispose();
container.remove(container.children[i]);
}
}
}
function invert_color(state){
if(state){
console.log("switch to white bg");
renderer.setClearColor( 0xFFFFFF, 1 );
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.children[i].material.color.setHex( 0x000000, 1 );
}
}else{
console.log("switch to black bg");
renderer.setClearColor( 0x000000, 1 );
for (var i = data.text.length - 1; i >= 0; i--) {
mesh.children[i].material.color.setHex( 0xFFFFFF, 1 );
}
}
}
<script src="../supporting_files/general/jquery-3.1.0.min.js"></script>
<script src="../supporting_files/3d_interactive/three.min.js"></script>
<script src="../supporting_files/3d_interactive/OrbitControls.js"></script>
<script src='../supporting_files/3d_interactive/geometry.js'></script>
<script src="../supporting_files/general/check_features.js"></script>
<script src="../supporting_files/main_page_js/desktop_main.js"></script>
<script src="../supporting_files/3d_interactive/main_3d_scene.js"></script>
If you need more info, context or something is unclear pls let me know so i can clarify :)
The threejs scene consists of sphere and plane geometry, The sphere is textured with image and plane geometry
is textured with 2d text, and plane geometry is attached with click event, When I click on plane geometry
with the mouse I need to remove the previous sphere and plane geometry and load new sphere with new textured
image and new plane geometry which is happening, but the previous sphere and plane geometry are still remaining in memory and i need
to remove those objects, i tried using "dispose" method but that didn't help me may be i am making some mistake
to implement the dispose method,because of this the scene gets hanged, can someone please help me how to solve
this problem. I have added part of my code which might give an idea regarding the problem.https://jsfiddle.net/v1ayw803/
var spheregeometry = new THREE.SphereGeometry(radius, 20, 20, 0, -6.283, 1, 1);
var texture = THREE.ImageUtils.loadTexture(response.ImagePath);
texture.minFilter = THREE.NearestFilter;
var spherematerial = new THREE.MeshBasicMaterial({map: texture});
var sphere = new THREE.Mesh(spheregeometry, spherematerial);
//texture.needsUpdate = true;
scene.add(sphere);
var objects = [];
var objects_sphere = [];
objects_sphere.push(sphere);
for(var i=0; i<spriteResponse.length; i++)
{
var cardinal = {ID: parseInt(spriteResponse[i].ID), lat: parseFloat(spriteResponse[i].lat), lon: parseFloat(spriteResponse[i].lng), name: spriteResponse[i].name};
//var sprite = new labelBox(cardinal, radius, root);
//sprite.update(); was previously commented
//spritearray.push(sprite);
var phi = Math.log( Math.tan( cardinal.lat*(Math.PI/180) / 2 + Math.PI / 4 ) / Math.tan( click_marker.getPosition().lat()* (Math.PI/180) / 2 + Math.PI / 4) );
var delta_lon = Math.abs( click_marker.getPosition().lng() - cardinal.lon )*Math.PI/180;
var bearing = Math.atan2( delta_lon , phi ) ;
var Z_value = Math.cos(bearing)*(radius*0.75);
var X_value = Math.sin(bearing)*(radius*0.75);
var canvas = document.createElement('canvas');
context = canvas.getContext('2d');
metrics = null,
textHeight = 32,
textWidth = 0,
// actualFontSize = 2;
context.font = "normal " + textHeight + "px Arial";
metrics = context.measureText(cardinal.name);
var textWidth = metrics.width;
//var textHeight = metrics.height;
canvas.width = textWidth;
canvas.height = textHeight;
context.font = "normal " + textHeight + "px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.beginPath();
context.rect(0, 0, textWidth, textHeight);
context.fillStyle = "white";
context.fill();
context.fillStyle = "black";
context.fillText(cardinal.name, textWidth / 2, textHeight / 2);
texture_plane = new THREE.Texture(canvas);
var GPU_Value = renderer.getMaxAnisotropy();
texture_plane.anisotropy = GPU_Value;
texture_plane.needsUpdate = true;
//var spriteAlignment = new THREE.Vector2(0,0) ;
material = new THREE.MeshBasicMaterial( {color: 0xffffff,side: THREE.DoubleSide ,map : texture_plane} );
material.needsUpdate = true;
//material.transparent=true;
geometry = new THREE.PlaneGeometry(0.3, 0.2);
plane = new THREE.Mesh( geometry, material );
plane.database_id = cardinal.ID;
plane.LabelText = cardinal.name;
//plane.scale.set( 0.3, 0.3,1 );
plane.scale.set( textWidth/165, textHeight/70, 1 );
plane.position.set(X_value,0,Z_value);
plane.coordinates = { X: X_value, Z: Z_value};
plane.lat_lon = { LAT: cardinal.lat, LON: cardinal.lon};
plane.textWidth = textWidth;
plane.textHeight = textHeight;
objects.push( plane );
scene.add(plane);
plane.userData = { keepMe: true };
//objects.push( plane );
//plane.id = cardinal.ID;
//var direction = camera.getWorldDirection();
camera.updateMatrixWorld();
var vector = camera.position.clone();
vector.applyMatrix3( camera.matrixWorld );
plane.lookAt(vector);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
}
function onDocumentMouseDown( event )
{
//clearScene();
event.preventDefault();
var mouse = new THREE.Vector2();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( objects );
var matched_marker = null;
if(intersects.length != 0)
{
for ( var i = 0; intersects.length > 0 && i < intersects.length; i++)
{
var x_id = intersects[0].object.database_id;
for( var j = 0; markers.length > 0 && j < markers.length; j++)
{
if(x_id == markers[j].ID)
{
matched_marker = markers[j];
break;
}
}
if(matched_marker != null)
{
break;
}
}
// loadScene();
clean_data();
google.maps.event.trigger( matched_marker, 'click' );
}
}
function clean_data()
{
for(var k=0;k<objects_sphere.length;k++)
{
scene.remove( objects_sphere[k] );
objects_sphere[k].geometry.dispose();
objects_sphere[k].material.map.dispose();
objects_sphere[k].material.dispose();
}
for (var j=0; j<objects.length; j++)
{
scene.remove( objects[j] );
objects[j].geometry.dispose();
objects[j].material.map.dispose();
objects[j].material.dispose();
// objects[j].material.needsUpdate = true;
}
/*spheregeometry.dispose();
spherematerial.dispose();
texture.dispose();
scene.remove( sphere );*/
} `
It looks like you're never rerendering the scene in either the example code or the jsFiddle. If you remove your object from the scene and the objects remain, it's likely that you've not rendered the scene again. Try adding a render loop.
animationLoop () {
myrenderer.render(myScene, myCamera)
window.requestAnimationFrame(animationLoop)
}
I am trying to make 3d Line chart ... Here
for points and lines are working good but i wanted to listen mouse hover on points ( spheres ) only not on lines or grid or other grid object ...
for that I have separate 3d-objects and wrapper them all in different 3d-objects..
i.e.
grid-object have all grid
points-object have all points
lines-object have all lines..
lineContainer = new THREE.Object3D();
d3.select( lineContainer )
.selectAll()
.data(currentVal)
.enter().append(
function (d, i) {
var LineMaterial = new THREE.LineBasicMaterial({color: d.color});
var PI2 = Math.PI * 2;
var geometry = new THREE.Geometry();
var k = -10;
d.points.forEach(function(p){
var _x = x1(p.id);
var _y = y1(p.y);
var _z = (i+1)*10;
geometry.vertices.push(new THREE.Vector3(_x,_y,_z));
});
// for (var j = 0; j < 30; j++) {
// var _point = new THREE.SphereGeometry(0.8);
// var _pointmaterial = new THREE.MeshBasicMaterial( {color: _color(i)} );
// var sphere = new THREE.Mesh( _point, _pointmaterial );
// sphere.position.set(_x,_y,_z);
// parentTransform.add( sphere );
// };
var line = new THREE.Line(geometry, LineMaterial);
line.userdata = d.id;
line.material.linewidth = 2;
return line;
}
);
mainContainer.add( lineContainer );
pointsContainer = new THREE.Object3D();
d3.select( pointsContainer )
.selectAll()
.data(_allPoints)
.enter().append(
function (d, i) {
var _x = x1(d.id);
var _y = y1(d.y);
var _z = d.z*10;
var _point = new THREE.SphereGeometry(1.8);
var _pointmaterial = new THREE.MeshBasicMaterial( {color: d.color} );
var sphere = new THREE.Mesh( _point, _pointmaterial );
// sphere.classes = "points";
sphere.position.set(_x,_y,_z);
sphere.userdata = d;
var _toolTipValue = ""+d.id+"("+d.line+")";
var spritey = makeTextSprite( _toolTipValue, { fontsize: 14} );
spritey.position.set(_x-5,_y+8,_z);
spritey.visible = false;
d.bindedTip = spritey;
tooltipContainer.add(spritey);
return sphere;
});
mainContainer.add(pointsContainer);
scene.add(mainContainer);
function render() {
if(!tooltipShow){
// find intersections
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
// vector.unproject( camera )
projector.unprojectVector( vector, camera );
raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
// raycaster.setFromCamera( mouse, camera );
//lineintersect
// var intersects = raycaster.intersectObjects( lineContainer.children, true);
var pointIntersects = raycaster.intersectObjects( pointsContainer.children, true);
// console.log(pointIntersects);
if ( pointIntersects.length > 0 ) {
console.log(pointIntersects)
if ( currentPointIntersected !== undefined ) {
}
if(currentPointIntersected != pointIntersects[ 0 ].object){
currentPointIntersected = pointIntersects[ 0 ].object;
console.log(currentIntersected);
_allPoints.forEach(function(e,i){
if(e.id && e.bindedTip){
if(e.id == currentPointIntersected.userdata.id){
e.bindedTip.visible = true;
e.bindedTip.getTexture().needsUpdate = true;
e.isHover = true;
}else{
e.bindedTip.visible = false;
e.bindedTip.getTexture().needsUpdate = true;
e.isHover = false;
}
}
});
}
} else {
if ( currentPointIntersected !== undefined ) {
currentPointIntersected.material.linewidth = 1;
}
currentPointIntersected = undefined;
}
}
renderer.render( scene, camera );
stats.update();
}
I have use same code and same version on THREEjs from here
but is only listen upper level points not the point that are behind in z-axis ....
how can I can manage that...
I added new scene and render two scenes in renderer ... And bind mouse on top scene ..and all other elements in another scene ..