How do you dynamically add points to a three js point system? - javascript

I am working on a small project where I am attempting to display NSF OpenTopography data in a point cloud visualization using three js. I have had success in plotting data that is pre-loaded, however I would like to read multiple data files via ajax and dynamically add points to the system as data is loaded. I am currently using BufferGeometry and BufferAttributes to generate the visualization.
Working example with pre-loaded data
http://jsfiddle.net/mcroteau/e6x73kad/
Current implementation with same data in multiple files
http://jsfiddle.net/mcroteau/22xvj97j/
var container, scene,
camera, renderer,
controls, stats,
geometry, material;
var SCREEN_WIDTH = window.innerWidth,
SCREEN_HEIGHT = window.innerHeight;
var VIEW_ANGLE = 45,
ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
NEAR = 0.1,
FAR = 20000;
var POINT_SIZE = 1,
PADDING = 20;
var BACKGROUND_COLOR = 0xefefef,
POINT_COLOR = 0x4466B0;
var COLOR_DIVISOR = 65025;
var maxX = maxY = maxZ = 0;
var minX = minY = minZ = 0;
var DATA_LENGTH = 896238;
var positions = new Float32Array( DATA_LENGTH * 3 );
var colors = new Float32Array( DATA_LENGTH * 3 );
var bufferPositions = new THREE.BufferAttribute( positions, 3 )
var bufferColors = new THREE.BufferAttribute( colors, 3 )
var NUMBER_FILES = 8;
var FILE_NAME = 'san-simeon';
var BASE_URL = 'http://104.237.155.7/jsfiddle/point-cloud1/data/output/';
function init() {
scene = new THREE.Scene();
container = document.createElement('div');
document.body.appendChild( container );
if ( Detector.webgl ){
renderer = new THREE.WebGLRenderer({ antialias: true });
}else{
renderer = new THREE.CanvasRenderer();
}
renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
renderer.setClearColor( BACKGROUND_COLOR, 1);
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.bottom = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
geometry = new THREE.BufferGeometry();
geometry.dynamic = true;
geometry.addAttribute( 'position', bufferPositions );
geometry.addAttribute( 'color', bufferColors );
loadDrawData();
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
camera.position.x = 10;
camera.position.z = 50;
camera.position.y = 20;
scene.add(camera);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls( camera, renderer.domElement );
material = new THREE.PointCloudMaterial({ size: POINT_SIZE, vertexColors: THREE.VertexColors });
particles = new THREE.PointCloud( geometry, material );
scene.add( particles );
window.addEventListener( 'resize', onWindowResize, false );
animate();
}
function loadDrawData(){
for(var m = 1; m < NUMBER_FILES; m++){
var file = FILE_NAME + '-' + m + '.csv'
var fileUrl = BASE_URL + FILE_NAME + '/' + file
console.log(fileUrl)
loadData(fileUrl).then(updateGeometryBuffers).then(recomputeGeometry).fail(failed)
}
}
function recomputeGeometry(){
console.info('recompute geometry')
geometry.attributes.position.needsUpdate = true
geometry.computeBoundingBox();
geometry.center();
}
function updateGeometryBuffers(csv){
var parsedData = csv.split(/\n/);
$(parsedData).each(function(index, row){
var data = row.split(',');
var x = data[1];
var y = data[2];
var z = data[0];
if(x && y && z){
positions[3 * index] = x
positions[3 * index + 1] = y;
positions[3 * index + 2] = z;
var red = data[3]
var green = data[4]
var blue = data[5]
red = red/COLOR_DIVISOR;
green = green/COLOR_DIVISOR;
blue = blue/COLOR_DIVISOR;
colors[3 * index] = red;
colors[3 * index + 1] = green;
colors[3 * index + 2] = blue;
}else{
console.warn('no data in row', index);
}
});
}
function loadData(url){
return $.ajax({
url : url,
dataType : 'text'
});
}
function failed(){
console.warn('failed to load data');
}
function onWindowResize() {
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
update();
}
function update(){
controls.update();
stats.update();
}
function render() {
renderer.render( scene, camera );
}
init();
In the current implementation, the geometry isn't re-centering which is one reason it isn't working. How do I force the geometry to re-center? What is the best way to dynamically add points to a three js point system?
Thanks in advance for any guidance provided.

