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