|
@@ -615,87 +615,224 @@ class VOXMesh extends Mesh {
|
|
|
const size = chunk.size;
|
|
const size = chunk.size;
|
|
|
const palette = chunk.palette;
|
|
const palette = chunk.palette;
|
|
|
|
|
|
|
|
- //
|
|
|
|
|
|
|
+ const sx = size.x;
|
|
|
|
|
+ const sy = size.y;
|
|
|
|
|
+ const sz = size.z;
|
|
|
|
|
+
|
|
|
|
|
+ // Build volume with color indices
|
|
|
|
|
+
|
|
|
|
|
+ const volume = new Uint8Array( sx * sy * sz );
|
|
|
|
|
+
|
|
|
|
|
+ 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 ];
|
|
|
|
|
+
|
|
|
|
|
+ volume[ x + y * sx + z * sx * sy ] = c;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Greedy meshing
|
|
|
|
|
|
|
|
const vertices = [];
|
|
const vertices = [];
|
|
|
|
|
+ const indices = [];
|
|
|
const colors = [];
|
|
const colors = [];
|
|
|
|
|
|
|
|
- const nx = [ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1 ];
|
|
|
|
|
- const px = [ 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 ];
|
|
|
|
|
- const py = [ 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 ];
|
|
|
|
|
- const ny = [ 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0 ];
|
|
|
|
|
- const nz = [ 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0 ];
|
|
|
|
|
- const pz = [ 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1 ];
|
|
|
|
|
-
|
|
|
|
|
const _color = new Color();
|
|
const _color = new Color();
|
|
|
|
|
+ let hasColors = false;
|
|
|
|
|
|
|
|
- function add( tile, x, y, z, r, g, b ) {
|
|
|
|
|
|
|
+ // 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 ];
|
|
|
|
|
|
|
|
- x -= size.x / 2;
|
|
|
|
|
- y -= size.z / 2;
|
|
|
|
|
- z += size.y / 2;
|
|
|
|
|
|
|
+ for ( let d = 0; d < 3; d ++ ) {
|
|
|
|
|
|
|
|
- for ( let i = 0; i < 18; i += 3 ) {
|
|
|
|
|
|
|
+ const u = ( d + 1 ) % 3;
|
|
|
|
|
+ const v = ( d + 2 ) % 3;
|
|
|
|
|
|
|
|
- _color.setRGB( r, g, b, SRGBColorSpace );
|
|
|
|
|
|
|
+ const dimsD = dims[ d ];
|
|
|
|
|
+ const dimsU = dims[ u ];
|
|
|
|
|
+ const dimsV = dims[ v ];
|
|
|
|
|
|
|
|
- vertices.push( tile[ i + 0 ] + x, tile[ i + 1 ] + y, tile[ i + 2 ] + z );
|
|
|
|
|
- colors.push( _color.r, _color.g, _color.b );
|
|
|
|
|
|
|
+ const q = [ 0, 0, 0 ];
|
|
|
|
|
+ const mask = new Int16Array( dimsU * dimsV );
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ q[ d ] = 1;
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // Sweep through slices
|
|
|
|
|
+ for ( let slice = 0; slice <= dimsD; slice ++ ) {
|
|
|
|
|
|
|
|
- // Store data in a volume for sampling
|
|
|
|
|
|
|
+ // Build mask for this slice
|
|
|
|
|
+ let n = 0;
|
|
|
|
|
|
|
|
- const offsety = size.x;
|
|
|
|
|
- const offsetz = size.x * size.y;
|
|
|
|
|
|
|
+ for ( let vv = 0; vv < dimsV; vv ++ ) {
|
|
|
|
|
|
|
|
- const array = new Uint8Array( size.x * size.y * size.z );
|
|
|
|
|
|
|
+ for ( let uu = 0; uu < dimsU; uu ++ ) {
|
|
|
|
|
|
|
|
- for ( let j = 0; j < data.length; j += 4 ) {
|
|
|
|
|
|
|
+ const pos = [ 0, 0, 0 ];
|
|
|
|
|
+ pos[ d ] = slice;
|
|
|
|
|
+ pos[ u ] = uu;
|
|
|
|
|
+ pos[ v ] = vv;
|
|
|
|
|
|
|
|
- const x = data[ j + 0 ];
|
|
|
|
|
- const y = data[ j + 1 ];
|
|
|
|
|
- const z = data[ j + 2 ];
|
|
|
|
|
|
|
+ const x0 = pos[ 0 ], y0 = pos[ 1 ], z0 = pos[ 2 ];
|
|
|
|
|
|
|
|
- const index = x + ( y * offsety ) + ( z * offsetz );
|
|
|
|
|
|
|
+ // 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;
|
|
|
|
|
|
|
|
- array[ index ] = 255;
|
|
|
|
|
|
|
+ // Face exists if exactly one side is solid
|
|
|
|
|
+ if ( behind > 0 && infront === 0 ) {
|
|
|
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ mask[ n ] = behind; // positive face
|
|
|
|
|
|
|
|
- // Construct geometry
|
|
|
|
|
|
|
+ } else if ( infront > 0 && behind === 0 ) {
|
|
|
|
|
|
|
|
- let hasColors = false;
|
|
|
|
|
|
|
+ mask[ n ] = - infront; // negative face
|
|
|
|
|
|
|
|
- for ( let j = 0; j < data.length; j += 4 ) {
|
|
|
|
|
|
|
+ } else {
|
|
|
|
|
|
|
|
- const x = data[ j + 0 ];
|
|
|
|
|
- const y = data[ j + 1 ];
|
|
|
|
|
- const z = data[ j + 2 ];
|
|
|
|
|
- const c = data[ j + 3 ];
|
|
|
|
|
|
|
+ mask[ n ] = 0;
|
|
|
|
|
|
|
|
- const hex = palette[ c ];
|
|
|
|
|
- const r = ( hex >> 0 & 0xff ) / 0xff;
|
|
|
|
|
- const g = ( hex >> 8 & 0xff ) / 0xff;
|
|
|
|
|
- const b = ( hex >> 16 & 0xff ) / 0xff;
|
|
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if ( r > 0 || g > 0 || b > 0 ) hasColors = true;
|
|
|
|
|
|
|
+ n ++;
|
|
|
|
|
|
|
|
- const index = x + ( y * offsety ) + ( z * offsetz );
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Greedy merge mask into quads
|
|
|
|
|
+ n = 0;
|
|
|
|
|
+
|
|
|
|
|
+ for ( let vv = 0; vv < dimsV; vv ++ ) {
|
|
|
|
|
+
|
|
|
|
|
+ for ( let uu = 0; uu < dimsU; ) {
|
|
|
|
|
+
|
|
|
|
|
+ const c = mask[ n ];
|
|
|
|
|
+
|
|
|
|
|
+ if ( c !== 0 ) {
|
|
|
|
|
|
|
|
- if ( array[ index + 1 ] === 0 || x === size.x - 1 ) add( px, x, z, - y, r, g, b );
|
|
|
|
|
- if ( array[ index - 1 ] === 0 || x === 0 ) add( nx, x, z, - y, r, g, b );
|
|
|
|
|
- if ( array[ index + offsety ] === 0 || y === size.y - 1 ) add( ny, x, z, - y, r, g, b );
|
|
|
|
|
- if ( array[ index - offsety ] === 0 || y === 0 ) add( py, x, z, - y, r, g, b );
|
|
|
|
|
- if ( array[ index + offsetz ] === 0 || z === size.z - 1 ) add( pz, x, z, - y, r, g, b );
|
|
|
|
|
- if ( array[ index - offsetz ] === 0 || z === 0 ) add( nz, x, z, - y, r, g, b );
|
|
|
|
|
|
|
+ // Find width
|
|
|
|
|
+ let w = 1;
|
|
|
|
|
+
|
|
|
|
|
+ while ( uu + w < dimsU && mask[ n + w ] === c ) {
|
|
|
|
|
+
|
|
|
|
|
+ w ++;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Find height
|
|
|
|
|
+ let h = 1;
|
|
|
|
|
+ let done = false;
|
|
|
|
|
+
|
|
|
|
|
+ while ( vv + h < dimsV && ! done ) {
|
|
|
|
|
+
|
|
|
|
|
+ for ( let k = 0; k < w; k ++ ) {
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+
|
|
|
|
|
+ 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;
|
|
|
|
|
+
|
|
|
|
|
+ if ( r > 0 || g > 0 || b > 0 ) hasColors = true;
|
|
|
|
|
+
|
|
|
|
|
+ _color.setRGB( r, g, b, SRGBColorSpace );
|
|
|
|
|
+
|
|
|
|
|
+ // 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
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ 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 ] ] );
|
|
|
|
|
+
|
|
|
|
|
+ const idx = vertices.length / 3;
|
|
|
|
|
+
|
|
|
|
|
+ // Winding order depends on face direction
|
|
|
|
|
+ if ( c > 0 ) {
|
|
|
|
|
+
|
|
|
|
|
+ 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 ++ ) {
|
|
|
|
|
+
|
|
|
|
|
+ for ( let ww = 0; ww < w; ww ++ ) {
|
|
|
|
|
+
|
|
|
|
|
+ mask[ n + ww + hh * dimsU ] = 0;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uu += w;
|
|
|
|
|
+ n += w;
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+
|
|
|
|
|
+ uu ++;
|
|
|
|
|
+ n ++;
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const geometry = new BufferGeometry();
|
|
const geometry = new BufferGeometry();
|
|
|
geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
|
geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
|
|
|
|
+ geometry.setIndex( indices );
|
|
|
geometry.computeVertexNormals();
|
|
geometry.computeVertexNormals();
|
|
|
|
|
|
|
|
const material = new MeshStandardMaterial();
|
|
const material = new MeshStandardMaterial();
|