Related

How can I improve the performance of a three.js script?

I have a really nice js animation that i would like to use as a website background. Unfortunately it seems to be very intensive in CPU/GPU usage. The animation itself runs quite smooth, but my GPU is at 100%. other animations on the website don't run smooth at all and seem to lag.
I already looked at other Stackoverflow posts concerning boosting the performance of three.js scripts, but the ideas didn't work for me yet. For example I reduced the calls from 600 down to 200 by reducing the "city" objects in order to improve performance, but GPU is still at 100%.
I updated three.js to the latest version and so on. Nothing worked so far. I am quite new to three.js and JS so please don't be too harsh with me. Also I didn't really know which parts of the code will really boost performance, so I included the whole thing - even though it is very long. I hope the comments help to skip to the right parts.
Thanks in advance for your help!
// Three JS Template
//----------------------------------------------------------------- BASIC parameters
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize( window.innerWidth, window.innerHeight );
if (window.innerWidth > 800) {
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.needsUpdate = true;
};
document.getElementById('animated-bg').appendChild(renderer.domElement);
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
};
var camera = new THREE.PerspectiveCamera( 20, window.innerWidth / window.innerHeight, 1, 500 );
camera.position.set(0, 2, 14);
var scene = new THREE.Scene();
var city = new THREE.Object3D();
var smoke = new THREE.Object3D();
var town = new THREE.Object3D();
var createCarPos = true;
var uSpeed = 0.001;
//----------------------------------------------------------------- FOG background
var setcolor = 0x862834;
scene.background = new THREE.Color(setcolor);
scene.fog = new THREE.Fog(setcolor, 10, 16);
//----------------------------------------------------------------- RANDOM Function
function mathRandom(num = 8) {
var numValue = - Math.random() * num + Math.random() * num;
return numValue;
};
//----------------------------------------------------------------- CHANGE bluilding colors
var setTintNum = true;
function setTintColor() {
if (setTintNum) {
setTintNum = false;
var setColor = 0x000000;
} else {
setTintNum = true;
var setColor = 0x000000;
};
//setColor = 0x222222;
return setColor;
};
//----------------------------------------------------------------- CREATE City
function init() {
var segments = 2;
for (var i = 1; i<50; i++) {
var geometry = new THREE.CubeGeometry(1,0,0,segments,segments,segments);
var material = new THREE.MeshStandardMaterial({
color:setTintColor(),
wireframe:false,
shading: THREE.SmoothShading,
side:THREE.DoubleSide});
var wmaterial = new THREE.MeshLambertMaterial({
color:0xFFFFFF,
wireframe:true,
transparent:true,
opacity: 0.03,
side:THREE.DoubleSide});
var cube = new THREE.Mesh(geometry, material);
var wire = new THREE.Mesh(geometry, wmaterial);
var floor = new THREE.Mesh(geometry, material);
var wfloor = new THREE.Mesh(geometry, wmaterial);
cube.add(wfloor);
cube.castShadow = true;
cube.receiveShadow = true;
cube.rotationValue = 0.1+Math.abs(mathRandom(8));
floor.scale.y = 0.05;//+mathRandom(0.5);
cube.scale.y = 0.1+Math.abs(mathRandom(8));
var cubeWidth = 0.9;
cube.scale.x = cube.scale.z = cubeWidth+mathRandom(1-cubeWidth);
cube.position.x = Math.round(mathRandom());
cube.position.z = Math.round(mathRandom());
floor.position.set(cube.position.x, 0/*floor.scale.y / 2*/, cube.position.z)
town.add(floor);
town.add(cube);
};
//----------------------------------------------------------------- Particular
var gmaterial = new THREE.MeshToonMaterial({color:0xFFFF00, side:THREE.DoubleSide});
var gparticular = new THREE.CircleGeometry(0.01, 3);
var aparticular = 5;
for (var h = 1; h<300; h++) {
var particular = new THREE.Mesh(gparticular, gmaterial);
particular.position.set(mathRandom(aparticular), mathRandom(aparticular),mathRandom(aparticular));
particular.rotation.set(mathRandom(),mathRandom(),mathRandom());
smoke.add(particular);
};
var pmaterial = new THREE.MeshPhongMaterial({
color:0x000000,
side:THREE.DoubleSide,
roughness: 10,
metalness: 0.6,
opacity:0.9,
transparent:true});
var pgeometry = new THREE.PlaneGeometry(60,60);
var pelement = new THREE.Mesh(pgeometry, pmaterial);
pelement.rotation.x = -90 * Math.PI / 180;
pelement.position.y = -0.001;
pelement.receiveShadow = true;
city.add(pelement);
};
//----------------------------------------------------------------- MOUSE function
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2(), INTERSECTED;
var intersected;
function onMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
};
function onDocumentTouchStart( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouse.x = event.touches[ 0 ].pageX - window.innerWidth / 2;
mouse.y = event.touches[ 0 ].pageY - window.innerHeight / 2;
};
};
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouse.x = event.touches[ 0 ].pageX - window.innerWidth / 2;
mouse.y = event.touches[ 0 ].pageY - window.innerHeight / 2;
}
}
window.addEventListener('mousemove', onMouseMove, false);
window.addEventListener('touchstart', onDocumentTouchStart, false );
window.addEventListener('touchmove', onDocumentTouchMove, false );
//----------------------------------------------------------------- Lights
var ambientLight = new THREE.AmbientLight(0xFFFFFF, 4);
var lightFront = new THREE.SpotLight(0xFFFFFF, 20, 10);
var lightBack = new THREE.PointLight(0xFFFFFF, 0.5);
var spotLightHelper = new THREE.SpotLightHelper( lightFront );
lightFront.rotation.x = 45 * Math.PI / 180;
lightFront.rotation.z = -45 * Math.PI / 180;
lightFront.position.set(5, 5, 5);
lightFront.castShadow = true;
lightFront.shadow.mapSize.width = 6000;
lightFront.shadow.mapSize.height = lightFront.shadow.mapSize.width;
lightFront.penumbra = 0.1;
lightBack.position.set(0,6,0);
smoke.position.y = 2;
scene.add(ambientLight);
city.add(lightFront);
scene.add(lightBack);
scene.add(city);
city.add(smoke);
city.add(town);
//----------------------------------------------------------------- GRID Helper
var gridHelper = new THREE.GridHelper( 60, 120, 0xFF0000, 0x000000);
city.add( gridHelper );
//----------------------------------------------------------------- CAR world
var generateCar = function() {
}
//----------------------------------------------------------------- LINES world
var createCars = function(cScale = 2, cPos = 20, cColor = 0xFFFF00) {
var cMat = new THREE.MeshToonMaterial({color:cColor, side:THREE.DoubleSide});
var cGeo = new THREE.CubeGeometry(1, cScale/40, cScale/40);
var cElem = new THREE.Mesh(cGeo, cMat);
var cAmp = 3;
if (createCarPos) {
createCarPos = false;
cElem.position.x = -cPos;
cElem.position.z = (mathRandom(cAmp));
TweenMax.to(cElem.position, 3, {x:cPos, repeat:-1, yoyo:true, delay:mathRandom(3)});
} else {
createCarPos = true;
cElem.position.x = (mathRandom(cAmp));
cElem.position.z = -cPos;
cElem.rotation.y = 90 * Math.PI / 180;
TweenMax.to(cElem.position, 5, {z:cPos, repeat:-1, yoyo:true, delay:mathRandom(3), ease:Power1.easeInOut});
};
cElem.receiveShadow = true;
cElem.castShadow = true;
cElem.position.y = Math.abs(mathRandom(5));
city.add(cElem);
};
var generateLines = function() {
for (var i = 0; i<60; i++) {
createCars(0.1, 20);
};
};
//----------------------------------------------------------------- CAMERA position
var cameraSet = function() {
createCars(0.1, 20, 0xFFFFFF);
};
//----------------------------------------------------------------- ANIMATE
var animate = function() {
var time = Date.now() * 0.00005;
requestAnimationFrame(animate);
city.rotation.y -= ((mouse.x * 8) - camera.rotation.y) * uSpeed;
city.rotation.x -= (-(mouse.y * 2) - camera.rotation.x) * uSpeed;
if (city.rotation.x < -0.05) city.rotation.x = -0.05;
else if (city.rotation.x>1) city.rotation.x = 1;
var cityRotation = Math.sin(Date.now() / 5000) * 13;
for ( let i = 0, l = town.children.length; i < l; i ++ ) {
var object = town.children[ i ];
}
smoke.rotation.y += 0.01;
smoke.rotation.x += 0.01;
camera.lookAt(city.position);
renderer.render( scene, camera );
}
//----------------------------------------------------------------- START functions
generateLines();
init();
animate();
There are many things you could do.
For starters, you want to keep your Mesh number low to reduce drawcalls. This means that you shouldn't create one mesh for cube and one for floor. If they share the same material, just create 2 separate geometries, then merge them with BufferGeometryUtils.mergeBufferGeometries.
If you have 50 buildings with the same material, you should also merge them so they all draw at once.
MeshStandardMaterial is pretty expensive to render, so since you're not using environment reflections, you should consider Phong or Lambert materials instead, which are much less resource-intensive.
Shadows basically double your drawcalls per frame because it has to first calculate all shadow-casting geometries. If your buildings aren't going to move, set lightFront.shadow.autoUpdate = false after the first frame.
Don't create a new circular Mesh for each particle. That's 300 meshes! Instead, use THREE.Points, which has the capacity of drawing thousands of particles on a single drawcall, saving you tons of render time, as in this example.
Don't set your renderer's pixelRatio to anything above 1, if you do. That'd just kill your performance.
I don't have time to get into the car creation, but the same principle applies: try to reduce your drawcalls!

