Optimization THREE.js with about 7500 lines - javascript

I've a optimization problem with my code.
I've this:
/////////// PARTICLE (var system[] about 5500 Particles with x, y, x inside) //////
var particle_system_geometry = new THREE.Geometry();
for (var i=0; i < system.length; i++) {
var x = system[i][0];
var y = system[i][1];
var z = system[i][2];
var sec = system[i][3] * 1;
if (sec.toFixed(2) <= 0) {
sec = 0;
}
particle_system_geometry.vertices.push(new THREE.Vector3(x, y, z));
colors[ i ] = new THREE.Color( 0xffffff );
colors[ i ].setHSL( ( sec * 0.5 ), 1, 0.5 );
}
particle_system_geometry.colors = colors;
var particle_system_material = new THREE.ParticleSystemMaterial( { size: 5, map: sprite, vertexColors: true, transparent: true } );
particle_system_material.color.setHSL( 1.0, 0.2, 0.7 );
var particleSystem = new THREE.ParticleSystem(
particle_system_geometry,
particle_system_material
);
particleSystem.sortParticles = true;
scene.add(particleSystem);
This work fine # 30 fps. it's not the probleme.
The problem is when I add this code to link particle between:
/////////// LINE (var jump[] About 7500 Line with x1, y1, z1 and x2, y2, z2 inside) //////
var testline;
lines = new THREE.Object3D();
for (var i=0; i < jump.length; i++) {
testline = new THREE.Geometry();
var x1 = jump[i][0];
var y1 = jump[i][1];
var z1 = jump[i][2];
var x2 = jump[i][3];
var y2 = jump[i][4];
var z2 = jump[i][5];
testline.vertices = [new THREE.Vector3(x1, y1, z1),new THREE.Vector3(x2, y2, z2)];
colorsjump[ i ] = new THREE.Color( 0xffffff );
colorsjump[ i ].setHSL( ( jump[i][6] * 1 ), 1, 0.5 );
//testline.colors = colorsjump[i];
var line = new THREE.Line(testline, new THREE.LineBasicMaterial({
transparent: true, opacity: 0.3, color: colorsjump[ i ] }));
lines.add(line);
}
scene.add(lines);
this code down my fps to 7-8...
How can optimize the "line" code for gain performance and FPS?
thank you !

This work fine
thank to WestLangley
/////////// LINE (About 7500 Line with x1, y1, z1 and x2, y2, z2) //////
var testline;
testline = new THREE.Geometry();
for (var i=0; i < jump.length; i++) {
// Define the line start and end //
var x1 = jump[i][0];
var y1 = jump[i][1];
var z1 = jump[i][2];
var x2 = jump[i][3];
var y2 = jump[i][4];
var z2 = jump[i][5];
// Push Coord to vertices //
testline.vertices.push(new THREE.Vector3(x1, y1, z1),new THREE.Vector3(x2, y2, z2));
// Colors "Hacks" //
colorsjump[ i ] = new THREE.Color( 0xffffff );
colorsjump[ i ].setHSL( ( jump[i][6] * 1 ), 1, 0.5 );
colorsjump2[ i ] = new THREE.Color( 0xffffff );
colorsjump2[ i ].setHSL( ( jump[i][6] * 1 ), 1, 0.5 );
// Final Colors push in array //
lastColor.push(colorsjump[ i ], colorsjump2[ i ]);
}
// Set Geometry colors //
testline.colors = lastColor;
// Create Material for testline //
var lineMaterial = new THREE.LineBasicMaterial( {
color: 0xffffff,
vertexColors: THREE.VertexColors,
opacity: 0.2,
transparent: true
} );
// Create Line //
var line = new THREE.Line(testline, lineMaterial, THREE.LinePieces );
// Add Line to scene //
scene.add(line);

Related

How do I plot random meshes on top of a terrain using a heightmap in three.js?

