5

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();

Ant
  • 1,812
  • 5
  • 22
  • 39
  • What is your version of three.js? Check also documentation [here](https://github.com/mrdoob/three.js/wiki/Updates) on how to update things... – Wilt Dec 16 '16 at 14:57
  • 2
    See http://stackoverflow.com/questions/36699389/verticesneedupdate-in-three-js/36699654#36699654 and http://stackoverflow.com/questions/31399856/drawing-a-line-with-three-js-dynamically/31411794#31411794. – WestLangley Dec 18 '16 at 02:16
  • @WestLangley I am currently exploring the BufferGeometry, it seems like the number of points is fixed though. – Ant Dec 18 '16 at 02:59
  • 1
    The max number is. The rendered number is not. Study the 2nd link I posted. – WestLangley Dec 18 '16 at 03:04
  • @WestLangley Thanks - I got it working with the maximum points and a BufferGeometry. Updating and writing an answer now. – Ant Dec 18 '16 at 03:50
  • 1
    From trial and error method, requesting animation frame before rendering this one, is like the worst thing you can do, just wrong, at your own risk there. and if true, why has no one noticed? – greenthings Aug 07 '20 at 20:26
  • @greenthings I guess you're probably right - I haven't touched 3.js for more than 3 years - so I can't recall. – Ant Aug 14 '20 at 10:24

2 Answers2

5

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

}
Ant
  • 1,812
  • 5
  • 22
  • 39
  • three.js moved away from `Geometry` in favor of `BufferGeometry` as you found out. Of course that's easy for me to say in 2021 ( ; – Jacksonkr May 13 '21 at 21:11
3

Here's a fiddle with your first setup installed: https://jsfiddle.net/87wg5z27/236/

var scene, renderer, camera;
var cube;
var controls;

init();
animate();

function init()
{
    renderer = new THREE.WebGLRenderer( {antialias:true} );
    var width = window.innerWidth;
    var height = window.innerHeight;
    renderer.setSize (width, height);
    document.body.appendChild (renderer.domElement);

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
    camera.position.y = 160;
    camera.position.z = 400;
    camera.lookAt (new THREE.Vector3(0,0,0));

    controls = new THREE.OrbitControls (camera, renderer.domElement);

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

   window.addEventListener ('resize', onWindowResize, false);
}

function onWindowResize ()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize (window.innerWidth, window.innerHeight);
}

function animate()
{
    controls.update();
    requestAnimationFrame ( animate );  
    renderer.render (scene, camera);
}

Here's one with your second: https://jsfiddle.net/87wg5z27/237/

var scene, renderer, camera;
var cube;
var controls;

init();
animate();

function init()
{
    renderer = new THREE.WebGLRenderer( {antialias:true} );
    var width = window.innerWidth;
    var height = window.innerHeight;
    renderer.setSize (width, height);
    document.body.appendChild (renderer.domElement);

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera (45, width/height, 1, 10000);
    camera.position.y = 160;
    camera.position.z = 400;
    camera.lookAt (new THREE.Vector3(0,0,0));

    controls = new THREE.OrbitControls (camera, renderer.domElement);

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

    window.addEventListener ('resize', onWindowResize, false);
}

function onWindowResize ()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize (window.innerWidth, window.innerHeight);
}

function animate()
{
    controls.update();
    requestAnimationFrame ( animate );  
    renderer.render (scene, camera);
}

In both cases the point cloud shows for me perfectly fine (release 82). Perhaps there is something else missing where you're neglecting to render something? I notice that your first example doesn't show at what step you call render(). I hope this helps!

Ant
  • 1,812
  • 5
  • 22
  • 39
Hectate
  • 210
  • 2
  • 9
  • 1
    Hi @Hectate, Thank you very much for the fiddle, I should have created one - really thank you. I did another fiddle that shows case my problem - see updated question. https://jsfiddle.net/jjjaLv3e/2/ I want to update the PointCloud after it has been rendered. – Ant Dec 18 '16 at 01:50