Galaxies simulation: Change color of a point and display text on mouseover

I am trying to create a simulation of positions of 4673 of the nearest galaxies.
The galaxies are points.
I want to color a point on mouseover and load the name of the galaxy.
I have spent many days trying to achieve it. I am able to change color, as well as do basic raycasting, however, I am unable to separately raycast/color individual point. All the points are raycasted and colored as a group as seen in the current version.
What should I do to correct this? Thank you so much for your time and patience with a beginner.
Complete code is available here.
Relevant code is included below:
window.addEventListener( "mousemove", onDocumentMouseMove, false );
var selectedObject = null;
function onDocumentMouseMove( event ) {
event.preventDefault();
if ( selectedObject ) {
selectedObject.material.color.set( '#fff' );
selectedObject = null;
}
var intersects = getIntersects( event.layerX, event.layerY );
if ( intersects.length > 0 ) {
var res = intersects.filter( function ( res ) {
return res && res.object;
} )[ 0 ];
if ( res && res.object ) {
selectedObject = res.object;
selectedObject.material.color.set( '#69f' );
}
}
}
var raycaster = new THREE.Raycaster();
var mouseVector = new THREE.Vector3();
function getIntersects( x, y ) {
x = ( x / window.innerWidth ) * 2 - 1;
y = - ( y / window.innerHeight ) * 2 + 1;
mouseVector.set( x, y, 0.5 );
raycaster.setFromCamera( mouseVector, camera );
return raycaster.intersectObject( dots, true );
}
First thing to do is to set raycaster.params.Points.threshold equal to the size of your points. This makes it so that the colors of all points change when a user hovers over any point:
(I increased your point size for ease of hovering):
<html>
<head>
<meta charset="UTF-8">
<style>
body { margin: 0; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/f32e6f14046b5affabe35a0f42f0cad7b5f2470e/examples/js/controls/TrackballControls.js"></script>
</head>
<body>
<script>
// Create an empty scene
var scene = new THREE.Scene();
// Create a basic perspective camera
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.x = 200;
// Create a renderer with Antialiasing
var renderer = new THREE.WebGLRenderer({antialias:true});
// Configure renderer clear color
renderer.setClearColor("#000000");
// Configure renderer size
renderer.setSize( window.innerWidth, window.innerHeight );
// Append Renderer to DOM
document.body.appendChild( renderer.domElement );
//Add Milky Way
var dotGeometry = new THREE.Geometry();
dotGeometry.vertices.push(new THREE.Vector3( 0, 0, 0));
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "https://rawcdn.githack.com/RiteshSingh/galaxies/9e6a4e54b37647e5a9a1d6f16c017769533fe258/galaxydata.txt", false);
rawFile.onreadystatechange = function ()
{
if(rawFile.readyState === 4)
{
if(rawFile.status === 200 || rawFile.status == 0)
{
var allText = rawFile.responseText;
var data = allText.split("\n");
for (var i = 0; i < 4672; i++) {
var parts = data[i].split("\t");
var D = parts[0];
var glon = parts[1]*3.1416/180;
var glat = parts[2]*3.1416/180;
var z = D*Math.sin(glat);
var xy = D*Math.cos(glat);
var x = xy*Math.cos(glon);
var y = xy*Math.sin(glon);
dotGeometry.vertices.push(new THREE.Vector3( x, y, z));
}
}
}
}
rawFile.send(null);
var size = 0.32;
var dotMaterial = new THREE.PointsMaterial( { size: size } );
var dots = new THREE.Points( dotGeometry, dotMaterial );
scene.add( dots );
var controls = new THREE.TrackballControls( camera, renderer.domElement );
// Render Loop
var render = function () {
requestAnimationFrame( render );
controls.update();
// Render the scene
renderer.render(scene, camera);
};
render();
window.addEventListener( "mousemove", onDocumentMouseMove, false );
var selectedObject = null;
function onDocumentMouseMove( event ) {
event.preventDefault();
if ( selectedObject ) {
selectedObject.material.color.set( '#fff' );
selectedObject = null;
}
var intersects = getIntersects( event.layerX, event.layerY );
if ( intersects.length > 0 ) {
var res = intersects.filter( function ( res ) {
return res && res.object;
} )[ 0 ];
if ( res && res.object ) {
selectedObject = res.object;
selectedObject.material.color.set( '#69f' );
}
}
}
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = size;
var mouseVector = new THREE.Vector3();
function getIntersects( x, y ) {
x = ( x / window.innerWidth ) * 2 - 1;
y = - ( y / window.innerHeight ) * 2 + 1;
mouseVector.set( x, y, 0.5 );
raycaster.setFromCamera( mouseVector, camera );
return raycaster.intersectObject( dots, true );
}
</script>
</body>
</html>
Then you just need to make it so that only the hovered point changes color:
<html>
<head>
<meta charset="UTF-8">
<style>
body { margin: 0; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script src="https://rawcdn.githack.com/mrdoob/three.js/f32e6f14046b5affabe35a0f42f0cad7b5f2470e/examples/js/controls/TrackballControls.js"></script></script>
</head>
<body>
<script>
// Create an empty scene
var scene = new THREE.Scene();
// Create a basic perspective camera
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.x = 200;
// Create a renderer with Antialiasing
var renderer = new THREE.WebGLRenderer({antialias:true});
// Configure renderer clear color
renderer.setClearColor("#000000");
// Configure renderer size
renderer.setSize( window.innerWidth, window.innerHeight );
// Append Renderer to DOM
document.body.appendChild( renderer.domElement );
//Add Milky Way
var dotGeometry = new THREE.Geometry();
dotGeometry.vertices.push();
var colors = [];
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "https://rawcdn.githack.com/RiteshSingh/galaxies/9e6a4e54b37647e5a9a1d6f16c017769533fe258/galaxydata.txt", false);
rawFile.onreadystatechange = function ()
{
if(rawFile.readyState === 4)
{
if(rawFile.status === 200 || rawFile.status == 0)
{
var allText = rawFile.responseText;
var data = allText.split("\n");
for (var i = 0; i < 4672; i++) {
var parts = data[i].split("\t");
var D = parts[0];
var glon = parts[1]*3.1416/180;
var glat = parts[2]*3.1416/180;
var z = D*Math.sin(glat);
var xy = D*Math.cos(glat);
var x = xy*Math.cos(glon);
var y = xy*Math.sin(glon);
dotGeometry.vertices.push(new THREE.Vector3( x, y, z));
colors.push(new THREE.Color(0xFF0000));
}
}
}
}
rawFile.send(null);
dotGeometry.colors = colors;
var size = 0.32;
var dotMaterial = new THREE.PointsMaterial({
size: size,
vertexColors: THREE.VertexColors,
});
var dots = new THREE.Points( dotGeometry, dotMaterial );
scene.add( dots );
var controls = new THREE.TrackballControls( camera, renderer.domElement );
// Render Loop
var render = function () {
requestAnimationFrame( render );
controls.update();
// Render the scene
renderer.render(scene, camera);
};
render();
window.addEventListener( "mousemove", onDocumentMouseMove, false );
var selectedObject = null;
function onDocumentMouseMove( event ) {
event.preventDefault();
if ( selectedObject ) {
selectedObject.material.color.set( '#fff' );
selectedObject = null;
}
var intersects = getIntersects( event.layerX, event.layerY );
if ( intersects.length > 0 ) {
var idx = intersects[0].index;
dots.geometry.colors[idx] = new THREE.Color(0xFFFFFF);
dots.geometry.colorsNeedUpdate = true;
console.log(idx)
}
}
var raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = size;
var mouseVector = new THREE.Vector3();
function getIntersects( x, y ) {
x = ( x / window.innerWidth ) * 2 - 1;
y = - ( y / window.innerHeight ) * 2 + 1;
mouseVector.set( x, y, 0.5 );
raycaster.setFromCamera( mouseVector, camera );
return raycaster.intersectObject( dots, true );
}
</script>
</body>
</html>
You'll see the points turn white after the user mouses over them.
I'll leave it as a pedagogical exercise for you to determine how to turn the points back to red after the mouse exits a given point :)

