Getting full Vectors from a BufferAttribute?

I’ve seen a few people get confused by geometry.getAttribute('position').array[i] returning a number instead of a Vector3. I’m guessing it’s because the legacy Geometry used to store vertex positions in Vec3s, and the new BufferGeometry stores it in a one-dimensional array, so that expectation has changed.

I understand there’s an alternative geometry.getAttribute('position').getX(i), that gives you only the x value of the vertex at position i. In order to get the full Vertex data, you need to do this 2 to 4 times and build it yourself. So I think it would be helpful to also have a .getVectorAt(i) method that returns the full vector already built for you, ready to use.

getVectorAt(index) {
	const vectorIndex = index * this.itemSize;

	// Build Vec2, 3, or 4, based on itemSize
	switch (this.itemSize) {
		case 1:
			return this.array[ vectorIndex ];
		break;
		case 2:
			return new THREE.Vector2(
				this.array[ vectorIndex + 0 ],
				this.array[ vectorIndex + 1 ]
			);
		break;
		case 3:
			return new THREE.Vector3(
				this.array[ vectorIndex + 0 ],
				this.array[ vectorIndex + 1 ],
				this.array[ vectorIndex + 2 ]
			);
		break;
		case 4:
			return new THREE.Vector4(
				this.array[ vectorIndex + 0 ],
				this.array[ vectorIndex + 1 ],
				this.array[ vectorIndex + 2 ],
				this.array[ vectorIndex + 3 ]
			);
		break;
	}
}

What are your thoughts on this? I think it would be very helpful, especially for people just getting started with BufferGeometries and how to extract vertex data from them.

All VectorX classes have .fromBufferAttribute() method.
let v3 = new THREE.Vector3().fromBufferAttribute(geometry.attributes.position, index);

I like the idea. Maybe enhance the method with a target vector .getVectorAt(index, target)?
On the other hand, I bet, if users get a vector this way and change its values, they’ll expect that values will be automatically applied to an attribute. And they’ll be confused again :thinking:

1 Like

The intended way for loading buffer attribute data into vectors is indeed the fromBufferAttribute() interface. I’m not sure how I feel about an alternative API.

2 Likes

Ah, I had no idea VectorX.fromBufferAttribute() existed! Thanks for the info.

It is redundant to have 2 methods for the same thing, but it seems a bit backward to have it as a method of Vec3. If I want to get data from my BufferAttribute, I go looking in the BufferAttribute documentation, not in Vector3.

It’s just like renderer.getClearColor() is a method of WebGLRenderer, not a method of Color. Wouldn’t you think?

When I remember correctly fromBufferAttribute() was added because there was already fromArray().

BTW: A method like getVectorAt() would definitely need a target parameter in order to avoid any object creation within the method.

1 Like

I think constructing Vectors for every vertex is a big enough performance problem that we don’t want to create APIs that encourage it. But it’s fair to say many users might read the BufferAttribute docs thoroughly and still not know what to do. We could fix that in a couple ways:

  1. Add a note in BufferAttribute docs about VectorN.fromBufferAttribute
  2. Add attribute.getXYZ( i, target ) methods, similar to the existing getX and getY methods.

With (2) it would be a little inconsistent that setXYZ takes three floats instead of a vector.

2 Likes