Skip to content

Commit

Permalink
Add SDFGeometryGenerator addon. (#26837)
Browse files Browse the repository at this point in the history
* add SDFGeometry ammend

* remove comment

* Update webgl_geometry_sdf.jpg

* pass that check....

* PR fixes

~ Code scanning checks
~ Update class name to SDFGeometryGenerator
~ Change class design
~ Remove confirm dialog
~ Standardize example structure
~ Remove shader editor

* 📺🔫

* Update SDFGeometryGenerator.js

- Remove extends Buffergeometry
- Replace logs by error
- Added option for more res if device supports 32k texture
- Remove unused properties

* semi missing

* move surface nets to jsm/libs

* Update SDFGeometryGenerator.js

Improve code style.

* Update SDFGeometryGenerator.js

Remove `maxTexSize` property.

* Update SDFGeometryGenerator.html

Improve doc page.

* Update webgl_geometry_sdf.html

* Update to example

Remove large glsl examples
Take screenshot

* Update SDFGeometryGenerator.js

More clean up.

* Update webgl_geometry_sdf.html

Clean up.

* Update webgl_geometry_sdf.html

Use `dispose()` methods.

* Update webgl_geometry_sdf.html

Avoid using textarea.

* Update webgl_geometry_sdf.html

Improve code style.

* Update SDFGeometryGenerator.js

Fix memory leak.

* Update SDFGeometryGenerator.js

Simplify usage of `setIndex()`.

* Remove w from position buffer

---------

Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
  • Loading branch information
santi-grau and Mugen87 committed Oct 11, 2023
1 parent cb42582 commit eb073dd
Show file tree
Hide file tree
Showing 7 changed files with 632 additions and 1 deletion.
70 changes: 70 additions & 0 deletions docs/examples/en/geometries/SDFGeometryGenerator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="../../../" />
<script src="page.js"></script>
<link type="text/css" rel="stylesheet" href="page.css" />
</head>
<body>

<h1>[name]</h1>

<p class="desc">
[name] generates instances of [page:BufferGeometry] from a Signed Distance Function</br>
Uses <a href="https://www.npmjs.com/package/isosurface" target="_blank" > Mikola Lysenko's Isosurface</a>
</p>

<h2>Import</h2>

<p>
[name] is an add-on, and must be imported explicitly.
See [link:#manual/introduction/Installation Installation / Addons].
</p>

<code>
import { SDFGeometryGenerator } from 'three/addons/geometries/SDFGeometryGenerator.js';
</code>

<h2>Code Example</h2>

<code>
const generator = new SDFGeometryGenerator( renderer );
const sdf = 'float dist( vec3 p ){ return length(p) - 0.5; }' // glsl
const geometry = generator.generate( 64, sdf, 1 ); // ~> THREE.BufferGeometry
</code>

<h2>Examples</h2>

<p>[example:webgl_geometry_sdf geometry / sdf ]</p>

<h2>Constructor</h2>

<h3>[name]( [param:WebGLRenderer renderer] )</h3>

<p>
[page:WebGLRenderer renderer] -- The renderer used to render the scene. <br />
</p>

<h2>Methods</h2>

<h3>[method:BufferGeometry generate]( [param:Int resolution], [param:String distanceField], [param:Int bounds] )</h3>

<p>
<b>resolution</b> - Int [ mandatory ] Amount of 'voxels' used for triangulation. Must be power of 2. <br/>Gets heavy after 256, most machines won't be able to process over 512. Defaults to 64.
</p>
<p>
<b>distanceField</b> - String [ mandatory ] String with glsl distance function. Name of function must be 'dist', with a vec3 argument. ( see code above ). Defaults to a sphere distance.
</p>
<p>
<b>bounds</b> - Int [ optional ] Bounds in which signed distance field will be evaluated. Defaults to 1.
</p>


<h2>Source</h2>

<p>
[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/geometries/SDFGeometry.js examples/jsm/geometries/SDFGeometryGenerator.js]
</p>
</body>
</html>
3 changes: 2 additions & 1 deletion docs/list.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,8 @@
"ConvexGeometry": "examples/en/geometries/ConvexGeometry",
"DecalGeometry": "examples/en/geometries/DecalGeometry",
"ParametricGeometry": "examples/en/geometries/ParametricGeometry",
"TextGeometry": "examples/en/geometries/TextGeometry"
"TextGeometry": "examples/en/geometries/TextGeometry",
"SDFGeometryGenerator": "examples/en/geometries/SDFGeometryGenerator"
},

"Helpers": {
Expand Down
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"webgl_geometry_extrude_splines",
"webgl_geometry_minecraft",
"webgl_geometry_nurbs",
"webgl_geometry_sdf",
"webgl_geometry_shapes",
"webgl_geometry_spline_editor",
"webgl_geometry_teapot",
Expand Down
144 changes: 144 additions & 0 deletions examples/jsm/geometries/SDFGeometryGenerator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* @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 };

0 comments on commit eb073dd

Please sign in to comment.