194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			194 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | import { | ||
|  | 	Group, | ||
|  | 	Mesh, | ||
|  | 	LineSegments, | ||
|  | 	BufferGeometry, | ||
|  | 	LineBasicMaterial, | ||
|  | 	Box3Helper, | ||
|  | 	Box3, | ||
|  | 	PlaneGeometry, | ||
|  | 	MeshBasicMaterial, | ||
|  | 	BufferAttribute, | ||
|  | 	DoubleSide | ||
|  | } from 'three'; | ||
|  | 
 | ||
|  | class CSMHelper extends Group { | ||
|  | 
 | ||
|  | 	constructor( csm ) { | ||
|  | 
 | ||
|  | 		super(); | ||
|  | 		this.csm = csm; | ||
|  | 		this.displayFrustum = true; | ||
|  | 		this.displayPlanes = true; | ||
|  | 		this.displayShadowBounds = true; | ||
|  | 
 | ||
|  | 		const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); | ||
|  | 		const positions = new Float32Array( 24 ); | ||
|  | 		const frustumGeometry = new BufferGeometry(); | ||
|  | 		frustumGeometry.setIndex( new BufferAttribute( indices, 1 ) ); | ||
|  | 		frustumGeometry.setAttribute( 'position', new BufferAttribute( positions, 3, false ) ); | ||
|  | 		const frustumLines = new LineSegments( frustumGeometry, new LineBasicMaterial() ); | ||
|  | 		this.add( frustumLines ); | ||
|  | 
 | ||
|  | 		this.frustumLines = frustumLines; | ||
|  | 		this.cascadeLines = []; | ||
|  | 		this.cascadePlanes = []; | ||
|  | 		this.shadowLines = []; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	updateVisibility() { | ||
|  | 
 | ||
|  | 		const displayFrustum = this.displayFrustum; | ||
|  | 		const displayPlanes = this.displayPlanes; | ||
|  | 		const displayShadowBounds = this.displayShadowBounds; | ||
|  | 
 | ||
|  | 		const frustumLines = this.frustumLines; | ||
|  | 		const cascadeLines = this.cascadeLines; | ||
|  | 		const cascadePlanes = this.cascadePlanes; | ||
|  | 		const shadowLines = this.shadowLines; | ||
|  | 		for ( let i = 0, l = cascadeLines.length; i < l; i ++ ) { | ||
|  | 
 | ||
|  | 			const cascadeLine = cascadeLines[ i ]; | ||
|  | 			const cascadePlane = cascadePlanes[ i ]; | ||
|  | 			const shadowLineGroup = shadowLines[ i ]; | ||
|  | 
 | ||
|  | 			cascadeLine.visible = displayFrustum; | ||
|  | 			cascadePlane.visible = displayFrustum && displayPlanes; | ||
|  | 			shadowLineGroup.visible = displayShadowBounds; | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		frustumLines.visible = displayFrustum; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	update() { | ||
|  | 
 | ||
|  | 		const csm = this.csm; | ||
|  | 		const camera = csm.camera; | ||
|  | 		const cascades = csm.cascades; | ||
|  | 		const mainFrustum = csm.mainFrustum; | ||
|  | 		const frustums = csm.frustums; | ||
|  | 		const lights = csm.lights; | ||
|  | 
 | ||
|  | 		const frustumLines = this.frustumLines; | ||
|  | 		const frustumLinePositions = frustumLines.geometry.getAttribute( 'position' ); | ||
|  | 		const cascadeLines = this.cascadeLines; | ||
|  | 		const cascadePlanes = this.cascadePlanes; | ||
|  | 		const shadowLines = this.shadowLines; | ||
|  | 
 | ||
|  | 		this.position.copy( camera.position ); | ||
|  | 		this.quaternion.copy( camera.quaternion ); | ||
|  | 		this.scale.copy( camera.scale ); | ||
|  | 		this.updateMatrixWorld( true ); | ||
|  | 
 | ||
|  | 		while ( cascadeLines.length > cascades ) { | ||
|  | 
 | ||
|  | 			this.remove( cascadeLines.pop() ); | ||
|  | 			this.remove( cascadePlanes.pop() ); | ||
|  | 			this.remove( shadowLines.pop() ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		while ( cascadeLines.length < cascades ) { | ||
|  | 
 | ||
|  | 			const cascadeLine = new Box3Helper( new Box3(), 0xffffff ); | ||
|  | 			const planeMat = new MeshBasicMaterial( { transparent: true, opacity: 0.1, depthWrite: false, side: DoubleSide } ); | ||
|  | 			const cascadePlane = new Mesh( new PlaneGeometry(), planeMat ); | ||
|  | 			const shadowLineGroup = new Group(); | ||
|  | 			const shadowLine = new Box3Helper( new Box3(), 0xffff00 ); | ||
|  | 			shadowLineGroup.add( shadowLine ); | ||
|  | 
 | ||
|  | 			this.add( cascadeLine ); | ||
|  | 			this.add( cascadePlane ); | ||
|  | 			this.add( shadowLineGroup ); | ||
|  | 
 | ||
|  | 			cascadeLines.push( cascadeLine ); | ||
|  | 			cascadePlanes.push( cascadePlane ); | ||
|  | 			shadowLines.push( shadowLineGroup ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		for ( let i = 0; i < cascades; i ++ ) { | ||
|  | 
 | ||
|  | 			const frustum = frustums[ i ]; | ||
|  | 			const light = lights[ i ]; | ||
|  | 			const shadowCam = light.shadow.camera; | ||
|  | 			const farVerts = frustum.vertices.far; | ||
|  | 
 | ||
|  | 			const cascadeLine = cascadeLines[ i ]; | ||
|  | 			const cascadePlane = cascadePlanes[ i ]; | ||
|  | 			const shadowLineGroup = shadowLines[ i ]; | ||
|  | 			const shadowLine = shadowLineGroup.children[ 0 ]; | ||
|  | 
 | ||
|  | 			cascadeLine.box.min.copy( farVerts[ 2 ] ); | ||
|  | 			cascadeLine.box.max.copy( farVerts[ 0 ] ); | ||
|  | 			cascadeLine.box.max.z += 1e-4; | ||
|  | 
 | ||
|  | 			cascadePlane.position.addVectors( farVerts[ 0 ], farVerts[ 2 ] ); | ||
|  | 			cascadePlane.position.multiplyScalar( 0.5 ); | ||
|  | 			cascadePlane.scale.subVectors( farVerts[ 0 ], farVerts[ 2 ] ); | ||
|  | 			cascadePlane.scale.z = 1e-4; | ||
|  | 
 | ||
|  | 			this.remove( shadowLineGroup ); | ||
|  | 			shadowLineGroup.position.copy( shadowCam.position ); | ||
|  | 			shadowLineGroup.quaternion.copy( shadowCam.quaternion ); | ||
|  | 			shadowLineGroup.scale.copy( shadowCam.scale ); | ||
|  | 			shadowLineGroup.updateMatrixWorld( true ); | ||
|  | 			this.attach( shadowLineGroup ); | ||
|  | 
 | ||
|  | 			shadowLine.box.min.set( shadowCam.bottom, shadowCam.left, - shadowCam.far ); | ||
|  | 			shadowLine.box.max.set( shadowCam.top, shadowCam.right, - shadowCam.near ); | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 		const nearVerts = mainFrustum.vertices.near; | ||
|  | 		const farVerts = mainFrustum.vertices.far; | ||
|  | 		frustumLinePositions.setXYZ( 0, farVerts[ 0 ].x, farVerts[ 0 ].y, farVerts[ 0 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 1, farVerts[ 3 ].x, farVerts[ 3 ].y, farVerts[ 3 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 2, farVerts[ 2 ].x, farVerts[ 2 ].y, farVerts[ 2 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 3, farVerts[ 1 ].x, farVerts[ 1 ].y, farVerts[ 1 ].z ); | ||
|  | 
 | ||
|  | 		frustumLinePositions.setXYZ( 4, nearVerts[ 0 ].x, nearVerts[ 0 ].y, nearVerts[ 0 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 5, nearVerts[ 3 ].x, nearVerts[ 3 ].y, nearVerts[ 3 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 6, nearVerts[ 2 ].x, nearVerts[ 2 ].y, nearVerts[ 2 ].z ); | ||
|  | 		frustumLinePositions.setXYZ( 7, nearVerts[ 1 ].x, nearVerts[ 1 ].y, nearVerts[ 1 ].z ); | ||
|  | 		frustumLinePositions.needsUpdate = true; | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | 	dispose() { | ||
|  | 
 | ||
|  | 		const frustumLines = this.frustumLines; | ||
|  | 		const cascadeLines = this.cascadeLines; | ||
|  | 		const cascadePlanes = this.cascadePlanes; | ||
|  | 		const shadowLines = this.shadowLines; | ||
|  | 
 | ||
|  | 		frustumLines.geometry.dispose(); | ||
|  | 		frustumLines.material.dispose(); | ||
|  | 
 | ||
|  | 		const cascades = this.csm.cascades; | ||
|  | 
 | ||
|  | 		for ( let i = 0; i < cascades; i ++ ) { | ||
|  | 
 | ||
|  | 			const cascadeLine = cascadeLines[ i ]; | ||
|  | 			const cascadePlane = cascadePlanes[ i ]; | ||
|  | 			const shadowLineGroup = shadowLines[ i ]; | ||
|  | 			const shadowLine = shadowLineGroup.children[ 0 ]; | ||
|  | 
 | ||
|  | 			cascadeLine.dispose(); // Box3Helper
 | ||
|  | 
 | ||
|  | 			cascadePlane.geometry.dispose(); | ||
|  | 			cascadePlane.material.dispose(); | ||
|  | 
 | ||
|  | 			shadowLine.dispose(); // Box3Helper
 | ||
|  | 
 | ||
|  | 		} | ||
|  | 
 | ||
|  | 	} | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | export { CSMHelper }; |