BufferGeometry groups to individual BufferGeometries

I’m trying to use the DRACOExporter for my project. Occasionally, my project will get a BufferGeometry that has “groups” in it. If that is the case, I want to have the DRACOExporter export one mesh for every group in the BufferGeometry. I’m struggling to find a good way to do this though.

My first thought was to just clone the BufferGeometry and manipulate the new copy. Looking at the source code, if I clone a BufferGeometry, that will also clone all it’s BufferAttributes, which will all copy their underlying TypedArray. I’m hesitant to do this, because some models will have very high poly counts (5 million or more) and I’m worried about how much time/memory that will consume copying the entire array multiple times (If there are 6 groups present, it would copy the same 5 million element array 6 times). However, even if I did just clone the original buffer geometry, I’m not sure how to modify it so that it only has the faces for a single group. Looking at the source code for the DRACOExporter, it seems to ignore things like draw ranges anyway?

Is there any way that I can create a BufferGeometry which shares the same underlying arrays as another BufferGeometry? I see that you can create a new BufferAttribute with a TypedArray, and I know that TypedArrays are views of an underlying ArrayBuffer. So I can perhaps create multiple views of the same ArrayBuffer and create different BufferGeometrys from each one. Once I have multiple BufferGeometrys I can then give them one by one to the DRACOExporter. I’ve started going down this road, but it’s starting to get a little complicated, and I worry that I might have missed a simpler solution to my problem. Will this strategy (using the same underlying arrays) work, or is there something about three.js that will prevent me from doing this? Alternatively, is there a better solution to my problem?

I think the best solution in memory and speed is to create new BufferGeometries from the original.

This is for non-indexed geometry. UV coordinates can also be added:

function separateGroups( bufGeom ) {

	var outGeometries = [];

	var groups = bufGeom.groups;

	var origVerts = bufGeom.getAttribute( 'position' ).array;
	var origNormals = bufGeom.getAttribute( 'normal' ).array;
	var origNumVerts = Math.floor( origVerts.length / 3 );

	for ( var ig = 0, ng = groups.length; ig < ng; ig ++ ) {

		var group = groups[ ig ];

		var destNumVerts = group.count;

		var newBufGeom = new THREE.BufferGeometry();
		var newPositions = new Float32Array( destNumVerts * 3 );
		var newNormals = new Float32Array( destNumVerts * 3 );

		for ( var iv = 0; iv < destNumVerts; iv ++ ) {

			var indexOrig = 3 * ( group.start + iv );
			var indexDest = 3 * iv;

			newPosition[ indexDest ] = origVerts[ indexOrig ];
			newPosition[ indexDest + 1 ] = origVerts[ indexOrig + 1 ];
			newPosition[ indexDest + 2 ] = origVerts[ indexOrig + 2 ];

			newNormals[ indexDest ] = origNormals[ indexOrig ];
			newNormals[ indexDest + 1 ] = origNormals[ indexOrig + 1 ];
			newNormals[ indexDest + 2 ] = origNormals[ indexOrig + 2 ];

		}

		newBufGeom.setAttribute( 'position', new Float32BufferAttribute( newPositions, 3 ) );
		newBufGeom.setAttribute( 'normal', new Float32BufferAttribute( newNormals, 3 ) );

		outGeometries.push( newBufGeom );

	}

	return outGeometries;

}
2 Likes