So as the title states I'd like to know how to plot randomly generated meshes at the y-position that matches the terrain's corresponding y-position in three.js. I've looked through the docs and feel like using a raycaster might work, but I can only see examples that uses the detection as a mouse event, and not before render, so I'm not sure how to implement it.
Here is my code for the terrain, heightmap, and mesh plotting so far. It all works technically, but as you can see the plotAssets meshes y-positions are just sitting at zero right now. Any insights would be very much appreciated, I'm pretty new to three.js.
Terrain:
var heightmaploader = new THREE.ImageLoader();
heightmaploader.load(
"assets/noisemaps/cloud.png",
function(img) {
data = getHeightData(img);
var terrainG = new THREE.PlaneBufferGeometry(700, 700, worldWidth - 1, worldDepth - 1);
terrainG.rotateX(-Math.PI / 2);
var vertices = terrainG.attributes.position.array;
for (var i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
vertices[j + 1] = data[i] * 5;
}
terrainG.computeFaceNormals();
terrainG.computeVertexNormals();
var material = new THREE.MeshLambertMaterial({
map: terrainT,
//side: THREE.DoubleSide,
color: 0xffffff,
transparent: false,
});
terrain = new THREE.Mesh(terrainG, material);
terrain.receiveShadow = true;
terrain.castShadow = true;
terrain.position.y = 0;
scene.add(terrain);
plotAsset('boulder-photo-01.png', 30, 18, data);
plotAsset('boulder-outline-01.png', 20, 20, data);
plotAsset('birch-outline-01.png', 10, 50, data);
plotAsset('tree-photo-01.png', 20, 50, data);
plotAsset('grass-outline-01.png', 10, 20, data);
plotAsset('grass-outline-02.png', 10, 20, data);
}
);
Plot Assets:
function plotAsset(texturefile, amount, size, array) {
console.log(array);
var loader = new THREE.TextureLoader();
loader.load(
"assets/textures/objects/" + texturefile,
function(texturefile) {
var geometry = new THREE.PlaneGeometry(size, size, 10, 1);
var material = new THREE.MeshBasicMaterial({
color: 0xFFFFFF,
map: texturefile,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false,
depthTest: false,
alphaTest: 0.5,
});
var uniforms = { texture: { value: texturefile } };
var vertexShader = document.getElementById( 'vertexShaderDepth' ).textContent;
var fragmentShader = document.getElementById( 'fragmentShaderDepth' ).textContent;
// add bunch o' stuff
for (var i = 0; i < amount; i++) {
var scale = Math.random() * (1 - 0.8 + 1) + 0.8;
var object = new THREE.Mesh(geometry, material);
var x = Math.random() * 400 - 400 / 2;
var z = Math.random() * 400 - 400 / 2;
object.rotation.y = 180 * Math.PI / 180;
//object.position.y = size * scale / 2;
object.position.x = x;
object.position.z = z;
object.position.y = 0;
object.castShadow = true;
object.scale.x = scale; // random scale
object.scale.y = scale;
object.scale.z = scale;
scene.add(object);
object.customDepthMaterial = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
side: THREE.DoubleSide
} );
}
}
);
}
Height Data:
function getHeightData(img) {
var canvas = document.createElement('canvas');
canvas.width = 2048 / 8;
canvas.height = 2048 / 8;
var context = canvas.getContext('2d');
var size = 2048 / 8 * 2048 / 8,
data = new Float32Array(size);
context.drawImage(img, 0, 0);
for (var i = 0; i < size; i++) {
data[i] = 0
}
var imgd = context.getImageData(0, 0, 2048 / 8, 2048 / 8);
var pix = imgd.data;
var j = 0;
for (var i = 0, n = pix.length; i < n; i += (4)) {
var all = pix[i] + pix[i + 1] + pix[i + 2];
data[j++] = all / 40;
}
return data;
}
Yes, using of THREE.Raycaster() works well.
A raycaster has the .set(origin, direction) method. The only thing you have to do here is to set the point of origin higher than the highest point of the height map.
var n = new THREE.Mesh(...); // the object we want to aling along y-axis
var collider = new THREE.Raycaster();
var shiftY = new THREE.Vector3();
var colliderDir = new THREE.Vector3(0, -1, 0); // down along y-axis to the mesh of height map
shiftY.set(n.position.x, 100, n.position.z); // set the point of the origin
collider.set(shiftY, colliderDir); //set the ray of the raycaster
colliderIntersects = collider.intersectObject(plane); // plane is the mesh of height map
if (colliderIntersects.length > 0){
n.position.y = colliderIntersects[0].point.y; // set the position of the object
}
jsfiddle example

Three.js scene gets hanged and becomes slow

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)
}

Three.js shape from random points

