TheJim01
TheJim01

Reputation: 8876

Three.js Lines larger than 65535 vertices (r71)

First, I'm currently stuck using Three.js r71 (it would currently be a pain to upgrade to the latest). That said, I don't think too much has changed with respect to my question, so hopefully there's still hope.

I'm trying to figure out how to use indexing and drawcalls in BufferGeometry. I think I'm 98% there, but something isn't clicking. I've created my BufferGeometry, and populated it with a single line with 70000 vertices (positions array has 210000 items, colors array has 210000 items, and index array has 139998 items ((70000 - 1) * 2 = 139998 ✓)).

I know that I can't reference an index above 65535, so I added items to geometry.drawcalls using BufferGeometry.addDrawCall. Those objects are:

[{"start":0,"count":65535,"index":0},{"start":65535,"count":4465,"index":65535}]

Now, when I run the sample (link and code below) with 65535 vertices, everything works as expected: A red spiral appears. But when I try to use 70000 vertices, something odd happens: I get An error in the console (below), and a smaller spiral appears.

Console error:

GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0

I've tried re-aligning the indices (commented section starting at line 56), but it encounters the same issue. Am I missing a step? Is there some other switch or property I need to populate?

Footnote: I tried using a UInt32Array for index, but WebGL throws a type error instead.

JSFiddle: Line Indexing Test

Code:

var numVerts = 70000; // works with 65535

var hostDiv, scene, renderer, camera, controls, light;

var WIDTH = window.innerWidth,
    HEIGHT = window.innerHeight,
    FOV = 35,
    NEAR = 1,
    FAR = 1000;

function init() {
  hostDiv = document.createElement('div');
  hostDiv.setAttribute('id', 'host');
  document.body.appendChild(hostDiv);

  renderer = new THREE.WebGLRenderer({ antialias: true, preserverDrawingBuffer: true });
  renderer.setSize(WIDTH, HEIGHT);
  hostDiv.appendChild(renderer.domElement);

  camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
  camera.position.z = 250;

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.rotateSpeed = 5.0;
  controls.dynamicDampingFactor = 0.5;

  light = new THREE.PointLight(0xffffff, 1, Infinity);
  light.position.copy(camera.position);

  scene = new THREE.Scene();
  scene.add(camera);
  scene.add(light);

  scene.add(createSpiral());
debugger;
  animate();
}

function createSpiral(){
  var positions = [],
      indices = [],
      colors = [];

  var vec = new THREE.Vector3(1, 0, 0),
      rotVec = new THREE.Vector3(0, 0, 1);

  for(var idx = 0, fakeIdx = 0; idx < numVerts; ++idx){
    // update position vector
    vec.setLength((idx + 1) / 1000); // slowly spiral outward
    vec.applyAxisAngle(rotVec, 0.01); // spin around the z-axis
    positions.push(vec.x, vec.y, vec.z);

    if(idx + 1 < numVerts){
      indices.push(idx, idx + 1);
    }
    //if((idx % 65535) + 1 < numVerts){
    //  indices.push((idx % 65535), (idx % 65535) + 1);
    //}

    if(idx > 65535){
      colors.push(0, 1, 0); // GREEN!
    }
    else{
      colors.push(1, 0, 0); // RED!
    }
  }

  bg = new THREE.BufferGeometry();

  if(numVerts > 65535){
    var remainingVerts = numVerts,
        start = 0,
        count = 0,
        index = 0;

    while(remainingVerts > 0){
      count = (remainingVerts < 65535)? remainingVerts : 65535;

      bg.addDrawCall(start, count, start);

      start += 65535;			
      remainingVerts -= 65535;
    }
  }

  bg.addAttribute('index', new THREE.BufferAttribute(new Uint16Array( indices ), 1));
  bg.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3));
  bg.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3));

  var mat = new THREE.LineBasicMaterial({ vertexColors: THREE.VertexColors });

  var spiral = new THREE.Line(bg, mat, THREE.LinePieces);
  spiral.name = "SPIRAL";
  debugger;
  return spiral;
}

function render() {
  light.position.copy(camera.position);
  renderer.render(scene, camera);
  controls.update();
}

function animate() {    
  requestAnimationFrame(animate);
  render();
}

init();
html *{
	padding: 0;
	margin: 0;
	height: 100%;
	width: 100%;
	overflow: hidden;
}

#host {
	width: 100%;
	height: 100%;
}
<script src="https://rawgithub.com/mrdoob/three.js/f73593b00e3b2c927bb1a3236240cea5597166ec/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>

Upvotes: 1

Views: 390

Answers (1)

Wilt
Wilt

Reputation: 44356

Using a UInt32Array works fine for me in r73.

In r73 they don't use THREE.LineStrip and THREE.LinePieces in the material, but there are two different classes THREE.Line and THREE.LineSegments for drawing lines continuous or in segments.

I made some other changes. I changed to use a continuous line instead of segments because it saves you half the amount of indices. I think this could also be one of your issues. Since you inserted 2 indices for each line segment, which means to draw 32768 line segments you already reach your max 65536 indices.

Here a fiddle, which I updated to r73

Note my changes important changes:

indices.push(idx);

bg.setIndex( new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );

var spiral = new THREE.Line(bg, mat);

And I removed the unneeded lines of code.

UPDATE

Interesting, I just noticed that you don't even need the indices. It works totally fine as a non-indexed THREE.BufferGeometry. This saves you all the headaches and will virtually allow you to have infinite points if I am not mistaken...

Here another fiddle that shows that it works totally fine without with a huge amount of points.

And this also works for your r71. Check that out here

Upvotes: 1

Related Questions