|
|
@@ -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 };
|