166 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			166 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	Vector2 | ||
|  | } from 'three'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * tool for "unwrapping" and debugging three.js geometries UV mapping | ||
|  |  * | ||
|  |  * Sample usage: | ||
|  |  *	document.body.appendChild( UVsDebug( new THREE.SphereGeometry( 10, 10, 10, 10 ) ); | ||
|  |  * | ||
|  |  */ | ||
|  | 
 | ||
|  | function UVsDebug( geometry, size = 1024 ) { | ||
|  | 
 | ||
|  | 	// handles wrapping of uv.x > 1 only
 | ||
|  | 
 | ||
|  | 	const abc = 'abc'; | ||
|  | 	const a = new Vector2(); | ||
|  | 	const b = new Vector2(); | ||
|  | 
 | ||
|  | 	const uvs = [ | ||
|  | 		new Vector2(), | ||
|  | 		new Vector2(), | ||
|  | 		new Vector2() | ||
|  | 	]; | ||
|  | 
 | ||
|  | 	const face = []; | ||
|  | 
 | ||
|  | 	const canvas = document.createElement( 'canvas' ); | ||
|  | 	const width = size; // power of 2 required for wrapping
 | ||
|  | 	const height = size; | ||
|  | 	canvas.width = width; | ||
|  | 	canvas.height = height; | ||
|  | 
 | ||
|  | 	const ctx = canvas.getContext( '2d' ); | ||
|  | 	ctx.lineWidth = 1; | ||
|  | 	ctx.strokeStyle = 'rgb( 63, 63, 63 )'; | ||
|  | 	ctx.textAlign = 'center'; | ||
|  | 
 | ||
|  | 	// paint background white
 | ||
|  | 
 | ||
|  | 	ctx.fillStyle = 'rgb( 255, 255, 255 )'; | ||
|  | 	ctx.fillRect( 0, 0, width, height ); | ||
|  | 
 | ||
|  | 	const index = geometry.index; | ||
|  | 	const uvAttribute = geometry.attributes.uv; | ||
|  | 
 | ||
|  | 	if ( index ) { | ||
|  | 
 | ||
|  | 		// indexed geometry
 | ||
|  | 
 | ||
|  | 		for ( let i = 0, il = index.count; i < il; i += 3 ) { | ||
|  | 
 | ||
|  | 			face[ 0 ] = index.getX( i ); | ||
|  | 			face[ 1 ] = index.getX( i + 1 ); | ||
|  | 			face[ 2 ] = index.getX( i + 2 ); | ||
|  | 
 | ||
|  | 			uvs[ 0 ].fromBufferAttribute( uvAttribute, face[ 0 ] ); | ||
|  | 			uvs[ 1 ].fromBufferAttribute( uvAttribute, face[ 1 ] ); | ||
|  | 			uvs[ 2 ].fromBufferAttribute( uvAttribute, face[ 2 ] ); | ||
|  | 
 | ||
|  | 			processFace( face, uvs, i / 3 ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} else { | ||
|  | 
 | ||
|  | 		// non-indexed geometry
 | ||
|  | 
 | ||
|  | 		for ( let i = 0, il = uvAttribute.count; i < il; i += 3 ) { | ||
|  | 
 | ||
|  | 			face[ 0 ] = i; | ||
|  | 			face[ 1 ] = i + 1; | ||
|  | 			face[ 2 ] = i + 2; | ||
|  | 
 | ||
|  | 			uvs[ 0 ].fromBufferAttribute( uvAttribute, face[ 0 ] ); | ||
|  | 			uvs[ 1 ].fromBufferAttribute( uvAttribute, face[ 1 ] ); | ||
|  | 			uvs[ 2 ].fromBufferAttribute( uvAttribute, face[ 2 ] ); | ||
|  | 
 | ||
|  | 			processFace( face, uvs, i / 3 ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return canvas; | ||
|  | 
 | ||
|  | 	function processFace( face, uvs, index ) { | ||
|  | 
 | ||
|  | 		// draw contour of face
 | ||
|  | 
 | ||
|  | 		ctx.beginPath(); | ||
|  | 
 | ||
|  | 		a.set( 0, 0 ); | ||
|  | 
 | ||
|  | 		for ( let j = 0, jl = uvs.length; j < jl; j ++ ) { | ||
|  | 
 | ||
|  | 			const uv = uvs[ j ]; | ||
|  | 
 | ||
|  | 			a.x += uv.x; | ||
|  | 			a.y += uv.y; | ||
|  | 
 | ||
|  | 			if ( j === 0 ) { | ||
|  | 
 | ||
|  | 				ctx.moveTo( uv.x * ( width - 2 ) + 0.5, ( 1 - uv.y ) * ( height - 2 ) + 0.5 ); | ||
|  | 
 | ||
|  | 			} else { | ||
|  | 
 | ||
|  | 				ctx.lineTo( uv.x * ( width - 2 ) + 0.5, ( 1 - uv.y ) * ( height - 2 ) + 0.5 ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		ctx.closePath(); | ||
|  | 		ctx.stroke(); | ||
|  | 
 | ||
|  | 		// calculate center of face
 | ||
|  | 
 | ||
|  | 		a.divideScalar( uvs.length ); | ||
|  | 
 | ||
|  | 		// label the face number
 | ||
|  | 
 | ||
|  | 		ctx.font = '18px Arial'; | ||
|  | 		ctx.fillStyle = 'rgb( 63, 63, 63 )'; | ||
|  | 		ctx.fillText( index, a.x * width, ( 1 - a.y ) * height ); | ||
|  | 
 | ||
|  | 		if ( a.x > 0.95 ) { | ||
|  | 
 | ||
|  | 			// wrap x // 0.95 is arbitrary
 | ||
|  | 
 | ||
|  | 			ctx.fillText( index, ( a.x % 1 ) * width, ( 1 - a.y ) * height ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		//
 | ||
|  | 
 | ||
|  | 		ctx.font = '12px Arial'; | ||
|  | 		ctx.fillStyle = 'rgb( 191, 191, 191 )'; | ||
|  | 
 | ||
|  | 		// label uv edge orders
 | ||
|  | 
 | ||
|  | 		for ( let j = 0, jl = uvs.length; j < jl; j ++ ) { | ||
|  | 
 | ||
|  | 			const uv = uvs[ j ]; | ||
|  | 			b.addVectors( a, uv ).divideScalar( 2 ); | ||
|  | 
 | ||
|  | 			const vnum = face[ j ]; | ||
|  | 			ctx.fillText( abc[ j ] + vnum, b.x * width, ( 1 - b.y ) * height ); | ||
|  | 
 | ||
|  | 			if ( b.x > 0.95 ) { | ||
|  | 
 | ||
|  | 				// wrap x
 | ||
|  | 
 | ||
|  | 				ctx.fillText( abc[ j ] + vnum, ( b.x % 1 ) * width, ( 1 - b.y ) * height ); | ||
|  | 
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export { UVsDebug }; |