Runaway raycasting when it should only respond on click

I've created a script to add a new plane to a scene every time I click on an existing plane - the detection uses a raycaster. However, instead of waiting for a click, the script uncontrollably adds more and more planes to the scene with no clicks at all. What have I missed?
Thanks!
var container, renderer, scene, camera;
var container = document.body;
var frustumSize = 1000;
var width, height;
var numRows = 4;
var numCols = 7;
var spacingSize = 300;
var raycaster;
var mouse;
var savedColor;
function init() {
width = window.innerWidth;
height = window.innerHeight;
container = document.createElement( 'div' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
scene = new THREE.Scene();
var aspect = window.innerWidth / window.innerHeight;
camera = new THREE.OrthographicCamera( frustumSize * aspect / - 2, frustumSize * aspect / 2, frustumSize / 2, frustumSize / - 2, 0, 2000 );
// camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 0, 2000 );
camera.updateProjectionMatrix();
// set up grid of colored planes
var startXPos = -((spacingSize*(numCols-1))/2);
var startYPos = -((spacingSize*(numRows-1))/2);
for ( var i = 0; i < numCols; i++ ) {
var x = startXPos + (i*spacingSize);
for ( var j = 0; j < numRows; j++ ) {
var y = startYPos + (j*spacingSize);
var z = -10 + (j * -1.0001);
var geometry = new THREE.PlaneGeometry( 50, 50 );
var material = new THREE.MeshBasicMaterial( {color: new THREE.Color( Math.random(), Math.random(), Math.random() ), side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.position.set( x, y, z );
scene.add(plane);
}
}
savedColor = null;
raycaster = new THREE.Raycaster();
mouse = new THREE.Vector2();
document.addEventListener( 'click', onDocumentClick, false );
var axesHelper = new THREE.AxesHelper( 100 );
scene.add( axesHelper );
container.appendChild( renderer.domElement );
scene.updateMatrixWorld();
render();
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
// calculate objects intersecting the picking ray
if ( intersects.length > 0 ) {
// console.log("yo!");
for ( var i = 0; i < intersects.length; i++ ) {
var geometry = new THREE.PlaneGeometry( 60, 60 );
var material = new THREE.MeshBasicMaterial( {color: new THREE.Color( 0xffff00 ), side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
plane.position.set( getRandomBetween(-300,300), getRandomBetween(-300,300), -20 );
scene.add(plane);
// console.log("hey!");
scene.updateMatrixWorld();
}
}
renderer.render( scene, camera );
requestAnimationFrame( render );
}
function getRandomBetween( min, max ) {
return Math.random() * (max - min) + min;
}
function onDocumentClick( event ) {
event.preventDefault();
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
// console.log("Oi!");
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.js"></script>
It's because the initial position of mouse is (0, 0), which intersects with the AxesHelper.
change the inital position of mouse or remove AxesHelper or change the intersecting target to a specific group should resolve this.

Three JS Cannot read property 'add' of undefined

Working on a project where on a button click script fetches a .svg and a .obj. Currently the button finds the .svg and the .obj but then spits out this error.
Uncaught TypeError: Cannot read property 'add' of undefined
Here is the code for the button, all other ThreeJS variables and calls are in the HTML and finish loading before the button is clicked.
function item(section, callback){
var tmp = itemName.split('-');
var cut = tmp[tmp.length-2];
var styleNo = tmp[tmp.length-1];
var path;
var model;
var loader = new THREE.OBJLoader( manager );
if (_product.svgPathAbsolute) {
path = itemName + '.svg';
model = styleCode + '.obj';
} else {
path = _product[section].svgPath + _product.name.toLowerCase() + '/' + cut.toLowerCase() + '/' + itemName + '.svg';
model = _product[section].svgPath + _product.name.toLowerCase() + '/' + cut.toLowerCase() + '/' + styleCode + '.obj';
}
loader.load( model, function ( object ) {
object.traverse(function(node){
if(node.material){
node.material.side = THREE.DoubleSide;
node.material = material;
}
});
object.position.y = -100;
geometry = object;
scene.add(geometry);
});
$.get(path, function (svg) {
_html[section] = svgToString(svg);
callback(section);
});
}
Included in HTML
<script>
var camera, scene, renderer, controls;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var geometry;
var material;
var svg = document.getElementById("svg-holder").querySelector("svg");
var img = document.createElement("img");
var canvas = document.createElement("canvas");
var svgSize = svg.getBoundingClientRect();
canvas.width = svgSize.width;
canvas.height = svgSize.height;
var ctx = canvas.getContext("2d");
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {
console.log( item, loaded, total );
};
var texture;
var loader = new THREE.OBJLoader( manager );
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 150;
// scene
scene = new THREE.Scene();
var ambientLight = new THREE.AmbientLight( 0xFFFFFF, 0.9 );
scene.add( ambientLight );
var directLight = new THREE.DirectionalLight( 0xFFFFFF, 0.2 );
camera.add( directLight );
scene.add( camera );
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
controls.minPolarAngle = 1.5;
controls.maxPolarAngle = 1.5;
var svgData = (new XMLSerializer()).serializeToString(svg);
img.setAttribute("src", "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(svgData))) );
img.onload = function() {
ctx.drawImage(img, 0, 0);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
material = new THREE.MeshPhongMaterial({
map: texture
});
material.map.minFilter = THREE.LinearFilter;
};
renderer = new THREE.WebGLRenderer({canvas:garmentOBJ,antialias:true});
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
make the scene object global or bind it to window object like this: window.scene = scene.

Three.js Dom event ( linkify.js)

I can't make it work. on the console, it says that the click event work,
but i cant link the sphere with an url.
Iam using last version of three.js
here is my code : `
// once everything is loaded, we run our Three.js stuff.
$(function () {
var geometry, material, mesha;
var clock = new THREE.Clock();
var raycaster;
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
// position and point the camera to the center of the scene
camera.position.x = 100;
camera.position.y = 10;
camera.position.z = 1000;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var camControls = new THREE.FirstPersonControls(camera);
camControls.lookSpeed = 0.4;
camControls.movementSpeed = 30;
camControls.noFly = true;
camControls.lookVertical = true;
camControls.constrainVertical = false;
camControls.verticalMin = 0.0;
camControls.verticalMax = 1.0;
camControls.lon = -150;
camControls.lat = 120;
var ambientLight = new THREE.AmbientLight(0x383838);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(100, 140, 130);
spotLight.intensity = 10;
scene.add(spotLight);
// add the output of the renderer to the html element
$("#WebGL-output").append(webGLRenderer.domElement);
// call the render function
var step = 0;
// setup the control gui
var controls = new function () {
// we need the first child, since it's a multimaterial
}
var domEvents = new THREEx.DomEvents(camera, webGLRenderer.domElement)
//////////////////////////////////////////////////////////////////////////////////
// add an object and make it move //
//////////////////////////////////////////////////////////////////////////////////
var geometry = new THREE.SphereGeometry( 100.5 )
var material = new THREE.MeshNormalMaterial()
var mesh = new THREE.Mesh( geometry, material )
scene.add( mesh )
//////////////////////////////////////////////////////////////////////////////////
// linkify our cube //
//////////////////////////////////////////////////////////////////////////////////
var url = 'http://jeromeetienne.github.io/threex/'
THREEx.Linkify(domEvents, mesh, url)
domEvents.addEventListener(mesh, 'click', function(event){
console.log('you clicked on mesh', mesh)
}, false)
var gui = new dat.GUI();
var mesh;
var onProgress = function ( xhr ) {
if ( xhr.lengthComputable ) {
var percentComplete = xhr.loaded / xhr.total * 100;
console.log( Math.round(percentComplete, 2) + '% downloaded' );
}
};
var onError = function ( xhr ) { };
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl( '../assets/models/door/' );
mtlLoader.setPath( '../assets/models/door/' );
mtlLoader.load( 'door.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( '../assets/models/door/' );
objLoader.load( 'door.obj', function ( object ) {
object.position.y = -1;
object.scale.x = 2;
object.scale.y = 2;
object.scale.z = 2;
scene.add( object );
}, onProgress, onError );
});
// floor
geometry = new THREE.PlaneGeometry( 2000, 2000, 100, 100 );
geometry.rotateX( - Math.PI / 2 );
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
var vertex = geometry.vertices[ i ];
vertex.x += Math.random() * 20 - 10;
vertex.y += Math.random() * 3;
vertex.z += Math.random() * 20 - 10;
}
for ( var i = 0, l = geometry.faces.length; i < l; i ++ ) {
var face = geometry.faces[ i ];
face.vertexColors[ 0 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
face.vertexColors[ 1 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 10.5, 0.75, Math.random() * 0.25 + 0.75 );
face.vertexColors[ 2 ] = new THREE.Color().setHSL( Math.random() * 0.3 + 0.5, 0.75, Math.random() * 0.25 + 0.75 );
}
material = new THREE.MeshBasicMaterial( { vertexColors: THREE.VertexColors } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
function setCamControls() {
}
render();
function setRandomColors(object, scale) {
var children = object.children;
if (children && children.length > 0) {
children.forEach(function (e) {
setRandomColors(e, scale)
});
} else {
// no children assume contains a mesh
if (object instanceof THREE.Mesh) {
object.material.color = new THREE.Color(scale(Math.random()).hex());
if (object.material.name.indexOf("building") == 0) {
object.material.emissive = new THREE.Color(0x444444);
object.material.transparent = true;
object.material.opacity = 0.8;
}
}
}
}
function render() {
stats.update();
var delta = clock.getDelta();
if (mesh) {
// mesh.rotation.y+=0.006;
}
camControls.update(delta);
webGLRenderer.clear();
// render using requestAnimationFrame
requestAnimationFrame(render);
webGLRenderer.render(scene, camera)
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
$("#Stats-output").append(stats.domElement);
return stats;
}
});
can somebody help ? have you an idea?
Iam quite a newbie...
Solution:
Get the latest version of DomEvents (https://github.com/jeromeetienne/threex.domevents) which has been updated to r74 right now.
Explaination:
This one took me some time to figure out, lets see whats going on:
THREEx.DomEvents is internally using the THREE.Raycaster to detect when the mouse is pointing on meshes. ThreeJS changed lately the behavior of the raycaster to not intersect with invisible meshes anymore (source). Yeah well I dont care, my click event is fireing you say?
Lets have a look at Linkyfy:
THREEx.Linkify = function(domEvents, mesh, url, withBoundingBox){
withBoundingBox = withBoundingBox !== undefined ? withBoundingBox : true
// create the boundingBox if needed
if( withBoundingBox ){
var boundingBox = new THREE.Mesh(new THREE.CubeGeometry(1,1,1), new THREE.MeshBasicMaterial({
wireframe : true
}))
boundingBox.visible = false
boundingBox.scale.copy(size)
mesh.add(boundingBox)
}
// ...
The fourth parameter withBoundingBox is evaluating to true if you dont supply it. Linkify is then creating a "bounding box mesh" which is invisible and wrapping around your mesh you want to linkify. The raycaster does not trigger an intersection anymore and there you have it. To allow the raycaster to detect an intersection although the boundingBox-object is invisible, set only the meshs materials visibility to false and not the mesh:
boundingBox.material.visible = false

Categories

Resources