Search code examples
javascriptthree.jsgeometrypoints

THREE.js dynamically add points to a Points geometry does not render


I am using Three.js r83.

I am trying to dynamically add points to a geometry, but the scene never gets updated.

This works :

var tmaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    size: 5,
    opacity: 1
});

var tgeometry = new THREE.Geometry();
var pointCloud = new THREE.Points(tgeometry, tmaterial);

for(var i = 0; i< 1000; i++) {
    x = (Math.random() * 200) - 100;
    y = (Math.random() * 200) - 100;
    z = (Math.random() * 200) - 100;
    tgeometry.vertices.push(new THREE.Vector3(x, y, z));
}
tgeometry.verticesNeedUpdate = true;
tgeometry.computeVertexNormals();

scene.add(pointCloud);

This doesn't work:

var tmaterial = new THREE.PointsMaterial({
    color: 0xff0000,
    size: 5,
    opacity: 1
});

var tgeometry = new THREE.Geometry();
var pointCloud = new THREE.Points(tgeometry, tmaterial);
scene.add(pointCloud);

for(var i = 0; i< 1000; i++) {
    x = (Math.random() * 200) - 100;
    y = (Math.random() * 200) - 100;
    z = (Math.random() * 200) - 100;
    tgeometry.vertices.push(new THREE.Vector3(x, y, z));
}
tgeometry.verticesNeedUpdate = true;
tgeometry.elementsNeedUpdate = true;
tgeometry.computeVertexNormals();
renderer.render(scene, camera);

As you can see, the only difference is the fact that I add scene.add(pointCloud); before adding vertexes.

What do I miss?

You can find a fiddle Thanks to @hectate

To see what I means, just replace

init(); setPoints(); animate();

by

init(); animate(); setPoints();


Solution

  • I am not sure why the THREE.Geometry object doesn't update Points after initial rendering, but I got it working with a THREE.BufferGeometry instead.

    Thanks to @Hectate who got a working fiddle for me and @WestLangley who directed me to the hints, here is the working fiddle

    BufferGeometry has a fixed number of Vertices, but you can decide how many of them you want to render. The trick is to make use of geometry.attributes.position.needsUpdate = true; and geometry.setDrawRange( 0, nbPointsYouWantToDisplay );

    var MAX_POINTS = 1000000;
    var geometry = new THREE.BufferGeometry();
    var positions = new Float32Array( MAX_POINTS * 3 ); 
    geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
    

    Then you can create your cloudpoints and add it to the scene:

    //material and scene defined in question
    pointCloud = new THREE.Points(geometry, material);
    scene.add(pointCloud);
    

    Now I want to add and render 500 new points every 10 milliseconds.

    var nbPoints = 500;
    var INTERVAL_DURATION = 10;
    

    All I have to do is :

    var interval = setInterval(function() {
      setPoints();
    }, INTERVAL_DURATION)
    
    function setPoints() {
    
      var positions = pointCloud.geometry.attributes.position.array;
    
      var x, y, z, index;
    
      var l  = currentPoints + nbPoints;
      if(l >= MAX_POINTS) {
        clearInterval(interval);
      }
    
      for ( var i = currentPoints; i < l; i ++ ) {
        x = ( Math.random() - 0.5 ) * 300;
        y = ( Math.random() - 0.5 ) * 300;
        z = ( Math.random() - 0.5 ) * 300;
        positions[ currentPointsIndex ++ ] = x;
        positions[ currentPointsIndex ++ ] = y;
        positions[ currentPointsIndex ++ ] = z;
      }
      currentPoints = l;
      pointCloud.geometry.attributes.position.needsUpdate = true;   
      pointCloud.geometry.setDrawRange( 0, currentPoints );  
      controls.update();
      renderer.render(scene, camera);
    
    }