I have a N number of random points (in this case 20), with a X,Y and Z constrains.
How can I create ANY (preferably closed) shape (using Three.js library) , given and starting only from N random points.
There are probably many variants, please share yours.
var program = new Program(reset,step)
program.add('g',false)
function reset() {
scene.clear()
scene.add(new THREE.GridHelper(100,1))
}
function step() {
}
program.startup()
var numpoints = 20;
var dots = []; //If you want to use for other task
for (var i = 0 ; i < numpoints ; i++){
var x = Math.random() * (80 - 1) + 1 //Math.random() * (max - min) + min
var y = Math.random() * (80 - 1) + 1
var z = Math.random() * (80 - 1) + 1
var dotGeometry = new THREE.Geometry();
dots.push(dotGeometry);
dotGeometry.vertices.push(new THREE.Vector3( x, y, z));
var dotMaterial = new THREE.PointsMaterial( { size: 3, sizeAttenuation: false, color: 0xFF0000 } );
var dot = new THREE.Points( dotGeometry, dotMaterial );
scene.add(dot);
}
Triangulation, Voronoi, I don't care, just show me ANY ideas you have, will help me learn a lot!
You can create a polyhedron which is the convex hull of a set of 3D points by using a pattern like so:
var points = [
new THREE.Vector3( 100, 0, 0 ),
new THREE.Vector3( 0, 100, 0 ),
...
new THREE.Vector3( 0, 0, 100 )
];
var geometry = new THREE.ConvexGeometry( points );
var material = new THREE.MeshPhongMaterial( {
color: 0xff0000,
shading: THREE.FlatShading
} );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
You must include the following in your project
<script src="/examples/js/geometries/ConvexGeometry.js"></script>
three.js r.78

Creating shapes based on Sphere vertices ThreeJs

Here's what i am trying to do, i already have created the vertices based on a formula, and connected lines between these vertices using a threejs method, and now i want to create shapes based on these vertices and connect them together to use them as map tiles, my suggestion is to modify the shape's vertices y, x, z axis, but i'm not able to find the right formula for these vertices:
mesh1 = new THREE.Mesh(); // sphere container
mesh2 = new THREE.Mesh(); // sphere container
mesh3 = new THREE.Mesh(); // sphere container
var R = 5.6; // radius
var LON = 32; var LAT = 16; // approximation
var PILAT = Math.PI/LAT;
var PILON = 2 * Math.PI/LON;
var cos1,cos2,sin1,sin2,t1,t2;
var y1,y2,r1,r2,t1,t2;
var plotG = new THREE.PlaneGeometry(0.06, 0.06);
var lineColor = new THREE.LineBasicMaterial({color: 0xaaaaaa});
var geometry = new THREE.Geometry();
var oldLATCounter = 0;
var oldLONCounter = 0;
for (var i=0; i<LAT; i++){
t1 = Math.PI - i*PILAT;
t2 = Math.PI - (i+1)*PILAT;
oldT1 = Math.PI - oldLATCounter*PILAT;
oldT2 = Math.PI - (oldLATCounter+1)*PILAT;
y1 = Math.cos(t1); // 1 latitudes radius y-position;
y2 = Math.cos(t2); // 2 latitudes radius y-position;
oldY1 = Math.cos(oldT1); // 1 latitudes radius y-position;
oldY2 = Math.cos(oldT2); // 2 latitudes radius y-position;
r1 = Math.abs( Math.sin(t1) ); // 1 latitudes radius;
r2 = Math.abs( Math.sin(t2) ); // 2 latitudes radius;
oldR1 = Math.abs( Math.sin(oldT1) ); // 1 latitudes radius;
oldR2 = Math.abs( Math.sin(oldT2) ); // 2 latitudes radius;
for (var j=0; j<LON; j++) // walk longitudes segments
{
t1 = j*PILON;
t2 = (j+1)*PILON;
oldT1 = oldLONCounter*PILON;
oldT2 = (oldLONCounter+1)*PILON;
cos1 = Math.cos(t1);
cos2 = Math.cos(t2);
sin1 = Math.sin(t1);
sin2 = Math.sin(t2);
oldCos1 = Math.cos(oldT1);
oldCos2 = Math.cos(oldT2);
oldSin1 = Math.sin(oldT1);
oldSin2 = Math.sin(oldT2);
geometry.vertices.push(
new THREE.Vector3( r1*cos1, y1, r1*sin1 ),
new THREE.Vector3( r2*cos1, y2, r2*sin1 ),
new THREE.Vector3( r2*cos2, y2, r2*sin2 )
);
geometry.dynamic = true;
var m1 = new THREE.Mesh( plotG );
m1.position.set(r2*cos2, y2, r2*sin2);
// m1.geometry.vertices[0].y = 0;
// m1.geometry.vertices[0].x = 0;
// m1.geometry.vertices[0].z = 0;
// m1.geometry.vertices[1].y = 0;
// m1.geometry.vertices[1].x = (oldR2*oldCos2) - (r2*cos2);
// m1.geometry.vertices[1].z = -(oldR2*oldSin2);
// m1.geometry.vertices[2].y = oldTy2;
// m1.geometry.vertices[2].x = 0;
// m1.geometry.vertices[2].z = 0.1;
// m1.geometry.vertices[3].y = 0;
// m1.geometry.vertices[3].x = 0;
// m1.geometry.vertices[3].z = 0.1;
mesh2.add(m1.clone());
oldLONCounter = j;
}
oldLATCounter = i;
}
mesh2.add( new THREE.Line( geometry, new THREE.LineBasicMaterial({color: 0xaaaaaa}) ) );
scene.add(mesh2);
mesh2.scale.set(R,R,R);
mesh2.position.x = 0;
This is the sphere i'm working on
Have you checked the THREE.Shape Class? It let you draw a shape/polygon based on some given points.
So my suggestion in your case is to loop through the vertices you need and draw a shape with them.
Here you can find out more about THREE.Shape
If you then want to create a geometry with that shape, to then add it to a mesh, then also have a look at THREE.ShapeGeometry

