Browse Source

VOXLoader: Replace VOXMesh and VOXData3DTexture classes with functions. (#32491)

mrdoob 2 months ago
parent
commit
26a71ef06f
3 changed files with 209 additions and 191 deletions
  1. 206 188
      examples/jsm/loaders/VOXLoader.js
  2. 1 1
      examples/webgl_loader_vox.html
  3. 2 2
      examples/webgl_volume_instancing.html

+ 206 - 188
examples/jsm/loaders/VOXLoader.js

@@ -165,7 +165,7 @@ function buildObject( nodeId, nodes, chunks ) {
 		if ( childNode.type === 'shape' && childNode.models.length === 1 ) {
 
 			const chunk = chunks[ childNode.models[ 0 ].modelId ];
-			const mesh = new VOXMesh( chunk );
+			const mesh = buildMesh( chunk );
 			applyTransform( mesh, node );
 			return mesh;
 
@@ -208,7 +208,7 @@ function buildObject( nodeId, nodes, chunks ) {
 		if ( node.models.length === 1 ) {
 
 			const chunk = chunks[ node.models[ 0 ].modelId ];
-			return new VOXMesh( chunk );
+			return buildMesh( chunk );
 
 		}
 
@@ -217,7 +217,7 @@ function buildObject( nodeId, nodes, chunks ) {
 		for ( const model of node.models ) {
 
 			const chunk = chunks[ model.modelId ];
-			group.add( new VOXMesh( chunk ) );
+			group.add( buildMesh( chunk ) );
 
 		}
 
@@ -596,231 +596,221 @@ class VOXLoader extends Loader {
 }
 
 /**
- * A VOX mesh.
+ * Builds a mesh from a VOX chunk.
  *
- * Instances of this class are created from the loaded chunks of {@link VOXLoader}.
- *
- * @augments Mesh
+ * @param {Object} chunk - A VOX chunk loaded via {@link VOXLoader}.
+ * @return {Mesh} The generated mesh.
  */
-class VOXMesh extends Mesh {
-
-	/**
-	 * Constructs a new VOX mesh.
-	 *
-	 * @param {Object} chunk - A VOX chunk loaded via {@link VOXLoader}.
-	 */
-	constructor( chunk ) {
-
-		const data = chunk.data;
-		const size = chunk.size;
-		const palette = chunk.palette;
-
-		const sx = size.x;
-		const sy = size.y;
-		const sz = size.z;
+function buildMesh( chunk ) {
 
-		// Build volume with color indices
+	const data = chunk.data;
+	const size = chunk.size;
+	const palette = chunk.palette;
 
-		const volume = new Uint8Array( sx * sy * sz );
+	const sx = size.x;
+	const sy = size.y;
+	const sz = size.z;
 
-		for ( let j = 0; j < data.length; j += 4 ) {
+	// Build volume with color indices
 
-			const x = data[ j + 0 ];
-			const y = data[ j + 1 ];
-			const z = data[ j + 2 ];
-			const c = data[ j + 3 ];
+	const volume = new Uint8Array( sx * sy * sz );
 
-			volume[ x + y * sx + z * sx * sy ] = c;
+	for ( let j = 0; j < data.length; j += 4 ) {
 
-		}
+		const x = data[ j + 0 ];
+		const y = data[ j + 1 ];
+		const z = data[ j + 2 ];
+		const c = data[ j + 3 ];
 
-		// Greedy meshing
+		volume[ x + y * sx + z * sx * sy ] = c;
 
-		const vertices = [];
-		const indices = [];
-		const colors = [];
+	}
 
-		const _color = new Color();
-		let hasColors = false;
+	// Greedy meshing
 
-		// Process each of the 6 face directions
-		// dims: the 3 axis sizes, d: which axis is normal to the face
-		const dims = [ sx, sy, sz ];
+	const vertices = [];
+	const indices = [];
+	const colors = [];
 
-		for ( let d = 0; d < 3; d ++ ) {
+	const _color = new Color();
+	let hasColors = false;
 
-			const u = ( d + 1 ) % 3;
-			const v = ( d + 2 ) % 3;
+	// Process each of the 6 face directions
+	// dims: the 3 axis sizes, d: which axis is normal to the face
+	const dims = [ sx, sy, sz ];
 
-			const dimsD = dims[ d ];
-			const dimsU = dims[ u ];
-			const dimsV = dims[ v ];
+	for ( let d = 0; d < 3; d ++ ) {
 
-			const q = [ 0, 0, 0 ];
-			const mask = new Int16Array( dimsU * dimsV );
+		const u = ( d + 1 ) % 3;
+		const v = ( d + 2 ) % 3;
 
-			q[ d ] = 1;
+		const dimsD = dims[ d ];
+		const dimsU = dims[ u ];
+		const dimsV = dims[ v ];
 
-			// Sweep through slices
-			for ( let slice = 0; slice <= dimsD; slice ++ ) {
+		const q = [ 0, 0, 0 ];
+		const mask = new Int16Array( dimsU * dimsV );
 
-				// Build mask for this slice
-				let n = 0;
+		q[ d ] = 1;
 
-				for ( let vv = 0; vv < dimsV; vv ++ ) {
+		// Sweep through slices
+		for ( let slice = 0; slice <= dimsD; slice ++ ) {
 
-					for ( let uu = 0; uu < dimsU; uu ++ ) {
+			// Build mask for this slice
+			let n = 0;
 
-						const pos = [ 0, 0, 0 ];
-						pos[ d ] = slice;
-						pos[ u ] = uu;
-						pos[ v ] = vv;
+			for ( let vv = 0; vv < dimsV; vv ++ ) {
 
-						const x0 = pos[ 0 ], y0 = pos[ 1 ], z0 = pos[ 2 ];
+				for ( let uu = 0; uu < dimsU; uu ++ ) {
 
-						// Get voxel behind and in front of this face
-						const behind = ( slice > 0 ) ? volume[ ( x0 - q[ 0 ] ) + ( y0 - q[ 1 ] ) * sx + ( z0 - q[ 2 ] ) * sx * sy ] : 0;
-						const infront = ( slice < dimsD ) ? volume[ x0 + y0 * sx + z0 * sx * sy ] : 0;
+					const pos = [ 0, 0, 0 ];
+					pos[ d ] = slice;
+					pos[ u ] = uu;
+					pos[ v ] = vv;
 
-						// Face exists if exactly one side is solid
-						if ( behind > 0 && infront === 0 ) {
+					const x0 = pos[ 0 ], y0 = pos[ 1 ], z0 = pos[ 2 ];
 
-							mask[ n ] = behind; // positive face
+					// Get voxel behind and in front of this face
+					const behind = ( slice > 0 ) ? volume[ ( x0 - q[ 0 ] ) + ( y0 - q[ 1 ] ) * sx + ( z0 - q[ 2 ] ) * sx * sy ] : 0;
+					const infront = ( slice < dimsD ) ? volume[ x0 + y0 * sx + z0 * sx * sy ] : 0;
 
-						} else if ( infront > 0 && behind === 0 ) {
+					// Face exists if exactly one side is solid
+					if ( behind > 0 && infront === 0 ) {
 
-							mask[ n ] = - infront; // negative face
+						mask[ n ] = behind; // positive face
 
-						} else {
+					} else if ( infront > 0 && behind === 0 ) {
 
-							mask[ n ] = 0;
+						mask[ n ] = - infront; // negative face
 
-						}
+					} else {
 
-						n ++;
+						mask[ n ] = 0;
 
 					}
 
+					n ++;
+
 				}
 
-				// Greedy merge mask into quads
-				n = 0;
+			}
 
-				for ( let vv = 0; vv < dimsV; vv ++ ) {
+			// Greedy merge mask into quads
+			n = 0;
 
-					for ( let uu = 0; uu < dimsU; ) {
+			for ( let vv = 0; vv < dimsV; vv ++ ) {
 
-						const c = mask[ n ];
+				for ( let uu = 0; uu < dimsU; ) {
 
-						if ( c !== 0 ) {
+					const c = mask[ n ];
 
-							// Find width
-							let w = 1;
+					if ( c !== 0 ) {
 
-							while ( uu + w < dimsU && mask[ n + w ] === c ) {
+						// Find width
+						let w = 1;
 
-								w ++;
+						while ( uu + w < dimsU && mask[ n + w ] === c ) {
 
-							}
+							w ++;
 
-							// Find height
-							let h = 1;
-							let done = false;
+						}
 
-							while ( vv + h < dimsV && ! done ) {
+						// Find height
+						let h = 1;
+						let done = false;
 
-								for ( let k = 0; k < w; k ++ ) {
+						while ( vv + h < dimsV && ! done ) {
 
-									if ( mask[ n + k + h * dimsU ] !== c ) {
+							for ( let k = 0; k < w; k ++ ) {
 
-										done = true;
-										break;
+								if ( mask[ n + k + h * dimsU ] !== c ) {
 
-									}
+									done = true;
+									break;
 
 								}
 
-								if ( ! done ) h ++;
-
 							}
 
-							// Add quad
-							const pos = [ 0, 0, 0 ];
-							pos[ d ] = slice;
-							pos[ u ] = uu;
-							pos[ v ] = vv;
+							if ( ! done ) h ++;
 
-							const du = [ 0, 0, 0 ];
-							const dv = [ 0, 0, 0 ];
-							du[ u ] = w;
-							dv[ v ] = h;
+						}
 
-							// Get color
-							const colorIndex = Math.abs( c );
-							const hex = palette[ colorIndex ];
-							const r = ( hex >> 0 & 0xff ) / 0xff;
-							const g = ( hex >> 8 & 0xff ) / 0xff;
-							const b = ( hex >> 16 & 0xff ) / 0xff;
+						// Add quad
+						const pos = [ 0, 0, 0 ];
+						pos[ d ] = slice;
+						pos[ u ] = uu;
+						pos[ v ] = vv;
 
-							if ( r > 0 || g > 0 || b > 0 ) hasColors = true;
+						const du = [ 0, 0, 0 ];
+						const dv = [ 0, 0, 0 ];
+						du[ u ] = w;
+						dv[ v ] = h;
 
-							_color.setRGB( r, g, b, SRGBColorSpace );
+						// Get color
+						const colorIndex = Math.abs( c );
+						const hex = palette[ colorIndex ];
+						const r = ( hex >> 0 & 0xff ) / 0xff;
+						const g = ( hex >> 8 & 0xff ) / 0xff;
+						const b = ( hex >> 16 & 0xff ) / 0xff;
 
-							// Convert VOX coords to Three.js coords (Y-up)
-							// VOX: X right, Y forward, Z up -> Three.js: X right, Y up, Z back
-							const toThree = ( p ) => [
-								p[ 0 ] - sx / 2,
-								p[ 2 ] - sz / 2,
-								- p[ 1 ] + sy / 2
-							];
+						if ( r > 0 || g > 0 || b > 0 ) hasColors = true;
 
-							const v0 = toThree( pos );
-							const v1 = toThree( [ pos[ 0 ] + du[ 0 ], pos[ 1 ] + du[ 1 ], pos[ 2 ] + du[ 2 ] ] );
-							const v2 = toThree( [ pos[ 0 ] + du[ 0 ] + dv[ 0 ], pos[ 1 ] + du[ 1 ] + dv[ 1 ], pos[ 2 ] + du[ 2 ] + dv[ 2 ] ] );
-							const v3 = toThree( [ pos[ 0 ] + dv[ 0 ], pos[ 1 ] + dv[ 1 ], pos[ 2 ] + dv[ 2 ] ] );
+						_color.setRGB( r, g, b, SRGBColorSpace );
 
-							const idx = vertices.length / 3;
+						// Convert VOX coords to Three.js coords (Y-up)
+						// VOX: X right, Y forward, Z up -> Three.js: X right, Y up, Z back
+						const toThree = ( p ) => [
+							p[ 0 ] - sx / 2,
+							p[ 2 ] - sz / 2,
+							- p[ 1 ] + sy / 2
+						];
 
-							// Winding order depends on face direction
-							if ( c > 0 ) {
+						const v0 = toThree( pos );
+						const v1 = toThree( [ pos[ 0 ] + du[ 0 ], pos[ 1 ] + du[ 1 ], pos[ 2 ] + du[ 2 ] ] );
+						const v2 = toThree( [ pos[ 0 ] + du[ 0 ] + dv[ 0 ], pos[ 1 ] + du[ 1 ] + dv[ 1 ], pos[ 2 ] + du[ 2 ] + dv[ 2 ] ] );
+						const v3 = toThree( [ pos[ 0 ] + dv[ 0 ], pos[ 1 ] + dv[ 1 ], pos[ 2 ] + dv[ 2 ] ] );
 
-								vertices.push( ...v0, ...v1, ...v2, ...v3 );
-								indices.push( idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 );
+						const idx = vertices.length / 3;
 
-							} else {
+						// Winding order depends on face direction
+						if ( c > 0 ) {
 
-								vertices.push( ...v0, ...v3, ...v2, ...v1 );
-								indices.push( idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 );
+							vertices.push( ...v0, ...v1, ...v2, ...v3 );
+							indices.push( idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 );
 
-							}
+						} else {
+
+							vertices.push( ...v0, ...v3, ...v2, ...v1 );
+							indices.push( idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 );
 
-							colors.push(
-								_color.r, _color.g, _color.b,
-								_color.r, _color.g, _color.b,
-								_color.r, _color.g, _color.b,
-								_color.r, _color.g, _color.b
-							);
+						}
 
-							// Clear mask
-							for ( let hh = 0; hh < h; hh ++ ) {
+						colors.push(
+							_color.r, _color.g, _color.b,
+							_color.r, _color.g, _color.b,
+							_color.r, _color.g, _color.b,
+							_color.r, _color.g, _color.b
+						);
 
-								for ( let ww = 0; ww < w; ww ++ ) {
+						// Clear mask
+						for ( let hh = 0; hh < h; hh ++ ) {
 
-									mask[ n + ww + hh * dimsU ] = 0;
+							for ( let ww = 0; ww < w; ww ++ ) {
 
-								}
+								mask[ n + ww + hh * dimsU ] = 0;
 
 							}
 
-							uu += w;
-							n += w;
+						}
 
-						} else {
+						uu += w;
+						n += w;
 
-							uu ++;
-							n ++;
+					} else {
 
-						}
+						uu ++;
+						n ++;
 
 					}
 
@@ -830,72 +820,100 @@ class VOXMesh extends Mesh {
 
 		}
 
-		const geometry = new BufferGeometry();
-		geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
-		geometry.setIndex( indices );
-		geometry.computeVertexNormals();
-
-		const material = new MeshStandardMaterial();
+	}
 
-		if ( hasColors ) {
+	const geometry = new BufferGeometry();
+	geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+	geometry.setIndex( indices );
+	geometry.computeVertexNormals();
 
-			geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
-			material.vertexColors = true;
+	const material = new MeshStandardMaterial();
 
-		}
+	if ( hasColors ) {
 
-		super( geometry, material );
+		geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );
+		material.vertexColors = true;
 
 	}
 
+	return new Mesh( geometry, material );
+
 }
 
 /**
- * A VOX 3D texture.
- *
- * Instances of this class are created from the loaded chunks of {@link VOXLoader}.
+ * Builds a 3D texture from a VOX chunk.
  *
- * @augments Data3DTexture
+ * @param {Object} chunk - A VOX chunk loaded via {@link VOXLoader}.
+ * @return {Data3DTexture} The generated 3D texture.
  */
-class VOXData3DTexture extends Data3DTexture {
+function buildData3DTexture( chunk ) {
+
+	const data = chunk.data;
+	const size = chunk.size;
+
+	const offsety = size.x;
+	const offsetz = size.x * size.y;
+
+	const array = new Uint8Array( size.x * size.y * size.z );
+
+	for ( let j = 0; j < data.length; j += 4 ) {
+
+		const x = data[ j + 0 ];
+		const y = data[ j + 1 ];
+		const z = data[ j + 2 ];
+
+		const index = x + ( y * offsety ) + ( z * offsetz );
+
+		array[ index ] = 255;
+
+	}
+
+	const texture = new Data3DTexture( array, size.x, size.y, size.z );
+
+	texture.format = RedFormat;
+	texture.minFilter = NearestFilter;
+	texture.magFilter = LinearFilter;
+	texture.unpackAlignment = 1;
+	texture.needsUpdate = true;
+
+	return texture;
+
+}
+
+// @deprecated, r182
+
+class VOXMesh extends Mesh {
 
-	/**
-	 * Constructs a new VOX 3D texture.
-	 *
-	 * @param {Object} chunk - A VOX chunk loaded via {@link VOXLoader}.
-	 */
 	constructor( chunk ) {
 
-		const data = chunk.data;
-		const size = chunk.size;
+		console.warn( 'VOXMesh has been deprecated. Use buildMesh() instead.' );
 
-		const offsety = size.x;
-		const offsetz = size.x * size.y;
+		const mesh = buildMesh( chunk );
 
-		const array = new Uint8Array( size.x * size.y * size.z );
+		super( mesh.geometry, mesh.material );
+
+	}
 
-		for ( let j = 0; j < data.length; j += 4 ) {
+}
 
-			const x = data[ j + 0 ];
-			const y = data[ j + 1 ];
-			const z = data[ j + 2 ];
+class VOXData3DTexture extends Data3DTexture {
 
-			const index = x + ( y * offsety ) + ( z * offsetz );
+	constructor( chunk ) {
 
-			array[ index ] = 255;
+		console.warn( 'VOXData3DTexture has been deprecated. Use buildData3DTexture() instead.' );
 
-		}
+		const texture = buildData3DTexture( chunk );
 
-		super( array, size.x, size.y, size.z );
+		super( texture.image.data, texture.image.width, texture.image.height, texture.image.depth );
 
-		this.format = RedFormat;
-		this.minFilter = NearestFilter;
-		this.magFilter = LinearFilter;
-		this.unpackAlignment = 1;
+		this.format = texture.format;
+		this.minFilter = texture.minFilter;
+		this.magFilter = texture.magFilter;
+		this.unpackAlignment = texture.unpackAlignment;
 		this.needsUpdate = true;
 
 	}
 
 }
 
-export { VOXLoader, VOXMesh, VOXData3DTexture };
+export { VOXLoader, buildMesh, buildData3DTexture, VOXMesh, VOXData3DTexture };

+ 1 - 1
examples/webgl_loader_vox.html

@@ -26,7 +26,7 @@
 			import * as THREE from 'three';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
-			import { VOXLoader, VOXMesh } from 'three/addons/loaders/VOXLoader.js';
+			import { VOXLoader } from 'three/addons/loaders/VOXLoader.js';
 
 			let camera, controls, scene, renderer;
 

+ 2 - 2
examples/webgl_volume_instancing.html

@@ -24,7 +24,7 @@
 		<script type="module">
 			import * as THREE from 'three';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
-			import { VOXLoader, VOXData3DTexture } from 'three/addons/loaders/VOXLoader.js';
+			import { VOXLoader, buildData3DTexture } from 'three/addons/loaders/VOXLoader.js';
 
 			let renderer, scene, camera, controls, clock;
 
@@ -199,7 +199,7 @@
 						const material = new THREE.RawShaderMaterial( {
 							glslVersion: THREE.GLSL3,
 							uniforms: {
-								map: { value: new VOXData3DTexture( chunk ) },
+								map: { value: buildData3DTexture( chunk ) },
 								cameraPos: { value: new THREE.Vector3() }
 							},
 							vertexShader,

粤ICP备19079148号