140 lines
2.4 KiB
JavaScript
140 lines
2.4 KiB
JavaScript
|
/**
|
||
|
* Parametric Surfaces Geometry
|
||
|
* based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
|
||
|
*/
|
||
|
|
||
|
import {
|
||
|
BufferGeometry,
|
||
|
Float32BufferAttribute,
|
||
|
Vector3
|
||
|
} from 'three';
|
||
|
|
||
|
class ParametricGeometry extends BufferGeometry {
|
||
|
|
||
|
constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
|
||
|
|
||
|
super();
|
||
|
|
||
|
this.type = 'ParametricGeometry';
|
||
|
|
||
|
this.parameters = {
|
||
|
func: func,
|
||
|
slices: slices,
|
||
|
stacks: stacks
|
||
|
};
|
||
|
|
||
|
// buffers
|
||
|
|
||
|
const indices = [];
|
||
|
const vertices = [];
|
||
|
const normals = [];
|
||
|
const uvs = [];
|
||
|
|
||
|
const EPS = 0.00001;
|
||
|
|
||
|
const normal = new Vector3();
|
||
|
|
||
|
const p0 = new Vector3(), p1 = new Vector3();
|
||
|
const pu = new Vector3(), pv = new Vector3();
|
||
|
|
||
|
// generate vertices, normals and uvs
|
||
|
|
||
|
const sliceCount = slices + 1;
|
||
|
|
||
|
for ( let i = 0; i <= stacks; i ++ ) {
|
||
|
|
||
|
const v = i / stacks;
|
||
|
|
||
|
for ( let j = 0; j <= slices; j ++ ) {
|
||
|
|
||
|
const u = j / slices;
|
||
|
|
||
|
// vertex
|
||
|
|
||
|
func( u, v, p0 );
|
||
|
vertices.push( p0.x, p0.y, p0.z );
|
||
|
|
||
|
// normal
|
||
|
|
||
|
// approximate tangent vectors via finite differences
|
||
|
|
||
|
if ( u - EPS >= 0 ) {
|
||
|
|
||
|
func( u - EPS, v, p1 );
|
||
|
pu.subVectors( p0, p1 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
func( u + EPS, v, p1 );
|
||
|
pu.subVectors( p1, p0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( v - EPS >= 0 ) {
|
||
|
|
||
|
func( u, v - EPS, p1 );
|
||
|
pv.subVectors( p0, p1 );
|
||
|
|
||
|
} else {
|
||
|
|
||
|
func( u, v + EPS, p1 );
|
||
|
pv.subVectors( p1, p0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
// cross product of tangent vectors returns surface normal
|
||
|
|
||
|
normal.crossVectors( pu, pv ).normalize();
|
||
|
normals.push( normal.x, normal.y, normal.z );
|
||
|
|
||
|
// uv
|
||
|
|
||
|
uvs.push( u, v );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// generate indices
|
||
|
|
||
|
for ( let i = 0; i < stacks; i ++ ) {
|
||
|
|
||
|
for ( let j = 0; j < slices; j ++ ) {
|
||
|
|
||
|
const a = i * sliceCount + j;
|
||
|
const b = i * sliceCount + j + 1;
|
||
|
const c = ( i + 1 ) * sliceCount + j + 1;
|
||
|
const d = ( i + 1 ) * sliceCount + j;
|
||
|
|
||
|
// faces one and two
|
||
|
|
||
|
indices.push( a, b, d );
|
||
|
indices.push( b, c, d );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// build geometry
|
||
|
|
||
|
this.setIndex( indices );
|
||
|
this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||
|
this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
|
||
|
this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
copy( source ) {
|
||
|
|
||
|
super.copy( source );
|
||
|
|
||
|
this.parameters = Object.assign( {}, source.parameters );
|
||
|
|
||
|
return this;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export { ParametricGeometry };
|