145 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			145 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @author santiago / @glitch_life
							 | 
						||
| 
								 | 
							
								 * wrapper of https://www.npmjs.com/package/isosurface by https://github.com/mikolalysenko
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Returns BufferGeometry from SDF
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import {
							 | 
						||
| 
								 | 
							
									BufferAttribute,
							 | 
						||
| 
								 | 
							
									BufferGeometry,
							 | 
						||
| 
								 | 
							
									FloatType,
							 | 
						||
| 
								 | 
							
									Mesh,
							 | 
						||
| 
								 | 
							
									OrthographicCamera,
							 | 
						||
| 
								 | 
							
									PlaneGeometry,
							 | 
						||
| 
								 | 
							
									Scene,
							 | 
						||
| 
								 | 
							
									ShaderMaterial,
							 | 
						||
| 
								 | 
							
									Vector2,
							 | 
						||
| 
								 | 
							
									WebGLRenderTarget
							 | 
						||
| 
								 | 
							
								} from 'three';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import { surfaceNet } from './../libs/surfaceNet.js';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SDFGeometryGenerator {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									constructor( renderer ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.renderer = renderer;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									generate( res = 64, distFunc = 'float dist( vec3 p ){ return length(p) - 0.5; }', bounds = 1 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let w, h;
							 | 
						||
| 
								 | 
							
										if ( res == 8 ) [ w, h ] = [ 32, 16 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 16 ) [ w, h ] = [ 64, 64 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 32 ) [ w, h ] = [ 256, 128 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 64 ) [ w, h ] = [ 512, 512 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 128 ) [ w, h ] = [ 2048, 1024 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 256 ) [ w, h ] = [ 4096, 4096 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 512 ) [ w, h ] = [ 16384, 8096 ];
							 | 
						||
| 
								 | 
							
										else if ( res == 1024 ) [ w, h ] = [ 32768, 32768 ];
							 | 
						||
| 
								 | 
							
										else throw new Error( 'THREE.SDFGeometryGenerator: Resolution must be in range 8 < res < 1024 and must be ^2' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const maxTexSize = this.renderer.capabilities.maxTextureSize;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( w > maxTexSize || h > maxTexSize ) throw new Error( 'THREE.SDFGeometryGenerator: Your device does not support this resolution ( ' + res + ' ), decrease [res] param.' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const [ tilesX, tilesY ] = [ ( w / res ), ( h / res ) ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const sdfCompute = `
							 | 
						||
| 
								 | 
							
											varying vec2 vUv;
							 | 
						||
| 
								 | 
							
											uniform float tileNum;
							 | 
						||
| 
								 | 
							
											uniform float bounds;
							 | 
						||
| 
								 | 
							
											[#dist#]
							 | 
						||
| 
								 | 
							
											void main()	{ gl_FragColor=vec4( ( dist( vec3( vUv, tileNum ) * 2.0 * bounds - vec3( bounds ) ) < 0.00001 ) ? 1.0 : 0.0 ); }
							 | 
						||
| 
								 | 
							
										`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const sdfRT = this.computeSDF( w, h, tilesX, tilesY, bounds, sdfCompute.replace( '[#dist#]', distFunc ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const read = new Float32Array( w * h * 4 );
							 | 
						||
| 
								 | 
							
										this.renderer.readRenderTargetPixels( sdfRT, 0, 0, w, h, read );
							 | 
						||
| 
								 | 
							
										sdfRT.dispose();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const mesh = surfaceNet( [ res, res, res ], ( x, y, z ) => {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											x = ( x + bounds ) * ( res / ( bounds * 2 ) );
							 | 
						||
| 
								 | 
							
											y = ( y + bounds ) * ( res / ( bounds * 2 ) );
							 | 
						||
| 
								 | 
							
											z = ( z + bounds ) * ( res / ( bounds * 2 ) );
							 | 
						||
| 
								 | 
							
											let p = ( x + ( z % tilesX ) * res ) + y * w + ( Math.floor( z / tilesX ) * res * w );
							 | 
						||
| 
								 | 
							
											p *= 4;
							 | 
						||
| 
								 | 
							
											return ( read[ p + 3 ] > 0 ) ? - 0.000000001 : 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}, [[ - bounds, - bounds, - bounds ], [ bounds, bounds, bounds ]] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const ps = [], ids = [];
							 | 
						||
| 
								 | 
							
										const geometry = new BufferGeometry();
							 | 
						||
| 
								 | 
							
										mesh.positions.forEach( p => {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											ps.push( p[ 0 ], p[ 1 ], p[ 2 ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} );
							 | 
						||
| 
								 | 
							
										mesh.cells.forEach( p => ids.push( p[ 0 ], p[ 1 ], p[ 2 ] ) );
							 | 
						||
| 
								 | 
							
										geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( ps ), 3 ) );
							 | 
						||
| 
								 | 
							
										geometry.setIndex( ids );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return geometry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									computeSDF( width, height, tilesX, tilesY, bounds, shader ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const rt = new WebGLRenderTarget( width, height, { type: FloatType } );
							 | 
						||
| 
								 | 
							
										const scn = new Scene();
							 | 
						||
| 
								 | 
							
										const cam = new OrthographicCamera();
							 | 
						||
| 
								 | 
							
										const tiles = tilesX * tilesY;
							 | 
						||
| 
								 | 
							
										let currentTile = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Object.assign( cam, { left: width / - 2, right: width / 2, top: height / 2, bottom: height / - 2 } ).updateProjectionMatrix();
							 | 
						||
| 
								 | 
							
										cam.position.z = 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const tileSize = width / tilesX;
							 | 
						||
| 
								 | 
							
										const geometry = new PlaneGeometry( tileSize, tileSize );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										while ( currentTile ++ < tiles ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const c = currentTile - 1;
							 | 
						||
| 
								 | 
							
											const [ px, py ] = [ ( tileSize ) / 2 + ( c % tilesX ) * ( tileSize ) - width / 2, ( tileSize ) / 2 + Math.floor( c / tilesX ) * ( tileSize ) - height / 2 ];
							 | 
						||
| 
								 | 
							
											const compPlane = new Mesh( geometry, new ShaderMaterial( {
							 | 
						||
| 
								 | 
							
												uniforms: {
							 | 
						||
| 
								 | 
							
													res: { value: new Vector2( width, height ) },
							 | 
						||
| 
								 | 
							
													tileNum: { value: c / ( tilesX * tilesY - 1 ) },
							 | 
						||
| 
								 | 
							
													bounds: { value: bounds }
							 | 
						||
| 
								 | 
							
												},
							 | 
						||
| 
								 | 
							
												vertexShader: 'varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}',
							 | 
						||
| 
								 | 
							
												fragmentShader: shader
							 | 
						||
| 
								 | 
							
											} ) );
							 | 
						||
| 
								 | 
							
											compPlane.position.set( px, py, 0 );
							 | 
						||
| 
								 | 
							
											scn.add( compPlane );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.renderer.setRenderTarget( rt );
							 | 
						||
| 
								 | 
							
										this.renderer.render( scn, cam );
							 | 
						||
| 
								 | 
							
										this.renderer.setRenderTarget( null );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										geometry.dispose();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										scn.traverse( function ( object ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( object.material !== undefined ) object.material.dispose();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return rt;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { SDFGeometryGenerator };
							 |