THREE.js - position particles evenly on objects faces rather than verticies

Currently I've managed to create a particleCloud with the particles appearing at each vertex of an object I've imported. However I'm trying to get the particles to firstly position on the flat faces of the object rather than the points between them and secondly evenly distribute particles on those faces.
Basically I'm trying to get my 3d object made out of particles
This is what I have so far:
var loader = new THREE.JSONLoader();
loader.load('./resources/model.json', function (geometry, materials) {
var material = new THREE.MeshFaceMaterial(materials);
var model = new THREE.Mesh(geometry, material);
var particleCount = geometry.vertices.length,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointCloudMaterial({
color: 0xFFFFFF,
size: 1
});
for (var p = 0; p < particleCount; p ++) {
particle = model.geometry.vertices[p];
particles.vertices.push(particle);
}
particleSystem = new THREE.PointCloud(particles, pMaterial);
particleSystem.position.set(0, -100, 0)
particleSystem.scale.set(100,100,100)
scene.add(particleSystem);
});
EDIT - 1
I've attached an image to try describe what i currently have and what i'm trying to achieve. Its using the front on a cube as an example. My object will have more sides to it.
EDIT: The previous answer was outdated.
You can now use MeshSurfaceSampler to generate random samples on the surface of a mesh.
The MeshSurfaceSampler.js file is located in the examples/jsm/math directory.
three.js r.128
You have to set the position of each particle individually to build up your 3d object out of particles. Here's an example that makes a cube:
var particles = 500000;
var geometry = new THREE.BufferGeometry();
var positions = new Float32Array( particles * 3 );
var colors = new Float32Array( particles * 3 );
var color = new THREE.Color();
var n = 1000, n2 = n / 2; // particles spread in the cube
for ( var i = 0; i < positions.length; i += 3 ) {
// positions
var x = Math.random() * n - n2;
var y = Math.random() * n - n2;
var z = Math.random() * n - n2;
positions[ i ] = x;
positions[ i + 1 ] = y;
positions[ i + 2 ] = z;
// colors
var vx = ( x / n ) + 0.5;
var vy = ( y / n ) + 0.5;
var vz = ( z / n ) + 0.5;
color.setRGB( vx, vy, vz );
colors[ i ] = color.r;
colors[ i + 1 ] = color.g;
colors[ i + 2 ] = color.b;
}
geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
geometry.computeBoundingSphere();
//
var material = new THREE.PointCloudMaterial( { size: 15, vertexColors: THREE.VertexColors } );
particleSystem = new THREE.PointCloud( geometry, material );
scene.add( particleSystem );
source: this threejs example

Categories