修改静态资源文件夹位置
This commit is contained in:
		
							
								
								
									
										215
									
								
								public/sdk/three/jsm/renderers/CSS2DRenderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								public/sdk/three/jsm/renderers/CSS2DRenderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| import { | ||||
| 	Matrix4, | ||||
| 	Object3D, | ||||
| 	Vector2, | ||||
| 	Vector3 | ||||
| } from 'three'; | ||||
|  | ||||
| class CSS2DObject extends Object3D { | ||||
|  | ||||
| 	constructor( element = document.createElement( 'div' ) ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.isCSS2DObject = true; | ||||
|  | ||||
| 		this.element = element; | ||||
|  | ||||
| 		this.element.style.position = 'absolute'; | ||||
| 		this.element.style.userSelect = 'none'; | ||||
|  | ||||
| 		this.element.setAttribute( 'draggable', false ); | ||||
|  | ||||
| 		this.center = new Vector2( 0.5, 0.5 ); // ( 0, 0 ) is the lower left; ( 1, 1 ) is the top right | ||||
|  | ||||
| 		this.addEventListener( 'removed', function () { | ||||
|  | ||||
| 			this.traverse( function ( object ) { | ||||
|  | ||||
| 				if ( object.element instanceof Element && object.element.parentNode !== null ) { | ||||
|  | ||||
| 					object.element.parentNode.removeChild( object.element ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copy( source, recursive ) { | ||||
|  | ||||
| 		super.copy( source, recursive ); | ||||
|  | ||||
| 		this.element = source.element.cloneNode( true ); | ||||
|  | ||||
| 		this.center = source.center; | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| const _vector = new Vector3(); | ||||
| const _viewMatrix = new Matrix4(); | ||||
| const _viewProjectionMatrix = new Matrix4(); | ||||
| const _a = new Vector3(); | ||||
| const _b = new Vector3(); | ||||
|  | ||||
| class CSS2DRenderer { | ||||
|  | ||||
| 	constructor( parameters = {} ) { | ||||
|  | ||||
| 		const _this = this; | ||||
|  | ||||
| 		let _width, _height; | ||||
| 		let _widthHalf, _heightHalf; | ||||
|  | ||||
| 		const cache = { | ||||
| 			objects: new WeakMap() | ||||
| 		}; | ||||
|  | ||||
| 		const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' ); | ||||
|  | ||||
| 		domElement.style.overflow = 'hidden'; | ||||
|  | ||||
| 		this.domElement = domElement; | ||||
|  | ||||
| 		this.getSize = function () { | ||||
|  | ||||
| 			return { | ||||
| 				width: _width, | ||||
| 				height: _height | ||||
| 			}; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.render = function ( scene, camera ) { | ||||
|  | ||||
| 			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); | ||||
| 			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); | ||||
|  | ||||
| 			_viewMatrix.copy( camera.matrixWorldInverse ); | ||||
| 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); | ||||
|  | ||||
| 			renderObject( scene, scene, camera ); | ||||
| 			zOrder( scene ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setSize = function ( width, height ) { | ||||
|  | ||||
| 			_width = width; | ||||
| 			_height = height; | ||||
|  | ||||
| 			_widthHalf = _width / 2; | ||||
| 			_heightHalf = _height / 2; | ||||
|  | ||||
| 			domElement.style.width = width + 'px'; | ||||
| 			domElement.style.height = height + 'px'; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		function renderObject( object, scene, camera ) { | ||||
|  | ||||
| 			if ( object.isCSS2DObject ) { | ||||
|  | ||||
| 				_vector.setFromMatrixPosition( object.matrixWorld ); | ||||
| 				_vector.applyMatrix4( _viewProjectionMatrix ); | ||||
|  | ||||
| 				const visible = ( object.visible === true ) && ( _vector.z >= - 1 && _vector.z <= 1 ) && ( object.layers.test( camera.layers ) === true ); | ||||
| 				object.element.style.display = ( visible === true ) ? '' : 'none'; | ||||
|  | ||||
| 				if ( visible === true ) { | ||||
|  | ||||
| 					object.onBeforeRender( _this, scene, camera ); | ||||
|  | ||||
| 					const element = object.element; | ||||
|  | ||||
| 					element.style.transform = 'translate(' + ( - 100 * object.center.x ) + '%,' + ( - 100 * object.center.y ) + '%)' + 'translate(' + ( _vector.x * _widthHalf + _widthHalf ) + 'px,' + ( - _vector.y * _heightHalf + _heightHalf ) + 'px)'; | ||||
|  | ||||
| 					if ( element.parentNode !== domElement ) { | ||||
|  | ||||
| 						domElement.appendChild( element ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					object.onAfterRender( _this, scene, camera ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				const objectData = { | ||||
| 					distanceToCameraSquared: getDistanceToSquared( camera, object ) | ||||
| 				}; | ||||
|  | ||||
| 				cache.objects.set( object, objectData ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			for ( let i = 0, l = object.children.length; i < l; i ++ ) { | ||||
|  | ||||
| 				renderObject( object.children[ i ], scene, camera ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getDistanceToSquared( object1, object2 ) { | ||||
|  | ||||
| 			_a.setFromMatrixPosition( object1.matrixWorld ); | ||||
| 			_b.setFromMatrixPosition( object2.matrixWorld ); | ||||
|  | ||||
| 			return _a.distanceToSquared( _b ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function filterAndFlatten( scene ) { | ||||
|  | ||||
| 			const result = []; | ||||
|  | ||||
| 			scene.traverse( function ( object ) { | ||||
|  | ||||
| 				if ( object.isCSS2DObject ) result.push( object ); | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 			return result; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function zOrder( scene ) { | ||||
|  | ||||
| 			const sorted = filterAndFlatten( scene ).sort( function ( a, b ) { | ||||
|  | ||||
| 				if ( a.renderOrder !== b.renderOrder ) { | ||||
|  | ||||
| 					return b.renderOrder - a.renderOrder; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				const distanceA = cache.objects.get( a ).distanceToCameraSquared; | ||||
| 				const distanceB = cache.objects.get( b ).distanceToCameraSquared; | ||||
|  | ||||
| 				return distanceA - distanceB; | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 			const zMax = sorted.length; | ||||
|  | ||||
| 			for ( let i = 0, l = sorted.length; i < l; i ++ ) { | ||||
|  | ||||
| 				sorted[ i ].element.style.zIndex = zMax - i; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { CSS2DObject, CSS2DRenderer }; | ||||
							
								
								
									
										329
									
								
								public/sdk/three/jsm/renderers/CSS3DRenderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								public/sdk/three/jsm/renderers/CSS3DRenderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,329 @@ | ||||
| import { | ||||
| 	Matrix4, | ||||
| 	Object3D, | ||||
| 	Quaternion, | ||||
| 	Vector3 | ||||
| } from 'three'; | ||||
|  | ||||
| /** | ||||
|  * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs | ||||
|  */ | ||||
|  | ||||
| const _position = new Vector3(); | ||||
| const _quaternion = new Quaternion(); | ||||
| const _scale = new Vector3(); | ||||
|  | ||||
| class CSS3DObject extends Object3D { | ||||
|  | ||||
| 	constructor( element = document.createElement( 'div' ) ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.isCSS3DObject = true; | ||||
|  | ||||
| 		this.element = element; | ||||
| 		this.element.style.position = 'absolute'; | ||||
| 		this.element.style.pointerEvents = 'auto'; | ||||
| 		this.element.style.userSelect = 'none'; | ||||
|  | ||||
| 		this.element.setAttribute( 'draggable', false ); | ||||
|  | ||||
| 		this.addEventListener( 'removed', function () { | ||||
|  | ||||
| 			this.traverse( function ( object ) { | ||||
|  | ||||
| 				if ( object.element instanceof Element && object.element.parentNode !== null ) { | ||||
|  | ||||
| 					object.element.parentNode.removeChild( object.element ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copy( source, recursive ) { | ||||
|  | ||||
| 		super.copy( source, recursive ); | ||||
|  | ||||
| 		this.element = source.element.cloneNode( true ); | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class CSS3DSprite extends CSS3DObject { | ||||
|  | ||||
| 	constructor( element ) { | ||||
|  | ||||
| 		super( element ); | ||||
|  | ||||
| 		this.isCSS3DSprite = true; | ||||
|  | ||||
| 		this.rotation2D = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copy( source, recursive ) { | ||||
|  | ||||
| 		super.copy( source, recursive ); | ||||
|  | ||||
| 		this.rotation2D = source.rotation2D; | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| const _matrix = new Matrix4(); | ||||
| const _matrix2 = new Matrix4(); | ||||
|  | ||||
| class CSS3DRenderer { | ||||
|  | ||||
| 	constructor( parameters = {} ) { | ||||
|  | ||||
| 		const _this = this; | ||||
|  | ||||
| 		let _width, _height; | ||||
| 		let _widthHalf, _heightHalf; | ||||
|  | ||||
| 		const cache = { | ||||
| 			camera: { style: '' }, | ||||
| 			objects: new WeakMap() | ||||
| 		}; | ||||
|  | ||||
| 		const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' ); | ||||
|  | ||||
| 		domElement.style.overflow = 'hidden'; | ||||
|  | ||||
| 		this.domElement = domElement; | ||||
|  | ||||
| 		const viewElement = document.createElement( 'div' ); | ||||
| 		viewElement.style.transformOrigin = '0 0'; | ||||
| 		viewElement.style.pointerEvents = 'none'; | ||||
| 		domElement.appendChild( viewElement ); | ||||
|  | ||||
| 		const cameraElement = document.createElement( 'div' ); | ||||
|  | ||||
| 		cameraElement.style.transformStyle = 'preserve-3d'; | ||||
|  | ||||
| 		viewElement.appendChild( cameraElement ); | ||||
|  | ||||
| 		this.getSize = function () { | ||||
|  | ||||
| 			return { | ||||
| 				width: _width, | ||||
| 				height: _height | ||||
| 			}; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.render = function ( scene, camera ) { | ||||
|  | ||||
| 			const fov = camera.projectionMatrix.elements[ 5 ] * _heightHalf; | ||||
|  | ||||
| 			if ( camera.view && camera.view.enabled ) { | ||||
|  | ||||
| 				// view offset | ||||
| 				viewElement.style.transform = `translate( ${ - camera.view.offsetX * ( _width / camera.view.width ) }px, ${ - camera.view.offsetY * ( _height / camera.view.height ) }px )`; | ||||
|  | ||||
| 				// view fullWidth and fullHeight, view width and height | ||||
| 				viewElement.style.transform += `scale( ${ camera.view.fullWidth / camera.view.width }, ${ camera.view.fullHeight / camera.view.height } )`; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				viewElement.style.transform = ''; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); | ||||
| 			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); | ||||
|  | ||||
| 			let tx, ty; | ||||
|  | ||||
| 			if ( camera.isOrthographicCamera ) { | ||||
|  | ||||
| 				tx = - ( camera.right + camera.left ) / 2; | ||||
| 				ty = ( camera.top + camera.bottom ) / 2; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const scaleByViewOffset = camera.view && camera.view.enabled ? camera.view.height / camera.view.fullHeight : 1; | ||||
| 			const cameraCSSMatrix = camera.isOrthographicCamera ? | ||||
| 				`scale( ${ scaleByViewOffset } )` + 'scale(' + fov + ')' + 'translate(' + epsilon( tx ) + 'px,' + epsilon( ty ) + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ) : | ||||
| 				`scale( ${ scaleByViewOffset } )` + 'translateZ(' + fov + 'px)' + getCameraCSSMatrix( camera.matrixWorldInverse ); | ||||
| 			const perspective = camera.isPerspectiveCamera ? 'perspective(' + fov + 'px) ' : ''; | ||||
|  | ||||
| 			const style = perspective + cameraCSSMatrix + | ||||
| 				'translate(' + _widthHalf + 'px,' + _heightHalf + 'px)'; | ||||
|  | ||||
| 			if ( cache.camera.style !== style ) { | ||||
|  | ||||
| 				cameraElement.style.transform = style; | ||||
|  | ||||
| 				cache.camera.style = style; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			renderObject( scene, scene, camera, cameraCSSMatrix ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setSize = function ( width, height ) { | ||||
|  | ||||
| 			_width = width; | ||||
| 			_height = height; | ||||
| 			_widthHalf = _width / 2; | ||||
| 			_heightHalf = _height / 2; | ||||
|  | ||||
| 			domElement.style.width = width + 'px'; | ||||
| 			domElement.style.height = height + 'px'; | ||||
|  | ||||
| 			viewElement.style.width = width + 'px'; | ||||
| 			viewElement.style.height = height + 'px'; | ||||
|  | ||||
| 			cameraElement.style.width = width + 'px'; | ||||
| 			cameraElement.style.height = height + 'px'; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		function epsilon( value ) { | ||||
|  | ||||
| 			return Math.abs( value ) < 1e-10 ? 0 : value; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getCameraCSSMatrix( matrix ) { | ||||
|  | ||||
| 			const elements = matrix.elements; | ||||
|  | ||||
| 			return 'matrix3d(' + | ||||
| 				epsilon( elements[ 0 ] ) + ',' + | ||||
| 				epsilon( - elements[ 1 ] ) + ',' + | ||||
| 				epsilon( elements[ 2 ] ) + ',' + | ||||
| 				epsilon( elements[ 3 ] ) + ',' + | ||||
| 				epsilon( elements[ 4 ] ) + ',' + | ||||
| 				epsilon( - elements[ 5 ] ) + ',' + | ||||
| 				epsilon( elements[ 6 ] ) + ',' + | ||||
| 				epsilon( elements[ 7 ] ) + ',' + | ||||
| 				epsilon( elements[ 8 ] ) + ',' + | ||||
| 				epsilon( - elements[ 9 ] ) + ',' + | ||||
| 				epsilon( elements[ 10 ] ) + ',' + | ||||
| 				epsilon( elements[ 11 ] ) + ',' + | ||||
| 				epsilon( elements[ 12 ] ) + ',' + | ||||
| 				epsilon( - elements[ 13 ] ) + ',' + | ||||
| 				epsilon( elements[ 14 ] ) + ',' + | ||||
| 				epsilon( elements[ 15 ] ) + | ||||
| 			')'; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getObjectCSSMatrix( matrix ) { | ||||
|  | ||||
| 			const elements = matrix.elements; | ||||
| 			const matrix3d = 'matrix3d(' + | ||||
| 				epsilon( elements[ 0 ] ) + ',' + | ||||
| 				epsilon( elements[ 1 ] ) + ',' + | ||||
| 				epsilon( elements[ 2 ] ) + ',' + | ||||
| 				epsilon( elements[ 3 ] ) + ',' + | ||||
| 				epsilon( - elements[ 4 ] ) + ',' + | ||||
| 				epsilon( - elements[ 5 ] ) + ',' + | ||||
| 				epsilon( - elements[ 6 ] ) + ',' + | ||||
| 				epsilon( - elements[ 7 ] ) + ',' + | ||||
| 				epsilon( elements[ 8 ] ) + ',' + | ||||
| 				epsilon( elements[ 9 ] ) + ',' + | ||||
| 				epsilon( elements[ 10 ] ) + ',' + | ||||
| 				epsilon( elements[ 11 ] ) + ',' + | ||||
| 				epsilon( elements[ 12 ] ) + ',' + | ||||
| 				epsilon( elements[ 13 ] ) + ',' + | ||||
| 				epsilon( elements[ 14 ] ) + ',' + | ||||
| 				epsilon( elements[ 15 ] ) + | ||||
| 			')'; | ||||
|  | ||||
| 			return 'translate(-50%,-50%)' + matrix3d; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function renderObject( object, scene, camera, cameraCSSMatrix ) { | ||||
|  | ||||
| 			if ( object.isCSS3DObject ) { | ||||
|  | ||||
| 				const visible = ( object.visible === true ) && ( object.layers.test( camera.layers ) === true ); | ||||
| 				object.element.style.display = ( visible === true ) ? '' : 'none'; | ||||
|  | ||||
| 				if ( visible === true ) { | ||||
|  | ||||
| 					object.onBeforeRender( _this, scene, camera ); | ||||
|  | ||||
| 					let style; | ||||
|  | ||||
| 					if ( object.isCSS3DSprite ) { | ||||
|  | ||||
| 						// http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/ | ||||
|  | ||||
| 						_matrix.copy( camera.matrixWorldInverse ); | ||||
| 						_matrix.transpose(); | ||||
|  | ||||
| 						if ( object.rotation2D !== 0 ) _matrix.multiply( _matrix2.makeRotationZ( object.rotation2D ) ); | ||||
|  | ||||
| 						object.matrixWorld.decompose( _position, _quaternion, _scale ); | ||||
| 						_matrix.setPosition( _position ); | ||||
| 						_matrix.scale( _scale ); | ||||
|  | ||||
| 						_matrix.elements[ 3 ] = 0; | ||||
| 						_matrix.elements[ 7 ] = 0; | ||||
| 						_matrix.elements[ 11 ] = 0; | ||||
| 						_matrix.elements[ 15 ] = 1; | ||||
|  | ||||
| 						style = getObjectCSSMatrix( _matrix ); | ||||
|  | ||||
| 					} else { | ||||
|  | ||||
| 						style = getObjectCSSMatrix( object.matrixWorld ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					const element = object.element; | ||||
| 					const cachedObject = cache.objects.get( object ); | ||||
|  | ||||
| 					if ( cachedObject === undefined || cachedObject.style !== style ) { | ||||
|  | ||||
| 						element.style.transform = style; | ||||
|  | ||||
| 						const objectData = { style: style }; | ||||
| 						cache.objects.set( object, objectData ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( element.parentNode !== cameraElement ) { | ||||
|  | ||||
| 						cameraElement.appendChild( element ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					object.onAfterRender( _this, scene, camera ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			for ( let i = 0, l = object.children.length; i < l; i ++ ) { | ||||
|  | ||||
| 				renderObject( object.children[ i ], scene, camera, cameraCSSMatrix ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { CSS3DObject, CSS3DSprite, CSS3DRenderer }; | ||||
							
								
								
									
										918
									
								
								public/sdk/three/jsm/renderers/Projector.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										918
									
								
								public/sdk/three/jsm/renderers/Projector.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,918 @@ | ||||
| import { | ||||
| 	Box3, | ||||
| 	Color, | ||||
| 	DoubleSide, | ||||
| 	Frustum, | ||||
| 	Matrix3, | ||||
| 	Matrix4, | ||||
| 	Vector2, | ||||
| 	Vector3, | ||||
| 	Vector4 | ||||
| } from 'three'; | ||||
|  | ||||
| class RenderableObject { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.id = 0; | ||||
|  | ||||
| 		this.object = null; | ||||
| 		this.z = 0; | ||||
| 		this.renderOrder = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| class RenderableFace { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.id = 0; | ||||
|  | ||||
| 		this.v1 = new RenderableVertex(); | ||||
| 		this.v2 = new RenderableVertex(); | ||||
| 		this.v3 = new RenderableVertex(); | ||||
|  | ||||
| 		this.normalModel = new Vector3(); | ||||
|  | ||||
| 		this.vertexNormalsModel = [ new Vector3(), new Vector3(), new Vector3() ]; | ||||
| 		this.vertexNormalsLength = 0; | ||||
|  | ||||
| 		this.color = new Color(); | ||||
| 		this.material = null; | ||||
| 		this.uvs = [ new Vector2(), new Vector2(), new Vector2() ]; | ||||
|  | ||||
| 		this.z = 0; | ||||
| 		this.renderOrder = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| class RenderableVertex { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.position = new Vector3(); | ||||
| 		this.positionWorld = new Vector3(); | ||||
| 		this.positionScreen = new Vector4(); | ||||
|  | ||||
| 		this.visible = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copy( vertex ) { | ||||
|  | ||||
| 		this.positionWorld.copy( vertex.positionWorld ); | ||||
| 		this.positionScreen.copy( vertex.positionScreen ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| class RenderableLine { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.id = 0; | ||||
|  | ||||
| 		this.v1 = new RenderableVertex(); | ||||
| 		this.v2 = new RenderableVertex(); | ||||
|  | ||||
| 		this.vertexColors = [ new Color(), new Color() ]; | ||||
| 		this.material = null; | ||||
|  | ||||
| 		this.z = 0; | ||||
| 		this.renderOrder = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| class RenderableSprite { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.id = 0; | ||||
|  | ||||
| 		this.object = null; | ||||
|  | ||||
| 		this.x = 0; | ||||
| 		this.y = 0; | ||||
| 		this.z = 0; | ||||
|  | ||||
| 		this.rotation = 0; | ||||
| 		this.scale = new Vector2(); | ||||
|  | ||||
| 		this.material = null; | ||||
| 		this.renderOrder = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| // | ||||
|  | ||||
| class Projector { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		let _object, _objectCount, _objectPoolLength = 0, | ||||
| 			_vertex, _vertexCount, _vertexPoolLength = 0, | ||||
| 			_face, _faceCount, _facePoolLength = 0, | ||||
| 			_line, _lineCount, _linePoolLength = 0, | ||||
| 			_sprite, _spriteCount, _spritePoolLength = 0, | ||||
| 			_modelMatrix; | ||||
|  | ||||
| 		const | ||||
|  | ||||
| 			_renderData = { objects: [], lights: [], elements: [] }, | ||||
|  | ||||
| 			_vector3 = new Vector3(), | ||||
| 			_vector4 = new Vector4(), | ||||
|  | ||||
| 			_clipBox = new Box3( new Vector3( - 1, - 1, - 1 ), new Vector3( 1, 1, 1 ) ), | ||||
| 			_boundingBox = new Box3(), | ||||
| 			_points3 = new Array( 3 ), | ||||
|  | ||||
| 			_viewMatrix = new Matrix4(), | ||||
| 			_viewProjectionMatrix = new Matrix4(), | ||||
|  | ||||
| 			_modelViewProjectionMatrix = new Matrix4(), | ||||
|  | ||||
| 			_frustum = new Frustum(), | ||||
|  | ||||
| 			_objectPool = [], _vertexPool = [], _facePool = [], _linePool = [], _spritePool = []; | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		function RenderList() { | ||||
|  | ||||
| 			const normals = []; | ||||
| 			const colors = []; | ||||
| 			const uvs = []; | ||||
|  | ||||
| 			let object = null; | ||||
|  | ||||
| 			const normalMatrix = new Matrix3(); | ||||
|  | ||||
| 			function setObject( value ) { | ||||
|  | ||||
| 				object = value; | ||||
|  | ||||
| 				normalMatrix.getNormalMatrix( object.matrixWorld ); | ||||
|  | ||||
| 				normals.length = 0; | ||||
| 				colors.length = 0; | ||||
| 				uvs.length = 0; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function projectVertex( vertex ) { | ||||
|  | ||||
| 				const position = vertex.position; | ||||
| 				const positionWorld = vertex.positionWorld; | ||||
| 				const positionScreen = vertex.positionScreen; | ||||
|  | ||||
| 				positionWorld.copy( position ).applyMatrix4( _modelMatrix ); | ||||
| 				positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); | ||||
|  | ||||
| 				const invW = 1 / positionScreen.w; | ||||
|  | ||||
| 				positionScreen.x *= invW; | ||||
| 				positionScreen.y *= invW; | ||||
| 				positionScreen.z *= invW; | ||||
|  | ||||
| 				vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && | ||||
| 						 positionScreen.y >= - 1 && positionScreen.y <= 1 && | ||||
| 						 positionScreen.z >= - 1 && positionScreen.z <= 1; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushVertex( x, y, z ) { | ||||
|  | ||||
| 				_vertex = getNextVertexInPool(); | ||||
| 				_vertex.position.set( x, y, z ); | ||||
|  | ||||
| 				projectVertex( _vertex ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushNormal( x, y, z ) { | ||||
|  | ||||
| 				normals.push( x, y, z ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushColor( r, g, b ) { | ||||
|  | ||||
| 				colors.push( r, g, b ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushUv( x, y ) { | ||||
|  | ||||
| 				uvs.push( x, y ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function checkTriangleVisibility( v1, v2, v3 ) { | ||||
|  | ||||
| 				if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; | ||||
|  | ||||
| 				_points3[ 0 ] = v1.positionScreen; | ||||
| 				_points3[ 1 ] = v2.positionScreen; | ||||
| 				_points3[ 2 ] = v3.positionScreen; | ||||
|  | ||||
| 				return _clipBox.intersectsBox( _boundingBox.setFromPoints( _points3 ) ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function checkBackfaceCulling( v1, v2, v3 ) { | ||||
|  | ||||
| 				return ( ( v3.positionScreen.x - v1.positionScreen.x ) * | ||||
| 					    ( v2.positionScreen.y - v1.positionScreen.y ) - | ||||
| 					    ( v3.positionScreen.y - v1.positionScreen.y ) * | ||||
| 					    ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushLine( a, b ) { | ||||
|  | ||||
| 				const v1 = _vertexPool[ a ]; | ||||
| 				const v2 = _vertexPool[ b ]; | ||||
|  | ||||
| 				// Clip | ||||
|  | ||||
| 				v1.positionScreen.copy( v1.position ).applyMatrix4( _modelViewProjectionMatrix ); | ||||
| 				v2.positionScreen.copy( v2.position ).applyMatrix4( _modelViewProjectionMatrix ); | ||||
|  | ||||
| 				if ( clipLine( v1.positionScreen, v2.positionScreen ) === true ) { | ||||
|  | ||||
| 					// Perform the perspective divide | ||||
| 					v1.positionScreen.multiplyScalar( 1 / v1.positionScreen.w ); | ||||
| 					v2.positionScreen.multiplyScalar( 1 / v2.positionScreen.w ); | ||||
|  | ||||
| 					_line = getNextLineInPool(); | ||||
| 					_line.id = object.id; | ||||
| 					_line.v1.copy( v1 ); | ||||
| 					_line.v2.copy( v2 ); | ||||
| 					_line.z = Math.max( v1.positionScreen.z, v2.positionScreen.z ); | ||||
| 					_line.renderOrder = object.renderOrder; | ||||
|  | ||||
| 					_line.material = object.material; | ||||
|  | ||||
| 					if ( object.material.vertexColors ) { | ||||
|  | ||||
| 						_line.vertexColors[ 0 ].fromArray( colors, a * 3 ); | ||||
| 						_line.vertexColors[ 1 ].fromArray( colors, b * 3 ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					_renderData.elements.push( _line ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			function pushTriangle( a, b, c, material ) { | ||||
|  | ||||
| 				const v1 = _vertexPool[ a ]; | ||||
| 				const v2 = _vertexPool[ b ]; | ||||
| 				const v3 = _vertexPool[ c ]; | ||||
|  | ||||
| 				if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; | ||||
|  | ||||
| 				if ( material.side === DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { | ||||
|  | ||||
| 					_face = getNextFaceInPool(); | ||||
|  | ||||
| 					_face.id = object.id; | ||||
| 					_face.v1.copy( v1 ); | ||||
| 					_face.v2.copy( v2 ); | ||||
| 					_face.v3.copy( v3 ); | ||||
| 					_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; | ||||
| 					_face.renderOrder = object.renderOrder; | ||||
|  | ||||
| 					// face normal | ||||
| 					_vector3.subVectors( v3.position, v2.position ); | ||||
| 					_vector4.subVectors( v1.position, v2.position ); | ||||
| 					_vector3.cross( _vector4 ); | ||||
| 					_face.normalModel.copy( _vector3 ); | ||||
| 					_face.normalModel.applyMatrix3( normalMatrix ).normalize(); | ||||
|  | ||||
| 					for ( let i = 0; i < 3; i ++ ) { | ||||
|  | ||||
| 						const normal = _face.vertexNormalsModel[ i ]; | ||||
| 						normal.fromArray( normals, arguments[ i ] * 3 ); | ||||
| 						normal.applyMatrix3( normalMatrix ).normalize(); | ||||
|  | ||||
| 						const uv = _face.uvs[ i ]; | ||||
| 						uv.fromArray( uvs, arguments[ i ] * 2 ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					_face.vertexNormalsLength = 3; | ||||
|  | ||||
| 					_face.material = material; | ||||
|  | ||||
| 					if ( material.vertexColors ) { | ||||
|  | ||||
| 						_face.color.fromArray( colors, a * 3 ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					_renderData.elements.push( _face ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return { | ||||
| 				setObject: setObject, | ||||
| 				projectVertex: projectVertex, | ||||
| 				checkTriangleVisibility: checkTriangleVisibility, | ||||
| 				checkBackfaceCulling: checkBackfaceCulling, | ||||
| 				pushVertex: pushVertex, | ||||
| 				pushNormal: pushNormal, | ||||
| 				pushColor: pushColor, | ||||
| 				pushUv: pushUv, | ||||
| 				pushLine: pushLine, | ||||
| 				pushTriangle: pushTriangle | ||||
| 			}; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const renderList = new RenderList(); | ||||
|  | ||||
| 		function projectObject( object ) { | ||||
|  | ||||
| 			if ( object.visible === false ) return; | ||||
|  | ||||
| 			if ( object.isLight ) { | ||||
|  | ||||
| 				_renderData.lights.push( object ); | ||||
|  | ||||
| 			} else if ( object.isMesh || object.isLine || object.isPoints ) { | ||||
|  | ||||
| 				if ( object.material.visible === false ) return; | ||||
| 				if ( object.frustumCulled === true && _frustum.intersectsObject( object ) === false ) return; | ||||
|  | ||||
| 				addObject( object ); | ||||
|  | ||||
| 			} else if ( object.isSprite ) { | ||||
|  | ||||
| 				if ( object.material.visible === false ) return; | ||||
| 				if ( object.frustumCulled === true && _frustum.intersectsSprite( object ) === false ) return; | ||||
|  | ||||
| 				addObject( object ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const children = object.children; | ||||
|  | ||||
| 			for ( let i = 0, l = children.length; i < l; i ++ ) { | ||||
|  | ||||
| 				projectObject( children[ i ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function addObject( object ) { | ||||
|  | ||||
| 			_object = getNextObjectInPool(); | ||||
| 			_object.id = object.id; | ||||
| 			_object.object = object; | ||||
|  | ||||
| 			_vector3.setFromMatrixPosition( object.matrixWorld ); | ||||
| 			_vector3.applyMatrix4( _viewProjectionMatrix ); | ||||
| 			_object.z = _vector3.z; | ||||
| 			_object.renderOrder = object.renderOrder; | ||||
|  | ||||
| 			_renderData.objects.push( _object ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.projectScene = function ( scene, camera, sortObjects, sortElements ) { | ||||
|  | ||||
| 			_faceCount = 0; | ||||
| 			_lineCount = 0; | ||||
| 			_spriteCount = 0; | ||||
|  | ||||
| 			_renderData.elements.length = 0; | ||||
|  | ||||
| 			if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); | ||||
| 			if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); | ||||
|  | ||||
| 			_viewMatrix.copy( camera.matrixWorldInverse ); | ||||
| 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); | ||||
|  | ||||
| 			_frustum.setFromProjectionMatrix( _viewProjectionMatrix ); | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			_objectCount = 0; | ||||
|  | ||||
| 			_renderData.objects.length = 0; | ||||
| 			_renderData.lights.length = 0; | ||||
|  | ||||
| 			projectObject( scene ); | ||||
|  | ||||
| 			if ( sortObjects === true ) { | ||||
|  | ||||
| 				_renderData.objects.sort( painterSort ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			const objects = _renderData.objects; | ||||
|  | ||||
| 			for ( let o = 0, ol = objects.length; o < ol; o ++ ) { | ||||
|  | ||||
| 				const object = objects[ o ].object; | ||||
| 				const geometry = object.geometry; | ||||
|  | ||||
| 				renderList.setObject( object ); | ||||
|  | ||||
| 				_modelMatrix = object.matrixWorld; | ||||
|  | ||||
| 				_vertexCount = 0; | ||||
|  | ||||
| 				if ( object.isMesh ) { | ||||
|  | ||||
| 					let material = object.material; | ||||
|  | ||||
| 					const isMultiMaterial = Array.isArray( material ); | ||||
|  | ||||
| 					const attributes = geometry.attributes; | ||||
| 					const groups = geometry.groups; | ||||
|  | ||||
| 					if ( attributes.position === undefined ) continue; | ||||
|  | ||||
| 					const positions = attributes.position.array; | ||||
|  | ||||
| 					for ( let i = 0, l = positions.length; i < l; i += 3 ) { | ||||
|  | ||||
| 						let x = positions[ i ]; | ||||
| 						let y = positions[ i + 1 ]; | ||||
| 						let z = positions[ i + 2 ]; | ||||
|  | ||||
| 						const morphTargets = geometry.morphAttributes.position; | ||||
|  | ||||
| 						if ( morphTargets !== undefined ) { | ||||
|  | ||||
| 							const morphTargetsRelative = geometry.morphTargetsRelative; | ||||
| 							const morphInfluences = object.morphTargetInfluences; | ||||
|  | ||||
| 							for ( let t = 0, tl = morphTargets.length; t < tl; t ++ ) { | ||||
|  | ||||
| 								const influence = morphInfluences[ t ]; | ||||
|  | ||||
| 								if ( influence === 0 ) continue; | ||||
|  | ||||
| 								const target = morphTargets[ t ]; | ||||
|  | ||||
| 								if ( morphTargetsRelative ) { | ||||
|  | ||||
| 									x += target.getX( i / 3 ) * influence; | ||||
| 									y += target.getY( i / 3 ) * influence; | ||||
| 									z += target.getZ( i / 3 ) * influence; | ||||
|  | ||||
| 								} else { | ||||
|  | ||||
| 									x += ( target.getX( i / 3 ) - positions[ i ] ) * influence; | ||||
| 									y += ( target.getY( i / 3 ) - positions[ i + 1 ] ) * influence; | ||||
| 									z += ( target.getZ( i / 3 ) - positions[ i + 2 ] ) * influence; | ||||
|  | ||||
| 								} | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 						renderList.pushVertex( x, y, z ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( attributes.normal !== undefined ) { | ||||
|  | ||||
| 						const normals = attributes.normal.array; | ||||
|  | ||||
| 						for ( let i = 0, l = normals.length; i < l; i += 3 ) { | ||||
|  | ||||
| 							renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( attributes.color !== undefined ) { | ||||
|  | ||||
| 						const colors = attributes.color.array; | ||||
|  | ||||
| 						for ( let i = 0, l = colors.length; i < l; i += 3 ) { | ||||
|  | ||||
| 							renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( attributes.uv !== undefined ) { | ||||
|  | ||||
| 						const uvs = attributes.uv.array; | ||||
|  | ||||
| 						for ( let i = 0, l = uvs.length; i < l; i += 2 ) { | ||||
|  | ||||
| 							renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( geometry.index !== null ) { | ||||
|  | ||||
| 						const indices = geometry.index.array; | ||||
|  | ||||
| 						if ( groups.length > 0 ) { | ||||
|  | ||||
| 							for ( let g = 0; g < groups.length; g ++ ) { | ||||
|  | ||||
| 								const group = groups[ g ]; | ||||
|  | ||||
| 								material = isMultiMaterial === true | ||||
| 									 ? object.material[ group.materialIndex ] | ||||
| 									 : object.material; | ||||
|  | ||||
| 								if ( material === undefined ) continue; | ||||
|  | ||||
| 								for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) { | ||||
|  | ||||
| 									renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); | ||||
|  | ||||
| 								} | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} else { | ||||
|  | ||||
| 							for ( let i = 0, l = indices.length; i < l; i += 3 ) { | ||||
|  | ||||
| 								renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ], material ); | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} else { | ||||
|  | ||||
| 						if ( groups.length > 0 ) { | ||||
|  | ||||
| 							for ( let g = 0; g < groups.length; g ++ ) { | ||||
|  | ||||
| 								const group = groups[ g ]; | ||||
|  | ||||
| 								material = isMultiMaterial === true | ||||
| 									 ? object.material[ group.materialIndex ] | ||||
| 									 : object.material; | ||||
|  | ||||
| 								if ( material === undefined ) continue; | ||||
|  | ||||
| 								for ( let i = group.start, l = group.start + group.count; i < l; i += 3 ) { | ||||
|  | ||||
| 									renderList.pushTriangle( i, i + 1, i + 2, material ); | ||||
|  | ||||
| 								} | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} else { | ||||
|  | ||||
| 							for ( let i = 0, l = positions.length / 3; i < l; i += 3 ) { | ||||
|  | ||||
| 								renderList.pushTriangle( i, i + 1, i + 2, material ); | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else if ( object.isLine ) { | ||||
|  | ||||
| 					_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); | ||||
|  | ||||
| 					const attributes = geometry.attributes; | ||||
|  | ||||
| 					if ( attributes.position !== undefined ) { | ||||
|  | ||||
| 						const positions = attributes.position.array; | ||||
|  | ||||
| 						for ( let i = 0, l = positions.length; i < l; i += 3 ) { | ||||
|  | ||||
| 							renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 						if ( attributes.color !== undefined ) { | ||||
|  | ||||
| 							const colors = attributes.color.array; | ||||
|  | ||||
| 							for ( let i = 0, l = colors.length; i < l; i += 3 ) { | ||||
|  | ||||
| 								renderList.pushColor( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ); | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 						if ( geometry.index !== null ) { | ||||
|  | ||||
| 							const indices = geometry.index.array; | ||||
|  | ||||
| 							for ( let i = 0, l = indices.length; i < l; i += 2 ) { | ||||
|  | ||||
| 								renderList.pushLine( indices[ i ], indices[ i + 1 ] ); | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} else { | ||||
|  | ||||
| 							const step = object.isLineSegments ? 2 : 1; | ||||
|  | ||||
| 							for ( let i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { | ||||
|  | ||||
| 								renderList.pushLine( i, i + 1 ); | ||||
|  | ||||
| 							} | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else if ( object.isPoints ) { | ||||
|  | ||||
| 					_modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); | ||||
|  | ||||
| 					const attributes = geometry.attributes; | ||||
|  | ||||
| 					if ( attributes.position !== undefined ) { | ||||
|  | ||||
| 						const positions = attributes.position.array; | ||||
|  | ||||
| 						for ( let i = 0, l = positions.length; i < l; i += 3 ) { | ||||
|  | ||||
| 							_vector4.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ], 1 ); | ||||
| 							_vector4.applyMatrix4( _modelViewProjectionMatrix ); | ||||
|  | ||||
| 							pushPoint( _vector4, object, camera ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else if ( object.isSprite ) { | ||||
|  | ||||
| 					object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); | ||||
| 					_vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); | ||||
| 					_vector4.applyMatrix4( _viewProjectionMatrix ); | ||||
|  | ||||
| 					pushPoint( _vector4, object, camera ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( sortElements === true ) { | ||||
|  | ||||
| 				_renderData.elements.sort( painterSort ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _renderData; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		function pushPoint( _vector4, object, camera ) { | ||||
|  | ||||
| 			const invW = 1 / _vector4.w; | ||||
|  | ||||
| 			_vector4.z *= invW; | ||||
|  | ||||
| 			if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { | ||||
|  | ||||
| 				_sprite = getNextSpriteInPool(); | ||||
| 				_sprite.id = object.id; | ||||
| 				_sprite.x = _vector4.x * invW; | ||||
| 				_sprite.y = _vector4.y * invW; | ||||
| 				_sprite.z = _vector4.z; | ||||
| 				_sprite.renderOrder = object.renderOrder; | ||||
| 				_sprite.object = object; | ||||
|  | ||||
| 				_sprite.rotation = object.rotation; | ||||
|  | ||||
| 				_sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); | ||||
| 				_sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); | ||||
|  | ||||
| 				_sprite.material = object.material; | ||||
|  | ||||
| 				_renderData.elements.push( _sprite ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// Pools | ||||
|  | ||||
| 		function getNextObjectInPool() { | ||||
|  | ||||
| 			if ( _objectCount === _objectPoolLength ) { | ||||
|  | ||||
| 				const object = new RenderableObject(); | ||||
| 				_objectPool.push( object ); | ||||
| 				_objectPoolLength ++; | ||||
| 				_objectCount ++; | ||||
| 				return object; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _objectPool[ _objectCount ++ ]; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getNextVertexInPool() { | ||||
|  | ||||
| 			if ( _vertexCount === _vertexPoolLength ) { | ||||
|  | ||||
| 				const vertex = new RenderableVertex(); | ||||
| 				_vertexPool.push( vertex ); | ||||
| 				_vertexPoolLength ++; | ||||
| 				_vertexCount ++; | ||||
| 				return vertex; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _vertexPool[ _vertexCount ++ ]; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getNextFaceInPool() { | ||||
|  | ||||
| 			if ( _faceCount === _facePoolLength ) { | ||||
|  | ||||
| 				const face = new RenderableFace(); | ||||
| 				_facePool.push( face ); | ||||
| 				_facePoolLength ++; | ||||
| 				_faceCount ++; | ||||
| 				return face; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _facePool[ _faceCount ++ ]; | ||||
|  | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getNextLineInPool() { | ||||
|  | ||||
| 			if ( _lineCount === _linePoolLength ) { | ||||
|  | ||||
| 				const line = new RenderableLine(); | ||||
| 				_linePool.push( line ); | ||||
| 				_linePoolLength ++; | ||||
| 				_lineCount ++; | ||||
| 				return line; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _linePool[ _lineCount ++ ]; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getNextSpriteInPool() { | ||||
|  | ||||
| 			if ( _spriteCount === _spritePoolLength ) { | ||||
|  | ||||
| 				const sprite = new RenderableSprite(); | ||||
| 				_spritePool.push( sprite ); | ||||
| 				_spritePoolLength ++; | ||||
| 				_spriteCount ++; | ||||
| 				return sprite; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _spritePool[ _spriteCount ++ ]; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		function painterSort( a, b ) { | ||||
|  | ||||
| 			if ( a.renderOrder !== b.renderOrder ) { | ||||
|  | ||||
| 				return a.renderOrder - b.renderOrder; | ||||
|  | ||||
| 			} else if ( a.z !== b.z ) { | ||||
|  | ||||
| 				return b.z - a.z; | ||||
|  | ||||
| 			} else if ( a.id !== b.id ) { | ||||
|  | ||||
| 				return a.id - b.id; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return 0; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function clipLine( s1, s2 ) { | ||||
|  | ||||
| 			let alpha1 = 0, alpha2 = 1; | ||||
|  | ||||
| 			// Calculate the boundary coordinate of each vertex for the near and far clip planes, | ||||
| 			// Z = -1 and Z = +1, respectively. | ||||
|  | ||||
| 			const bc1near = s1.z + s1.w, | ||||
| 				bc2near = s2.z + s2.w, | ||||
| 				bc1far = - s1.z + s1.w, | ||||
| 				bc2far = - s2.z + s2.w; | ||||
|  | ||||
| 			if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { | ||||
|  | ||||
| 				// Both vertices lie entirely within all clip planes. | ||||
| 				return true; | ||||
|  | ||||
| 			} else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { | ||||
|  | ||||
| 				// Both vertices lie entirely outside one of the clip planes. | ||||
| 				return false; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				// The line segment spans at least one clip plane. | ||||
|  | ||||
| 				if ( bc1near < 0 ) { | ||||
|  | ||||
| 					// v1 lies outside the near plane, v2 inside | ||||
| 					alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); | ||||
|  | ||||
| 				} else if ( bc2near < 0 ) { | ||||
|  | ||||
| 					// v2 lies outside the near plane, v1 inside | ||||
| 					alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( bc1far < 0 ) { | ||||
|  | ||||
| 					// v1 lies outside the far plane, v2 inside | ||||
| 					alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); | ||||
|  | ||||
| 				} else if ( bc2far < 0 ) { | ||||
|  | ||||
| 					// v2 lies outside the far plane, v2 inside | ||||
| 					alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( alpha2 < alpha1 ) { | ||||
|  | ||||
| 					// The line segment spans two boundaries, but is outside both of them. | ||||
| 					// (This can't happen when we're only clipping against just near/far but good | ||||
| 					//  to leave the check here for future usage if other clip planes are added.) | ||||
| 					return false; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					// Update the s1 and s2 vertices to match the clipped line segment. | ||||
| 					s1.lerp( s2, alpha1 ); | ||||
| 					s2.lerp( s1, 1 - alpha2 ); | ||||
|  | ||||
| 					return true; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { RenderableObject, RenderableFace, RenderableVertex, RenderableLine, RenderableSprite, Projector }; | ||||
							
								
								
									
										556
									
								
								public/sdk/three/jsm/renderers/SVGRenderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										556
									
								
								public/sdk/three/jsm/renderers/SVGRenderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,556 @@ | ||||
| import { | ||||
| 	Box2, | ||||
| 	Camera, | ||||
| 	Color, | ||||
| 	Matrix3, | ||||
| 	Matrix4, | ||||
| 	Object3D, | ||||
| 	SRGBColorSpace, | ||||
| 	Vector3 | ||||
| } from 'three'; | ||||
| import { Projector } from '../renderers/Projector.js'; | ||||
| import { RenderableFace } from '../renderers/Projector.js'; | ||||
| import { RenderableLine } from '../renderers/Projector.js'; | ||||
| import { RenderableSprite } from '../renderers/Projector.js'; | ||||
|  | ||||
| class SVGObject extends Object3D { | ||||
|  | ||||
| 	constructor( node ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.isSVGObject = true; | ||||
|  | ||||
| 		this.node = node; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class SVGRenderer { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		let _renderData, _elements, _lights, | ||||
| 			_svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, | ||||
|  | ||||
| 			_v1, _v2, _v3, | ||||
|  | ||||
| 			_svgNode, | ||||
| 			_pathCount = 0, | ||||
|  | ||||
| 			_precision = null, | ||||
| 			_quality = 1, | ||||
|  | ||||
| 			_currentPath, _currentStyle; | ||||
|  | ||||
| 		const _this = this, | ||||
| 			_clipBox = new Box2(), | ||||
| 			_elemBox = new Box2(), | ||||
|  | ||||
| 			_color = new Color(), | ||||
| 			_diffuseColor = new Color(), | ||||
| 			_ambientLight = new Color(), | ||||
| 			_directionalLights = new Color(), | ||||
| 			_pointLights = new Color(), | ||||
| 			_clearColor = new Color(), | ||||
|  | ||||
| 			_vector3 = new Vector3(), // Needed for PointLight | ||||
| 			_centroid = new Vector3(), | ||||
| 			_normal = new Vector3(), | ||||
| 			_normalViewMatrix = new Matrix3(), | ||||
|  | ||||
| 			_viewMatrix = new Matrix4(), | ||||
| 			_viewProjectionMatrix = new Matrix4(), | ||||
|  | ||||
| 			_svgPathPool = [], | ||||
|  | ||||
| 			_projector = new Projector(), | ||||
| 			_svg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); | ||||
|  | ||||
| 		this.domElement = _svg; | ||||
|  | ||||
| 		this.autoClear = true; | ||||
| 		this.sortObjects = true; | ||||
| 		this.sortElements = true; | ||||
|  | ||||
| 		this.overdraw = 0.5; | ||||
|  | ||||
| 		this.outputColorSpace = SRGBColorSpace; | ||||
|  | ||||
| 		this.info = { | ||||
|  | ||||
| 			render: { | ||||
|  | ||||
| 				vertices: 0, | ||||
| 				faces: 0 | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setQuality = function ( quality ) { | ||||
|  | ||||
| 			switch ( quality ) { | ||||
|  | ||||
| 				case 'high': _quality = 1; break; | ||||
| 				case 'low': _quality = 0; break; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setClearColor = function ( color ) { | ||||
|  | ||||
| 			_clearColor.set( color ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setPixelRatio = function () {}; | ||||
|  | ||||
| 		this.setSize = function ( width, height ) { | ||||
|  | ||||
| 			_svgWidth = width; _svgHeight = height; | ||||
| 			_svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; | ||||
|  | ||||
| 			_svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); | ||||
| 			_svg.setAttribute( 'width', _svgWidth ); | ||||
| 			_svg.setAttribute( 'height', _svgHeight ); | ||||
|  | ||||
| 			_clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf ); | ||||
| 			_clipBox.max.set( _svgWidthHalf, _svgHeightHalf ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.getSize = function () { | ||||
|  | ||||
| 			return { | ||||
| 				width: _svgWidth, | ||||
| 				height: _svgHeight | ||||
| 			}; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.setPrecision = function ( precision ) { | ||||
|  | ||||
| 			_precision = precision; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		function removeChildNodes() { | ||||
|  | ||||
| 			_pathCount = 0; | ||||
|  | ||||
| 			while ( _svg.childNodes.length > 0 ) { | ||||
|  | ||||
| 				_svg.removeChild( _svg.childNodes[ 0 ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function convert( c ) { | ||||
|  | ||||
| 			return _precision !== null ? c.toFixed( _precision ) : c; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.clear = function () { | ||||
|  | ||||
| 			removeChildNodes(); | ||||
| 			_svg.style.backgroundColor = _clearColor.getStyle( _this.outputColorSpace ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.render = function ( scene, camera ) { | ||||
|  | ||||
| 			if ( camera instanceof Camera === false ) { | ||||
|  | ||||
| 				console.error( 'THREE.SVGRenderer.render: camera is not an instance of Camera.' ); | ||||
| 				return; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const background = scene.background; | ||||
|  | ||||
| 			if ( background && background.isColor ) { | ||||
|  | ||||
| 				removeChildNodes(); | ||||
| 				_svg.style.backgroundColor = background.getStyle( _this.outputColorSpace ); | ||||
|  | ||||
| 			} else if ( this.autoClear === true ) { | ||||
|  | ||||
| 				this.clear(); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			_this.info.render.vertices = 0; | ||||
| 			_this.info.render.faces = 0; | ||||
|  | ||||
| 			_viewMatrix.copy( camera.matrixWorldInverse ); | ||||
| 			_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); | ||||
|  | ||||
| 			_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); | ||||
| 			_elements = _renderData.elements; | ||||
| 			_lights = _renderData.lights; | ||||
|  | ||||
| 			_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); | ||||
|  | ||||
| 			calculateLights( _lights ); | ||||
|  | ||||
| 			 // reset accumulated path | ||||
|  | ||||
| 			_currentPath = ''; | ||||
| 			_currentStyle = ''; | ||||
|  | ||||
| 			for ( let e = 0, el = _elements.length; e < el; e ++ ) { | ||||
|  | ||||
| 				const element = _elements[ e ]; | ||||
| 				const material = element.material; | ||||
|  | ||||
| 				if ( material === undefined || material.opacity === 0 ) continue; | ||||
|  | ||||
| 				_elemBox.makeEmpty(); | ||||
|  | ||||
| 				if ( element instanceof RenderableSprite ) { | ||||
|  | ||||
| 					_v1 = element; | ||||
| 					_v1.x *= _svgWidthHalf; _v1.y *= - _svgHeightHalf; | ||||
|  | ||||
| 					renderSprite( _v1, element, material ); | ||||
|  | ||||
| 				} else if ( element instanceof RenderableLine ) { | ||||
|  | ||||
| 					_v1 = element.v1; _v2 = element.v2; | ||||
|  | ||||
| 					_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; | ||||
| 					_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; | ||||
|  | ||||
| 					_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] ); | ||||
|  | ||||
| 					if ( _clipBox.intersectsBox( _elemBox ) === true ) { | ||||
|  | ||||
| 						renderLine( _v1, _v2, material ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else if ( element instanceof RenderableFace ) { | ||||
|  | ||||
| 					_v1 = element.v1; _v2 = element.v2; _v3 = element.v3; | ||||
|  | ||||
| 					if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; | ||||
| 					if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; | ||||
| 					if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; | ||||
|  | ||||
| 					_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; | ||||
| 					_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; | ||||
| 					_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; | ||||
|  | ||||
| 					if ( this.overdraw > 0 ) { | ||||
|  | ||||
| 						expand( _v1.positionScreen, _v2.positionScreen, this.overdraw ); | ||||
| 						expand( _v2.positionScreen, _v3.positionScreen, this.overdraw ); | ||||
| 						expand( _v3.positionScreen, _v1.positionScreen, this.overdraw ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					_elemBox.setFromPoints( [ | ||||
| 						_v1.positionScreen, | ||||
| 						_v2.positionScreen, | ||||
| 						_v3.positionScreen | ||||
| 					] ); | ||||
|  | ||||
| 					if ( _clipBox.intersectsBox( _elemBox ) === true ) { | ||||
|  | ||||
| 						renderFace3( _v1, _v2, _v3, element, material ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			flushPath(); // just to flush last svg:path | ||||
|  | ||||
| 			scene.traverseVisible( function ( object ) { | ||||
|  | ||||
| 				 if ( object.isSVGObject ) { | ||||
|  | ||||
| 					_vector3.setFromMatrixPosition( object.matrixWorld ); | ||||
| 					_vector3.applyMatrix4( _viewProjectionMatrix ); | ||||
|  | ||||
| 					if ( _vector3.z < - 1 || _vector3.z > 1 ) return; | ||||
|  | ||||
| 					const x = _vector3.x * _svgWidthHalf; | ||||
| 					const y = - _vector3.y * _svgHeightHalf; | ||||
|  | ||||
| 					const node = object.node; | ||||
| 					node.setAttribute( 'transform', 'translate(' + x + ',' + y + ')' ); | ||||
|  | ||||
| 					_svg.appendChild( node ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		function calculateLights( lights ) { | ||||
|  | ||||
| 			_ambientLight.setRGB( 0, 0, 0 ); | ||||
| 			_directionalLights.setRGB( 0, 0, 0 ); | ||||
| 			_pointLights.setRGB( 0, 0, 0 ); | ||||
|  | ||||
| 			for ( let l = 0, ll = lights.length; l < ll; l ++ ) { | ||||
|  | ||||
| 				const light = lights[ l ]; | ||||
| 				const lightColor = light.color; | ||||
|  | ||||
| 				if ( light.isAmbientLight ) { | ||||
|  | ||||
| 					_ambientLight.r += lightColor.r; | ||||
| 					_ambientLight.g += lightColor.g; | ||||
| 					_ambientLight.b += lightColor.b; | ||||
|  | ||||
| 				} else if ( light.isDirectionalLight ) { | ||||
|  | ||||
| 					_directionalLights.r += lightColor.r; | ||||
| 					_directionalLights.g += lightColor.g; | ||||
| 					_directionalLights.b += lightColor.b; | ||||
|  | ||||
| 				} else if ( light.isPointLight ) { | ||||
|  | ||||
| 					_pointLights.r += lightColor.r; | ||||
| 					_pointLights.g += lightColor.g; | ||||
| 					_pointLights.b += lightColor.b; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function calculateLight( lights, position, normal, color ) { | ||||
|  | ||||
| 			for ( let l = 0, ll = lights.length; l < ll; l ++ ) { | ||||
|  | ||||
| 				const light = lights[ l ]; | ||||
| 				const lightColor = light.color; | ||||
|  | ||||
| 				if ( light.isDirectionalLight ) { | ||||
|  | ||||
| 					const lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); | ||||
|  | ||||
| 					let amount = normal.dot( lightPosition ); | ||||
|  | ||||
| 					if ( amount <= 0 ) continue; | ||||
|  | ||||
| 					amount *= light.intensity; | ||||
|  | ||||
| 					color.r += lightColor.r * amount; | ||||
| 					color.g += lightColor.g * amount; | ||||
| 					color.b += lightColor.b * amount; | ||||
|  | ||||
| 				} else if ( light.isPointLight ) { | ||||
|  | ||||
| 					const lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); | ||||
|  | ||||
| 					let amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); | ||||
|  | ||||
| 					if ( amount <= 0 ) continue; | ||||
|  | ||||
| 					amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); | ||||
|  | ||||
| 					if ( amount == 0 ) continue; | ||||
|  | ||||
| 					amount *= light.intensity; | ||||
|  | ||||
| 					color.r += lightColor.r * amount; | ||||
| 					color.g += lightColor.g * amount; | ||||
| 					color.b += lightColor.b * amount; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function renderSprite( v1, element, material ) { | ||||
|  | ||||
| 			let scaleX = element.scale.x * _svgWidthHalf; | ||||
| 			let scaleY = element.scale.y * _svgHeightHalf; | ||||
|  | ||||
| 			if ( material.isPointsMaterial ) { | ||||
|  | ||||
| 				scaleX *= material.size; | ||||
| 				scaleY *= material.size; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert( - scaleX ) + 'z'; | ||||
| 			let style = ''; | ||||
|  | ||||
| 			if ( material.isSpriteMaterial || material.isPointsMaterial ) { | ||||
|  | ||||
| 				style = 'fill:' + material.color.getStyle( _this.outputColorSpace ) + ';fill-opacity:' + material.opacity; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			addPath( style, path ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function renderLine( v1, v2, material ) { | ||||
|  | ||||
| 			const path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ); | ||||
|  | ||||
| 			if ( material.isLineBasicMaterial ) { | ||||
|  | ||||
| 				let style = 'fill:none;stroke:' + material.color.getStyle( _this.outputColorSpace ) + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap; | ||||
|  | ||||
| 				if ( material.isLineDashedMaterial ) { | ||||
|  | ||||
| 					style = style + ';stroke-dasharray:' + material.dashSize + ',' + material.gapSize; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				addPath( style, path ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function renderFace3( v1, v2, v3, element, material ) { | ||||
|  | ||||
| 			_this.info.render.vertices += 3; | ||||
| 			_this.info.render.faces ++; | ||||
|  | ||||
| 			const path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z'; | ||||
| 			let style = ''; | ||||
|  | ||||
| 			if ( material.isMeshBasicMaterial ) { | ||||
|  | ||||
| 				_color.copy( material.color ); | ||||
|  | ||||
| 				if ( material.vertexColors ) { | ||||
|  | ||||
| 					_color.multiply( element.color ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else if ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) { | ||||
|  | ||||
| 				_diffuseColor.copy( material.color ); | ||||
|  | ||||
| 				if ( material.vertexColors ) { | ||||
|  | ||||
| 					_diffuseColor.multiply( element.color ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				_color.copy( _ambientLight ); | ||||
|  | ||||
| 				_centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); | ||||
|  | ||||
| 				calculateLight( _lights, _centroid, element.normalModel, _color ); | ||||
|  | ||||
| 				_color.multiply( _diffuseColor ).add( material.emissive ); | ||||
|  | ||||
| 			} else if ( material.isMeshNormalMaterial ) { | ||||
|  | ||||
| 				_normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ).normalize(); | ||||
|  | ||||
| 				_color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( material.wireframe ) { | ||||
|  | ||||
| 				style = 'fill:none;stroke:' + _color.getStyle( _this.outputColorSpace ) + ';stroke-opacity:' + material.opacity + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				style = 'fill:' + _color.getStyle( _this.outputColorSpace ) + ';fill-opacity:' + material.opacity; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			addPath( style, path ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// Hide anti-alias gaps | ||||
|  | ||||
| 		function expand( v1, v2, pixels ) { | ||||
|  | ||||
| 			let x = v2.x - v1.x, y = v2.y - v1.y; | ||||
| 			const det = x * x + y * y; | ||||
|  | ||||
| 			if ( det === 0 ) return; | ||||
|  | ||||
| 			const idet = pixels / Math.sqrt( det ); | ||||
|  | ||||
| 			x *= idet; y *= idet; | ||||
|  | ||||
| 			v2.x += x; v2.y += y; | ||||
| 			v1.x -= x; v1.y -= y; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function addPath( style, path ) { | ||||
|  | ||||
| 			if ( _currentStyle === style ) { | ||||
|  | ||||
| 				_currentPath += path; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				flushPath(); | ||||
|  | ||||
| 				_currentStyle = style; | ||||
| 				_currentPath = path; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function flushPath() { | ||||
|  | ||||
| 			if ( _currentPath ) { | ||||
|  | ||||
| 				_svgNode = getPathNode( _pathCount ++ ); | ||||
| 				_svgNode.setAttribute( 'd', _currentPath ); | ||||
| 				_svgNode.setAttribute( 'style', _currentStyle ); | ||||
| 				_svg.appendChild( _svgNode ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			_currentPath = ''; | ||||
| 			_currentStyle = ''; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		function getPathNode( id ) { | ||||
|  | ||||
| 			if ( _svgPathPool[ id ] == null ) { | ||||
|  | ||||
| 				_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); | ||||
|  | ||||
| 				if ( _quality == 0 ) { | ||||
|  | ||||
| 					_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				return _svgPathPool[ id ]; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return _svgPathPool[ id ]; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { SVGObject, SVGRenderer }; | ||||
							
								
								
									
										50
									
								
								public/sdk/three/jsm/renderers/common/Animation.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								public/sdk/three/jsm/renderers/common/Animation.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| class Animation { | ||||
|  | ||||
| 	constructor( nodes, info ) { | ||||
|  | ||||
| 		this.nodes = nodes; | ||||
| 		this.info = info; | ||||
|  | ||||
| 		this.animationLoop = null; | ||||
| 		this.requestId = null; | ||||
|  | ||||
| 		this._init(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_init() { | ||||
|  | ||||
| 		const update = ( time, frame ) => { | ||||
|  | ||||
| 			this.requestId = self.requestAnimationFrame( update ); | ||||
|  | ||||
| 			if ( this.info.autoReset === true ) this.info.reset(); | ||||
|  | ||||
| 			this.nodes.nodeFrame.update(); | ||||
|  | ||||
| 			this.info.frame = this.nodes.nodeFrame.frameId; | ||||
|  | ||||
| 			if ( this.animationLoop !== null ) this.animationLoop( time, frame ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		update(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		self.cancelAnimationFrame( this.requestId ); | ||||
| 		this.requestId = null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setAnimationLoop( callback ) { | ||||
|  | ||||
| 		this.animationLoop = callback; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Animation; | ||||
							
								
								
									
										75
									
								
								public/sdk/three/jsm/renderers/common/Attributes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								public/sdk/three/jsm/renderers/common/Attributes.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
| import { AttributeType } from './Constants.js'; | ||||
| import { DynamicDrawUsage } from 'three'; | ||||
|  | ||||
| class Attributes extends DataMap { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( attribute ) { | ||||
|  | ||||
| 		const attributeData = super.delete( attribute ); | ||||
|  | ||||
| 		if ( attributeData !== undefined ) { | ||||
|  | ||||
| 			this.backend.destroyAttribute( attribute ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update( attribute, type ) { | ||||
|  | ||||
| 		const data = this.get( attribute ); | ||||
|  | ||||
| 		if ( data.version === undefined ) { | ||||
|  | ||||
| 			if ( type === AttributeType.VERTEX ) { | ||||
|  | ||||
| 				this.backend.createAttribute( attribute ); | ||||
|  | ||||
| 			} else if ( type === AttributeType.INDEX ) { | ||||
|  | ||||
| 				this.backend.createIndexAttribute( attribute ); | ||||
|  | ||||
| 			} else if ( type === AttributeType.STORAGE ) { | ||||
|  | ||||
| 				this.backend.createStorageAttribute( attribute ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			data.version = this._getBufferAttribute( attribute ).version; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const bufferAttribute = this._getBufferAttribute( attribute ); | ||||
|  | ||||
| 			if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) { | ||||
|  | ||||
| 				this.backend.updateAttribute( attribute ); | ||||
|  | ||||
| 				data.version = bufferAttribute.version; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBufferAttribute( attribute ) { | ||||
|  | ||||
| 		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; | ||||
|  | ||||
| 		return attribute; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Attributes; | ||||
							
								
								
									
										195
									
								
								public/sdk/three/jsm/renderers/common/Backend.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								public/sdk/three/jsm/renderers/common/Backend.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,195 @@ | ||||
| let vector2 = null; | ||||
| let vector4 = null; | ||||
| let color4 = null; | ||||
|  | ||||
| import Color4 from './Color4.js'; | ||||
| import { Vector2, Vector4, REVISION, createCanvasElement } from 'three'; | ||||
|  | ||||
| class Backend { | ||||
|  | ||||
| 	constructor( parameters = {} ) { | ||||
|  | ||||
| 		this.parameters = Object.assign( {}, parameters ); | ||||
| 		this.data = new WeakMap(); | ||||
| 		this.renderer = null; | ||||
| 		this.domElement = null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	async init( renderer ) { | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// render context | ||||
|  | ||||
| 	begin( renderContext ) { } | ||||
|  | ||||
| 	finish( renderContext ) { } | ||||
|  | ||||
| 	// render object | ||||
|  | ||||
| 	draw( renderObject, info ) { } | ||||
|  | ||||
| 	// program | ||||
|  | ||||
| 	createProgram( program ) { } | ||||
|  | ||||
| 	destroyProgram( program ) { } | ||||
|  | ||||
| 	// bindings | ||||
|  | ||||
| 	createBindings( renderObject ) { } | ||||
|  | ||||
| 	updateBindings( renderObject ) { } | ||||
|  | ||||
| 	// pipeline | ||||
|  | ||||
| 	createRenderPipeline( renderObject ) { } | ||||
|  | ||||
| 	createComputePipeline( computeNode, pipeline ) { } | ||||
|  | ||||
| 	destroyPipeline( pipeline ) { } | ||||
|  | ||||
| 	// cache key | ||||
|  | ||||
| 	needsRenderUpdate( renderObject ) { } // return Boolean ( fast test ) | ||||
|  | ||||
| 	getRenderCacheKey( renderObject ) { } // return String | ||||
|  | ||||
| 	// node builder | ||||
|  | ||||
| 	createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT) | ||||
|  | ||||
| 	// textures | ||||
|  | ||||
| 	createSampler( texture ) { } | ||||
|  | ||||
| 	createDefaultTexture( texture ) { } | ||||
|  | ||||
| 	createTexture( texture ) { } | ||||
|  | ||||
| 	copyTextureToBuffer( texture, x, y, width, height ) {} | ||||
|  | ||||
| 	// attributes | ||||
|  | ||||
| 	createAttribute( attribute ) { } | ||||
|  | ||||
| 	createIndexAttribute( attribute ) { } | ||||
|  | ||||
| 	updateAttribute( attribute ) { } | ||||
|  | ||||
| 	destroyAttribute( attribute ) { } | ||||
|  | ||||
| 	// canvas | ||||
|  | ||||
| 	getContext() { } | ||||
|  | ||||
| 	updateSize() { } | ||||
|  | ||||
| 	// utils | ||||
|  | ||||
| 	resolveTimestampAsync( renderContext, type ) { } | ||||
|  | ||||
| 	hasFeatureAsync( name ) { } // return Boolean | ||||
|  | ||||
| 	hasFeature( name ) { } // return Boolean | ||||
|  | ||||
| 	getInstanceCount( renderObject ) { | ||||
|  | ||||
| 		const { object, geometry } = renderObject; | ||||
|  | ||||
| 		return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getDrawingBufferSize() { | ||||
|  | ||||
| 		vector2 = vector2 || new Vector2(); | ||||
|  | ||||
| 		return this.renderer.getDrawingBufferSize( vector2 ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getScissor() { | ||||
|  | ||||
| 		vector4 = vector4 || new Vector4(); | ||||
|  | ||||
| 		return this.renderer.getScissor( vector4 ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setScissorTest( boolean ) { } | ||||
|  | ||||
| 	getClearColor() { | ||||
|  | ||||
| 		const renderer = this.renderer; | ||||
|  | ||||
| 		color4 = color4 || new Color4(); | ||||
|  | ||||
| 		renderer.getClearColor( color4 ); | ||||
|  | ||||
| 		color4.getRGB( color4, this.renderer.currentColorSpace ); | ||||
|  | ||||
| 		return color4; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getDomElement() { | ||||
|  | ||||
| 		let domElement = this.domElement; | ||||
|  | ||||
| 		if ( domElement === null ) { | ||||
|  | ||||
| 			domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : createCanvasElement(); | ||||
|  | ||||
| 			// OffscreenCanvas does not have setAttribute, see #22811 | ||||
| 			if ( 'setAttribute' in domElement ) domElement.setAttribute( 'data-engine', `three.js r${REVISION} webgpu` ); | ||||
|  | ||||
| 			this.domElement = domElement; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return domElement; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// resource properties | ||||
|  | ||||
| 	set( object, value ) { | ||||
|  | ||||
| 		this.data.set( object, value ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( object ) { | ||||
|  | ||||
| 		let map = this.data.get( object ); | ||||
|  | ||||
| 		if ( map === undefined ) { | ||||
|  | ||||
| 			map = {}; | ||||
| 			this.data.set( object, map ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return map; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	has( object ) { | ||||
|  | ||||
| 		return this.data.has( object ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( object ) { | ||||
|  | ||||
| 		this.data.delete( object ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Backend; | ||||
							
								
								
									
										134
									
								
								public/sdk/three/jsm/renderers/common/Background.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								public/sdk/three/jsm/renderers/common/Background.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
| import Color4 from './Color4.js'; | ||||
| import { Mesh, SphereGeometry, BackSide, LinearSRGBColorSpace } from 'three'; | ||||
| import { vec4, context, normalWorld, backgroundBlurriness, backgroundIntensity, NodeMaterial, modelViewProjection } from '../../nodes/Nodes.js'; | ||||
|  | ||||
| const _clearColor = new Color4(); | ||||
|  | ||||
| class Background extends DataMap { | ||||
|  | ||||
| 	constructor( renderer, nodes ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.nodes = nodes; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update( scene, renderList, renderContext ) { | ||||
|  | ||||
| 		const renderer = this.renderer; | ||||
| 		const background = this.nodes.getBackgroundNode( scene ) || scene.background; | ||||
|  | ||||
| 		let forceClear = false; | ||||
|  | ||||
| 		if ( background === null ) { | ||||
|  | ||||
| 			// no background settings, use clear color configuration from the renderer | ||||
|  | ||||
| 			renderer._clearColor.getRGB( _clearColor, LinearSRGBColorSpace ); | ||||
| 			_clearColor.a = renderer._clearColor.a; | ||||
|  | ||||
| 		} else if ( background.isColor === true ) { | ||||
|  | ||||
| 			// background is an opaque color | ||||
|  | ||||
| 			background.getRGB( _clearColor, LinearSRGBColorSpace ); | ||||
| 			_clearColor.a = 1; | ||||
|  | ||||
| 			forceClear = true; | ||||
|  | ||||
| 		} else if ( background.isNode === true ) { | ||||
|  | ||||
| 			const sceneData = this.get( scene ); | ||||
| 			const backgroundNode = background; | ||||
|  | ||||
| 			_clearColor.copy( renderer._clearColor ); | ||||
|  | ||||
| 			let backgroundMesh = sceneData.backgroundMesh; | ||||
|  | ||||
| 			if ( backgroundMesh === undefined ) { | ||||
|  | ||||
| 				const backgroundMeshNode = context( vec4( backgroundNode ).mul( backgroundIntensity ), { | ||||
| 					// @TODO: Add Texture2D support using node context | ||||
| 					getUV: () => normalWorld, | ||||
| 					getTextureLevel: () => backgroundBlurriness | ||||
| 				} ); | ||||
|  | ||||
| 				let viewProj = modelViewProjection(); | ||||
| 				viewProj = viewProj.setZ( viewProj.w ); | ||||
|  | ||||
| 				const nodeMaterial = new NodeMaterial(); | ||||
| 				nodeMaterial.side = BackSide; | ||||
| 				nodeMaterial.depthTest = false; | ||||
| 				nodeMaterial.depthWrite = false; | ||||
| 				nodeMaterial.fog = false; | ||||
| 				nodeMaterial.vertexNode = viewProj; | ||||
| 				nodeMaterial.fragmentNode = backgroundMeshNode; | ||||
|  | ||||
| 				sceneData.backgroundMeshNode = backgroundMeshNode; | ||||
| 				sceneData.backgroundMesh = backgroundMesh = new Mesh( new SphereGeometry( 1, 32, 32 ), nodeMaterial ); | ||||
| 				backgroundMesh.frustumCulled = false; | ||||
|  | ||||
| 				backgroundMesh.onBeforeRender = function ( renderer, scene, camera ) { | ||||
|  | ||||
| 					this.matrixWorld.copyPosition( camera.matrixWorld ); | ||||
|  | ||||
| 				}; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const backgroundCacheKey = backgroundNode.getCacheKey(); | ||||
|  | ||||
| 			if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) { | ||||
|  | ||||
| 				sceneData.backgroundMeshNode.node = vec4( backgroundNode ).mul( backgroundIntensity ); | ||||
|  | ||||
| 				backgroundMesh.material.needsUpdate = true; | ||||
|  | ||||
| 				sceneData.backgroundCacheKey = backgroundCacheKey; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			renderList.unshift( backgroundMesh, backgroundMesh.geometry, backgroundMesh.material, 0, 0, null ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			console.error( 'THREE.Renderer: Unsupported background configuration.', background ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		if ( renderer.autoClear === true || forceClear === true ) { | ||||
|  | ||||
| 			_clearColor.multiplyScalar( _clearColor.a ); | ||||
|  | ||||
| 			const clearColorValue = renderContext.clearColorValue; | ||||
|  | ||||
| 			clearColorValue.r = _clearColor.r; | ||||
| 			clearColorValue.g = _clearColor.g; | ||||
| 			clearColorValue.b = _clearColor.b; | ||||
| 			clearColorValue.a = _clearColor.a; | ||||
|  | ||||
| 			renderContext.depthClearValue = renderer._clearDepth; | ||||
| 			renderContext.stencilClearValue = renderer._clearStencil; | ||||
|  | ||||
| 			renderContext.clearColor = renderer.autoClearColor === true; | ||||
| 			renderContext.clearDepth = renderer.autoClearDepth === true; | ||||
| 			renderContext.clearStencil = renderer.autoClearStencil === true; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			renderContext.clearColor = false; | ||||
| 			renderContext.clearDepth = false; | ||||
| 			renderContext.clearStencil = false; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Background; | ||||
							
								
								
									
										25
									
								
								public/sdk/three/jsm/renderers/common/Binding.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								public/sdk/three/jsm/renderers/common/Binding.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | ||||
| class Binding { | ||||
|  | ||||
| 	constructor( name = '' ) { | ||||
|  | ||||
| 		this.name = name; | ||||
|  | ||||
| 		this.visibility = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setVisibility( visibility ) { | ||||
|  | ||||
| 		this.visibility |= visibility; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	clone() { | ||||
|  | ||||
| 		return Object.assign( new this.constructor(), this ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Binding; | ||||
							
								
								
									
										173
									
								
								public/sdk/three/jsm/renderers/common/Bindings.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								public/sdk/three/jsm/renderers/common/Bindings.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,173 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
| import { AttributeType } from './Constants.js'; | ||||
|  | ||||
| class Bindings extends DataMap { | ||||
|  | ||||
| 	constructor( backend, nodes, textures, attributes, pipelines, info ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.backend = backend; | ||||
| 		this.textures = textures; | ||||
| 		this.pipelines = pipelines; | ||||
| 		this.attributes = attributes; | ||||
| 		this.nodes = nodes; | ||||
| 		this.info = info; | ||||
|  | ||||
| 		this.pipelines.bindings = this; // assign bindings to pipelines | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForRender( renderObject ) { | ||||
|  | ||||
| 		const bindings = renderObject.getBindings(); | ||||
|  | ||||
| 		const data = this.get( renderObject ); | ||||
|  | ||||
| 		if ( data.bindings !== bindings ) { | ||||
|  | ||||
| 			// each object defines an array of bindings (ubos, textures, samplers etc.) | ||||
|  | ||||
| 			data.bindings = bindings; | ||||
|  | ||||
| 			this._init( bindings ); | ||||
|  | ||||
| 			this.backend.createBindings( bindings ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return data.bindings; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForCompute( computeNode ) { | ||||
|  | ||||
| 		const data = this.get( computeNode ); | ||||
|  | ||||
| 		if ( data.bindings === undefined ) { | ||||
|  | ||||
| 			const nodeBuilderState = this.nodes.getForCompute( computeNode ); | ||||
|  | ||||
| 			const bindings = nodeBuilderState.bindings; | ||||
|  | ||||
| 			data.bindings = bindings; | ||||
|  | ||||
| 			this._init( bindings ); | ||||
|  | ||||
| 			this.backend.createBindings( bindings ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return data.bindings; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForCompute( computeNode ) { | ||||
|  | ||||
| 		this._update( computeNode, this.getForCompute( computeNode ) ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForRender( renderObject ) { | ||||
|  | ||||
| 		this._update( renderObject, this.getForRender( renderObject ) ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_init( bindings ) { | ||||
|  | ||||
| 		for ( const binding of bindings ) { | ||||
|  | ||||
| 			if ( binding.isSampledTexture ) { | ||||
|  | ||||
| 				this.textures.updateTexture( binding.texture ); | ||||
|  | ||||
| 			} else if ( binding.isStorageBuffer ) { | ||||
|  | ||||
| 				const attribute = binding.attribute; | ||||
|  | ||||
| 				this.attributes.update( attribute, AttributeType.STORAGE ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_update( object, bindings ) { | ||||
|  | ||||
| 		const { backend } = this; | ||||
|  | ||||
| 		let needsBindingsUpdate = false; | ||||
|  | ||||
| 		// iterate over all bindings and check if buffer updates or a new binding group is required | ||||
|  | ||||
| 		for ( const binding of bindings ) { | ||||
|  | ||||
| 			if ( binding.isNodeUniformsGroup ) { | ||||
|  | ||||
| 				const updated = this.nodes.updateGroup( binding ); | ||||
|  | ||||
| 				if ( ! updated ) continue; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( binding.isUniformBuffer ) { | ||||
|  | ||||
| 				const updated = binding.update(); | ||||
|  | ||||
| 				if ( updated ) { | ||||
|  | ||||
| 					backend.updateBinding( binding ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else if ( binding.isSampledTexture ) { | ||||
|  | ||||
| 				const texture = binding.texture; | ||||
|  | ||||
| 				if ( binding.needsBindingsUpdate ) needsBindingsUpdate = true; | ||||
|  | ||||
| 				const updated = binding.update(); | ||||
|  | ||||
| 				if ( updated ) { | ||||
|  | ||||
| 					this.textures.updateTexture( binding.texture ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( texture.isStorageTexture === true ) { | ||||
|  | ||||
| 					const textureData = this.get( texture ); | ||||
|  | ||||
| 					if ( binding.store === true ) { | ||||
|  | ||||
| 						textureData.needsMipmap = true; | ||||
|  | ||||
| 					} else if ( texture.generateMipmaps === true && this.textures.needsMipmaps( texture ) && textureData.needsMipmap === true ) { | ||||
|  | ||||
| 						this.backend.generateMipmaps( texture ); | ||||
|  | ||||
| 						textureData.needsMipmap = false; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( needsBindingsUpdate === true ) { | ||||
|  | ||||
| 			const pipeline = this.pipelines.getForRender( object ); | ||||
|  | ||||
| 			this.backend.updateBindings( bindings, pipeline ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Bindings; | ||||
							
								
								
									
										38
									
								
								public/sdk/three/jsm/renderers/common/Buffer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								public/sdk/three/jsm/renderers/common/Buffer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| import Binding from './Binding.js'; | ||||
| import { getFloatLength } from './BufferUtils.js'; | ||||
|  | ||||
| class Buffer extends Binding { | ||||
|  | ||||
| 	constructor( name, buffer = null ) { | ||||
|  | ||||
| 		super( name ); | ||||
|  | ||||
| 		this.isBuffer = true; | ||||
|  | ||||
| 		this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT; | ||||
|  | ||||
| 		this._buffer = buffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get byteLength() { | ||||
|  | ||||
| 		return getFloatLength( this._buffer.byteLength ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get buffer() { | ||||
|  | ||||
| 		return this._buffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update() { | ||||
|  | ||||
| 		return true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Buffer; | ||||
							
								
								
									
										33
									
								
								public/sdk/three/jsm/renderers/common/BufferUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								public/sdk/three/jsm/renderers/common/BufferUtils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| import { GPU_CHUNK_BYTES } from './Constants.js'; | ||||
|  | ||||
| function getFloatLength( floatLength ) { | ||||
|  | ||||
| 	// ensure chunk size alignment (STD140 layout) | ||||
|  | ||||
| 	return floatLength + ( ( GPU_CHUNK_BYTES - ( floatLength % GPU_CHUNK_BYTES ) ) % GPU_CHUNK_BYTES ); | ||||
|  | ||||
| } | ||||
|  | ||||
| function getVectorLength( count, vectorLength = 4 ) { | ||||
|  | ||||
| 	const strideLength = getStrideLength( vectorLength ); | ||||
|  | ||||
| 	const floatLength = strideLength * count; | ||||
|  | ||||
| 	return getFloatLength( floatLength ); | ||||
|  | ||||
| } | ||||
|  | ||||
| function getStrideLength( vectorLength ) { | ||||
|  | ||||
| 	const strideLength = 4; | ||||
|  | ||||
| 	return vectorLength + ( ( strideLength - ( vectorLength % strideLength ) ) % strideLength ); | ||||
|  | ||||
| } | ||||
|  | ||||
| export { | ||||
| 	getFloatLength, | ||||
| 	getVectorLength, | ||||
| 	getStrideLength | ||||
| }; | ||||
							
								
								
									
										89
									
								
								public/sdk/three/jsm/renderers/common/ChainMap.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								public/sdk/three/jsm/renderers/common/ChainMap.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| export default class ChainMap { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.weakMap = new WeakMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( keys ) { | ||||
|  | ||||
| 		if ( Array.isArray( keys ) ) { | ||||
|  | ||||
| 			let map = this.weakMap; | ||||
|  | ||||
| 			for ( let i = 0; i < keys.length; i ++ ) { | ||||
|  | ||||
| 				map = map.get( keys[ i ] ); | ||||
|  | ||||
| 				if ( map === undefined ) return undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return map.get( keys[ keys.length - 1 ] ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			return super.get( keys ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	set( keys, value ) { | ||||
|  | ||||
| 		if ( Array.isArray( keys ) ) { | ||||
|  | ||||
| 			let map = this.weakMap; | ||||
|  | ||||
| 			for ( let i = 0; i < keys.length; i ++ ) { | ||||
|  | ||||
| 				const key = keys[ i ]; | ||||
|  | ||||
| 				if ( map.has( key ) === false ) map.set( key, new WeakMap() ); | ||||
|  | ||||
| 				map = map.get( key ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return map.set( keys[ keys.length - 1 ], value ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			return super.set( keys, value ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( keys ) { | ||||
|  | ||||
| 		if ( Array.isArray( keys ) ) { | ||||
|  | ||||
| 			let map = this.weakMap; | ||||
|  | ||||
| 			for ( let i = 0; i < keys.length; i ++ ) { | ||||
|  | ||||
| 				map = map.get( keys[ i ] ); | ||||
|  | ||||
| 				if ( map === undefined ) return false; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return map.delete( keys[ keys.length - 1 ] ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			return super.delete( keys ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.weakMap.clear(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										165
									
								
								public/sdk/three/jsm/renderers/common/ClippingContext.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								public/sdk/three/jsm/renderers/common/ClippingContext.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,165 @@ | ||||
| import { Matrix3, Plane, Vector4 } from 'three'; | ||||
|  | ||||
| const _plane = new Plane(); | ||||
| const _viewNormalMatrix = new Matrix3(); | ||||
|  | ||||
| let _clippingContextVersion = 0; | ||||
|  | ||||
| class ClippingContext { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.version = ++ _clippingContextVersion; | ||||
|  | ||||
| 		this.globalClippingCount = 0; | ||||
|  | ||||
| 		this.localClippingCount = 0; | ||||
| 		this.localClippingEnabled = false; | ||||
| 		this.localClipIntersection = false; | ||||
|  | ||||
| 		this.planes = []; | ||||
|  | ||||
| 		this.parentVersion = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	projectPlanes( source, offset ) { | ||||
|  | ||||
| 		const l = source.length; | ||||
| 		const planes = this.planes; | ||||
|  | ||||
| 		for ( let i = 0; i < l; i ++ ) { | ||||
|  | ||||
| 			_plane.copy( source[ i ] ).applyMatrix4( this.viewMatrix, _viewNormalMatrix ); | ||||
|  | ||||
| 			const v = planes[ offset + i ]; | ||||
| 			const normal = _plane.normal; | ||||
|  | ||||
| 			v.x = - normal.x; | ||||
| 			v.y = - normal.y; | ||||
| 			v.z = - normal.z; | ||||
| 			v.w = _plane.constant; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateGlobal( renderer, camera ) { | ||||
|  | ||||
| 		const rendererClippingPlanes = renderer.clippingPlanes; | ||||
| 		this.viewMatrix = camera.matrixWorldInverse; | ||||
|  | ||||
| 		_viewNormalMatrix.getNormalMatrix( this.viewMatrix ); | ||||
|  | ||||
| 		let update = false; | ||||
|  | ||||
| 		if ( Array.isArray( rendererClippingPlanes ) && rendererClippingPlanes.length !== 0 ) { | ||||
|  | ||||
| 			const l = rendererClippingPlanes.length; | ||||
|  | ||||
| 			if ( l !== this.globalClippingCount ) { | ||||
|  | ||||
| 				const planes = []; | ||||
|  | ||||
| 				for ( let i = 0; i < l; i ++ ) { | ||||
|  | ||||
| 					planes.push( new Vector4() ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				this.globalClippingCount = l; | ||||
| 				this.planes = planes; | ||||
|  | ||||
| 				update = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			this.projectPlanes( rendererClippingPlanes, 0 ); | ||||
|  | ||||
| 		} else if ( this.globalClippingCount !== 0 ) { | ||||
|  | ||||
| 			this.globalClippingCount = 0; | ||||
| 			this.planes = []; | ||||
| 			update = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( renderer.localClippingEnabled !== this.localClippingEnabled ) { | ||||
|  | ||||
| 			this.localClippingEnabled = renderer.localClippingEnabled; | ||||
| 			update = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( update ) this.version = _clippingContextVersion ++; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update( parent, material ) { | ||||
|  | ||||
| 		let update = false; | ||||
|  | ||||
| 		if ( this !== parent && parent.version !== this.parentVersion ) { | ||||
|  | ||||
| 			this.globalClippingCount = material.isShadowNodeMaterial ? 0 : parent.globalClippingCount; | ||||
| 			this.localClippingEnabled = parent.localClippingEnabled; | ||||
| 			this.planes = Array.from( parent.planes ); | ||||
| 			this.parentVersion = parent.version; | ||||
| 			this.viewMatrix = parent.viewMatrix; | ||||
|  | ||||
|  | ||||
| 			update = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( this.localClippingEnabled ) { | ||||
|  | ||||
| 			const localClippingPlanes = material.clippingPlanes; | ||||
|  | ||||
| 			if ( ( Array.isArray( localClippingPlanes ) && localClippingPlanes.length !== 0 ) ) { | ||||
|  | ||||
| 				const l = localClippingPlanes.length; | ||||
| 				const planes = this.planes; | ||||
| 				const offset = this.globalClippingCount; | ||||
|  | ||||
| 				if ( update || l !== this.localClippingCount ) { | ||||
|  | ||||
| 					planes.length = offset + l; | ||||
|  | ||||
| 					for ( let i = 0; i < l; i ++ ) { | ||||
|  | ||||
| 						planes[ offset + i ] = new Vector4(); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					this.localClippingCount = l; | ||||
| 					update = true; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				this.projectPlanes( localClippingPlanes, offset ); | ||||
|  | ||||
|  | ||||
| 			} else if ( this.localClippingCount !== 0 ) { | ||||
|  | ||||
| 				this.localClippingCount = 0; | ||||
| 				update = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( this.localClipIntersection !== material.clipIntersection ) { | ||||
|  | ||||
| 				this.localClipIntersection = material.clipIntersection; | ||||
| 				update = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( update ) this.version = _clippingContextVersion ++; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default ClippingContext; | ||||
							
								
								
									
										37
									
								
								public/sdk/three/jsm/renderers/common/Color4.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								public/sdk/three/jsm/renderers/common/Color4.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| import { Color } from 'three'; | ||||
|  | ||||
| class Color4 extends Color { | ||||
|  | ||||
| 	constructor( r, g, b, a = 1 ) { | ||||
|  | ||||
| 		super( r, g, b ); | ||||
|  | ||||
| 		this.a = a; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	set( r, g, b, a = 1 ) { | ||||
|  | ||||
| 		this.a = a; | ||||
|  | ||||
| 		return super.set( r, g, b ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copy( color ) { | ||||
|  | ||||
| 		if ( color.a !== undefined ) this.a = color.a; | ||||
|  | ||||
| 		return super.copy( color ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	clone() { | ||||
|  | ||||
| 		return new this.constructor( this.r, this.g, this.b, this.a ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Color4; | ||||
							
								
								
									
										17
									
								
								public/sdk/three/jsm/renderers/common/ComputePipeline.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								public/sdk/three/jsm/renderers/common/ComputePipeline.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import Pipeline from './Pipeline.js'; | ||||
|  | ||||
| class ComputePipeline extends Pipeline { | ||||
|  | ||||
| 	constructor( cacheKey, computeProgram ) { | ||||
|  | ||||
| 		super( cacheKey ); | ||||
|  | ||||
| 		this.computeProgram = computeProgram; | ||||
|  | ||||
| 		this.isComputePipeline = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default ComputePipeline; | ||||
							
								
								
									
										14
									
								
								public/sdk/three/jsm/renderers/common/Constants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								public/sdk/three/jsm/renderers/common/Constants.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | ||||
| export const AttributeType = { | ||||
| 	VERTEX: 1, | ||||
| 	INDEX: 2, | ||||
| 	STORAGE: 4 | ||||
| }; | ||||
|  | ||||
| // size of a chunk in bytes (STD140 layout) | ||||
|  | ||||
| export const GPU_CHUNK_BYTES = 16; | ||||
|  | ||||
| // @TODO: Move to src/constants.js | ||||
|  | ||||
| export const BlendColorFactor = 211; | ||||
| export const OneMinusBlendColorFactor = 212; | ||||
							
								
								
									
										65
									
								
								public/sdk/three/jsm/renderers/common/CubeRenderTarget.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								public/sdk/three/jsm/renderers/common/CubeRenderTarget.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| import { WebGLCubeRenderTarget, Scene, CubeCamera, BoxGeometry, Mesh, BackSide, NoBlending, LinearFilter, LinearMipmapLinearFilter } from 'three'; | ||||
| import { equirectUV } from '../../nodes/utils/EquirectUVNode.js'; | ||||
| import { texture as TSL_Texture } from '../../nodes/accessors/TextureNode.js'; | ||||
| import { positionWorldDirection } from '../../nodes/accessors/PositionNode.js'; | ||||
| import { createNodeMaterialFromType } from '../../nodes/materials/NodeMaterial.js'; | ||||
|  | ||||
| // @TODO: Consider rename WebGLCubeRenderTarget to just CubeRenderTarget | ||||
|  | ||||
| class CubeRenderTarget extends WebGLCubeRenderTarget { | ||||
|  | ||||
| 	constructor( size = 1, options = {} ) { | ||||
|  | ||||
| 		super( size, options ); | ||||
|  | ||||
| 		this.isCubeRenderTarget = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	fromEquirectangularTexture( renderer, texture ) { | ||||
|  | ||||
| 		const currentMinFilter = texture.minFilter; | ||||
| 		const currentGenerateMipmaps = texture.generateMipmaps; | ||||
|  | ||||
| 		texture.generateMipmaps = true; | ||||
|  | ||||
| 		this.texture.type = texture.type; | ||||
| 		this.texture.colorSpace = texture.colorSpace; | ||||
|  | ||||
| 		this.texture.generateMipmaps = texture.generateMipmaps; | ||||
| 		this.texture.minFilter = texture.minFilter; | ||||
| 		this.texture.magFilter = texture.magFilter; | ||||
|  | ||||
| 		const geometry = new BoxGeometry( 5, 5, 5 ); | ||||
|  | ||||
| 		const uvNode = equirectUV( positionWorldDirection ); | ||||
|  | ||||
| 		const material = createNodeMaterialFromType( 'MeshBasicNodeMaterial' ); | ||||
| 		material.colorNode = TSL_Texture( texture, uvNode, 0 ); | ||||
| 		material.side = BackSide; | ||||
| 		material.blending = NoBlending; | ||||
|  | ||||
| 		const mesh = new Mesh( geometry, material ); | ||||
|  | ||||
| 		const scene = new Scene(); | ||||
| 		scene.add( mesh ); | ||||
|  | ||||
| 		// Avoid blurred poles | ||||
| 		if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; | ||||
|  | ||||
| 		const camera = new CubeCamera( 1, 10, this ); | ||||
| 		camera.update( renderer, scene ); | ||||
|  | ||||
| 		texture.minFilter = currentMinFilter; | ||||
| 		texture.currentGenerateMipmaps = currentGenerateMipmaps; | ||||
|  | ||||
| 		mesh.geometry.dispose(); | ||||
| 		mesh.material.dispose(); | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default CubeRenderTarget; | ||||
							
								
								
									
										54
									
								
								public/sdk/three/jsm/renderers/common/DataMap.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								public/sdk/three/jsm/renderers/common/DataMap.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| class DataMap { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.data = new WeakMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( object ) { | ||||
|  | ||||
| 		let map = this.data.get( object ); | ||||
|  | ||||
| 		if ( map === undefined ) { | ||||
|  | ||||
| 			map = {}; | ||||
| 			this.data.set( object, map ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return map; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( object ) { | ||||
|  | ||||
| 		let map; | ||||
|  | ||||
| 		if ( this.data.has( object ) ) { | ||||
|  | ||||
| 			map = this.data.get( object ); | ||||
|  | ||||
| 			this.data.delete( object ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return map; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	has( object ) { | ||||
|  | ||||
| 		return this.data.has( object ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.data = new WeakMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default DataMap; | ||||
							
								
								
									
										215
									
								
								public/sdk/three/jsm/renderers/common/Geometries.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								public/sdk/three/jsm/renderers/common/Geometries.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,215 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
| import { AttributeType } from './Constants.js'; | ||||
| import { Uint32BufferAttribute, Uint16BufferAttribute } from 'three'; | ||||
|  | ||||
| function arrayNeedsUint32( array ) { | ||||
|  | ||||
| 	// assumes larger values usually on last | ||||
|  | ||||
| 	for ( let i = array.length - 1; i >= 0; -- i ) { | ||||
|  | ||||
| 		if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
|  | ||||
| } | ||||
|  | ||||
| function getWireframeVersion( geometry ) { | ||||
|  | ||||
| 	return ( geometry.index !== null ) ? geometry.index.version : geometry.attributes.position.version; | ||||
|  | ||||
| } | ||||
|  | ||||
| function getWireframeIndex( geometry ) { | ||||
|  | ||||
| 	const indices = []; | ||||
|  | ||||
| 	const geometryIndex = geometry.index; | ||||
| 	const geometryPosition = geometry.attributes.position; | ||||
|  | ||||
| 	if ( geometryIndex !== null ) { | ||||
|  | ||||
| 		const array = geometryIndex.array; | ||||
|  | ||||
| 		for ( let i = 0, l = array.length; i < l; i += 3 ) { | ||||
|  | ||||
| 			const a = array[ i + 0 ]; | ||||
| 			const b = array[ i + 1 ]; | ||||
| 			const c = array[ i + 2 ]; | ||||
|  | ||||
| 			indices.push( a, b, b, c, c, a ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		const array = geometryPosition.array; | ||||
|  | ||||
| 		for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { | ||||
|  | ||||
| 			const a = i + 0; | ||||
| 			const b = i + 1; | ||||
| 			const c = i + 2; | ||||
|  | ||||
| 			indices.push( a, b, b, c, c, a ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); | ||||
| 	attribute.version = getWireframeVersion( geometry ); | ||||
|  | ||||
| 	return attribute; | ||||
|  | ||||
| } | ||||
|  | ||||
| class Geometries extends DataMap { | ||||
|  | ||||
| 	constructor( attributes, info ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.attributes = attributes; | ||||
| 		this.info = info; | ||||
|  | ||||
| 		this.wireframes = new WeakMap(); | ||||
| 		this.attributeCall = new WeakMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	has( renderObject ) { | ||||
|  | ||||
| 		const geometry = renderObject.geometry; | ||||
|  | ||||
| 		return super.has( geometry ) && this.get( geometry ).initialized === true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForRender( renderObject ) { | ||||
|  | ||||
| 		if ( this.has( renderObject ) === false ) this.initGeometry( renderObject ); | ||||
|  | ||||
| 		this.updateAttributes( renderObject ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	initGeometry( renderObject ) { | ||||
|  | ||||
| 		const geometry = renderObject.geometry; | ||||
| 		const geometryData = this.get( geometry ); | ||||
|  | ||||
| 		geometryData.initialized = true; | ||||
|  | ||||
| 		this.info.memory.geometries ++; | ||||
|  | ||||
| 		const onDispose = () => { | ||||
|  | ||||
| 			this.info.memory.geometries --; | ||||
|  | ||||
| 			const index = geometry.index; | ||||
| 			const geometryAttributes = renderObject.getAttributes(); | ||||
|  | ||||
| 			if ( index !== null ) { | ||||
|  | ||||
| 				this.attributes.delete( index ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			for ( const geometryAttribute of geometryAttributes ) { | ||||
|  | ||||
| 				this.attributes.delete( geometryAttribute ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const wireframeAttribute = this.wireframes.get( geometry ); | ||||
|  | ||||
| 			if ( wireframeAttribute !== undefined ) { | ||||
|  | ||||
| 				this.attributes.delete( wireframeAttribute ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			geometry.removeEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		geometry.addEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateAttributes( renderObject ) { | ||||
|  | ||||
| 		const attributes = renderObject.getAttributes(); | ||||
|  | ||||
| 		for ( const attribute of attributes ) { | ||||
|  | ||||
| 			this.updateAttribute( attribute, AttributeType.VERTEX ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const index = this.getIndex( renderObject ); | ||||
|  | ||||
| 		if ( index !== null ) { | ||||
|  | ||||
| 			this.updateAttribute( index, AttributeType.INDEX ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateAttribute( attribute, type ) { | ||||
|  | ||||
| 		const callId = this.info.render.calls; | ||||
|  | ||||
| 		if ( this.attributeCall.get( attribute ) !== callId ) { | ||||
|  | ||||
| 			this.attributes.update( attribute, type ); | ||||
|  | ||||
| 			this.attributeCall.set( attribute, callId ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getIndex( renderObject ) { | ||||
|  | ||||
| 		const { geometry, material } = renderObject; | ||||
|  | ||||
| 		let index = geometry.index; | ||||
|  | ||||
| 		if ( material.wireframe === true ) { | ||||
|  | ||||
| 			const wireframes = this.wireframes; | ||||
|  | ||||
| 			let wireframeAttribute = wireframes.get( geometry ); | ||||
|  | ||||
| 			if ( wireframeAttribute === undefined ) { | ||||
|  | ||||
| 				wireframeAttribute = getWireframeIndex( geometry ); | ||||
|  | ||||
| 				wireframes.set( geometry, wireframeAttribute ); | ||||
|  | ||||
| 			} else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) { | ||||
|  | ||||
| 				this.attributes.delete( wireframeAttribute ); | ||||
|  | ||||
| 				wireframeAttribute = getWireframeIndex( geometry ); | ||||
|  | ||||
| 				wireframes.set( geometry, wireframeAttribute ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			index = wireframeAttribute; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return index; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Geometries; | ||||
							
								
								
									
										99
									
								
								public/sdk/three/jsm/renderers/common/Info.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								public/sdk/three/jsm/renderers/common/Info.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | ||||
| class Info { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.autoReset = true; | ||||
|  | ||||
| 		this.frame = 0; | ||||
| 		this.calls = 0; | ||||
|  | ||||
| 		this.render = { | ||||
| 			calls: 0, | ||||
| 			drawCalls: 0, | ||||
| 			triangles: 0, | ||||
| 			points: 0, | ||||
| 			lines: 0, | ||||
| 			timestamp: 0 | ||||
| 		}; | ||||
|  | ||||
| 		this.compute = { | ||||
| 			calls: 0, | ||||
| 			computeCalls: 0, | ||||
| 			timestamp: 0 | ||||
| 		}; | ||||
|  | ||||
| 		this.memory = { | ||||
| 			geometries: 0, | ||||
| 			textures: 0 | ||||
| 		}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update( object, count, instanceCount ) { | ||||
|  | ||||
| 		this.render.drawCalls ++; | ||||
|  | ||||
| 		if ( object.isMesh || object.isSprite ) { | ||||
|  | ||||
| 			this.render.triangles += instanceCount * ( count / 3 ); | ||||
|  | ||||
| 		} else if ( object.isPoints ) { | ||||
|  | ||||
| 			this.render.points += instanceCount * count; | ||||
|  | ||||
| 		} else if ( object.isLineSegments ) { | ||||
|  | ||||
| 			this.render.lines += instanceCount * ( count / 2 ); | ||||
|  | ||||
| 		} else if ( object.isLine ) { | ||||
|  | ||||
| 			this.render.lines += instanceCount * ( count - 1 ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			console.error( 'THREE.WebGPUInfo: Unknown object type.' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateTimestamp( type, time ) { | ||||
|  | ||||
| 		this[ type ].timestamp += time; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	reset() { | ||||
|  | ||||
| 		this.render.drawCalls = 0; | ||||
| 		this.compute.computeCalls = 0; | ||||
|  | ||||
| 		this.render.triangles = 0; | ||||
| 		this.render.points = 0; | ||||
| 		this.render.lines = 0; | ||||
|  | ||||
| 		this.render.timestamp = 0; | ||||
| 		this.compute.timestamp = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.reset(); | ||||
|  | ||||
| 		this.calls = 0; | ||||
|  | ||||
| 		this.render.calls = 0; | ||||
| 		this.compute.calls = 0; | ||||
|  | ||||
| 		this.render.timestamp = 0; | ||||
| 		this.compute.timestamp = 0; | ||||
| 		this.memory.geometries = 0; | ||||
| 		this.memory.textures = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| export default Info; | ||||
							
								
								
									
										13
									
								
								public/sdk/three/jsm/renderers/common/Pipeline.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								public/sdk/three/jsm/renderers/common/Pipeline.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| class Pipeline { | ||||
|  | ||||
| 	constructor( cacheKey ) { | ||||
|  | ||||
| 		this.cacheKey = cacheKey; | ||||
|  | ||||
| 		this.usedTimes = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Pipeline; | ||||
							
								
								
									
										322
									
								
								public/sdk/three/jsm/renderers/common/Pipelines.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								public/sdk/three/jsm/renderers/common/Pipelines.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,322 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
| import RenderPipeline from './RenderPipeline.js'; | ||||
| import ComputePipeline from './ComputePipeline.js'; | ||||
| import ProgrammableStage from './ProgrammableStage.js'; | ||||
|  | ||||
| class Pipelines extends DataMap { | ||||
|  | ||||
| 	constructor( backend, nodes ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.backend = backend; | ||||
| 		this.nodes = nodes; | ||||
|  | ||||
| 		this.bindings = null; // set by the bindings | ||||
|  | ||||
| 		this.caches = new Map(); | ||||
| 		this.programs = { | ||||
| 			vertex: new Map(), | ||||
| 			fragment: new Map(), | ||||
| 			compute: new Map() | ||||
| 		}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForCompute( computeNode, bindings ) { | ||||
|  | ||||
| 		const { backend } = this; | ||||
|  | ||||
| 		const data = this.get( computeNode ); | ||||
|  | ||||
| 		if ( this._needsComputeUpdate( computeNode ) ) { | ||||
|  | ||||
| 			const previousPipeline = data.pipeline; | ||||
|  | ||||
| 			if ( previousPipeline ) { | ||||
|  | ||||
| 				previousPipeline.usedTimes --; | ||||
| 				previousPipeline.computeProgram.usedTimes --; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// get shader | ||||
|  | ||||
| 			const nodeBuilderState = this.nodes.getForCompute( computeNode ); | ||||
|  | ||||
| 			// programmable stage | ||||
|  | ||||
| 			let stageCompute = this.programs.compute.get( nodeBuilderState.computeShader ); | ||||
|  | ||||
| 			if ( stageCompute === undefined ) { | ||||
|  | ||||
| 				if ( previousPipeline && previousPipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.computeProgram ); | ||||
|  | ||||
| 				stageCompute = new ProgrammableStage( nodeBuilderState.computeShader, 'compute', nodeBuilderState.transforms, nodeBuilderState.nodeAttributes ); | ||||
| 				this.programs.compute.set( nodeBuilderState.computeShader, stageCompute ); | ||||
|  | ||||
| 				backend.createProgram( stageCompute ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// determine compute pipeline | ||||
|  | ||||
| 			const cacheKey = this._getComputeCacheKey( computeNode, stageCompute ); | ||||
|  | ||||
| 			let pipeline = this.caches.get( cacheKey ); | ||||
|  | ||||
| 			if ( pipeline === undefined ) { | ||||
|  | ||||
| 				if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( computeNode ); | ||||
|  | ||||
| 				pipeline = this._getComputePipeline( computeNode, stageCompute, cacheKey, bindings ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// keep track of all used times | ||||
|  | ||||
| 			pipeline.usedTimes ++; | ||||
| 			stageCompute.usedTimes ++; | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			data.version = computeNode.version; | ||||
| 			data.pipeline = pipeline; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return data.pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForRender( renderObject, promises = null ) { | ||||
|  | ||||
| 		const { backend } = this; | ||||
|  | ||||
| 		const data = this.get( renderObject ); | ||||
|  | ||||
| 		if ( this._needsRenderUpdate( renderObject ) ) { | ||||
|  | ||||
| 			const previousPipeline = data.pipeline; | ||||
|  | ||||
| 			if ( previousPipeline ) { | ||||
|  | ||||
| 				previousPipeline.usedTimes --; | ||||
| 				previousPipeline.vertexProgram.usedTimes --; | ||||
| 				previousPipeline.fragmentProgram.usedTimes --; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// get shader | ||||
|  | ||||
| 			const nodeBuilderState = renderObject.getNodeBuilderState(); | ||||
|  | ||||
| 			// programmable stages | ||||
|  | ||||
| 			let stageVertex = this.programs.vertex.get( nodeBuilderState.vertexShader ); | ||||
|  | ||||
| 			if ( stageVertex === undefined ) { | ||||
|  | ||||
| 				if ( previousPipeline && previousPipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.vertexProgram ); | ||||
|  | ||||
| 				stageVertex = new ProgrammableStage( nodeBuilderState.vertexShader, 'vertex' ); | ||||
| 				this.programs.vertex.set( nodeBuilderState.vertexShader, stageVertex ); | ||||
|  | ||||
| 				backend.createProgram( stageVertex ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			let stageFragment = this.programs.fragment.get( nodeBuilderState.fragmentShader ); | ||||
|  | ||||
| 			if ( stageFragment === undefined ) { | ||||
|  | ||||
| 				if ( previousPipeline && previousPipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( previousPipeline.fragmentProgram ); | ||||
|  | ||||
| 				stageFragment = new ProgrammableStage( nodeBuilderState.fragmentShader, 'fragment' ); | ||||
| 				this.programs.fragment.set( nodeBuilderState.fragmentShader, stageFragment ); | ||||
|  | ||||
| 				backend.createProgram( stageFragment ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// determine render pipeline | ||||
|  | ||||
| 			const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); | ||||
|  | ||||
| 			let pipeline = this.caches.get( cacheKey ); | ||||
|  | ||||
| 			if ( pipeline === undefined ) { | ||||
|  | ||||
| 				if ( previousPipeline && previousPipeline.usedTimes === 0 ) this._releasePipeline( previousPipeline ); | ||||
|  | ||||
| 				pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				renderObject.pipeline = pipeline; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// keep track of all used times | ||||
|  | ||||
| 			pipeline.usedTimes ++; | ||||
| 			stageVertex.usedTimes ++; | ||||
| 			stageFragment.usedTimes ++; | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			data.pipeline = pipeline; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return data.pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( object ) { | ||||
|  | ||||
| 		const pipeline = this.get( object ).pipeline; | ||||
|  | ||||
| 		if ( pipeline ) { | ||||
|  | ||||
| 			// pipeline | ||||
|  | ||||
| 			pipeline.usedTimes --; | ||||
|  | ||||
| 			if ( pipeline.usedTimes === 0 ) this._releasePipeline( pipeline ); | ||||
|  | ||||
| 			// programs | ||||
|  | ||||
| 			if ( pipeline.isComputePipeline ) { | ||||
|  | ||||
| 				pipeline.computeProgram.usedTimes --; | ||||
|  | ||||
| 				if ( pipeline.computeProgram.usedTimes === 0 ) this._releaseProgram( pipeline.computeProgram ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				pipeline.fragmentProgram.usedTimes --; | ||||
| 				pipeline.vertexProgram.usedTimes --; | ||||
|  | ||||
| 				if ( pipeline.vertexProgram.usedTimes === 0 ) this._releaseProgram( pipeline.vertexProgram ); | ||||
| 				if ( pipeline.fragmentProgram.usedTimes === 0 ) this._releaseProgram( pipeline.fragmentProgram ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		super.delete( object ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		super.dispose(); | ||||
|  | ||||
| 		this.caches = new Map(); | ||||
| 		this.programs = { | ||||
| 			vertex: new Map(), | ||||
| 			fragment: new Map(), | ||||
| 			compute: new Map() | ||||
| 		}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForRender( renderObject ) { | ||||
|  | ||||
| 		this.getForRender( renderObject ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getComputePipeline( computeNode, stageCompute, cacheKey, bindings ) { | ||||
|  | ||||
| 		// check for existing pipeline | ||||
|  | ||||
| 		cacheKey = cacheKey || this._getComputeCacheKey( computeNode, stageCompute ); | ||||
|  | ||||
| 		let pipeline = this.caches.get( cacheKey ); | ||||
|  | ||||
| 		if ( pipeline === undefined ) { | ||||
|  | ||||
| 			pipeline = new ComputePipeline( cacheKey, stageCompute ); | ||||
|  | ||||
| 			this.caches.set( cacheKey, pipeline ); | ||||
|  | ||||
| 			this.backend.createComputePipeline( pipeline, bindings ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getRenderPipeline( renderObject, stageVertex, stageFragment, cacheKey, promises ) { | ||||
|  | ||||
| 		// check for existing pipeline | ||||
|  | ||||
| 		cacheKey = cacheKey || this._getRenderCacheKey( renderObject, stageVertex, stageFragment ); | ||||
|  | ||||
| 		let pipeline = this.caches.get( cacheKey ); | ||||
|  | ||||
| 		if ( pipeline === undefined ) { | ||||
|  | ||||
| 			pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment ); | ||||
|  | ||||
| 			this.caches.set( cacheKey, pipeline ); | ||||
|  | ||||
| 			renderObject.pipeline = pipeline; | ||||
|  | ||||
| 			this.backend.createRenderPipeline( renderObject, promises ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getComputeCacheKey( computeNode, stageCompute ) { | ||||
|  | ||||
| 		return computeNode.id + ',' + stageCompute.id; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getRenderCacheKey( renderObject, stageVertex, stageFragment ) { | ||||
|  | ||||
| 		return stageVertex.id + ',' + stageFragment.id + ',' + this.backend.getRenderCacheKey( renderObject ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_releasePipeline( pipeline ) { | ||||
|  | ||||
| 		this.caches.delete( pipeline.cacheKey ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_releaseProgram( program ) { | ||||
|  | ||||
| 		const code = program.code; | ||||
| 		const stage = program.stage; | ||||
|  | ||||
| 		this.programs[ stage ].delete( code ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_needsComputeUpdate( computeNode ) { | ||||
|  | ||||
| 		const data = this.get( computeNode ); | ||||
|  | ||||
| 		return data.pipeline === undefined || data.version !== computeNode.version; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_needsRenderUpdate( renderObject ) { | ||||
|  | ||||
| 		const data = this.get( renderObject ); | ||||
|  | ||||
| 		return data.pipeline === undefined || this.backend.needsRenderUpdate( renderObject ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Pipelines; | ||||
							
								
								
									
										33
									
								
								public/sdk/three/jsm/renderers/common/PostProcessing.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								public/sdk/three/jsm/renderers/common/PostProcessing.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| import { vec4, NodeMaterial } from '../../nodes/Nodes.js'; | ||||
| import QuadMesh from '../../objects/QuadMesh.js'; | ||||
|  | ||||
| const quadMesh = new QuadMesh( new NodeMaterial() ); | ||||
|  | ||||
| class PostProcessing { | ||||
|  | ||||
| 	constructor( renderer, outputNode = vec4( 0, 0, 1, 1 ) ) { | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.outputNode = outputNode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	render() { | ||||
|  | ||||
| 		quadMesh.material.fragmentNode = this.outputNode; | ||||
|  | ||||
| 		quadMesh.render( this.renderer ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	renderAsync() { | ||||
|  | ||||
| 		quadMesh.material.fragmentNode = this.outputNode; | ||||
|  | ||||
| 		return quadMesh.renderAsync( this.renderer ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default PostProcessing; | ||||
							
								
								
									
										20
									
								
								public/sdk/three/jsm/renderers/common/ProgrammableStage.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								public/sdk/three/jsm/renderers/common/ProgrammableStage.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| let _id = 0; | ||||
|  | ||||
| class ProgrammableStage { | ||||
|  | ||||
| 	constructor( code, type, transforms = null, attributes = null ) { | ||||
|  | ||||
| 		this.id = _id ++; | ||||
|  | ||||
| 		this.code = code; | ||||
| 		this.stage = type; | ||||
| 		this.transforms = transforms; | ||||
| 		this.attributes = attributes; | ||||
|  | ||||
| 		this.usedTimes = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default ProgrammableStage; | ||||
							
								
								
									
										43
									
								
								public/sdk/three/jsm/renderers/common/RenderContext.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								public/sdk/three/jsm/renderers/common/RenderContext.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| import { Vector4 } from 'three'; | ||||
|  | ||||
| let id = 0; | ||||
|  | ||||
| class RenderContext { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.id = id ++; | ||||
|  | ||||
| 		this.color = true; | ||||
| 		this.clearColor = true; | ||||
| 		this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 }; | ||||
|  | ||||
| 		this.depth = true; | ||||
| 		this.clearDepth = true; | ||||
| 		this.clearDepthValue = 1; | ||||
|  | ||||
| 		this.stencil = false; | ||||
| 		this.clearStencil = true; | ||||
| 		this.clearStencilValue = 1; | ||||
|  | ||||
| 		this.viewport = false; | ||||
| 		this.viewportValue = new Vector4(); | ||||
|  | ||||
| 		this.scissor = false; | ||||
| 		this.scissorValue = new Vector4(); | ||||
|  | ||||
| 		this.textures = null; | ||||
| 		this.depthTexture = null; | ||||
| 		this.activeCubeFace = 0; | ||||
| 		this.sampleCount = 1; | ||||
|  | ||||
| 		this.width = 0; | ||||
| 		this.height = 0; | ||||
|  | ||||
| 		this.isRenderContext = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderContext; | ||||
							
								
								
									
										63
									
								
								public/sdk/three/jsm/renderers/common/RenderContexts.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								public/sdk/three/jsm/renderers/common/RenderContexts.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| import ChainMap from './ChainMap.js'; | ||||
| import RenderContext from './RenderContext.js'; | ||||
|  | ||||
| class RenderContexts { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.chainMaps = {}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( scene, camera, renderTarget = null ) { | ||||
|  | ||||
| 		const chainKey = [ scene, camera ]; | ||||
|  | ||||
| 		let attachmentState; | ||||
|  | ||||
| 		if ( renderTarget === null ) { | ||||
|  | ||||
| 			attachmentState = 'default'; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const format = renderTarget.texture.format; | ||||
| 			const count = renderTarget.count; | ||||
|  | ||||
| 			attachmentState = `${ count }:${ format }:${ renderTarget.samples }:${ renderTarget.depthBuffer }:${ renderTarget.stencilBuffer }`; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const chainMap = this.getChainMap( attachmentState ); | ||||
|  | ||||
| 		let renderState = chainMap.get( chainKey ); | ||||
|  | ||||
| 		if ( renderState === undefined ) { | ||||
|  | ||||
| 			renderState = new RenderContext(); | ||||
|  | ||||
| 			chainMap.set( chainKey, renderState ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( renderTarget !== null ) renderState.sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples; | ||||
|  | ||||
| 		return renderState; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getChainMap( attachmentState ) { | ||||
|  | ||||
| 		return this.chainMaps[ attachmentState ] || ( this.chainMaps[ attachmentState ] = new ChainMap() ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.chainMaps = {}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderContexts; | ||||
							
								
								
									
										186
									
								
								public/sdk/three/jsm/renderers/common/RenderList.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								public/sdk/three/jsm/renderers/common/RenderList.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,186 @@ | ||||
| import { LightsNode } from '../../nodes/Nodes.js'; | ||||
|  | ||||
| function painterSortStable( a, b ) { | ||||
|  | ||||
| 	if ( a.groupOrder !== b.groupOrder ) { | ||||
|  | ||||
| 		return a.groupOrder - b.groupOrder; | ||||
|  | ||||
| 	} else if ( a.renderOrder !== b.renderOrder ) { | ||||
|  | ||||
| 		return a.renderOrder - b.renderOrder; | ||||
|  | ||||
| 	} else if ( a.material.id !== b.material.id ) { | ||||
|  | ||||
| 		return a.material.id - b.material.id; | ||||
|  | ||||
| 	} else if ( a.z !== b.z ) { | ||||
|  | ||||
| 		return a.z - b.z; | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		return a.id - b.id; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| function reversePainterSortStable( a, b ) { | ||||
|  | ||||
| 	if ( a.groupOrder !== b.groupOrder ) { | ||||
|  | ||||
| 		return a.groupOrder - b.groupOrder; | ||||
|  | ||||
| 	} else if ( a.renderOrder !== b.renderOrder ) { | ||||
|  | ||||
| 		return a.renderOrder - b.renderOrder; | ||||
|  | ||||
| 	} else if ( a.z !== b.z ) { | ||||
|  | ||||
| 		return b.z - a.z; | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		return a.id - b.id; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class RenderList { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.renderItems = []; | ||||
| 		this.renderItemsIndex = 0; | ||||
|  | ||||
| 		this.opaque = []; | ||||
| 		this.transparent = []; | ||||
|  | ||||
| 		this.lightsNode = new LightsNode( [] ); | ||||
| 		this.lightsArray = []; | ||||
|  | ||||
| 		this.occlusionQueryCount = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	begin() { | ||||
|  | ||||
| 		this.renderItemsIndex = 0; | ||||
|  | ||||
| 		this.opaque.length = 0; | ||||
| 		this.transparent.length = 0; | ||||
| 		this.lightsArray.length = 0; | ||||
|  | ||||
| 		this.occlusionQueryCount = 0; | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNextRenderItem( object, geometry, material, groupOrder, z, group ) { | ||||
|  | ||||
| 		let renderItem = this.renderItems[ this.renderItemsIndex ]; | ||||
|  | ||||
| 		if ( renderItem === undefined ) { | ||||
|  | ||||
| 			renderItem = { | ||||
| 				id: object.id, | ||||
| 				object: object, | ||||
| 				geometry: geometry, | ||||
| 				material: material, | ||||
| 				groupOrder: groupOrder, | ||||
| 				renderOrder: object.renderOrder, | ||||
| 				z: z, | ||||
| 				group: group | ||||
| 			}; | ||||
|  | ||||
| 			this.renderItems[ this.renderItemsIndex ] = renderItem; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			renderItem.id = object.id; | ||||
| 			renderItem.object = object; | ||||
| 			renderItem.geometry = geometry; | ||||
| 			renderItem.material = material; | ||||
| 			renderItem.groupOrder = groupOrder; | ||||
| 			renderItem.renderOrder = object.renderOrder; | ||||
| 			renderItem.z = z; | ||||
| 			renderItem.group = group; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.renderItemsIndex ++; | ||||
|  | ||||
| 		return renderItem; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	push( object, geometry, material, groupOrder, z, group ) { | ||||
|  | ||||
| 		const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group ); | ||||
|  | ||||
| 		if ( object.occlusionTest === true ) this.occlusionQueryCount ++; | ||||
|  | ||||
| 		( material.transparent === true || material.transmission > 0 ? this.transparent : this.opaque ).push( renderItem ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	unshift( object, geometry, material, groupOrder, z, group ) { | ||||
|  | ||||
| 		const renderItem = this.getNextRenderItem( object, geometry, material, groupOrder, z, group ); | ||||
|  | ||||
| 		( material.transparent === true ? this.transparent : this.opaque ).unshift( renderItem ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	pushLight( light ) { | ||||
|  | ||||
| 		this.lightsArray.push( light ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getLightsNode() { | ||||
|  | ||||
| 		return this.lightsNode.fromLights( this.lightsArray ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	sort( customOpaqueSort, customTransparentSort ) { | ||||
|  | ||||
| 		if ( this.opaque.length > 1 ) this.opaque.sort( customOpaqueSort || painterSortStable ); | ||||
| 		if ( this.transparent.length > 1 ) this.transparent.sort( customTransparentSort || reversePainterSortStable ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	finish() { | ||||
|  | ||||
| 		// update lights | ||||
|  | ||||
| 		this.lightsNode.fromLights( this.lightsArray ); | ||||
|  | ||||
| 		// Clear references from inactive renderItems in the list | ||||
|  | ||||
| 		for ( let i = this.renderItemsIndex, il = this.renderItems.length; i < il; i ++ ) { | ||||
|  | ||||
| 			const renderItem = this.renderItems[ i ]; | ||||
|  | ||||
| 			if ( renderItem.id === null ) break; | ||||
|  | ||||
| 			renderItem.id = null; | ||||
| 			renderItem.object = null; | ||||
| 			renderItem.geometry = null; | ||||
| 			renderItem.material = null; | ||||
| 			renderItem.groupOrder = null; | ||||
| 			renderItem.renderOrder = null; | ||||
| 			renderItem.z = null; | ||||
| 			renderItem.group = null; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderList; | ||||
							
								
								
									
										38
									
								
								public/sdk/three/jsm/renderers/common/RenderLists.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								public/sdk/three/jsm/renderers/common/RenderLists.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| import ChainMap from './ChainMap.js'; | ||||
| import RenderList from './RenderList.js'; | ||||
|  | ||||
| class RenderLists { | ||||
|  | ||||
| 	constructor() { | ||||
|  | ||||
| 		this.lists = new ChainMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( scene, camera ) { | ||||
|  | ||||
| 		const lists = this.lists; | ||||
| 		const keys = [ scene, camera ]; | ||||
|  | ||||
| 		let list = lists.get( keys ); | ||||
|  | ||||
| 		if ( list === undefined ) { | ||||
|  | ||||
| 			list = new RenderList(); | ||||
| 			lists.set( keys, list ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return list; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.lists = new ChainMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderLists; | ||||
							
								
								
									
										263
									
								
								public/sdk/three/jsm/renderers/common/RenderObject.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								public/sdk/three/jsm/renderers/common/RenderObject.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,263 @@ | ||||
| import ClippingContext from './ClippingContext.js'; | ||||
|  | ||||
| let id = 0; | ||||
|  | ||||
| function getKeys( obj ) { | ||||
|  | ||||
| 	const keys = Object.keys( obj ); | ||||
|  | ||||
| 	let proto = Object.getPrototypeOf( obj ); | ||||
|  | ||||
| 	while ( proto ) { | ||||
|  | ||||
| 		const descriptors = Object.getOwnPropertyDescriptors( proto ); | ||||
|  | ||||
| 		for ( const key in descriptors ) { | ||||
|  | ||||
| 			if ( descriptors[ key ] !== undefined ) { | ||||
|  | ||||
| 				const descriptor = descriptors[ key ]; | ||||
|  | ||||
| 				if ( descriptor && typeof descriptor.get === 'function' ) { | ||||
|  | ||||
| 					keys.push( key ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		proto = Object.getPrototypeOf( proto ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return keys; | ||||
|  | ||||
| } | ||||
|  | ||||
| export default class RenderObject { | ||||
|  | ||||
| 	constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext ) { | ||||
|  | ||||
| 		this._nodes = nodes; | ||||
| 		this._geometries = geometries; | ||||
|  | ||||
| 		this.id = id ++; | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.object = object; | ||||
| 		this.material = material; | ||||
| 		this.scene = scene; | ||||
| 		this.camera = camera; | ||||
| 		this.lightsNode = lightsNode; | ||||
| 		this.context = renderContext; | ||||
|  | ||||
| 		this.geometry = object.geometry; | ||||
| 		this.version = material.version; | ||||
|  | ||||
| 		this.drawRange = null; | ||||
|  | ||||
| 		this.attributes = null; | ||||
| 		this.pipeline = null; | ||||
| 		this.vertexBuffers = null; | ||||
|  | ||||
| 		this.updateClipping( renderContext.clippingContext ); | ||||
|  | ||||
| 		this.clippingContextVersion = this.clippingContext.version; | ||||
|  | ||||
| 		this.initialNodesCacheKey = this.getNodesCacheKey(); | ||||
| 		this.initialCacheKey = this.getCacheKey(); | ||||
|  | ||||
| 		this._nodeBuilderState = null; | ||||
| 		this._bindings = null; | ||||
|  | ||||
| 		this.onDispose = null; | ||||
|  | ||||
| 		this.isRenderObject = true; | ||||
|  | ||||
| 		this.onMaterialDispose = () => { | ||||
|  | ||||
| 			this.dispose(); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.material.addEventListener( 'dispose', this.onMaterialDispose ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateClipping( parent ) { | ||||
|  | ||||
| 		const material = this.material; | ||||
|  | ||||
| 		let clippingContext = this.clippingContext; | ||||
|  | ||||
| 		if ( Array.isArray( material.clippingPlanes ) ) { | ||||
|  | ||||
| 			if ( clippingContext === parent || ! clippingContext ) { | ||||
|  | ||||
| 				clippingContext = new ClippingContext(); | ||||
| 				this.clippingContext = clippingContext; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			clippingContext.update( parent, material ); | ||||
|  | ||||
| 		} else if ( this.clippingContext !== parent ) { | ||||
|  | ||||
| 			this.clippingContext = parent; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get clippingNeedsUpdate() { | ||||
|  | ||||
| 		if ( this.clippingContext.version === this.clippingContextVersion ) return false; | ||||
|  | ||||
| 		this.clippingContextVersion = this.clippingContext.version; | ||||
|  | ||||
| 		return true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNodeBuilderState() { | ||||
|  | ||||
| 		return this._nodeBuilderState || ( this._nodeBuilderState = this._nodes.getForRender( this ) ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getBindings() { | ||||
|  | ||||
| 		return this._bindings || ( this._bindings = this.getNodeBuilderState().createBindings() ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getIndex() { | ||||
|  | ||||
| 		return this._geometries.getIndex( this ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getChainArray() { | ||||
|  | ||||
| 		return [ this.object, this.material, this.context, this.lightsNode ]; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getAttributes() { | ||||
|  | ||||
| 		if ( this.attributes !== null ) return this.attributes; | ||||
|  | ||||
| 		const nodeAttributes = this.getNodeBuilderState().nodeAttributes; | ||||
| 		const geometry = this.geometry; | ||||
|  | ||||
| 		const attributes = []; | ||||
| 		const vertexBuffers = new Set(); | ||||
|  | ||||
| 		for ( const nodeAttribute of nodeAttributes ) { | ||||
|  | ||||
| 			const attribute = nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name ); | ||||
|  | ||||
| 			if ( attribute === undefined ) continue; | ||||
|  | ||||
| 			attributes.push( attribute ); | ||||
|  | ||||
| 			const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute; | ||||
| 			vertexBuffers.add( bufferAttribute ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.attributes = attributes; | ||||
| 		this.vertexBuffers = Array.from( vertexBuffers.values() ); | ||||
|  | ||||
| 		return attributes; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getVertexBuffers() { | ||||
|  | ||||
| 		if ( this.vertexBuffers === null ) this.getAttributes(); | ||||
|  | ||||
| 		return this.vertexBuffers; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getMaterialCacheKey() { | ||||
|  | ||||
| 		const { object, material } = this; | ||||
|  | ||||
| 		let cacheKey = material.customProgramCacheKey(); | ||||
|  | ||||
| 		for ( const property of getKeys( material ) ) { | ||||
|  | ||||
| 			if ( /^(is[A-Z]|_)|^(visible|version|uuid|name|opacity|userData)$/.test( property ) ) continue; | ||||
|  | ||||
| 			let value = material[ property ]; | ||||
|  | ||||
| 			if ( value !== null ) { | ||||
|  | ||||
| 				const type = typeof value; | ||||
|  | ||||
| 				if ( type === 'number' ) value = value !== 0 ? '1' : '0'; // Convert to on/off, important for clearcoat, transmission, etc | ||||
| 				else if ( type === 'object' ) value = '{}'; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			cacheKey += /*property + ':' +*/ value + ','; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		cacheKey += this.clippingContextVersion + ','; | ||||
|  | ||||
| 		if ( object.skeleton ) { | ||||
|  | ||||
| 			cacheKey += object.skeleton.bones.length + ','; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( object.morphTargetInfluences ) { | ||||
|  | ||||
| 			cacheKey += object.morphTargetInfluences.length + ','; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( object.isBatchedMesh ) { | ||||
|  | ||||
| 			cacheKey += object._matricesTexture.uuid + ','; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return cacheKey; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get needsUpdate() { | ||||
|  | ||||
| 		return this.initialNodesCacheKey !== this.getNodesCacheKey() || this.clippingNeedsUpdate; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNodesCacheKey() { | ||||
|  | ||||
| 		// Environment Nodes Cache Key | ||||
|  | ||||
| 		return this._nodes.getCacheKey( this.scene, this.lightsNode ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCacheKey() { | ||||
|  | ||||
| 		return this.getMaterialCacheKey() + ',' + this.getNodesCacheKey(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.material.removeEventListener( 'dispose', this.onMaterialDispose ); | ||||
|  | ||||
| 		this.onDispose(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										93
									
								
								public/sdk/three/jsm/renderers/common/RenderObjects.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								public/sdk/three/jsm/renderers/common/RenderObjects.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| import ChainMap from './ChainMap.js'; | ||||
| import RenderObject from './RenderObject.js'; | ||||
|  | ||||
| class RenderObjects { | ||||
|  | ||||
| 	constructor( renderer, nodes, geometries, pipelines, bindings, info ) { | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.nodes = nodes; | ||||
| 		this.geometries = geometries; | ||||
| 		this.pipelines = pipelines; | ||||
| 		this.bindings = bindings; | ||||
| 		this.info = info; | ||||
|  | ||||
| 		this.chainMaps = {}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( object, material, scene, camera, lightsNode, renderContext, passId ) { | ||||
|  | ||||
| 		const chainMap = this.getChainMap( passId ); | ||||
| 		const chainArray = [ object, material, renderContext, lightsNode ]; | ||||
|  | ||||
| 		let renderObject = chainMap.get( chainArray ); | ||||
|  | ||||
| 		if ( renderObject === undefined ) { | ||||
|  | ||||
| 			renderObject = this.createRenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode, renderContext, passId ); | ||||
|  | ||||
| 			chainMap.set( chainArray, renderObject ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			renderObject.updateClipping( renderContext.clippingContext ); | ||||
|  | ||||
| 			if ( renderObject.version !== material.version || renderObject.needsUpdate ) { | ||||
|  | ||||
| 				if ( renderObject.initialCacheKey !== renderObject.getCacheKey() ) { | ||||
|  | ||||
| 					renderObject.dispose(); | ||||
|  | ||||
| 					renderObject = this.get( object, material, scene, camera, lightsNode, renderContext, passId ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					renderObject.version = material.version; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return renderObject; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getChainMap( passId = 'default' ) { | ||||
|  | ||||
| 		return this.chainMaps[ passId ] || ( this.chainMaps[ passId ] = new ChainMap() ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		this.chainMaps = {}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createRenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext, passId ) { | ||||
|  | ||||
| 		const chainMap = this.getChainMap( passId ); | ||||
|  | ||||
| 		const renderObject = new RenderObject( nodes, geometries, renderer, object, material, scene, camera, lightsNode, renderContext ); | ||||
|  | ||||
| 		renderObject.onDispose = () => { | ||||
|  | ||||
| 			this.pipelines.delete( renderObject ); | ||||
| 			this.bindings.delete( renderObject ); | ||||
| 			this.nodes.delete( renderObject ); | ||||
|  | ||||
| 			chainMap.delete( renderObject.getChainArray() ); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		return renderObject; | ||||
|  | ||||
| 	} | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderObjects; | ||||
							
								
								
									
										16
									
								
								public/sdk/three/jsm/renderers/common/RenderPipeline.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								public/sdk/three/jsm/renderers/common/RenderPipeline.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| import Pipeline from './Pipeline.js'; | ||||
|  | ||||
| class RenderPipeline extends Pipeline { | ||||
|  | ||||
| 	constructor( cacheKey, vertexProgram, fragmentProgram ) { | ||||
|  | ||||
| 		super( cacheKey ); | ||||
|  | ||||
| 		this.vertexProgram = vertexProgram; | ||||
| 		this.fragmentProgram = fragmentProgram; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default RenderPipeline; | ||||
							
								
								
									
										1436
									
								
								public/sdk/three/jsm/renderers/common/Renderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1436
									
								
								public/sdk/three/jsm/renderers/common/Renderer.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										83
									
								
								public/sdk/three/jsm/renderers/common/SampledTexture.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								public/sdk/three/jsm/renderers/common/SampledTexture.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| import Binding from './Binding.js'; | ||||
|  | ||||
| let id = 0; | ||||
|  | ||||
| class SampledTexture extends Binding { | ||||
|  | ||||
| 	constructor( name, texture ) { | ||||
|  | ||||
| 		super( name ); | ||||
|  | ||||
| 		this.id = id ++; | ||||
|  | ||||
| 		this.texture = texture; | ||||
| 		this.version = texture ? texture.version : 0; | ||||
| 		this.store = false; | ||||
|  | ||||
| 		this.isSampledTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get needsBindingsUpdate() { | ||||
|  | ||||
| 		const { texture, version } = this; | ||||
|  | ||||
| 		return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( add it just to External Textures like PNG,JPG ) | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update() { | ||||
|  | ||||
| 		const { texture, version } = this; | ||||
|  | ||||
| 		if ( version !== texture.version ) { | ||||
|  | ||||
| 			this.version = texture.version; | ||||
|  | ||||
| 			return true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class SampledArrayTexture extends SampledTexture { | ||||
|  | ||||
| 	constructor( name, texture ) { | ||||
|  | ||||
| 		super( name, texture ); | ||||
|  | ||||
| 		this.isSampledArrayTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Sampled3DTexture extends SampledTexture { | ||||
|  | ||||
| 	constructor( name, texture ) { | ||||
|  | ||||
| 		super( name, texture ); | ||||
|  | ||||
| 		this.isSampled3DTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class SampledCubeTexture extends SampledTexture { | ||||
|  | ||||
| 	constructor( name, texture ) { | ||||
|  | ||||
| 		super( name, texture ); | ||||
|  | ||||
| 		this.isSampledCubeTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { SampledTexture, SampledArrayTexture, Sampled3DTexture, SampledCubeTexture }; | ||||
							
								
								
									
										18
									
								
								public/sdk/three/jsm/renderers/common/Sampler.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								public/sdk/three/jsm/renderers/common/Sampler.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| import Binding from './Binding.js'; | ||||
|  | ||||
| class Sampler extends Binding { | ||||
|  | ||||
| 	constructor( name, texture ) { | ||||
|  | ||||
| 		super( name ); | ||||
|  | ||||
| 		this.texture = texture; | ||||
| 		this.version = texture ? texture.version : 0; | ||||
|  | ||||
| 		this.isSampler = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Sampler; | ||||
							
								
								
									
										17
									
								
								public/sdk/three/jsm/renderers/common/StorageBuffer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								public/sdk/three/jsm/renderers/common/StorageBuffer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import Buffer from './Buffer.js'; | ||||
|  | ||||
| class StorageBuffer extends Buffer { | ||||
|  | ||||
| 	constructor( name, attribute ) { | ||||
|  | ||||
| 		super( name, attribute ? attribute.array : null ); | ||||
|  | ||||
| 		this.attribute = attribute; | ||||
|  | ||||
| 		this.isStorageBuffer = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default StorageBuffer; | ||||
| @ -0,0 +1,17 @@ | ||||
| import { BufferAttribute } from 'three'; | ||||
|  | ||||
| class StorageBufferAttribute extends BufferAttribute { | ||||
|  | ||||
| 	constructor( array, itemSize, typeClass = Float32Array ) { | ||||
|  | ||||
| 		if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize ); | ||||
|  | ||||
| 		super( array, itemSize ); | ||||
|  | ||||
| 		this.isStorageBufferAttribute = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default StorageBufferAttribute; | ||||
| @ -0,0 +1,17 @@ | ||||
| import { InstancedBufferAttribute } from 'three'; | ||||
|  | ||||
| class StorageInstancedBufferAttribute extends InstancedBufferAttribute { | ||||
|  | ||||
| 	constructor( array, itemSize, typeClass = Float32Array ) { | ||||
|  | ||||
| 		if ( ArrayBuffer.isView( array ) === false ) array = new typeClass( array * itemSize ); | ||||
|  | ||||
| 		super( array, itemSize ); | ||||
|  | ||||
| 		this.isStorageInstancedBufferAttribute = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default StorageInstancedBufferAttribute; | ||||
							
								
								
									
										20
									
								
								public/sdk/three/jsm/renderers/common/StorageTexture.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								public/sdk/three/jsm/renderers/common/StorageTexture.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| import { Texture, LinearFilter } from 'three'; | ||||
|  | ||||
| class StorageTexture extends Texture { | ||||
|  | ||||
| 	constructor( width = 1, height = 1 ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.image = { width, height }; | ||||
|  | ||||
| 		this.magFilter = LinearFilter; | ||||
| 		this.minFilter = LinearFilter; | ||||
|  | ||||
| 		this.isStorageTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default StorageTexture; | ||||
							
								
								
									
										344
									
								
								public/sdk/three/jsm/renderers/common/Textures.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								public/sdk/three/jsm/renderers/common/Textures.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,344 @@ | ||||
| import DataMap from './DataMap.js'; | ||||
|  | ||||
| import { Vector3, DepthTexture, DepthStencilFormat, DepthFormat, UnsignedIntType, UnsignedInt248Type, LinearFilter, NearestFilter, EquirectangularReflectionMapping, EquirectangularRefractionMapping, CubeReflectionMapping, CubeRefractionMapping, UnsignedByteType } from 'three'; | ||||
|  | ||||
| const _size = new Vector3(); | ||||
|  | ||||
| class Textures extends DataMap { | ||||
|  | ||||
| 	constructor( renderer, backend, info ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.backend = backend; | ||||
| 		this.info = info; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateRenderTarget( renderTarget, activeMipmapLevel = 0 ) { | ||||
|  | ||||
| 		const renderTargetData = this.get( renderTarget ); | ||||
|  | ||||
| 		const sampleCount = renderTarget.samples === 0 ? 1 : renderTarget.samples; | ||||
| 		const depthTextureMips = renderTargetData.depthTextureMips || ( renderTargetData.depthTextureMips = {} ); | ||||
|  | ||||
| 		const texture = renderTarget.texture; | ||||
| 		const textures = renderTarget.textures; | ||||
|  | ||||
| 		const size = this.getSize( texture ); | ||||
|  | ||||
| 		const mipWidth = size.width >> activeMipmapLevel; | ||||
| 		const mipHeight = size.height >> activeMipmapLevel; | ||||
|  | ||||
| 		let depthTexture = renderTarget.depthTexture || depthTextureMips[ activeMipmapLevel ]; | ||||
| 		let textureNeedsUpdate = false; | ||||
|  | ||||
| 		if ( depthTexture === undefined ) { | ||||
|  | ||||
| 			depthTexture = new DepthTexture(); | ||||
| 			depthTexture.format = renderTarget.stencilBuffer ? DepthStencilFormat : DepthFormat; | ||||
| 			depthTexture.type = renderTarget.stencilBuffer ? UnsignedInt248Type : UnsignedIntType; // FloatType | ||||
| 			depthTexture.image.width = mipWidth; | ||||
| 			depthTexture.image.height = mipHeight; | ||||
|  | ||||
| 			depthTextureMips[ activeMipmapLevel ] = depthTexture; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( renderTargetData.width !== size.width || size.height !== renderTargetData.height ) { | ||||
|  | ||||
| 			textureNeedsUpdate = true; | ||||
| 			depthTexture.needsUpdate = true; | ||||
|  | ||||
| 			depthTexture.image.width = mipWidth; | ||||
| 			depthTexture.image.height = mipHeight; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		renderTargetData.width = size.width; | ||||
| 		renderTargetData.height = size.height; | ||||
| 		renderTargetData.textures = textures; | ||||
| 		renderTargetData.depthTexture = depthTexture; | ||||
| 		renderTargetData.depth = renderTarget.depthBuffer; | ||||
| 		renderTargetData.stencil = renderTarget.stencilBuffer; | ||||
| 		renderTargetData.renderTarget = renderTarget; | ||||
|  | ||||
| 		if ( renderTargetData.sampleCount !== sampleCount ) { | ||||
|  | ||||
| 			textureNeedsUpdate = true; | ||||
| 			depthTexture.needsUpdate = true; | ||||
|  | ||||
| 			renderTargetData.sampleCount = sampleCount; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		const options = { sampleCount }; | ||||
|  | ||||
| 		for ( let i = 0; i < textures.length; i ++ ) { | ||||
|  | ||||
| 			const texture = textures[ i ]; | ||||
|  | ||||
| 			if ( textureNeedsUpdate ) texture.needsUpdate = true; | ||||
|  | ||||
| 			this.updateTexture( texture, options ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.updateTexture( depthTexture, options ); | ||||
|  | ||||
| 		// dispose handler | ||||
|  | ||||
| 		if ( renderTargetData.initialized !== true ) { | ||||
|  | ||||
| 			renderTargetData.initialized = true; | ||||
|  | ||||
| 			// dispose | ||||
|  | ||||
| 			const onDispose = () => { | ||||
|  | ||||
| 				renderTarget.removeEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 				if ( textures !== undefined ) { | ||||
|  | ||||
| 					for ( let i = 0; i < textures.length; i ++ ) { | ||||
|  | ||||
| 						this._destroyTexture( textures[ i ] ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					this._destroyTexture( texture ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				this._destroyTexture( depthTexture ); | ||||
|  | ||||
| 			}; | ||||
|  | ||||
| 			renderTarget.addEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateTexture( texture, options = {} ) { | ||||
|  | ||||
| 		const textureData = this.get( texture ); | ||||
| 		if ( textureData.initialized === true && textureData.version === texture.version ) return; | ||||
|  | ||||
| 		const isRenderTarget = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture; | ||||
| 		const backend = this.backend; | ||||
|  | ||||
| 		if ( isRenderTarget && textureData.initialized === true ) { | ||||
|  | ||||
| 			// it's an update | ||||
|  | ||||
| 			backend.destroySampler( texture ); | ||||
| 			backend.destroyTexture( texture ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		if ( texture.isFramebufferTexture ) { | ||||
|  | ||||
| 			const renderer = this.renderer; | ||||
| 			const renderTarget = renderer.getRenderTarget(); | ||||
|  | ||||
| 			if ( renderTarget ) { | ||||
|  | ||||
| 				texture.type = renderTarget.texture.type; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				texture.type = UnsignedByteType; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		const { width, height, depth } = this.getSize( texture ); | ||||
|  | ||||
| 		options.width = width; | ||||
| 		options.height = height; | ||||
| 		options.depth = depth; | ||||
| 		options.needsMipmaps = this.needsMipmaps( texture ); | ||||
| 		options.levels = options.needsMipmaps ? this.getMipLevels( texture, width, height ) : 1; | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		if ( isRenderTarget || texture.isStorageTexture === true ) { | ||||
|  | ||||
| 			backend.createSampler( texture ); | ||||
| 			backend.createTexture( texture, options ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const needsCreate = textureData.initialized !== true; | ||||
|  | ||||
| 			if ( needsCreate ) backend.createSampler( texture ); | ||||
|  | ||||
| 			if ( texture.version > 0 ) { | ||||
|  | ||||
| 				const image = texture.image; | ||||
|  | ||||
| 				if ( image === undefined ) { | ||||
|  | ||||
| 					console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' ); | ||||
|  | ||||
| 				} else if ( image.complete === false ) { | ||||
|  | ||||
| 					console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					if ( texture.images ) { | ||||
|  | ||||
| 						const images = []; | ||||
|  | ||||
| 						for ( const image of texture.images ) { | ||||
|  | ||||
| 							images.push( image ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 						options.images = images; | ||||
|  | ||||
| 					} else { | ||||
|  | ||||
| 						options.image = image; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) { | ||||
|  | ||||
| 						backend.createTexture( texture, options ); | ||||
|  | ||||
| 						textureData.isDefaultTexture = false; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 					if ( texture.source.dataReady === true ) backend.updateTexture( texture, options ); | ||||
|  | ||||
| 					if ( options.needsMipmaps && texture.mipmaps.length === 0 ) backend.generateMipmaps( texture ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				// async update | ||||
|  | ||||
| 				backend.createDefaultTexture( texture ); | ||||
|  | ||||
| 				textureData.isDefaultTexture = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// dispose handler | ||||
|  | ||||
| 		if ( textureData.initialized !== true ) { | ||||
|  | ||||
| 			textureData.initialized = true; | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			this.info.memory.textures ++; | ||||
|  | ||||
| 			// dispose | ||||
|  | ||||
| 			const onDispose = () => { | ||||
|  | ||||
| 				texture.removeEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 				this._destroyTexture( texture ); | ||||
|  | ||||
| 				this.info.memory.textures --; | ||||
|  | ||||
| 			}; | ||||
|  | ||||
| 			texture.addEventListener( 'dispose', onDispose ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		textureData.version = texture.version; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getSize( texture, target = _size ) { | ||||
|  | ||||
| 		let image = texture.images ? texture.images[ 0 ] : texture.image; | ||||
|  | ||||
| 		if ( image ) { | ||||
|  | ||||
| 			if ( image.image !== undefined ) image = image.image; | ||||
|  | ||||
| 			target.width = image.width; | ||||
| 			target.height = image.height; | ||||
| 			target.depth = texture.isCubeTexture ? 6 : ( image.depth || 1 ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			target.width = target.height = target.depth = 1; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return target; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getMipLevels( texture, width, height ) { | ||||
|  | ||||
| 		let mipLevelCount; | ||||
|  | ||||
| 		if ( texture.isCompressedTexture ) { | ||||
|  | ||||
| 			mipLevelCount = texture.mipmaps.length; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return mipLevelCount; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	needsMipmaps( texture ) { | ||||
|  | ||||
| 		if ( this.isEnvironmentTexture( texture ) ) return true; | ||||
|  | ||||
| 		return ( texture.isCompressedTexture === true ) || ( ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter ) ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	isEnvironmentTexture( texture ) { | ||||
|  | ||||
| 		const mapping = texture.mapping; | ||||
|  | ||||
| 		return ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) || ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_destroyTexture( texture ) { | ||||
|  | ||||
| 		this.backend.destroySampler( texture ); | ||||
| 		this.backend.destroyTexture( texture ); | ||||
|  | ||||
| 		this.delete( texture ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Textures; | ||||
							
								
								
									
										140
									
								
								public/sdk/three/jsm/renderers/common/Uniform.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								public/sdk/three/jsm/renderers/common/Uniform.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,140 @@ | ||||
| import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three'; | ||||
|  | ||||
| class Uniform { | ||||
|  | ||||
| 	constructor( name, value = null ) { | ||||
|  | ||||
| 		this.name = name; | ||||
| 		this.value = value; | ||||
|  | ||||
| 		this.boundary = 0; // used to build the uniform buffer according to the STD140 layout | ||||
| 		this.itemSize = 0; | ||||
|  | ||||
| 		this.offset = 0; // this property is set by WebGPUUniformsGroup and marks the start position in the uniform buffer | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setValue( value ) { | ||||
|  | ||||
| 		this.value = value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class FloatUniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = 0 ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isFloatUniform = true; | ||||
|  | ||||
| 		this.boundary = 4; | ||||
| 		this.itemSize = 1; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector2Uniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Vector2() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isVector2Uniform = true; | ||||
|  | ||||
| 		this.boundary = 8; | ||||
| 		this.itemSize = 2; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector3Uniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Vector3() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isVector3Uniform = true; | ||||
|  | ||||
| 		this.boundary = 16; | ||||
| 		this.itemSize = 3; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector4Uniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Vector4() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isVector4Uniform = true; | ||||
|  | ||||
| 		this.boundary = 16; | ||||
| 		this.itemSize = 4; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class ColorUniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Color() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isColorUniform = true; | ||||
|  | ||||
| 		this.boundary = 16; | ||||
| 		this.itemSize = 3; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Matrix3Uniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Matrix3() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isMatrix3Uniform = true; | ||||
|  | ||||
| 		this.boundary = 48; | ||||
| 		this.itemSize = 12; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Matrix4Uniform extends Uniform { | ||||
|  | ||||
| 	constructor( name, value = new Matrix4() ) { | ||||
|  | ||||
| 		super( name, value ); | ||||
|  | ||||
| 		this.isMatrix4Uniform = true; | ||||
|  | ||||
| 		this.boundary = 64; | ||||
| 		this.itemSize = 16; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { | ||||
| 	FloatUniform, | ||||
| 	Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, | ||||
| 	Matrix3Uniform, Matrix4Uniform | ||||
| }; | ||||
							
								
								
									
										15
									
								
								public/sdk/three/jsm/renderers/common/UniformBuffer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								public/sdk/three/jsm/renderers/common/UniformBuffer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| import Buffer from './Buffer.js'; | ||||
|  | ||||
| class UniformBuffer extends Buffer { | ||||
|  | ||||
| 	constructor( name, buffer = null ) { | ||||
|  | ||||
| 		super( name, buffer ); | ||||
|  | ||||
| 		this.isUniformBuffer = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default UniformBuffer; | ||||
							
								
								
									
										301
									
								
								public/sdk/three/jsm/renderers/common/UniformsGroup.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								public/sdk/three/jsm/renderers/common/UniformsGroup.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | ||||
| import UniformBuffer from './UniformBuffer.js'; | ||||
| import { GPU_CHUNK_BYTES } from './Constants.js'; | ||||
|  | ||||
| class UniformsGroup extends UniformBuffer { | ||||
|  | ||||
| 	constructor( name ) { | ||||
|  | ||||
| 		super( name ); | ||||
|  | ||||
| 		this.isUniformsGroup = true; | ||||
|  | ||||
| 		// the order of uniforms in this array must match the order of uniforms in the shader | ||||
|  | ||||
| 		this.uniforms = []; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	addUniform( uniform ) { | ||||
|  | ||||
| 		this.uniforms.push( uniform ); | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	removeUniform( uniform ) { | ||||
|  | ||||
| 		const index = this.uniforms.indexOf( uniform ); | ||||
|  | ||||
| 		if ( index !== - 1 ) { | ||||
|  | ||||
| 			this.uniforms.splice( index, 1 ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return this; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get buffer() { | ||||
|  | ||||
| 		let buffer = this._buffer; | ||||
|  | ||||
| 		if ( buffer === null ) { | ||||
|  | ||||
| 			const byteLength = this.byteLength; | ||||
|  | ||||
| 			buffer = new Float32Array( new ArrayBuffer( byteLength ) ); | ||||
|  | ||||
| 			this._buffer = buffer; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return buffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get byteLength() { | ||||
|  | ||||
| 		let offset = 0; // global buffer offset in bytes | ||||
|  | ||||
| 		for ( let i = 0, l = this.uniforms.length; i < l; i ++ ) { | ||||
|  | ||||
| 			const uniform = this.uniforms[ i ]; | ||||
|  | ||||
| 			const { boundary, itemSize } = uniform; | ||||
|  | ||||
| 			// offset within a single chunk in bytes | ||||
|  | ||||
| 			const chunkOffset = offset % GPU_CHUNK_BYTES; | ||||
| 			const remainingSizeInChunk = GPU_CHUNK_BYTES - chunkOffset; | ||||
|  | ||||
| 			// conformance tests | ||||
|  | ||||
| 			if ( chunkOffset !== 0 && ( remainingSizeInChunk - boundary ) < 0 ) { | ||||
|  | ||||
| 				// check for chunk overflow | ||||
|  | ||||
| 				offset += ( GPU_CHUNK_BYTES - chunkOffset ); | ||||
|  | ||||
| 			} else if ( chunkOffset % boundary !== 0 ) { | ||||
|  | ||||
| 				// check for correct alignment | ||||
|  | ||||
| 				offset += ( chunkOffset % boundary ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			uniform.offset = ( offset / this.bytesPerElement ); | ||||
|  | ||||
| 			offset += ( itemSize * this.bytesPerElement ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update() { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		for ( const uniform of this.uniforms ) { | ||||
|  | ||||
| 			if ( this.updateByType( uniform ) === true ) { | ||||
|  | ||||
| 				updated = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateByType( uniform ) { | ||||
|  | ||||
| 		if ( uniform.isFloatUniform ) return this.updateNumber( uniform ); | ||||
| 		if ( uniform.isVector2Uniform ) return this.updateVector2( uniform ); | ||||
| 		if ( uniform.isVector3Uniform ) return this.updateVector3( uniform ); | ||||
| 		if ( uniform.isVector4Uniform ) return this.updateVector4( uniform ); | ||||
| 		if ( uniform.isColorUniform ) return this.updateColor( uniform ); | ||||
| 		if ( uniform.isMatrix3Uniform ) return this.updateMatrix3( uniform ); | ||||
| 		if ( uniform.isMatrix4Uniform ) return this.updateMatrix4( uniform ); | ||||
|  | ||||
| 		console.error( 'THREE.WebGPUUniformsGroup: Unsupported uniform type.', uniform ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateNumber( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const v = uniform.getValue(); | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset ] !== v ) { | ||||
|  | ||||
| 			a[ offset ] = v; | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateVector2( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const v = uniform.getValue(); | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y ) { | ||||
|  | ||||
| 			a[ offset + 0 ] = v.x; | ||||
| 			a[ offset + 1 ] = v.y; | ||||
|  | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateVector3( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const v = uniform.getValue(); | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z ) { | ||||
|  | ||||
| 			a[ offset + 0 ] = v.x; | ||||
| 			a[ offset + 1 ] = v.y; | ||||
| 			a[ offset + 2 ] = v.z; | ||||
|  | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateVector4( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const v = uniform.getValue(); | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset + 0 ] !== v.x || a[ offset + 1 ] !== v.y || a[ offset + 2 ] !== v.z || a[ offset + 4 ] !== v.w ) { | ||||
|  | ||||
| 			a[ offset + 0 ] = v.x; | ||||
| 			a[ offset + 1 ] = v.y; | ||||
| 			a[ offset + 2 ] = v.z; | ||||
| 			a[ offset + 3 ] = v.w; | ||||
|  | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateColor( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const c = uniform.getValue(); | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset + 0 ] !== c.r || a[ offset + 1 ] !== c.g || a[ offset + 2 ] !== c.b ) { | ||||
|  | ||||
| 			a[ offset + 0 ] = c.r; | ||||
| 			a[ offset + 1 ] = c.g; | ||||
| 			a[ offset + 2 ] = c.b; | ||||
|  | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateMatrix3( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const e = uniform.getValue().elements; | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( a[ offset + 0 ] !== e[ 0 ] || a[ offset + 1 ] !== e[ 1 ] || a[ offset + 2 ] !== e[ 2 ] || | ||||
| 			a[ offset + 4 ] !== e[ 3 ] || a[ offset + 5 ] !== e[ 4 ] || a[ offset + 6 ] !== e[ 5 ] || | ||||
| 			a[ offset + 8 ] !== e[ 6 ] || a[ offset + 9 ] !== e[ 7 ] || a[ offset + 10 ] !== e[ 8 ] ) { | ||||
|  | ||||
| 			a[ offset + 0 ] = e[ 0 ]; | ||||
| 			a[ offset + 1 ] = e[ 1 ]; | ||||
| 			a[ offset + 2 ] = e[ 2 ]; | ||||
| 			a[ offset + 4 ] = e[ 3 ]; | ||||
| 			a[ offset + 5 ] = e[ 4 ]; | ||||
| 			a[ offset + 6 ] = e[ 5 ]; | ||||
| 			a[ offset + 8 ] = e[ 6 ]; | ||||
| 			a[ offset + 9 ] = e[ 7 ]; | ||||
| 			a[ offset + 10 ] = e[ 8 ]; | ||||
|  | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateMatrix4( uniform ) { | ||||
|  | ||||
| 		let updated = false; | ||||
|  | ||||
| 		const a = this.buffer; | ||||
| 		const e = uniform.getValue().elements; | ||||
| 		const offset = uniform.offset; | ||||
|  | ||||
| 		if ( arraysEqual( a, e, offset ) === false ) { | ||||
|  | ||||
| 			a.set( e, offset ); | ||||
| 			updated = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return updated; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| function arraysEqual( a, b, offset ) { | ||||
|  | ||||
| 	for ( let i = 0, l = b.length; i < l; i ++ ) { | ||||
|  | ||||
| 		if ( a[ offset + i ] !== b[ i ] ) return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
|  | ||||
| } | ||||
|  | ||||
| export default UniformsGroup; | ||||
							
								
								
									
										773
									
								
								public/sdk/three/jsm/renderers/common/extras/PMREMGenerator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										773
									
								
								public/sdk/three/jsm/renderers/common/extras/PMREMGenerator.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,773 @@ | ||||
| import NodeMaterial from '../../../nodes/materials/NodeMaterial.js'; | ||||
| import { getDirection, blur } from '../../../nodes/pmrem/PMREMUtils.js'; | ||||
| import { equirectUV } from '../../../nodes/utils/EquirectUVNode.js'; | ||||
| import { uniform } from '../../../nodes/core/UniformNode.js'; | ||||
| import { uniforms } from '../../../nodes/accessors/UniformsNode.js'; | ||||
| import { texture } from '../../../nodes/accessors/TextureNode.js'; | ||||
| import { cubeTexture } from '../../../nodes/accessors/CubeTextureNode.js'; | ||||
| import { float, vec3 } from '../../../nodes/shadernode/ShaderNode.js'; | ||||
| import { uv } from '../../../nodes/accessors/UVNode.js'; | ||||
| import { attribute } from '../../../nodes/core/AttributeNode.js'; | ||||
| import { | ||||
| 	OrthographicCamera, | ||||
| 	Color, | ||||
| 	Vector3, | ||||
| 	BufferGeometry, | ||||
| 	BufferAttribute, | ||||
| 	RenderTarget, | ||||
| 	Mesh, | ||||
| 	CubeReflectionMapping, | ||||
| 	CubeRefractionMapping, | ||||
| 	CubeUVReflectionMapping, | ||||
| 	LinearFilter, | ||||
| 	NoBlending, | ||||
| 	RGBAFormat, | ||||
| 	HalfFloatType, | ||||
| 	BackSide, | ||||
| 	LinearSRGBColorSpace, | ||||
| 	PerspectiveCamera, | ||||
| 	MeshBasicMaterial, | ||||
| 	BoxGeometry | ||||
| } from 'three'; | ||||
|  | ||||
| const LOD_MIN = 4; | ||||
|  | ||||
| // The standard deviations (radians) associated with the extra mips. These are | ||||
| // chosen to approximate a Trowbridge-Reitz distribution function times the | ||||
| // geometric shadowing function. These sigma values squared must match the | ||||
| // variance #defines in cube_uv_reflection_fragment.glsl.js. | ||||
| const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ]; | ||||
|  | ||||
| // The maximum length of the blur for loop. Smaller sigmas will use fewer | ||||
| // samples and exit early, but not recompile the shader. | ||||
| const MAX_SAMPLES = 20; | ||||
|  | ||||
| const _flatCamera = /*@__PURE__*/ new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); | ||||
| const _cubeCamera = /*@__PURE__*/ new PerspectiveCamera( 90, 1 ); | ||||
| const _clearColor = /*@__PURE__*/ new Color(); | ||||
| let _oldTarget = null; | ||||
| let _oldActiveCubeFace = 0; | ||||
| let _oldActiveMipmapLevel = 0; | ||||
|  | ||||
| // Golden Ratio | ||||
| const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; | ||||
| const INV_PHI = 1 / PHI; | ||||
|  | ||||
| // Vertices of a dodecahedron (except the opposites, which represent the | ||||
| // same axis), used as axis directions evenly spread on a sphere. | ||||
| const _axisDirections = [ | ||||
| 	/*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ), | ||||
| 	/*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), | ||||
| 	/*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), | ||||
| 	/*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), | ||||
| 	/*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), | ||||
| 	/*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), | ||||
| 	/*@__PURE__*/ new Vector3( - 1, 1, - 1 ), | ||||
| 	/*@__PURE__*/ new Vector3( 1, 1, - 1 ), | ||||
| 	/*@__PURE__*/ new Vector3( - 1, 1, 1 ), | ||||
| 	/*@__PURE__*/ new Vector3( 1, 1, 1 ) | ||||
| ]; | ||||
|  | ||||
| // | ||||
|  | ||||
| // WebGPU Face indices | ||||
| const _faceLib = [ | ||||
| 	3, 1, 5, | ||||
| 	0, 4, 2 | ||||
| ]; | ||||
|  | ||||
| const direction = getDirection( uv(), attribute( 'faceIndex' ) ).normalize(); | ||||
| const outputDirection = vec3( direction.x, direction.y.negate(), direction.z ); | ||||
|  | ||||
| /** | ||||
|  * This class generates a Prefiltered, Mipmapped Radiance Environment Map | ||||
|  * (PMREM) from a cubeMap environment texture. This allows different levels of | ||||
|  * blur to be quickly accessed based on material roughness. It is packed into a | ||||
|  * special CubeUV format that allows us to perform custom interpolation so that | ||||
|  * we can support nonlinear formats such as RGBE. Unlike a traditional mipmap | ||||
|  * chain, it only goes down to the LOD_MIN level (above), and then creates extra | ||||
|  * even more filtered 'mips' at the same LOD_MIN resolution, associated with | ||||
|  * higher roughness levels. In this way we maintain resolution to smoothly | ||||
|  * interpolate diffuse lighting while limiting sampling computation. | ||||
|  * | ||||
|  * Paper: Fast, Accurate Image-Based Lighting | ||||
|  * https://drive.google.com/file/d/15y8r_UpKlU9SvV4ILb0C3qCPecS8pvLz/view | ||||
| */ | ||||
|  | ||||
| class PMREMGenerator { | ||||
|  | ||||
| 	constructor( renderer ) { | ||||
|  | ||||
| 		this._renderer = renderer; | ||||
| 		this._pingPongRenderTarget = null; | ||||
|  | ||||
| 		this._lodMax = 0; | ||||
| 		this._cubeSize = 0; | ||||
| 		this._lodPlanes = []; | ||||
| 		this._sizeLods = []; | ||||
| 		this._sigmas = []; | ||||
| 		this._lodMeshes = []; | ||||
|  | ||||
| 		this._blurMaterial = null; | ||||
| 		this._cubemapMaterial = null; | ||||
| 		this._equirectMaterial = null; | ||||
| 		this._backgroundBox = null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Generates a PMREM from a supplied Scene, which can be faster than using an | ||||
| 	 * image if networking bandwidth is low. Optional sigma specifies a blur radius | ||||
| 	 * in radians to be applied to the scene before PMREM generation. Optional near | ||||
| 	 * and far planes ensure the scene is rendered in its entirety (the cubeCamera | ||||
| 	 * is placed at the origin). | ||||
| 	 */ | ||||
| 	fromScene( scene, sigma = 0, near = 0.1, far = 100 ) { | ||||
|  | ||||
| 		_oldTarget = this._renderer.getRenderTarget(); | ||||
| 		_oldActiveCubeFace = this._renderer.getActiveCubeFace(); | ||||
| 		_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); | ||||
|  | ||||
| 		this._setSize( 256 ); | ||||
|  | ||||
| 		const cubeUVRenderTarget = this._allocateTargets(); | ||||
| 		cubeUVRenderTarget.depthBuffer = true; | ||||
|  | ||||
| 		this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); | ||||
|  | ||||
| 		if ( sigma > 0 ) { | ||||
|  | ||||
| 			this._blur( cubeUVRenderTarget, 0, 0, sigma ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this._applyPMREM( cubeUVRenderTarget ); | ||||
|  | ||||
| 		this._cleanup( cubeUVRenderTarget ); | ||||
|  | ||||
| 		return cubeUVRenderTarget; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Generates a PMREM from an equirectangular texture, which can be either LDR | ||||
| 	 * or HDR. The ideal input image size is 1k (1024 x 512), | ||||
| 	 * as this matches best with the 256 x 256 cubemap output. | ||||
| 	 */ | ||||
| 	fromEquirectangular( equirectangular, renderTarget = null ) { | ||||
|  | ||||
| 		return this._fromTexture( equirectangular, renderTarget ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Generates a PMREM from an cubemap texture, which can be either LDR | ||||
| 	 * or HDR. The ideal input cube size is 256 x 256, | ||||
| 	 * as this matches best with the 256 x 256 cubemap output. | ||||
| 	 */ | ||||
| 	fromCubemap( cubemap, renderTarget = null ) { | ||||
|  | ||||
| 		return this._fromTexture( cubemap, renderTarget ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Pre-compiles the cubemap shader. You can get faster start-up by invoking this method during | ||||
| 	 * your texture's network fetch for increased concurrency. | ||||
| 	 */ | ||||
| 	compileCubemapShader() { | ||||
|  | ||||
| 		if ( this._cubemapMaterial === null ) { | ||||
|  | ||||
| 			this._cubemapMaterial = _getCubemapMaterial(); | ||||
| 			this._compileMaterial( this._cubemapMaterial ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Pre-compiles the equirectangular shader. You can get faster start-up by invoking this method during | ||||
| 	 * your texture's network fetch for increased concurrency. | ||||
| 	 */ | ||||
| 	compileEquirectangularShader() { | ||||
|  | ||||
| 		if ( this._equirectMaterial === null ) { | ||||
|  | ||||
| 			this._equirectMaterial = _getEquirectMaterial(); | ||||
| 			this._compileMaterial( this._equirectMaterial ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Disposes of the PMREMGenerator's internal memory. Note that PMREMGenerator is a static class, | ||||
| 	 * so you should not need more than one PMREMGenerator object. If you do, calling dispose() on | ||||
| 	 * one of them will cause any others to also become unusable. | ||||
| 	 */ | ||||
| 	dispose() { | ||||
|  | ||||
| 		this._dispose(); | ||||
|  | ||||
| 		if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); | ||||
| 		if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); | ||||
| 		if ( this._backgroundBox !== null ) { | ||||
|  | ||||
| 			this._backgroundBox.geometry.dispose(); | ||||
| 			this._backgroundBox.material.dispose(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// private interface | ||||
|  | ||||
| 	_setSize( cubeSize ) { | ||||
|  | ||||
| 		this._lodMax = Math.floor( Math.log2( cubeSize ) ); | ||||
| 		this._cubeSize = Math.pow( 2, this._lodMax ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_dispose() { | ||||
|  | ||||
| 		if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); | ||||
|  | ||||
| 		if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); | ||||
|  | ||||
| 		for ( let i = 0; i < this._lodPlanes.length; i ++ ) { | ||||
|  | ||||
| 			this._lodPlanes[ i ].dispose(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_cleanup( outputTarget ) { | ||||
|  | ||||
| 		this._renderer.setRenderTarget( _oldTarget, _oldActiveCubeFace, _oldActiveMipmapLevel ); | ||||
| 		outputTarget.scissorTest = false; | ||||
| 		_setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_fromTexture( texture, renderTarget ) { | ||||
|  | ||||
| 		if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { | ||||
|  | ||||
| 			this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); | ||||
|  | ||||
| 		} else { // Equirectangular | ||||
|  | ||||
| 			this._setSize( texture.image.width / 4 ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		_oldTarget = this._renderer.getRenderTarget(); | ||||
| 		_oldActiveCubeFace = this._renderer.getActiveCubeFace(); | ||||
| 		_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel(); | ||||
|  | ||||
| 		const cubeUVRenderTarget = renderTarget || this._allocateTargets(); | ||||
| 		this._textureToCubeUV( texture, cubeUVRenderTarget ); | ||||
| 		this._applyPMREM( cubeUVRenderTarget ); | ||||
| 		this._cleanup( cubeUVRenderTarget ); | ||||
|  | ||||
| 		return cubeUVRenderTarget; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_allocateTargets() { | ||||
|  | ||||
| 		const width = 3 * Math.max( this._cubeSize, 16 * 7 ); | ||||
| 		const height = 4 * this._cubeSize; | ||||
|  | ||||
| 		const params = { | ||||
| 			magFilter: LinearFilter, | ||||
| 			minFilter: LinearFilter, | ||||
| 			generateMipmaps: false, | ||||
| 			type: HalfFloatType, | ||||
| 			format: RGBAFormat, | ||||
| 			colorSpace: LinearSRGBColorSpace, | ||||
| 			//depthBuffer: false | ||||
| 		}; | ||||
|  | ||||
| 		const cubeUVRenderTarget = _createRenderTarget( width, height, params ); | ||||
|  | ||||
| 		if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { | ||||
|  | ||||
| 			if ( this._pingPongRenderTarget !== null ) { | ||||
|  | ||||
| 				this._dispose(); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			this._pingPongRenderTarget = _createRenderTarget( width, height, params ); | ||||
|  | ||||
| 			const { _lodMax } = this; | ||||
| 			( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas, lodMeshes: this._lodMeshes } = _createPlanes( _lodMax ) ); | ||||
|  | ||||
| 			this._blurMaterial = _getBlurShader( _lodMax, width, height ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return cubeUVRenderTarget; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_compileMaterial( material ) { | ||||
|  | ||||
| 		const tmpMesh = this._lodMeshes[ 0 ]; | ||||
| 		tmpMesh.material = material; | ||||
|  | ||||
| 		this._renderer.compile( tmpMesh, _flatCamera ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { | ||||
|  | ||||
| 		const cubeCamera = _cubeCamera; | ||||
| 		cubeCamera.near = near; | ||||
| 		cubeCamera.far = far; | ||||
|  | ||||
| 		// px, py, pz, nx, ny, nz | ||||
| 		const upSign = [ - 1, 1, - 1, - 1, - 1, - 1 ]; | ||||
| 		const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; | ||||
|  | ||||
| 		const renderer = this._renderer; | ||||
|  | ||||
| 		const originalAutoClear = renderer.autoClear; | ||||
|  | ||||
| 		renderer.getClearColor( _clearColor ); | ||||
|  | ||||
| 		renderer.autoClear = false; | ||||
|  | ||||
| 		let backgroundBox = this._backgroundBox; | ||||
|  | ||||
| 		if ( backgroundBox === null ) { | ||||
|  | ||||
| 			const backgroundMaterial = new MeshBasicMaterial( { | ||||
| 				name: 'PMREM.Background', | ||||
| 				side: BackSide, | ||||
| 				depthWrite: false, | ||||
| 				depthTest: false | ||||
| 			} ); | ||||
|  | ||||
| 			backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		let useSolidColor = false; | ||||
| 		const background = scene.background; | ||||
|  | ||||
| 		if ( background ) { | ||||
|  | ||||
| 			if ( background.isColor ) { | ||||
|  | ||||
| 				backgroundBox.material.color.copy( background ); | ||||
| 				scene.background = null; | ||||
| 				useSolidColor = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			backgroundBox.material.color.copy( _clearColor ); | ||||
| 			useSolidColor = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		renderer.setRenderTarget( cubeUVRenderTarget ); | ||||
|  | ||||
| 		renderer.clear(); | ||||
|  | ||||
| 		if ( useSolidColor ) { | ||||
|  | ||||
| 			renderer.render( backgroundBox, cubeCamera ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		for ( let i = 0; i < 6; i ++ ) { | ||||
|  | ||||
| 			const col = i % 3; | ||||
|  | ||||
| 			if ( col === 0 ) { | ||||
|  | ||||
| 				cubeCamera.up.set( 0, upSign[ i ], 0 ); | ||||
| 				cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); | ||||
|  | ||||
| 			} else if ( col === 1 ) { | ||||
|  | ||||
| 				cubeCamera.up.set( 0, 0, upSign[ i ] ); | ||||
| 				cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				cubeCamera.up.set( 0, upSign[ i ], 0 ); | ||||
| 				cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const size = this._cubeSize; | ||||
|  | ||||
| 			_setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); | ||||
|  | ||||
| 			renderer.render( scene, cubeCamera ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		renderer.autoClear = originalAutoClear; | ||||
| 		scene.background = background; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_textureToCubeUV( texture, cubeUVRenderTarget ) { | ||||
|  | ||||
| 		const renderer = this._renderer; | ||||
|  | ||||
| 		const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); | ||||
|  | ||||
| 		if ( isCubeTexture ) { | ||||
|  | ||||
| 			if ( this._cubemapMaterial === null ) { | ||||
|  | ||||
| 				this._cubemapMaterial = _getCubemapMaterial( texture ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( this._equirectMaterial === null ) { | ||||
|  | ||||
| 				this._equirectMaterial = _getEquirectMaterial( texture ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; | ||||
| 		material.fragmentNode.value = texture; | ||||
|  | ||||
| 		const mesh = this._lodMeshes[ 0 ]; | ||||
| 		mesh.material = material; | ||||
|  | ||||
| 		const size = this._cubeSize; | ||||
|  | ||||
| 		_setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); | ||||
|  | ||||
| 		renderer.setRenderTarget( cubeUVRenderTarget ); | ||||
| 		renderer.render( mesh, _flatCamera ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_applyPMREM( cubeUVRenderTarget ) { | ||||
|  | ||||
| 		const renderer = this._renderer; | ||||
| 		const autoClear = renderer.autoClear; | ||||
| 		renderer.autoClear = false; | ||||
| 		const n = this._lodPlanes.length; | ||||
|  | ||||
| 		for ( let i = 1; i < n; i ++ ) { | ||||
|  | ||||
| 			const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); | ||||
|  | ||||
| 			const poleAxis = _axisDirections[ ( n - i - 1 ) % _axisDirections.length ]; | ||||
|  | ||||
| 			this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		renderer.autoClear = autoClear; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * This is a two-pass Gaussian blur for a cubemap. Normally this is done | ||||
| 	 * vertically and horizontally, but this breaks down on a cube. Here we apply | ||||
| 	 * the blur latitudinally (around the poles), and then longitudinally (towards | ||||
| 	 * the poles) to approximate the orthogonally-separable blur. It is least | ||||
| 	 * accurate at the poles, but still does a decent job. | ||||
| 	 */ | ||||
| 	_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) { | ||||
|  | ||||
| 		const pingPongRenderTarget = this._pingPongRenderTarget; | ||||
|  | ||||
| 		this._halfBlur( | ||||
| 			cubeUVRenderTarget, | ||||
| 			pingPongRenderTarget, | ||||
| 			lodIn, | ||||
| 			lodOut, | ||||
| 			sigma, | ||||
| 			'latitudinal', | ||||
| 			poleAxis ); | ||||
|  | ||||
| 		this._halfBlur( | ||||
| 			pingPongRenderTarget, | ||||
| 			cubeUVRenderTarget, | ||||
| 			lodOut, | ||||
| 			lodOut, | ||||
| 			sigma, | ||||
| 			'longitudinal', | ||||
| 			poleAxis ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { | ||||
|  | ||||
| 		const renderer = this._renderer; | ||||
| 		const blurMaterial = this._blurMaterial; | ||||
|  | ||||
| 		if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { | ||||
|  | ||||
| 			console.error( 'blur direction must be either latitudinal or longitudinal!' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// Number of standard deviations at which to cut off the discrete approximation. | ||||
| 		const STANDARD_DEVIATIONS = 3; | ||||
|  | ||||
| 		const blurMesh = this._lodMeshes[ lodOut ]; | ||||
| 		blurMesh.material = blurMaterial; | ||||
|  | ||||
| 		const blurUniforms = blurMaterial.uniforms; | ||||
|  | ||||
| 		const pixels = this._sizeLods[ lodIn ] - 1; | ||||
| 		const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 ); | ||||
| 		const sigmaPixels = sigmaRadians / radiansPerPixel; | ||||
| 		const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES; | ||||
|  | ||||
| 		if ( samples > MAX_SAMPLES ) { | ||||
|  | ||||
| 			console.warn( `sigmaRadians, ${ | ||||
| 				sigmaRadians}, is too large and will clip, as it requested ${ | ||||
| 				samples} samples when the maximum is set to ${MAX_SAMPLES}` ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const weights = []; | ||||
| 		let sum = 0; | ||||
|  | ||||
| 		for ( let i = 0; i < MAX_SAMPLES; ++ i ) { | ||||
|  | ||||
| 			const x = i / sigmaPixels; | ||||
| 			const weight = Math.exp( - x * x / 2 ); | ||||
| 			weights.push( weight ); | ||||
|  | ||||
| 			if ( i === 0 ) { | ||||
|  | ||||
| 				sum += weight; | ||||
|  | ||||
| 			} else if ( i < samples ) { | ||||
|  | ||||
| 				sum += 2 * weight; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		for ( let i = 0; i < weights.length; i ++ ) { | ||||
|  | ||||
| 			weights[ i ] = weights[ i ] / sum; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		targetIn.texture.frame = ( targetIn.texture.frame || 0 ) + 1; | ||||
|  | ||||
| 		blurUniforms.envMap.value = targetIn.texture; | ||||
| 		blurUniforms.samples.value = samples; | ||||
| 		blurUniforms.weights.array = weights; | ||||
| 		blurUniforms.latitudinal.value = direction === 'latitudinal' ? 1 : 0; | ||||
|  | ||||
| 		if ( poleAxis ) { | ||||
|  | ||||
| 			blurUniforms.poleAxis.value = poleAxis; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const { _lodMax } = this; | ||||
| 		blurUniforms.dTheta.value = radiansPerPixel; | ||||
| 		blurUniforms.mipInt.value = _lodMax - lodIn; | ||||
|  | ||||
| 		const outputSize = this._sizeLods[ lodOut ]; | ||||
| 		const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 ); | ||||
| 		const y = 4 * ( this._cubeSize - outputSize ); | ||||
|  | ||||
| 		_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize ); | ||||
| 		renderer.setRenderTarget( targetOut ); | ||||
| 		renderer.render( blurMesh, _flatCamera ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| function _createPlanes( lodMax ) { | ||||
|  | ||||
| 	const lodPlanes = []; | ||||
| 	const sizeLods = []; | ||||
| 	const sigmas = []; | ||||
| 	const lodMeshes = []; | ||||
|  | ||||
| 	let lod = lodMax; | ||||
|  | ||||
| 	const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; | ||||
|  | ||||
| 	for ( let i = 0; i < totalLods; i ++ ) { | ||||
|  | ||||
| 		const sizeLod = Math.pow( 2, lod ); | ||||
| 		sizeLods.push( sizeLod ); | ||||
| 		let sigma = 1.0 / sizeLod; | ||||
|  | ||||
| 		if ( i > lodMax - LOD_MIN ) { | ||||
|  | ||||
| 			sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; | ||||
|  | ||||
| 		} else if ( i === 0 ) { | ||||
|  | ||||
| 			sigma = 0; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		sigmas.push( sigma ); | ||||
|  | ||||
| 		const texelSize = 1.0 / ( sizeLod - 2 ); | ||||
| 		const min = - texelSize; | ||||
| 		const max = 1 + texelSize; | ||||
| 		const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ]; | ||||
|  | ||||
| 		const cubeFaces = 6; | ||||
| 		const vertices = 6; | ||||
| 		const positionSize = 3; | ||||
| 		const uvSize = 2; | ||||
| 		const faceIndexSize = 1; | ||||
|  | ||||
| 		const position = new Float32Array( positionSize * vertices * cubeFaces ); | ||||
| 		const uv = new Float32Array( uvSize * vertices * cubeFaces ); | ||||
| 		const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); | ||||
|  | ||||
| 		for ( let face = 0; face < cubeFaces; face ++ ) { | ||||
|  | ||||
| 			const x = ( face % 3 ) * 2 / 3 - 1; | ||||
| 			const y = face > 2 ? 0 : - 1; | ||||
| 			const coordinates = [ | ||||
| 				x, y, 0, | ||||
| 				x + 2 / 3, y, 0, | ||||
| 				x + 2 / 3, y + 1, 0, | ||||
| 				x, y, 0, | ||||
| 				x + 2 / 3, y + 1, 0, | ||||
| 				x, y + 1, 0 | ||||
| 			]; | ||||
|  | ||||
| 			const faceIdx = _faceLib[ face ]; | ||||
| 			position.set( coordinates, positionSize * vertices * faceIdx ); | ||||
| 			uv.set( uv1, uvSize * vertices * faceIdx ); | ||||
| 			const fill = [ faceIdx, faceIdx, faceIdx, faceIdx, faceIdx, faceIdx ]; | ||||
| 			faceIndex.set( fill, faceIndexSize * vertices * faceIdx ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const planes = new BufferGeometry(); | ||||
| 		planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) ); | ||||
| 		planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) ); | ||||
| 		planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) ); | ||||
| 		lodPlanes.push( planes ); | ||||
| 		lodMeshes.push( new Mesh( planes, null ) ); | ||||
|  | ||||
| 		if ( lod > LOD_MIN ) { | ||||
|  | ||||
| 			lod --; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return { lodPlanes, sizeLods, sigmas, lodMeshes }; | ||||
|  | ||||
| } | ||||
|  | ||||
| function _createRenderTarget( width, height, params ) { | ||||
|  | ||||
| 	const cubeUVRenderTarget = new RenderTarget( width, height, params ); | ||||
| 	cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; | ||||
| 	cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; | ||||
| 	cubeUVRenderTarget.texture.isPMREMTexture = true; | ||||
| 	cubeUVRenderTarget.scissorTest = true; | ||||
| 	return cubeUVRenderTarget; | ||||
|  | ||||
| } | ||||
|  | ||||
| function _setViewport( target, x, y, width, height ) { | ||||
|  | ||||
| 	const viewY = target.height - height - y; | ||||
|  | ||||
| 	target.viewport.set( x, viewY, width, height ); | ||||
| 	target.scissor.set( x, viewY, width, height ); | ||||
|  | ||||
| } | ||||
|  | ||||
| function _getMaterial() { | ||||
|  | ||||
| 	const material = new NodeMaterial(); | ||||
| 	material.depthTest = false; | ||||
| 	material.depthWrite = false; | ||||
| 	material.blending = NoBlending; | ||||
|  | ||||
| 	return material; | ||||
|  | ||||
| } | ||||
|  | ||||
| function _getBlurShader( lodMax, width, height ) { | ||||
|  | ||||
| 	const weights = uniforms( new Array( MAX_SAMPLES ).fill( 0 ) ); | ||||
| 	const poleAxis = uniform( new Vector3( 0, 1, 0 ) ); | ||||
| 	const dTheta = uniform( 0 ); | ||||
| 	const n = float( MAX_SAMPLES ); | ||||
| 	const latitudinal = uniform( 0 ); // false, bool | ||||
| 	const samples = uniform( 1 ); // int | ||||
| 	const envMap = texture( null ); | ||||
| 	const mipInt = uniform( 0 ); // int | ||||
| 	const CUBEUV_TEXEL_WIDTH = float( 1 / width ); | ||||
| 	const CUBEUV_TEXEL_HEIGHT = float( 1 / height ); | ||||
| 	const CUBEUV_MAX_MIP = float( lodMax ); | ||||
|  | ||||
| 	const materialUniforms = { | ||||
| 		n, | ||||
| 		latitudinal, | ||||
| 		weights, | ||||
| 		poleAxis, | ||||
| 		outputDirection, | ||||
| 		dTheta, | ||||
| 		samples, | ||||
| 		envMap, | ||||
| 		mipInt, | ||||
| 		CUBEUV_TEXEL_WIDTH, | ||||
| 		CUBEUV_TEXEL_HEIGHT, | ||||
| 		CUBEUV_MAX_MIP | ||||
| 	}; | ||||
|  | ||||
| 	const material = _getMaterial(); | ||||
| 	material.uniforms = materialUniforms; // TODO: Move to outside of the material | ||||
| 	material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( 1 ) } ); | ||||
|  | ||||
| 	return material; | ||||
|  | ||||
| } | ||||
|  | ||||
| function _getCubemapMaterial( envTexture ) { | ||||
|  | ||||
| 	const material = _getMaterial(); | ||||
| 	material.fragmentNode = cubeTexture( envTexture, outputDirection ); | ||||
|  | ||||
| 	return material; | ||||
|  | ||||
| } | ||||
|  | ||||
| function _getEquirectMaterial( envTexture ) { | ||||
|  | ||||
| 	const material = _getMaterial(); | ||||
| 	material.fragmentNode = texture( envTexture, equirectUV( outputDirection ), 0 ); | ||||
|  | ||||
| 	return material; | ||||
|  | ||||
| } | ||||
|  | ||||
| export default PMREMGenerator; | ||||
| @ -0,0 +1,44 @@ | ||||
| class NodeBuilderState { | ||||
|  | ||||
| 	constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, transforms = [] ) { | ||||
|  | ||||
| 		this.vertexShader = vertexShader; | ||||
| 		this.fragmentShader = fragmentShader; | ||||
| 		this.computeShader = computeShader; | ||||
| 		this.transforms = transforms; | ||||
|  | ||||
| 		this.nodeAttributes = nodeAttributes; | ||||
| 		this.bindings = bindings; | ||||
|  | ||||
| 		this.updateNodes = updateNodes; | ||||
| 		this.updateBeforeNodes = updateBeforeNodes; | ||||
|  | ||||
| 		this.usedTimes = 0; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createBindings() { | ||||
|  | ||||
| 		const bindingsArray = []; | ||||
|  | ||||
| 		for ( const instanceBinding of this.bindings ) { | ||||
|  | ||||
| 			let binding = instanceBinding; | ||||
|  | ||||
| 			if ( instanceBinding.shared !== true ) { | ||||
|  | ||||
| 				binding = instanceBinding.clone(); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			bindingsArray.push( binding ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return bindingsArray; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default NodeBuilderState; | ||||
| @ -0,0 +1,49 @@ | ||||
| import { SampledTexture } from '../SampledTexture.js'; | ||||
|  | ||||
| class NodeSampledTexture extends SampledTexture { | ||||
|  | ||||
| 	constructor( name, textureNode ) { | ||||
|  | ||||
| 		super( name, textureNode ? textureNode.value : null ); | ||||
|  | ||||
| 		this.textureNode = textureNode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get needsBindingsUpdate() { | ||||
|  | ||||
| 		return this.textureNode.value !== this.texture || super.needsBindingsUpdate; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	update() { | ||||
|  | ||||
| 		const { textureNode } = this; | ||||
|  | ||||
| 		if ( this.texture !== textureNode.value ) { | ||||
|  | ||||
| 			this.texture = textureNode.value; | ||||
|  | ||||
| 			return true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return super.update(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class NodeSampledCubeTexture extends NodeSampledTexture { | ||||
|  | ||||
| 	constructor( name, textureNode ) { | ||||
|  | ||||
| 		super( name, textureNode ); | ||||
|  | ||||
| 		this.isSampledCubeTexture = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { NodeSampledTexture, NodeSampledCubeTexture }; | ||||
							
								
								
									
										15
									
								
								public/sdk/three/jsm/renderers/common/nodes/NodeSampler.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								public/sdk/three/jsm/renderers/common/nodes/NodeSampler.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| import Sampler from '../Sampler.js'; | ||||
|  | ||||
| class NodeSampler extends Sampler { | ||||
|  | ||||
| 	constructor( name, textureNode ) { | ||||
|  | ||||
| 		super( name, textureNode ? textureNode.value : null ); | ||||
|  | ||||
| 		this.textureNode = textureNode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default NodeSampler; | ||||
| @ -0,0 +1,23 @@ | ||||
| import StorageBuffer from '../StorageBuffer.js'; | ||||
|  | ||||
| let _id = 0; | ||||
|  | ||||
| class NodeStorageBuffer extends StorageBuffer { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get buffer() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default NodeStorageBuffer; | ||||
							
								
								
									
										135
									
								
								public/sdk/three/jsm/renderers/common/nodes/NodeUniform.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								public/sdk/three/jsm/renderers/common/nodes/NodeUniform.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,135 @@ | ||||
| import { | ||||
| 	FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform, | ||||
| 	ColorUniform, Matrix3Uniform, Matrix4Uniform | ||||
| } from '../Uniform.js'; | ||||
|  | ||||
| class FloatNodeUniform extends FloatUniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector2NodeUniform extends Vector2Uniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector3NodeUniform extends Vector3Uniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Vector4NodeUniform extends Vector4Uniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class ColorNodeUniform extends ColorUniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Matrix3NodeUniform extends Matrix3Uniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class Matrix4NodeUniform extends Matrix4Uniform { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( nodeUniform.name, nodeUniform.value ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getValue() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export { | ||||
| 	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, | ||||
| 	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform | ||||
| }; | ||||
| @ -0,0 +1,23 @@ | ||||
| import UniformBuffer from '../UniformBuffer.js'; | ||||
|  | ||||
| let _id = 0; | ||||
|  | ||||
| class NodeUniformBuffer extends UniformBuffer { | ||||
|  | ||||
| 	constructor( nodeUniform ) { | ||||
|  | ||||
| 		super( 'UniformBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null ); | ||||
|  | ||||
| 		this.nodeUniform = nodeUniform; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get buffer() { | ||||
|  | ||||
| 		return this.nodeUniform.value; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default NodeUniformBuffer; | ||||
| @ -0,0 +1,44 @@ | ||||
| import UniformsGroup from '../UniformsGroup.js'; | ||||
|  | ||||
| let id = 0; | ||||
|  | ||||
| class NodeUniformsGroup extends UniformsGroup { | ||||
|  | ||||
| 	constructor( name, groupNode ) { | ||||
|  | ||||
| 		super( name ); | ||||
|  | ||||
| 		this.id = id ++; | ||||
| 		this.groupNode = groupNode; | ||||
|  | ||||
| 		this.isNodeUniformsGroup = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get shared() { | ||||
|  | ||||
| 		return this.groupNode.shared; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNodes() { | ||||
|  | ||||
| 		const nodes = []; | ||||
|  | ||||
| 		for ( const uniform of this.uniforms ) { | ||||
|  | ||||
| 			const node = uniform.nodeUniform.node; | ||||
|  | ||||
| 			if ( ! node ) throw new Error( 'NodeUniformsGroup: Uniform has no node.' ); | ||||
|  | ||||
| 			nodes.push( node ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return nodes; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default NodeUniformsGroup; | ||||
							
								
								
									
										467
									
								
								public/sdk/three/jsm/renderers/common/nodes/Nodes.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										467
									
								
								public/sdk/three/jsm/renderers/common/nodes/Nodes.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,467 @@ | ||||
| import DataMap from '../DataMap.js'; | ||||
| import ChainMap from '../ChainMap.js'; | ||||
| import NodeBuilderState from './NodeBuilderState.js'; | ||||
| import { EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping, SRGBColorSpace } from 'three'; | ||||
| import { NodeFrame, vec4, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, viewportBottomLeft, normalWorld, pmremTexture, viewportTopLeft } from '../../../nodes/Nodes.js'; | ||||
|  | ||||
| class Nodes extends DataMap { | ||||
|  | ||||
| 	constructor( renderer, backend ) { | ||||
|  | ||||
| 		super(); | ||||
|  | ||||
| 		this.renderer = renderer; | ||||
| 		this.backend = backend; | ||||
| 		this.nodeFrame = new NodeFrame(); | ||||
| 		this.nodeBuilderCache = new Map(); | ||||
| 		this.callHashCache = new ChainMap(); | ||||
| 		this.groupsData = new ChainMap(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateGroup( nodeUniformsGroup ) { | ||||
|  | ||||
| 		const groupNode = nodeUniformsGroup.groupNode; | ||||
| 		const name = groupNode.name; | ||||
|  | ||||
| 		// objectGroup is every updated | ||||
|  | ||||
| 		if ( name === objectGroup.name ) return true; | ||||
|  | ||||
| 		// renderGroup is updated once per render/compute call | ||||
|  | ||||
| 		if ( name === renderGroup.name ) { | ||||
|  | ||||
| 			const uniformsGroupData = this.get( nodeUniformsGroup ); | ||||
| 			const renderId = this.nodeFrame.renderId; | ||||
|  | ||||
| 			if ( uniformsGroupData.renderId !== renderId ) { | ||||
|  | ||||
| 				uniformsGroupData.renderId = renderId; | ||||
|  | ||||
| 				return true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return false; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// frameGroup is updated once per frame | ||||
|  | ||||
| 		if ( name === frameGroup.name ) { | ||||
|  | ||||
| 			const uniformsGroupData = this.get( nodeUniformsGroup ); | ||||
| 			const frameId = this.nodeFrame.frameId; | ||||
|  | ||||
| 			if ( uniformsGroupData.frameId !== frameId ) { | ||||
|  | ||||
| 				uniformsGroupData.frameId = frameId; | ||||
|  | ||||
| 				return true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return false; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// other groups are updated just when groupNode.needsUpdate is true | ||||
|  | ||||
| 		const groupChain = [ groupNode, nodeUniformsGroup ]; | ||||
|  | ||||
| 		let groupData = this.groupsData.get( groupChain ); | ||||
| 		if ( groupData === undefined ) this.groupsData.set( groupChain, groupData = {} ); | ||||
|  | ||||
| 		if ( groupData.version !== groupNode.version ) { | ||||
|  | ||||
| 			groupData.version = groupNode.version; | ||||
|  | ||||
| 			return true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForRenderCacheKey( renderObject ) { | ||||
|  | ||||
| 		return renderObject.initialCacheKey; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForRender( renderObject ) { | ||||
|  | ||||
| 		const renderObjectData = this.get( renderObject ); | ||||
|  | ||||
| 		let nodeBuilderState = renderObjectData.nodeBuilderState; | ||||
|  | ||||
| 		if ( nodeBuilderState === undefined ) { | ||||
|  | ||||
| 			const { nodeBuilderCache } = this; | ||||
|  | ||||
| 			const cacheKey = this.getForRenderCacheKey( renderObject ); | ||||
|  | ||||
| 			nodeBuilderState = nodeBuilderCache.get( cacheKey ); | ||||
|  | ||||
| 			if ( nodeBuilderState === undefined ) { | ||||
|  | ||||
| 				const nodeBuilder = this.backend.createNodeBuilder( renderObject.object, this.renderer, renderObject.scene ); | ||||
| 				nodeBuilder.material = renderObject.material; | ||||
| 				nodeBuilder.context.material = renderObject.material; | ||||
| 				nodeBuilder.lightsNode = renderObject.lightsNode; | ||||
| 				nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene ); | ||||
| 				nodeBuilder.fogNode = this.getFogNode( renderObject.scene ); | ||||
| 				nodeBuilder.clippingContext = renderObject.clippingContext; | ||||
| 				nodeBuilder.build(); | ||||
|  | ||||
| 				nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); | ||||
|  | ||||
| 				nodeBuilderCache.set( cacheKey, nodeBuilderState ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			nodeBuilderState.usedTimes ++; | ||||
|  | ||||
| 			renderObjectData.nodeBuilderState = nodeBuilderState; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return nodeBuilderState; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	delete( object ) { | ||||
|  | ||||
| 		if ( object.isRenderObject ) { | ||||
|  | ||||
| 			const nodeBuilderState = this.get( object ).nodeBuilderState; | ||||
| 			nodeBuilderState.usedTimes --; | ||||
|  | ||||
| 			if ( nodeBuilderState.usedTimes === 0 ) { | ||||
|  | ||||
| 				this.nodeBuilderCache.delete( this.getForRenderCacheKey( object ) ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return super.delete( object ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getForCompute( computeNode ) { | ||||
|  | ||||
| 		const computeData = this.get( computeNode ); | ||||
|  | ||||
| 		let nodeBuilderState = computeData.nodeBuilderState; | ||||
|  | ||||
| 		if ( nodeBuilderState === undefined ) { | ||||
|  | ||||
| 			const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); | ||||
| 			nodeBuilder.build(); | ||||
|  | ||||
| 			nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); | ||||
|  | ||||
| 			computeData.nodeBuilderState = nodeBuilderState; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return nodeBuilderState; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_createNodeBuilderState( nodeBuilder ) { | ||||
|  | ||||
| 		return new NodeBuilderState( | ||||
| 			nodeBuilder.vertexShader, | ||||
| 			nodeBuilder.fragmentShader, | ||||
| 			nodeBuilder.computeShader, | ||||
| 			nodeBuilder.getAttributesArray(), | ||||
| 			nodeBuilder.getBindings(), | ||||
| 			nodeBuilder.updateNodes, | ||||
| 			nodeBuilder.updateBeforeNodes, | ||||
| 			nodeBuilder.transforms | ||||
| 		); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getEnvironmentNode( scene ) { | ||||
|  | ||||
| 		return scene.environmentNode || this.get( scene ).environmentNode || null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getBackgroundNode( scene ) { | ||||
|  | ||||
| 		return scene.backgroundNode || this.get( scene ).backgroundNode || null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getFogNode( scene ) { | ||||
|  | ||||
| 		return scene.fogNode || this.get( scene ).fogNode || null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCacheKey( scene, lightsNode ) { | ||||
|  | ||||
| 		const chain = [ scene, lightsNode ]; | ||||
| 		const callId = this.renderer.info.calls; | ||||
|  | ||||
| 		let cacheKeyData = this.callHashCache.get( chain ); | ||||
|  | ||||
| 		if ( cacheKeyData === undefined || cacheKeyData.callId !== callId ) { | ||||
|  | ||||
| 			const environmentNode = this.getEnvironmentNode( scene ); | ||||
| 			const fogNode = this.getFogNode( scene ); | ||||
|  | ||||
| 			const cacheKey = []; | ||||
|  | ||||
| 			if ( lightsNode ) cacheKey.push( lightsNode.getCacheKey() ); | ||||
| 			if ( environmentNode ) cacheKey.push( environmentNode.getCacheKey() ); | ||||
| 			if ( fogNode ) cacheKey.push( fogNode.getCacheKey() ); | ||||
|  | ||||
| 			cacheKeyData = { | ||||
| 				callId, | ||||
| 				cacheKey: cacheKey.join( ',' ) | ||||
| 			}; | ||||
|  | ||||
| 			this.callHashCache.set( chain, cacheKeyData ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return cacheKeyData.cacheKey; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateScene( scene ) { | ||||
|  | ||||
| 		this.updateEnvironment( scene ); | ||||
| 		this.updateFog( scene ); | ||||
| 		this.updateBackground( scene ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get isToneMappingState() { | ||||
|  | ||||
| 		return this.renderer.getRenderTarget() ? false : true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateBackground( scene ) { | ||||
|  | ||||
| 		const sceneData = this.get( scene ); | ||||
| 		const background = scene.background; | ||||
|  | ||||
| 		if ( background ) { | ||||
|  | ||||
| 			if ( sceneData.background !== background ) { | ||||
|  | ||||
| 				let backgroundNode = null; | ||||
|  | ||||
| 				if ( background.isCubeTexture === true || ( background.mapping === EquirectangularReflectionMapping || background.mapping === EquirectangularRefractionMapping ) ) { | ||||
|  | ||||
| 					backgroundNode = pmremTexture( background, normalWorld ); | ||||
|  | ||||
| 				} else if ( background.isTexture === true ) { | ||||
|  | ||||
| 					backgroundNode = texture( background, viewportBottomLeft ).setUpdateMatrix( true ); | ||||
|  | ||||
| 				} else if ( background.isColor !== true ) { | ||||
|  | ||||
| 					console.error( 'WebGPUNodes: Unsupported background configuration.', background ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				sceneData.backgroundNode = backgroundNode; | ||||
| 				sceneData.background = background; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( sceneData.backgroundNode ) { | ||||
|  | ||||
| 			delete sceneData.backgroundNode; | ||||
| 			delete sceneData.background; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateFog( scene ) { | ||||
|  | ||||
| 		const sceneData = this.get( scene ); | ||||
| 		const fog = scene.fog; | ||||
|  | ||||
| 		if ( fog ) { | ||||
|  | ||||
| 			if ( sceneData.fog !== fog ) { | ||||
|  | ||||
| 				let fogNode = null; | ||||
|  | ||||
| 				if ( fog.isFogExp2 ) { | ||||
|  | ||||
| 					fogNode = densityFog( reference( 'color', 'color', fog ), reference( 'density', 'float', fog ) ); | ||||
|  | ||||
| 				} else if ( fog.isFog ) { | ||||
|  | ||||
| 					fogNode = rangeFog( reference( 'color', 'color', fog ), reference( 'near', 'float', fog ), reference( 'far', 'float', fog ) ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					console.error( 'WebGPUNodes: Unsupported fog configuration.', fog ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				sceneData.fogNode = fogNode; | ||||
| 				sceneData.fog = fog; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			delete sceneData.fogNode; | ||||
| 			delete sceneData.fog; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateEnvironment( scene ) { | ||||
|  | ||||
| 		const sceneData = this.get( scene ); | ||||
| 		const environment = scene.environment; | ||||
|  | ||||
| 		if ( environment ) { | ||||
|  | ||||
| 			if ( sceneData.environment !== environment ) { | ||||
|  | ||||
| 				let environmentNode = null; | ||||
|  | ||||
| 				if ( environment.isCubeTexture === true ) { | ||||
|  | ||||
| 					environmentNode = cubeTexture( environment ); | ||||
|  | ||||
| 				} else if ( environment.isTexture === true ) { | ||||
|  | ||||
| 					environmentNode = texture( environment ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					console.error( 'Nodes: Unsupported environment configuration.', environment ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				sceneData.environmentNode = environmentNode; | ||||
| 				sceneData.environment = environment; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( sceneData.environmentNode ) { | ||||
|  | ||||
| 			delete sceneData.environmentNode; | ||||
| 			delete sceneData.environment; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNodeFrame( renderer = this.renderer, scene = null, object = null, camera = null, material = null ) { | ||||
|  | ||||
| 		const nodeFrame = this.nodeFrame; | ||||
| 		nodeFrame.renderer = renderer; | ||||
| 		nodeFrame.scene = scene; | ||||
| 		nodeFrame.object = object; | ||||
| 		nodeFrame.camera = camera; | ||||
| 		nodeFrame.material = material; | ||||
|  | ||||
| 		return nodeFrame; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getNodeFrameForRender( renderObject ) { | ||||
|  | ||||
| 		return this.getNodeFrame( renderObject.renderer, renderObject.scene, renderObject.object, renderObject.camera, renderObject.material ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getOutputNode( outputTexture ) { | ||||
|  | ||||
| 		let output = texture( outputTexture, viewportTopLeft ); | ||||
|  | ||||
| 		if ( this.isToneMappingState ) { | ||||
|  | ||||
| 			if ( this.renderer.toneMappingNode ) { | ||||
|  | ||||
| 				output = vec4( this.renderer.toneMappingNode.context( { color: output.rgb } ), output.a ); | ||||
|  | ||||
| 			} else if ( this.renderer.toneMapping !== NoToneMapping ) { | ||||
|  | ||||
| 				output = output.toneMapping( this.renderer.toneMapping ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( this.renderer.currentColorSpace === SRGBColorSpace ) { | ||||
|  | ||||
| 			output = output.linearToColorSpace( this.renderer.currentColorSpace ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return output; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateBefore( renderObject ) { | ||||
|  | ||||
| 		const nodeFrame = this.getNodeFrameForRender( renderObject ); | ||||
| 		const nodeBuilder = renderObject.getNodeBuilderState(); | ||||
|  | ||||
| 		for ( const node of nodeBuilder.updateBeforeNodes ) { | ||||
|  | ||||
| 			nodeFrame.updateBeforeNode( node ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForCompute( computeNode ) { | ||||
|  | ||||
| 		const nodeFrame = this.getNodeFrame(); | ||||
| 		const nodeBuilder = this.getForCompute( computeNode ); | ||||
|  | ||||
| 		for ( const node of nodeBuilder.updateNodes ) { | ||||
|  | ||||
| 			nodeFrame.updateNode( node ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateForRender( renderObject ) { | ||||
|  | ||||
| 		const nodeFrame = this.getNodeFrameForRender( renderObject ); | ||||
| 		const nodeBuilder = renderObject.getNodeBuilderState(); | ||||
|  | ||||
| 		for ( const node of nodeBuilder.updateNodes ) { | ||||
|  | ||||
| 			nodeFrame.updateNode( node ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	dispose() { | ||||
|  | ||||
| 		super.dispose(); | ||||
|  | ||||
| 		this.nodeFrame = new NodeFrame(); | ||||
| 		this.nodeBuilderCache = new Map(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default Nodes; | ||||
							
								
								
									
										1492
									
								
								public/sdk/three/jsm/renderers/webgl/WebGLBackend.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1492
									
								
								public/sdk/three/jsm/renderers/webgl/WebGLBackend.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										145
									
								
								public/sdk/three/jsm/renderers/webgl/WebGLBufferRenderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								public/sdk/three/jsm/renderers/webgl/WebGLBufferRenderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | ||||
| class WebGLBufferRenderer { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.gl = backend.gl; | ||||
| 		this.extensions = backend.extensions; | ||||
| 		this.info = backend.renderer.info; | ||||
| 		this.mode = null; | ||||
| 		this.index = 0; | ||||
| 		this.type = null; | ||||
| 		this.object = null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	render( start, count ) { | ||||
|  | ||||
| 		const { gl, mode, object, type, info, index } = this; | ||||
|  | ||||
| 		if ( index !== 0 ) { | ||||
|  | ||||
| 			gl.drawElements( mode, count, type, start ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			gl.drawArrays( mode, start, count ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		info.update( object, count, mode, 1 ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	renderInstances( start, count, primcount ) { | ||||
|  | ||||
| 		const { gl, mode, type, index, object, info } = this; | ||||
|  | ||||
| 		if ( primcount === 0 ) return; | ||||
|  | ||||
| 		if ( index !== 0 ) { | ||||
|  | ||||
| 			gl.drawElementsInstanced( mode, count, type, start, primcount ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			gl.drawArraysInstanced( mode, start, count, primcount ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		info.update( object, count, mode, primcount ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	renderMultiDraw( starts, counts, drawCount ) { | ||||
|  | ||||
| 		const { extensions, mode, object, info } = this; | ||||
|  | ||||
| 		if ( drawCount === 0 ) return; | ||||
|  | ||||
| 		const extension = extensions.get( 'WEBGL_multi_draw' ); | ||||
|  | ||||
| 		if ( extension === null ) { | ||||
|  | ||||
| 			for ( let i = 0; i < drawCount; i ++ ) { | ||||
|  | ||||
| 				this.render( starts[ i ], counts[ i ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( this.index !== 0 ) { | ||||
|  | ||||
| 				extension.multiDrawElementsWEBGL( mode, counts, 0, this.type, starts, 0, drawCount ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				extension.multiDrawArraysWEBGL( mode, starts, 0, counts, 0, drawCount ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			let elementCount = 0; | ||||
| 			for ( let i = 0; i < drawCount; i ++ ) { | ||||
|  | ||||
| 				elementCount += counts[ i ]; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			info.update( object, elementCount, mode, 1 ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	renderMultiDrawInstances( starts, counts, drawCount, primcount ) { | ||||
|  | ||||
| 		const { extensions, mode, object, info } = this; | ||||
|  | ||||
| 		if ( drawCount === 0 ) return; | ||||
|  | ||||
| 		const extension = extensions.get( 'WEBGL_multi_draw' ); | ||||
|  | ||||
| 		if ( extension === null ) { | ||||
|  | ||||
| 			for ( let i = 0; i < drawCount; i ++ ) { | ||||
|  | ||||
| 				this.renderInstances( starts[ i ], counts[ i ], primcount[ i ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( this.index !== 0 ) { | ||||
|  | ||||
| 				extension.multiDrawElementsInstancedWEBGL( mode, counts, 0, this.type, starts, 0, primcount, 0, drawCount ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				extension.multiDrawArraysInstancedWEBGL( mode, starts, 0, counts, 0, primcount, 0, drawCount ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			let elementCount = 0; | ||||
|  | ||||
| 			for ( let i = 0; i < drawCount; i ++ ) { | ||||
|  | ||||
| 				elementCount += counts[ i ]; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			for ( let i = 0; i < primcount.length; i ++ ) { | ||||
|  | ||||
| 				info.update( object, elementCount, mode, primcount[ i ] ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| export { WebGLBufferRenderer }; | ||||
							
								
								
									
										810
									
								
								public/sdk/three/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										810
									
								
								public/sdk/three/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,810 @@ | ||||
| import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js'; | ||||
|  | ||||
| import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js'; | ||||
| import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js'; | ||||
|  | ||||
| import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js'; | ||||
|  | ||||
| import { RedFormat, RGFormat, IntType, DataTexture, RGBFormat, RGBAFormat, FloatType } from 'three'; | ||||
|  | ||||
| const glslMethods = { | ||||
| 	[ MathNode.ATAN2 ]: 'atan', | ||||
| 	textureDimensions: 'textureSize', | ||||
| 	equals: 'equal' | ||||
| }; | ||||
|  | ||||
| const precisionLib = { | ||||
| 	low: 'lowp', | ||||
| 	medium: 'mediump', | ||||
| 	high: 'highp' | ||||
| }; | ||||
|  | ||||
| const supports = { | ||||
| 	instance: true, | ||||
| 	swizzleAssign: true | ||||
| }; | ||||
|  | ||||
| const defaultPrecisions = ` | ||||
| precision highp float; | ||||
| precision highp int; | ||||
| precision mediump sampler2DArray; | ||||
| precision lowp sampler2DShadow; | ||||
| `; | ||||
|  | ||||
| class GLSLNodeBuilder extends NodeBuilder { | ||||
|  | ||||
| 	constructor( object, renderer, scene = null ) { | ||||
|  | ||||
| 		super( object, renderer, new GLSLNodeParser(), scene ); | ||||
|  | ||||
| 		this.uniformGroups = {}; | ||||
| 		this.transforms = []; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getMethod( method ) { | ||||
|  | ||||
| 		return glslMethods[ method ] || method; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getPropertyName( node, shaderStage ) { | ||||
|  | ||||
| 		if ( node.isOutputStructVar ) return ''; | ||||
|  | ||||
| 		return super.getPropertyName( node, shaderStage ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	buildFunctionCode( shaderNode ) { | ||||
|  | ||||
| 		const layout = shaderNode.layout; | ||||
| 		const flowData = this.flowShaderNode( shaderNode ); | ||||
|  | ||||
| 		const parameters = []; | ||||
|  | ||||
| 		for ( const input of layout.inputs ) { | ||||
|  | ||||
| 			parameters.push( this.getType( input.type ) + ' ' + input.name ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) { | ||||
|  | ||||
| 	${ flowData.vars } | ||||
|  | ||||
| ${ flowData.code } | ||||
| 	return ${ flowData.result }; | ||||
|  | ||||
| }`; | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		return code; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setupPBO( storageBufferNode ) { | ||||
|  | ||||
| 		const attribute = storageBufferNode.value; | ||||
|  | ||||
| 		if ( attribute.pbo === undefined ) { | ||||
|  | ||||
| 			const originalArray = attribute.array; | ||||
| 			const numElements = attribute.count * attribute.itemSize; | ||||
|  | ||||
| 			const { itemSize } = attribute; | ||||
| 			let format = RedFormat; | ||||
|  | ||||
| 			if ( itemSize === 2 ) { | ||||
|  | ||||
| 				format = RGFormat; | ||||
|  | ||||
| 			} else if ( itemSize === 3 ) { | ||||
|  | ||||
| 				format = RGBFormat; | ||||
|  | ||||
| 			} else if ( itemSize === 4 ) { | ||||
|  | ||||
| 				format = RGBAFormat; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const width = Math.pow( 2, Math.ceil( Math.log2( Math.sqrt( numElements / itemSize ) ) ) ); | ||||
| 			let height = Math.ceil( ( numElements / itemSize ) / width ); | ||||
| 			if ( width * height * itemSize < numElements ) height ++; // Ensure enough space | ||||
|  | ||||
| 			const newSize = width * height * itemSize; | ||||
|  | ||||
| 			const newArray = new Float32Array( newSize ); | ||||
|  | ||||
| 			newArray.set( originalArray, 0 ); | ||||
|  | ||||
| 			attribute.array = newArray; | ||||
|  | ||||
| 			const pboTexture = new DataTexture( attribute.array, width, height, format, FloatType ); | ||||
| 			pboTexture.needsUpdate = true; | ||||
| 			pboTexture.isPBOTexture = true; | ||||
|  | ||||
| 			const pbo = new UniformNode( pboTexture ); | ||||
| 			pbo.setPrecision( 'high' ); | ||||
|  | ||||
| 			attribute.pboNode = pbo; | ||||
| 			attribute.pbo = pbo.value; | ||||
|  | ||||
| 			this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generatePBO( storageArrayElementNode ) { | ||||
|  | ||||
| 		const { node, indexNode } = storageArrayElementNode; | ||||
| 		const attribute = node.value; | ||||
|  | ||||
| 		if ( this.renderer.backend.has( attribute ) ) { | ||||
|  | ||||
| 			const attributeData = this.renderer.backend.get( attribute ); | ||||
| 			attributeData.pbo = attribute.pbo; | ||||
|  | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		const nodeUniform = this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); | ||||
| 		const textureName = this.getPropertyName( nodeUniform ); | ||||
|  | ||||
| 		indexNode.increaseUsage( this ); // force cache generate to be used as index in x,y | ||||
| 		const indexSnippet = indexNode.build( this, 'uint' ); | ||||
|  | ||||
| 		const elementNodeData = this.getDataFromNode( storageArrayElementNode ); | ||||
|  | ||||
| 		let propertyName = elementNodeData.propertyName; | ||||
|  | ||||
| 		if ( propertyName === undefined ) { | ||||
|  | ||||
| 			// property element | ||||
|  | ||||
| 			const nodeVar = this.getVarFromNode( storageArrayElementNode ); | ||||
|  | ||||
| 			propertyName = this.getPropertyName( nodeVar ); | ||||
|  | ||||
| 			// property size | ||||
|  | ||||
| 			const bufferNodeData = this.getDataFromNode( node ); | ||||
|  | ||||
| 			let propertySizeName = bufferNodeData.propertySizeName; | ||||
|  | ||||
| 			if ( propertySizeName === undefined ) { | ||||
|  | ||||
| 				propertySizeName = propertyName + 'Size'; | ||||
|  | ||||
| 				this.getVarFromNode( node, propertySizeName, 'uint' ); | ||||
|  | ||||
| 				this.addLineFlowCode( `${ propertySizeName } = uint( textureSize( ${ textureName }, 0 ).x )` ); | ||||
|  | ||||
| 				bufferNodeData.propertySizeName = propertySizeName; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			const { itemSize } = attribute; | ||||
|  | ||||
| 			const channel = '.' + vectorComponents.join( '' ).slice( 0, itemSize ); | ||||
| 			const uvSnippet = `ivec2(${indexSnippet} % ${ propertySizeName }, ${indexSnippet} / ${ propertySizeName })`; | ||||
|  | ||||
| 			const snippet = this.generateTextureLoad( null, textureName, uvSnippet, null, '0' ); | ||||
|  | ||||
| 			// | ||||
|  | ||||
| 			this.addLineFlowCode( `${ propertyName } = ${ snippet + channel }` ); | ||||
|  | ||||
| 			elementNodeData.propertyName = propertyName; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return propertyName; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateTextureLoad( texture, textureProperty, uvIndexSnippet, depthSnippet, levelSnippet = '0' ) { | ||||
|  | ||||
| 		if ( depthSnippet ) { | ||||
|  | ||||
| 			return `texelFetch( ${ textureProperty }, ivec3( ${ uvIndexSnippet }, ${ depthSnippet } ), ${ levelSnippet } )`; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			return `texelFetch( ${ textureProperty }, ${ uvIndexSnippet }, ${ levelSnippet } )`; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateTexture( texture, textureProperty, uvSnippet, depthSnippet ) { | ||||
|  | ||||
| 		if ( texture.isDepthTexture ) { | ||||
|  | ||||
| 			return `texture( ${ textureProperty }, ${ uvSnippet } ).x`; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`; | ||||
|  | ||||
| 			return `texture( ${ textureProperty }, ${ uvSnippet } )`; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet ) { | ||||
|  | ||||
| 		return `textureLod( ${ textureProperty }, ${ uvSnippet }, ${ levelSnippet } )`; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet ) { | ||||
|  | ||||
| 		return `textureGrad( ${ textureProperty }, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) { | ||||
|  | ||||
| 		if ( shaderStage === 'fragment' ) { | ||||
|  | ||||
| 			return `texture( ${ textureProperty }, vec3( ${ uvSnippet }, ${ compareSnippet } ) )`; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			console.error( `WebGPURenderer: THREE.DepthTexture.compareFunction() does not support ${ shaderStage } shader.` ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getVars( shaderStage ) { | ||||
|  | ||||
| 		const snippets = []; | ||||
|  | ||||
| 		const vars = this.vars[ shaderStage ]; | ||||
|  | ||||
| 		if ( vars !== undefined ) { | ||||
|  | ||||
| 			for ( const variable of vars ) { | ||||
|  | ||||
| 				if ( variable.isOutputStructVar ) continue; | ||||
|  | ||||
| 				snippets.push( `${ this.getVar( variable.type, variable.name ) };` ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippets.join( '\n\t' ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getUniforms( shaderStage ) { | ||||
|  | ||||
| 		const uniforms = this.uniforms[ shaderStage ]; | ||||
|  | ||||
| 		const bindingSnippets = []; | ||||
| 		const uniformGroups = {}; | ||||
|  | ||||
| 		for ( const uniform of uniforms ) { | ||||
|  | ||||
| 			let snippet = null; | ||||
| 			let group = false; | ||||
|  | ||||
| 			if ( uniform.type === 'texture' ) { | ||||
|  | ||||
| 				const texture = uniform.node.value; | ||||
|  | ||||
| 				if ( texture.compareFunction ) { | ||||
|  | ||||
| 					snippet = `sampler2DShadow ${ uniform.name };`; | ||||
|  | ||||
| 				} else if ( texture.isDataArrayTexture === true ) { | ||||
|  | ||||
| 					snippet = `sampler2DArray ${ uniform.name };`; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					snippet = `sampler2D ${ uniform.name };`; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else if ( uniform.type === 'cubeTexture' ) { | ||||
|  | ||||
| 				snippet = `samplerCube ${ uniform.name };`; | ||||
|  | ||||
| 			} else if ( uniform.type === 'buffer' ) { | ||||
|  | ||||
| 				const bufferNode = uniform.node; | ||||
| 				const bufferType = this.getType( bufferNode.bufferType ); | ||||
| 				const bufferCount = bufferNode.bufferCount; | ||||
|  | ||||
| 				const bufferCountSnippet = bufferCount > 0 ? bufferCount : ''; | ||||
| 				snippet = `${bufferNode.name} {\n\t${ bufferType } ${ uniform.name }[${ bufferCountSnippet }];\n};\n`; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				const vectorType = this.getVectorType( uniform.type ); | ||||
|  | ||||
| 				snippet = `${vectorType} ${uniform.name};`; | ||||
|  | ||||
| 				group = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const precision = uniform.node.precision; | ||||
|  | ||||
| 			if ( precision !== null ) { | ||||
|  | ||||
| 				snippet = precisionLib[ precision ] + ' ' + snippet; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( group ) { | ||||
|  | ||||
| 				snippet = '\t' + snippet; | ||||
|  | ||||
| 				const groupName = uniform.groupNode.name; | ||||
| 				const groupSnippets = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = [] ); | ||||
|  | ||||
| 				groupSnippets.push( snippet ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				snippet = 'uniform ' + snippet; | ||||
|  | ||||
| 				bindingSnippets.push( snippet ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		let output = ''; | ||||
|  | ||||
| 		for ( const name in uniformGroups ) { | ||||
|  | ||||
| 			const groupSnippets = uniformGroups[ name ]; | ||||
|  | ||||
| 			output += this._getGLSLUniformStruct( shaderStage + '_' + name, groupSnippets.join( '\n' ) ) + '\n'; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		output += bindingSnippets.join( '\n' ); | ||||
|  | ||||
| 		return output; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getTypeFromAttribute( attribute ) { | ||||
|  | ||||
| 		let nodeType = super.getTypeFromAttribute( attribute ); | ||||
|  | ||||
| 		if ( /^[iu]/.test( nodeType ) && attribute.gpuType !== IntType ) { | ||||
|  | ||||
| 			let dataAttribute = attribute; | ||||
|  | ||||
| 			if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data; | ||||
|  | ||||
| 			const array = dataAttribute.array; | ||||
|  | ||||
| 			if ( ( array instanceof Uint32Array || array instanceof Int32Array || array instanceof Uint16Array || array instanceof Int16Array ) === false ) { | ||||
|  | ||||
| 				nodeType = nodeType.slice( 1 ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return nodeType; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getAttributes( shaderStage ) { | ||||
|  | ||||
| 		let snippet = ''; | ||||
|  | ||||
| 		if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { | ||||
|  | ||||
| 			const attributes = this.getAttributesArray(); | ||||
|  | ||||
| 			let location = 0; | ||||
|  | ||||
| 			for ( const attribute of attributes ) { | ||||
|  | ||||
| 				snippet += `layout( location = ${ location ++ } ) in ${ attribute.type } ${ attribute.name };\n`; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippet; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getStructMembers( struct ) { | ||||
|  | ||||
| 		const snippets = []; | ||||
| 		const members = struct.getMemberTypes(); | ||||
|  | ||||
| 		for ( let i = 0; i < members.length; i ++ ) { | ||||
|  | ||||
| 			const member = members[ i ]; | ||||
| 			snippets.push( `layout( location = ${i} ) out ${ member} m${i};` ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippets.join( '\n' ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getStructs( shaderStage ) { | ||||
|  | ||||
| 		const snippets = []; | ||||
| 		const structs = this.structs[ shaderStage ]; | ||||
|  | ||||
| 		if ( structs.length === 0 ) { | ||||
|  | ||||
| 			return 'layout( location = 0 ) out vec4 fragColor;\n'; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		for ( let index = 0, length = structs.length; index < length; index ++ ) { | ||||
|  | ||||
| 			const struct = structs[ index ]; | ||||
|  | ||||
| 			let snippet = '\n'; | ||||
| 			snippet += this.getStructMembers( struct ); | ||||
| 			snippet += '\n'; | ||||
|  | ||||
| 			snippets.push( snippet ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippets.join( '\n\n' ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getVaryings( shaderStage ) { | ||||
|  | ||||
| 		let snippet = ''; | ||||
|  | ||||
| 		const varyings = this.varyings; | ||||
|  | ||||
| 		if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { | ||||
|  | ||||
| 			for ( const varying of varyings ) { | ||||
|  | ||||
| 				if ( shaderStage === 'compute' ) varying.needsInterpolation = true; | ||||
| 				const type = varying.type; | ||||
| 				const flat = type === 'int' || type === 'uint' ? 'flat ' : ''; | ||||
|  | ||||
| 				snippet += `${flat}${varying.needsInterpolation ? 'out' : '/*out*/'} ${type} ${varying.name};\n`; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( shaderStage === 'fragment' ) { | ||||
|  | ||||
| 			for ( const varying of varyings ) { | ||||
|  | ||||
| 				if ( varying.needsInterpolation ) { | ||||
|  | ||||
| 					const type = varying.type; | ||||
| 					const flat = type === 'int' || type === 'uint' ? 'flat ' : ''; | ||||
|  | ||||
| 					snippet += `${flat}in ${type} ${varying.name};\n`; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippet; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getVertexIndex() { | ||||
|  | ||||
| 		return 'uint( gl_VertexID )'; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getInstanceIndex() { | ||||
|  | ||||
| 		return 'uint( gl_InstanceID )'; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getFrontFacing() { | ||||
|  | ||||
| 		return 'gl_FrontFacing'; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getFragCoord() { | ||||
|  | ||||
| 		return 'gl_FragCoord'; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getFragDepth() { | ||||
|  | ||||
| 		return 'gl_FragDepth'; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	isAvailable( name ) { | ||||
|  | ||||
| 		return supports[ name ] === true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	isFlipY() { | ||||
|  | ||||
| 		return true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	registerTransform( varyingName, attributeNode ) { | ||||
|  | ||||
| 		this.transforms.push( { varyingName, attributeNode } ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getTransforms( /* shaderStage  */ ) { | ||||
|  | ||||
| 		const transforms = this.transforms; | ||||
|  | ||||
| 		let snippet = ''; | ||||
|  | ||||
| 		for ( let i = 0; i < transforms.length; i ++ ) { | ||||
|  | ||||
| 			const transform = transforms[ i ]; | ||||
|  | ||||
| 			const attributeName = this.getPropertyName( transform.attributeNode ); | ||||
|  | ||||
| 			snippet += `${ transform.varyingName } = ${ attributeName };\n\t`; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return snippet; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getGLSLUniformStruct( name, vars ) { | ||||
|  | ||||
| 		return ` | ||||
| layout( std140 ) uniform ${name} { | ||||
| ${vars} | ||||
| };`; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getGLSLVertexCode( shaderData ) { | ||||
|  | ||||
| 		return `#version 300 es | ||||
|  | ||||
| ${ this.getSignature() } | ||||
|  | ||||
| // precision | ||||
| ${ defaultPrecisions } | ||||
|  | ||||
| // uniforms | ||||
| ${shaderData.uniforms} | ||||
|  | ||||
| // varyings | ||||
| ${shaderData.varyings} | ||||
|  | ||||
| // attributes | ||||
| ${shaderData.attributes} | ||||
|  | ||||
| // codes | ||||
| ${shaderData.codes} | ||||
|  | ||||
| void main() { | ||||
|  | ||||
| 	// vars | ||||
| 	${shaderData.vars} | ||||
|  | ||||
| 	// transforms | ||||
| 	${shaderData.transforms} | ||||
|  | ||||
| 	// flow | ||||
| 	${shaderData.flow} | ||||
|  | ||||
| 	gl_PointSize = 1.0; | ||||
|  | ||||
| } | ||||
| `; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getGLSLFragmentCode( shaderData ) { | ||||
|  | ||||
| 		return `#version 300 es | ||||
|  | ||||
| ${ this.getSignature() } | ||||
|  | ||||
| // precision | ||||
| ${ defaultPrecisions } | ||||
|  | ||||
| // uniforms | ||||
| ${shaderData.uniforms} | ||||
|  | ||||
| // varyings | ||||
| ${shaderData.varyings} | ||||
|  | ||||
| // codes | ||||
| ${shaderData.codes} | ||||
|  | ||||
| ${shaderData.structs} | ||||
|  | ||||
| void main() { | ||||
|  | ||||
| 	// vars | ||||
| 	${shaderData.vars} | ||||
|  | ||||
| 	// flow | ||||
| 	${shaderData.flow} | ||||
|  | ||||
| } | ||||
| `; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	buildCode() { | ||||
|  | ||||
| 		const shadersData = this.material !== null ? { fragment: {}, vertex: {} } : { compute: {} }; | ||||
|  | ||||
| 		for ( const shaderStage in shadersData ) { | ||||
|  | ||||
| 			let flow = '// code\n\n'; | ||||
| 			flow += this.flowCode[ shaderStage ]; | ||||
|  | ||||
| 			const flowNodes = this.flowNodes[ shaderStage ]; | ||||
| 			const mainNode = flowNodes[ flowNodes.length - 1 ]; | ||||
|  | ||||
| 			for ( const node of flowNodes ) { | ||||
|  | ||||
| 				const flowSlotData = this.getFlowData( node/*, shaderStage*/ ); | ||||
| 				const slotName = node.name; | ||||
|  | ||||
| 				if ( slotName ) { | ||||
|  | ||||
| 					if ( flow.length > 0 ) flow += '\n'; | ||||
|  | ||||
| 					flow += `\t// flow -> ${ slotName }\n\t`; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				flow += `${ flowSlotData.code }\n\t`; | ||||
|  | ||||
| 				if ( node === mainNode && shaderStage !== 'compute' ) { | ||||
|  | ||||
| 					flow += '// result\n\t'; | ||||
|  | ||||
| 					if ( shaderStage === 'vertex' ) { | ||||
|  | ||||
| 						flow += 'gl_Position = '; | ||||
| 						flow += `${ flowSlotData.result };`; | ||||
|  | ||||
| 					} else if ( shaderStage === 'fragment' ) { | ||||
|  | ||||
| 						if ( ! node.outputNode.isOutputStructNode ) { | ||||
|  | ||||
| 							flow += 'fragColor = '; | ||||
| 							flow += `${ flowSlotData.result };`; | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const stageData = shadersData[ shaderStage ]; | ||||
|  | ||||
| 			stageData.uniforms = this.getUniforms( shaderStage ); | ||||
| 			stageData.attributes = this.getAttributes( shaderStage ); | ||||
| 			stageData.varyings = this.getVaryings( shaderStage ); | ||||
| 			stageData.vars = this.getVars( shaderStage ); | ||||
| 			stageData.structs = this.getStructs( shaderStage ); | ||||
| 			stageData.codes = this.getCodes( shaderStage ); | ||||
| 			stageData.transforms = this.getTransforms( shaderStage ); | ||||
| 			stageData.flow = flow; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( this.material !== null ) { | ||||
|  | ||||
| 			this.vertexShader = this._getGLSLVertexCode( shadersData.vertex ); | ||||
| 			this.fragmentShader = this._getGLSLFragmentCode( shadersData.fragment ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.computeShader = this._getGLSLVertexCode( shadersData.compute ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getUniformFromNode( node, type, shaderStage, name = null ) { | ||||
|  | ||||
| 		const uniformNode = super.getUniformFromNode( node, type, shaderStage, name ); | ||||
| 		const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache ); | ||||
|  | ||||
| 		let uniformGPU = nodeData.uniformGPU; | ||||
|  | ||||
| 		if ( uniformGPU === undefined ) { | ||||
|  | ||||
| 			if ( type === 'texture' ) { | ||||
|  | ||||
| 				uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node ); | ||||
|  | ||||
| 				this.bindings[ shaderStage ].push( uniformGPU ); | ||||
|  | ||||
| 			} else if ( type === 'cubeTexture' ) { | ||||
|  | ||||
| 				uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node ); | ||||
|  | ||||
| 				this.bindings[ shaderStage ].push( uniformGPU ); | ||||
|  | ||||
| 			} else if ( type === 'buffer' ) { | ||||
|  | ||||
| 				node.name = `NodeBuffer_${ node.id }`; | ||||
| 				uniformNode.name = `buffer${ node.id }`; | ||||
|  | ||||
| 				const buffer = new NodeUniformBuffer( node ); | ||||
| 				buffer.name = node.name; | ||||
|  | ||||
| 				this.bindings[ shaderStage ].push( buffer ); | ||||
|  | ||||
| 				uniformGPU = buffer; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				const group = node.groupNode; | ||||
| 				const groupName = group.name; | ||||
|  | ||||
| 				const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} ); | ||||
|  | ||||
| 				let uniformsGroup = uniformsStage[ groupName ]; | ||||
|  | ||||
| 				if ( uniformsGroup === undefined ) { | ||||
|  | ||||
| 					uniformsGroup = new NodeUniformsGroup( shaderStage + '_' + groupName, group ); | ||||
| 					//uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); | ||||
|  | ||||
| 					uniformsStage[ groupName ] = uniformsGroup; | ||||
|  | ||||
| 					this.bindings[ shaderStage ].push( uniformsGroup ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				uniformGPU = this.getNodeUniform( uniformNode, type ); | ||||
|  | ||||
| 				uniformsGroup.addUniform( uniformGPU ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			nodeData.uniformGPU = uniformGPU; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return uniformNode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default GLSLNodeBuilder; | ||||
| @ -0,0 +1,258 @@ | ||||
| import { IntType } from 'three'; | ||||
|  | ||||
| let _id = 0; | ||||
|  | ||||
| class DualAttributeData { | ||||
|  | ||||
| 	constructor( attributeData, dualBuffer ) { | ||||
|  | ||||
| 		this.buffers = [ attributeData.bufferGPU, dualBuffer ]; | ||||
| 		this.type = attributeData.type; | ||||
| 		this.bufferType = attributeData.bufferType; | ||||
| 		this.pbo = attributeData.pbo; | ||||
| 		this.byteLength = attributeData.byteLength; | ||||
| 		this.bytesPerElement = attributeData.BYTES_PER_ELEMENT; | ||||
| 		this.version = attributeData.version; | ||||
| 		this.isInteger = attributeData.isInteger; | ||||
| 		this.activeBufferIndex = 0; | ||||
| 		this.baseId = attributeData.id; | ||||
|  | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	get id() { | ||||
|  | ||||
| 		return `${ this.baseId }|${ this.activeBufferIndex }`; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get bufferGPU() { | ||||
|  | ||||
| 		return this.buffers[ this.activeBufferIndex ]; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get transformBuffer() { | ||||
|  | ||||
| 		return this.buffers[ this.activeBufferIndex ^ 1 ]; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	switchBuffers() { | ||||
|  | ||||
| 		this.activeBufferIndex ^= 1; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| class WebGLAttributeUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createAttribute( attribute, bufferType ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const { gl } = backend; | ||||
|  | ||||
| 		const array = attribute.array; | ||||
| 		const usage = attribute.usage || gl.STATIC_DRAW; | ||||
|  | ||||
| 		const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute; | ||||
| 		const bufferData = backend.get( bufferAttribute ); | ||||
|  | ||||
| 		let bufferGPU = bufferData.bufferGPU; | ||||
|  | ||||
| 		if ( bufferGPU === undefined ) { | ||||
|  | ||||
| 			bufferGPU = this._createBuffer( gl, bufferType, array, usage ); | ||||
|  | ||||
| 			bufferData.bufferGPU = bufferGPU; | ||||
| 			bufferData.bufferType = bufferType; | ||||
| 			bufferData.version = bufferAttribute.version; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		//attribute.onUploadCallback(); | ||||
|  | ||||
| 		let type; | ||||
|  | ||||
| 		if ( array instanceof Float32Array ) { | ||||
|  | ||||
| 			type = gl.FLOAT; | ||||
|  | ||||
| 		} else if ( array instanceof Uint16Array ) { | ||||
|  | ||||
| 			if ( attribute.isFloat16BufferAttribute ) { | ||||
|  | ||||
| 				type = gl.HALF_FLOAT; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				type = gl.UNSIGNED_SHORT; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( array instanceof Int16Array ) { | ||||
|  | ||||
| 			type = gl.SHORT; | ||||
|  | ||||
| 		} else if ( array instanceof Uint32Array ) { | ||||
|  | ||||
| 			type = gl.UNSIGNED_INT; | ||||
|  | ||||
| 		} else if ( array instanceof Int32Array ) { | ||||
|  | ||||
| 			type = gl.INT; | ||||
|  | ||||
| 		} else if ( array instanceof Int8Array ) { | ||||
|  | ||||
| 			type = gl.BYTE; | ||||
|  | ||||
| 		} else if ( array instanceof Uint8Array ) { | ||||
|  | ||||
| 			type = gl.UNSIGNED_BYTE; | ||||
|  | ||||
| 		} else if ( array instanceof Uint8ClampedArray ) { | ||||
|  | ||||
| 			type = gl.UNSIGNED_BYTE; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			throw new Error( 'THREE.WebGLBackend: Unsupported buffer data format: ' + array ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		let attributeData = { | ||||
| 			bufferGPU, | ||||
| 			bufferType, | ||||
| 			type, | ||||
| 			byteLength: array.byteLength, | ||||
| 			bytesPerElement: array.BYTES_PER_ELEMENT, | ||||
| 			version: attribute.version, | ||||
| 			pbo: attribute.pbo, | ||||
| 			isInteger: type === gl.INT || type === gl.UNSIGNED_INT || type === gl.UNSIGNED_SHORT || attribute.gpuType === IntType, | ||||
| 			id: _id ++ | ||||
| 		}; | ||||
|  | ||||
| 		if ( attribute.isStorageBufferAttribute || attribute.isStorageInstancedBufferAttribute ) { | ||||
|  | ||||
| 			// create buffer for tranform feedback use | ||||
| 			const bufferGPUDual = this._createBuffer( gl, bufferType, array, usage ); | ||||
| 			attributeData = new DualAttributeData( attributeData, bufferGPUDual ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		backend.set( attribute, attributeData ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateAttribute( attribute ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const { gl } = backend; | ||||
|  | ||||
| 		const array = attribute.array; | ||||
| 		const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute; | ||||
| 		const bufferData = backend.get( bufferAttribute ); | ||||
| 		const bufferType = bufferData.bufferType; | ||||
| 		const updateRanges = attribute.isInterleavedBufferAttribute ? attribute.data.updateRanges : attribute.updateRanges; | ||||
|  | ||||
| 		gl.bindBuffer( bufferType, bufferData.bufferGPU ); | ||||
|  | ||||
| 		if ( updateRanges.length === 0 ) { | ||||
|  | ||||
| 			// Not using update ranges | ||||
|  | ||||
| 			gl.bufferSubData( bufferType, 0, array ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { | ||||
|  | ||||
| 				const range = updateRanges[ i ]; | ||||
| 				gl.bufferSubData( bufferType, range.start * array.BYTES_PER_ELEMENT, | ||||
| 					array, range.start, range.count ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			bufferAttribute.clearUpdateRanges(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		gl.bindBuffer( bufferType, null ); | ||||
|  | ||||
| 		bufferData.version = bufferAttribute.version; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	destroyAttribute( attribute ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const { gl } = backend; | ||||
|  | ||||
| 		if ( attribute.isInterleavedBufferAttribute ) { | ||||
|  | ||||
| 			backend.delete( attribute.data ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const attributeData = backend.get( attribute ); | ||||
|  | ||||
| 		gl.deleteBuffer( attributeData.bufferGPU ); | ||||
|  | ||||
| 		backend.delete( attribute ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	async getArrayBufferAsync( attribute ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const { gl } = backend; | ||||
|  | ||||
| 		const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute; | ||||
| 		const { bufferGPU } = backend.get( bufferAttribute ); | ||||
|  | ||||
| 		const array = attribute.array; | ||||
| 		const byteLength = array.byteLength; | ||||
|  | ||||
| 		gl.bindBuffer( gl.COPY_READ_BUFFER, bufferGPU ); | ||||
|  | ||||
| 		const writeBuffer = gl.createBuffer(); | ||||
|  | ||||
| 		gl.bindBuffer( gl.COPY_WRITE_BUFFER, writeBuffer ); | ||||
| 		gl.bufferData( gl.COPY_WRITE_BUFFER, byteLength, gl.STREAM_READ ); | ||||
|  | ||||
| 		gl.copyBufferSubData( gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, byteLength ); | ||||
|  | ||||
| 		await backend.utils._clientWaitAsync(); | ||||
|  | ||||
| 		const dstBuffer = new attribute.array.constructor( array.length ); | ||||
|  | ||||
| 		gl.getBufferSubData( gl.COPY_WRITE_BUFFER, 0, dstBuffer ); | ||||
|  | ||||
| 		gl.deleteBuffer( writeBuffer ); | ||||
|  | ||||
| 		return dstBuffer.buffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_createBuffer( gl, bufferType, array, usage ) { | ||||
|  | ||||
| 		const bufferGPU = gl.createBuffer(); | ||||
|  | ||||
| 		gl.bindBuffer( bufferType, bufferGPU ); | ||||
| 		gl.bufferData( bufferType, array, usage ); | ||||
| 		gl.bindBuffer( bufferType, null ); | ||||
|  | ||||
| 		return bufferGPU; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLAttributeUtils; | ||||
| @ -0,0 +1,36 @@ | ||||
| class WebGLCapabilities { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 		this.maxAnisotropy = null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getMaxAnisotropy() { | ||||
|  | ||||
| 		if ( this.maxAnisotropy !== null ) return this.maxAnisotropy; | ||||
|  | ||||
| 		const gl = this.backend.gl; | ||||
| 		const extensions = this.backend.extensions; | ||||
|  | ||||
| 		if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { | ||||
|  | ||||
| 			const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); | ||||
|  | ||||
| 			this.maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.maxAnisotropy = 0; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return this.maxAnisotropy; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLCapabilities; | ||||
							
								
								
									
										12
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLConstants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLConstants.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| export const GLFeatureName = { | ||||
|  | ||||
| 	'WEBGL_compressed_texture_astc': 'texture-compression-astc', | ||||
| 	'WEBGL_compressed_texture_etc': 'texture-compression-etc2', | ||||
| 	'WEBGL_compressed_texture_etc1': 'texture-compression-etc1', | ||||
| 	'WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc', | ||||
| 	'WEBKIT_WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc', | ||||
| 	'WEBGL_compressed_texture_s3tc': 'texture-compression-bc', | ||||
| 	'EXT_texture_compression_bptc': 'texture-compression-bptc', | ||||
| 	'EXT_disjoint_timer_query_webgl2': 'timestamp-query', | ||||
|  | ||||
| }; | ||||
| @ -0,0 +1,36 @@ | ||||
| class WebGLExtensions { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 		this.gl = this.backend.gl; | ||||
| 		this.availableExtensions = this.gl.getSupportedExtensions(); | ||||
|  | ||||
| 		this.extensions = {}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	get( name ) { | ||||
|  | ||||
| 		let extension = this.extensions[ name ]; | ||||
|  | ||||
| 		if ( extension === undefined ) { | ||||
|  | ||||
| 			extension = this.gl.getExtension( name ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return extension; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	has( name ) { | ||||
|  | ||||
| 		return this.availableExtensions.includes( name ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLExtensions; | ||||
							
								
								
									
										738
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLState.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										738
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLState.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,738 @@ | ||||
| import { | ||||
| 	CullFaceNone, CullFaceBack, CullFaceFront, DoubleSide, BackSide, | ||||
| 	NormalBlending, NoBlending, CustomBlending, AddEquation, | ||||
| 	AdditiveBlending, SubtractiveBlending, MultiplyBlending, SubtractEquation, ReverseSubtractEquation, | ||||
| 	ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor, | ||||
| 	OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor, | ||||
| 	NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth | ||||
| } from 'three'; | ||||
|  | ||||
| let initialized = false, equationToGL, factorToGL; | ||||
|  | ||||
| class WebGLState { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 		this.gl = this.backend.gl; | ||||
|  | ||||
| 		this.enabled = {}; | ||||
| 		this.currentFlipSided = null; | ||||
| 		this.currentCullFace = null; | ||||
| 		this.currentProgram = null; | ||||
| 		this.currentBlendingEnabled = false; | ||||
| 		this.currentBlending = null; | ||||
| 		this.currentBlendSrc = null; | ||||
| 		this.currentBlendDst = null; | ||||
| 		this.currentBlendSrcAlpha = null; | ||||
| 		this.currentBlendDstAlpha = null; | ||||
| 		this.currentPremultipledAlpha = null; | ||||
| 		this.currentPolygonOffsetFactor = null; | ||||
| 		this.currentPolygonOffsetUnits = null; | ||||
| 		this.currentColorMask = null; | ||||
| 		this.currentDepthFunc = null; | ||||
| 		this.currentDepthMask = null; | ||||
| 		this.currentStencilFunc = null; | ||||
| 		this.currentStencilRef = null; | ||||
| 		this.currentStencilFuncMask = null; | ||||
| 		this.currentStencilFail = null; | ||||
| 		this.currentStencilZFail = null; | ||||
| 		this.currentStencilZPass = null; | ||||
| 		this.currentStencilMask = null; | ||||
| 		this.currentLineWidth = null; | ||||
|  | ||||
| 		this.currentBoundFramebuffers = {}; | ||||
| 		this.currentDrawbuffers = new WeakMap(); | ||||
|  | ||||
| 		this.maxTextures = this.gl.getParameter( this.gl.MAX_TEXTURE_IMAGE_UNITS ); | ||||
| 		this.currentTextureSlot = null; | ||||
| 		this.currentBoundTextures = {}; | ||||
|  | ||||
| 		if ( initialized === false ) { | ||||
|  | ||||
| 			this._init( this.gl ); | ||||
|  | ||||
| 			initialized = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_init( gl ) { | ||||
|  | ||||
| 		// Store only WebGL constants here. | ||||
|  | ||||
| 		equationToGL = { | ||||
| 			[ AddEquation ]: gl.FUNC_ADD, | ||||
| 			[ SubtractEquation ]: gl.FUNC_SUBTRACT, | ||||
| 			[ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT | ||||
| 		}; | ||||
|  | ||||
| 		factorToGL = { | ||||
| 			[ ZeroFactor ]: gl.ZERO, | ||||
| 			[ OneFactor ]: gl.ONE, | ||||
| 			[ SrcColorFactor ]: gl.SRC_COLOR, | ||||
| 			[ SrcAlphaFactor ]: gl.SRC_ALPHA, | ||||
| 			[ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE, | ||||
| 			[ DstColorFactor ]: gl.DST_COLOR, | ||||
| 			[ DstAlphaFactor ]: gl.DST_ALPHA, | ||||
| 			[ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR, | ||||
| 			[ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA, | ||||
| 			[ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR, | ||||
| 			[ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA | ||||
| 		}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	enable( id ) { | ||||
|  | ||||
| 		const { enabled } = this; | ||||
|  | ||||
| 		if ( enabled[ id ] !== true ) { | ||||
|  | ||||
| 			this.gl.enable( id ); | ||||
| 			enabled[ id ] = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	disable( id ) { | ||||
|  | ||||
| 		const { enabled } = this; | ||||
|  | ||||
| 		if ( enabled[ id ] !== false ) { | ||||
|  | ||||
| 			this.gl.disable( id ); | ||||
| 			enabled[ id ] = false; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setFlipSided( flipSided ) { | ||||
|  | ||||
| 		if ( this.currentFlipSided !== flipSided ) { | ||||
|  | ||||
| 			const { gl } = this; | ||||
|  | ||||
| 			if ( flipSided ) { | ||||
|  | ||||
| 				gl.frontFace( gl.CW ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				gl.frontFace( gl.CCW ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			this.currentFlipSided = flipSided; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setCullFace( cullFace ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( cullFace !== CullFaceNone ) { | ||||
|  | ||||
| 			this.enable( gl.CULL_FACE ); | ||||
|  | ||||
| 			if ( cullFace !== this.currentCullFace ) { | ||||
|  | ||||
| 				if ( cullFace === CullFaceBack ) { | ||||
|  | ||||
| 					gl.cullFace( gl.BACK ); | ||||
|  | ||||
| 				} else if ( cullFace === CullFaceFront ) { | ||||
|  | ||||
| 					gl.cullFace( gl.FRONT ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					gl.cullFace( gl.FRONT_AND_BACK ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.disable( gl.CULL_FACE ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.currentCullFace = cullFace; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setLineWidth( width ) { | ||||
|  | ||||
| 		const { currentLineWidth, gl } = this; | ||||
|  | ||||
| 		if ( width !== currentLineWidth ) { | ||||
|  | ||||
| 			gl.lineWidth( width ); | ||||
|  | ||||
| 			this.currentLineWidth = width; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( blending === NoBlending ) { | ||||
|  | ||||
| 			if ( this.currentBlendingEnabled === true ) { | ||||
|  | ||||
| 				this.disable( gl.BLEND ); | ||||
| 				this.currentBlendingEnabled = false; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( this.currentBlendingEnabled === false ) { | ||||
|  | ||||
| 			this.enable( gl.BLEND ); | ||||
| 			this.currentBlendingEnabled = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( blending !== CustomBlending ) { | ||||
|  | ||||
| 			if ( blending !== this.currentBlending || premultipliedAlpha !== this.currentPremultipledAlpha ) { | ||||
|  | ||||
| 				if ( this.currentBlendEquation !== AddEquation || this.currentBlendEquationAlpha !== AddEquation ) { | ||||
|  | ||||
| 					gl.blendEquation( gl.FUNC_ADD ); | ||||
|  | ||||
| 					this.currentBlendEquation = AddEquation; | ||||
| 					this.currentBlendEquationAlpha = AddEquation; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( premultipliedAlpha ) { | ||||
|  | ||||
| 					switch ( blending ) { | ||||
|  | ||||
| 						case NormalBlending: | ||||
| 							gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); | ||||
| 							break; | ||||
|  | ||||
| 						case AdditiveBlending: | ||||
| 							gl.blendFunc( gl.ONE, gl.ONE ); | ||||
| 							break; | ||||
|  | ||||
| 						case SubtractiveBlending: | ||||
| 							gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); | ||||
| 							break; | ||||
|  | ||||
| 						case MultiplyBlending: | ||||
| 							gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); | ||||
| 							break; | ||||
|  | ||||
| 						default: | ||||
| 							console.error( 'THREE.WebGLState: Invalid blending: ', blending ); | ||||
| 							break; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					switch ( blending ) { | ||||
|  | ||||
| 						case NormalBlending: | ||||
| 							gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); | ||||
| 							break; | ||||
|  | ||||
| 						case AdditiveBlending: | ||||
| 							gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); | ||||
| 							break; | ||||
|  | ||||
| 						case SubtractiveBlending: | ||||
| 							gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); | ||||
| 							break; | ||||
|  | ||||
| 						case MultiplyBlending: | ||||
| 							gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); | ||||
| 							break; | ||||
|  | ||||
| 						default: | ||||
| 							console.error( 'THREE.WebGLState: Invalid blending: ', blending ); | ||||
| 							break; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				this.currentBlendSrc = null; | ||||
| 				this.currentBlendDst = null; | ||||
| 				this.currentBlendSrcAlpha = null; | ||||
| 				this.currentBlendDstAlpha = null; | ||||
|  | ||||
| 				this.currentBlending = blending; | ||||
| 				this.currentPremultipledAlpha = premultipliedAlpha; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// custom blending | ||||
|  | ||||
| 		blendEquationAlpha = blendEquationAlpha || blendEquation; | ||||
| 		blendSrcAlpha = blendSrcAlpha || blendSrc; | ||||
| 		blendDstAlpha = blendDstAlpha || blendDst; | ||||
|  | ||||
| 		if ( blendEquation !== this.currentBlendEquation || blendEquationAlpha !== this.currentBlendEquationAlpha ) { | ||||
|  | ||||
| 			gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); | ||||
|  | ||||
| 			this.currentBlendEquation = blendEquation; | ||||
| 			this.currentBlendEquationAlpha = blendEquationAlpha; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( blendSrc !== this.currentBlendSrc || blendDst !== this.currentBlendDst || blendSrcAlpha !== this.currentBlendSrcAlpha || blendDstAlpha !== this.currentBlendDstAlpha ) { | ||||
|  | ||||
| 			gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); | ||||
|  | ||||
| 			this.currentBlendSrc = blendSrc; | ||||
| 			this.currentBlendDst = blendDst; | ||||
| 			this.currentBlendSrcAlpha = blendSrcAlpha; | ||||
| 			this.currentBlendDstAlpha = blendDstAlpha; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.currentBlending = blending; | ||||
| 		this.currentPremultipledAlpha = false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setColorMask( colorMask ) { | ||||
|  | ||||
| 		if ( this.currentColorMask !== colorMask ) { | ||||
|  | ||||
| 			this.gl.colorMask( colorMask, colorMask, colorMask, colorMask ); | ||||
| 			this.currentColorMask = colorMask; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setDepthTest( depthTest ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( depthTest ) { | ||||
|  | ||||
| 			this.enable( gl.DEPTH_TEST ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.disable( gl.DEPTH_TEST ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setDepthMask( depthMask ) { | ||||
|  | ||||
| 		if ( this.currentDepthMask !== depthMask ) { | ||||
|  | ||||
| 			this.gl.depthMask( depthMask ); | ||||
| 			this.currentDepthMask = depthMask; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setDepthFunc( depthFunc ) { | ||||
|  | ||||
| 		if ( this.currentDepthFunc !== depthFunc ) { | ||||
|  | ||||
| 			const { gl } = this; | ||||
|  | ||||
| 			switch ( depthFunc ) { | ||||
|  | ||||
| 				case NeverDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.NEVER ); | ||||
| 					break; | ||||
|  | ||||
| 				case AlwaysDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.ALWAYS ); | ||||
| 					break; | ||||
|  | ||||
| 				case LessDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.LESS ); | ||||
| 					break; | ||||
|  | ||||
| 				case LessEqualDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.LEQUAL ); | ||||
| 					break; | ||||
|  | ||||
| 				case EqualDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.EQUAL ); | ||||
| 					break; | ||||
|  | ||||
| 				case GreaterEqualDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.GEQUAL ); | ||||
| 					break; | ||||
|  | ||||
| 				case GreaterDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.GREATER ); | ||||
| 					break; | ||||
|  | ||||
| 				case NotEqualDepth: | ||||
|  | ||||
| 					gl.depthFunc( gl.NOTEQUAL ); | ||||
| 					break; | ||||
|  | ||||
| 				default: | ||||
|  | ||||
| 					gl.depthFunc( gl.LEQUAL ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			this.currentDepthFunc = depthFunc; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setStencilTest( stencilTest ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( stencilTest ) { | ||||
|  | ||||
| 			this.enable( gl.STENCIL_TEST ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.disable( gl.STENCIL_TEST ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setStencilMask( stencilMask ) { | ||||
|  | ||||
| 		if ( this.currentStencilMask !== stencilMask ) { | ||||
|  | ||||
| 			this.gl.stencilMask( stencilMask ); | ||||
| 			this.currentStencilMask = stencilMask; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setStencilFunc( stencilFunc, stencilRef, stencilMask ) { | ||||
|  | ||||
| 		if ( this.currentStencilFunc !== stencilFunc || | ||||
| 			 this.currentStencilRef !== stencilRef || | ||||
| 			 this.currentStencilFuncMask !== stencilMask ) { | ||||
|  | ||||
| 			this.gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); | ||||
|  | ||||
| 			this.currentStencilFunc = stencilFunc; | ||||
| 			this.currentStencilRef = stencilRef; | ||||
| 			this.currentStencilFuncMask = stencilMask; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setStencilOp( stencilFail, stencilZFail, stencilZPass ) { | ||||
|  | ||||
| 		if ( this.currentStencilFail !== stencilFail || | ||||
| 			 this.currentStencilZFail !== stencilZFail || | ||||
| 			 this.currentStencilZPass !== stencilZPass ) { | ||||
|  | ||||
| 			this.gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); | ||||
|  | ||||
| 			this.currentStencilFail = stencilFail; | ||||
| 			this.currentStencilZFail = stencilZFail; | ||||
| 			this.currentStencilZPass = stencilZPass; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setMaterial( material, frontFaceCW ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		material.side === DoubleSide | ||||
| 			? this.disable( gl.CULL_FACE ) | ||||
| 			: this.enable( gl.CULL_FACE ); | ||||
|  | ||||
| 		let flipSided = ( material.side === BackSide ); | ||||
| 		if ( frontFaceCW ) flipSided = ! flipSided; | ||||
|  | ||||
| 		this.setFlipSided( flipSided ); | ||||
|  | ||||
| 		( material.blending === NormalBlending && material.transparent === false ) | ||||
| 			? this.setBlending( NoBlending ) | ||||
| 			: this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); | ||||
|  | ||||
| 		this.setDepthFunc( material.depthFunc ); | ||||
| 		this.setDepthTest( material.depthTest ); | ||||
| 		this.setDepthMask( material.depthWrite ); | ||||
| 		this.setColorMask( material.colorWrite ); | ||||
|  | ||||
| 		const stencilWrite = material.stencilWrite; | ||||
| 		this.setStencilTest( stencilWrite ); | ||||
| 		if ( stencilWrite ) { | ||||
|  | ||||
| 			this.setStencilMask( material.stencilWriteMask ); | ||||
| 			this.setStencilFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); | ||||
| 			this.setStencilOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); | ||||
|  | ||||
| 		material.alphaToCoverage === true | ||||
| 			? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE ) | ||||
| 			: this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setPolygonOffset( polygonOffset, factor, units ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( polygonOffset ) { | ||||
|  | ||||
| 			this.enable( gl.POLYGON_OFFSET_FILL ); | ||||
|  | ||||
| 			if ( this.currentPolygonOffsetFactor !== factor || this.currentPolygonOffsetUnits !== units ) { | ||||
|  | ||||
| 				gl.polygonOffset( factor, units ); | ||||
|  | ||||
| 				this.currentPolygonOffsetFactor = factor; | ||||
| 				this.currentPolygonOffsetUnits = units; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			this.disable( gl.POLYGON_OFFSET_FILL ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	useProgram( program ) { | ||||
|  | ||||
| 		if ( this.currentProgram !== program ) { | ||||
|  | ||||
| 			this.gl.useProgram( program ); | ||||
|  | ||||
| 			this.currentProgram = program; | ||||
|  | ||||
| 			return true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// framebuffer | ||||
|  | ||||
|  | ||||
| 	bindFramebuffer( target, framebuffer ) { | ||||
|  | ||||
| 		const { gl, currentBoundFramebuffers } = this; | ||||
|  | ||||
| 		if ( currentBoundFramebuffers[ target ] !== framebuffer ) { | ||||
|  | ||||
| 			gl.bindFramebuffer( target, framebuffer ); | ||||
|  | ||||
| 			currentBoundFramebuffers[ target ] = framebuffer; | ||||
|  | ||||
| 			// gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER | ||||
|  | ||||
| 			if ( target === gl.DRAW_FRAMEBUFFER ) { | ||||
|  | ||||
| 				currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( target === gl.FRAMEBUFFER ) { | ||||
|  | ||||
| 				currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	drawBuffers( renderContext, framebuffer ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		let drawBuffers = []; | ||||
|  | ||||
| 		let needsUpdate = false; | ||||
|  | ||||
| 		if ( renderContext.textures !== null ) { | ||||
|  | ||||
| 			drawBuffers = this.currentDrawbuffers.get( framebuffer ); | ||||
|  | ||||
| 			if ( drawBuffers === undefined ) { | ||||
|  | ||||
| 				drawBuffers = []; | ||||
| 				this.currentDrawbuffers.set( framebuffer, drawBuffers ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
|  | ||||
| 			const textures = renderContext.textures; | ||||
|  | ||||
| 			if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { | ||||
|  | ||||
| 				for ( let i = 0, il = textures.length; i < il; i ++ ) { | ||||
|  | ||||
| 					drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				drawBuffers.length = textures.length; | ||||
|  | ||||
| 				needsUpdate = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( drawBuffers[ 0 ] !== gl.BACK ) { | ||||
|  | ||||
| 				drawBuffers[ 0 ] = gl.BACK; | ||||
|  | ||||
| 				needsUpdate = true; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( needsUpdate ) { | ||||
|  | ||||
| 			gl.drawBuffers( drawBuffers ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	// texture | ||||
|  | ||||
| 	activeTexture( webglSlot ) { | ||||
|  | ||||
| 		const { gl, currentTextureSlot, maxTextures } = this; | ||||
|  | ||||
| 		if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; | ||||
|  | ||||
| 		if ( currentTextureSlot !== webglSlot ) { | ||||
|  | ||||
| 			gl.activeTexture( webglSlot ); | ||||
| 			this.currentTextureSlot = webglSlot; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	bindTexture( webglType, webglTexture, webglSlot ) { | ||||
|  | ||||
| 		const { gl, currentTextureSlot, currentBoundTextures, maxTextures } = this; | ||||
|  | ||||
| 		if ( webglSlot === undefined ) { | ||||
|  | ||||
| 			if ( currentTextureSlot === null ) { | ||||
|  | ||||
| 				webglSlot = gl.TEXTURE0 + maxTextures - 1; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				webglSlot = currentTextureSlot; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		let boundTexture = currentBoundTextures[ webglSlot ]; | ||||
|  | ||||
| 		if ( boundTexture === undefined ) { | ||||
|  | ||||
| 			boundTexture = { type: undefined, texture: undefined }; | ||||
| 			currentBoundTextures[ webglSlot ] = boundTexture; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { | ||||
|  | ||||
| 			if ( currentTextureSlot !== webglSlot ) { | ||||
|  | ||||
| 				gl.activeTexture( webglSlot ); | ||||
| 				this.currentTextureSlot = webglSlot; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			gl.bindTexture( webglType, webglTexture ); | ||||
|  | ||||
| 			boundTexture.type = webglType; | ||||
| 			boundTexture.texture = webglTexture; | ||||
|  | ||||
|  | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	unbindTexture() { | ||||
|  | ||||
| 		const { gl, currentTextureSlot, currentBoundTextures } = this; | ||||
|  | ||||
| 		const boundTexture = currentBoundTextures[ currentTextureSlot ]; | ||||
|  | ||||
| 		if ( boundTexture !== undefined && boundTexture.type !== undefined ) { | ||||
|  | ||||
| 			gl.bindTexture( boundTexture.type, null ); | ||||
|  | ||||
| 			boundTexture.type = undefined; | ||||
| 			boundTexture.texture = undefined; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLState; | ||||
							
								
								
									
										751
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLTextureUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										751
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLTextureUtils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,751 @@ | ||||
| import { LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, FloatType, MirroredRepeatWrapping, ClampToEdgeWrapping, RepeatWrapping, SRGBColorSpace, NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare } from 'three'; | ||||
|  | ||||
| let initialized = false, wrappingToGL, filterToGL, compareToGL; | ||||
|  | ||||
| class WebGLTextureUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 		this.gl = backend.gl; | ||||
| 		this.extensions = backend.extensions; | ||||
| 		this.defaultTextures = {}; | ||||
|  | ||||
| 		if ( initialized === false ) { | ||||
|  | ||||
| 			this._init( this.gl ); | ||||
|  | ||||
| 			initialized = true; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_init( gl ) { | ||||
|  | ||||
| 		// Store only WebGL constants here. | ||||
|  | ||||
| 		wrappingToGL = { | ||||
| 			[ RepeatWrapping ]: gl.REPEAT, | ||||
| 			[ ClampToEdgeWrapping ]: gl.CLAMP_TO_EDGE, | ||||
| 			[ MirroredRepeatWrapping ]: gl.MIRRORED_REPEAT | ||||
| 		}; | ||||
|  | ||||
| 		filterToGL = { | ||||
| 			[ NearestFilter ]: gl.NEAREST, | ||||
| 			[ NearestMipmapNearestFilter ]: gl.NEAREST_MIPMAP_NEAREST, | ||||
| 			[ NearestMipmapLinearFilter ]: gl.NEAREST_MIPMAP_LINEAR, | ||||
|  | ||||
| 			[ LinearFilter ]: gl.LINEAR, | ||||
| 			[ LinearMipmapNearestFilter ]: gl.LINEAR_MIPMAP_NEAREST, | ||||
| 			[ LinearMipmapLinearFilter ]: gl.LINEAR_MIPMAP_LINEAR | ||||
| 		}; | ||||
|  | ||||
| 		compareToGL = { | ||||
| 			[ NeverCompare ]: gl.NEVER, | ||||
| 			[ AlwaysCompare ]: gl.ALWAYS, | ||||
| 			[ LessCompare ]: gl.LESS, | ||||
| 			[ LessEqualCompare ]: gl.LEQUAL, | ||||
| 			[ EqualCompare ]: gl.EQUAL, | ||||
| 			[ GreaterEqualCompare ]: gl.GEQUAL, | ||||
| 			[ GreaterCompare ]: gl.GREATER, | ||||
| 			[ NotEqualCompare ]: gl.NOTEQUAL | ||||
| 		}; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	filterFallback( f ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { | ||||
|  | ||||
| 			return gl.NEAREST; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return gl.LINEAR; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getGLTextureType( texture ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		let glTextureType; | ||||
|  | ||||
| 		if ( texture.isCubeTexture === true ) { | ||||
|  | ||||
| 			glTextureType = gl.TEXTURE_CUBE_MAP; | ||||
|  | ||||
| 		} else if ( texture.isDataArrayTexture === true ) { | ||||
|  | ||||
| 			glTextureType = gl.TEXTURE_2D_ARRAY; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			glTextureType = gl.TEXTURE_2D; | ||||
|  | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return glTextureType; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) { | ||||
|  | ||||
| 		const { gl, extensions } = this; | ||||
|  | ||||
| 		if ( internalFormatName !== null ) { | ||||
|  | ||||
| 			if ( gl[ internalFormatName ] !== undefined ) return gl[ internalFormatName ]; | ||||
|  | ||||
| 			console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		let internalFormat = glFormat; | ||||
|  | ||||
| 		if ( glFormat === gl.RED ) { | ||||
|  | ||||
| 			if ( glType === gl.FLOAT ) internalFormat = gl.R32F; | ||||
| 			if ( glType === gl.HALF_FLOAT ) internalFormat = gl.R16F; | ||||
| 			if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.RED_INTEGER ) { | ||||
|  | ||||
| 			if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.R8UI; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT ) internalFormat = gl.R16UI; | ||||
| 			if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.R32UI; | ||||
| 			if ( glType === gl.BYTE ) internalFormat = gl.R8I; | ||||
| 			if ( glType === gl.SHORT ) internalFormat = gl.R16I; | ||||
| 			if ( glType === gl.INT ) internalFormat = gl.R32I; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.RG ) { | ||||
|  | ||||
| 			if ( glType === gl.FLOAT ) internalFormat = gl.RG32F; | ||||
| 			if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RG16F; | ||||
| 			if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RG8; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.RGB ) { | ||||
|  | ||||
| 			if ( glType === gl.FLOAT ) internalFormat = gl.RGB32F; | ||||
| 			if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGB16F; | ||||
| 			if ( glType === gl.UNSIGNED_BYTE ) internalFormat = gl.RGB8; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) internalFormat = gl.RGB565; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGB4; | ||||
| 			if ( glType === gl.UNSIGNED_INT_5_9_9_9_REV ) internalFormat = gl.RGB9_E5; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.RGBA ) { | ||||
|  | ||||
| 			if ( glType === gl.FLOAT ) internalFormat = gl.RGBA32F; | ||||
| 			if ( glType === gl.HALF_FLOAT ) internalFormat = gl.RGBA16F; | ||||
| 			if ( glType === gl.UNSIGNED_BYTE ) internalFormat = ( colorSpace === SRGBColorSpace && forceLinearTransfer === false ) ? gl.SRGB8_ALPHA8 : gl.RGBA8; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) internalFormat = gl.RGBA4; | ||||
| 			if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) internalFormat = gl.RGB5_A1; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.DEPTH_COMPONENT ) { | ||||
|  | ||||
| 			if ( glType === gl.UNSIGNED_INT ) internalFormat = gl.DEPTH24_STENCIL8; | ||||
| 			if ( glType === gl.FLOAT ) internalFormat = gl.DEPTH_COMPONENT32F; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( glFormat === gl.DEPTH_STENCIL ) { | ||||
|  | ||||
| 			if ( glType === gl.UNSIGNED_INT_24_8 ) internalFormat = gl.DEPTH24_STENCIL8; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( internalFormat === gl.R16F || internalFormat === gl.R32F || | ||||
| 			internalFormat === gl.RG16F || internalFormat === gl.RG32F || | ||||
| 			internalFormat === gl.RGBA16F || internalFormat === gl.RGBA32F ) { | ||||
|  | ||||
| 			extensions.get( 'EXT_color_buffer_float' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return internalFormat; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	setTextureParameters( textureType, texture ) { | ||||
|  | ||||
| 		const { gl, extensions, backend } = this; | ||||
|  | ||||
| 		const { currentAnisotropy } = backend.get( texture ); | ||||
|  | ||||
| 		gl.texParameteri( textureType, gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); | ||||
| 		gl.texParameteri( textureType, gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); | ||||
|  | ||||
| 		if ( textureType === gl.TEXTURE_3D || textureType === gl.TEXTURE_2D_ARRAY ) { | ||||
|  | ||||
| 			gl.texParameteri( textureType, gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		gl.texParameteri( textureType, gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] ); | ||||
|  | ||||
|  | ||||
| 		// follow WebGPU backend mapping for texture filtering | ||||
| 		const minFilter = ! texture.isVideoTexture && texture.minFilter === LinearFilter ? LinearMipmapLinearFilter : texture.minFilter; | ||||
|  | ||||
| 		gl.texParameteri( textureType, gl.TEXTURE_MIN_FILTER, filterToGL[ minFilter ] ); | ||||
|  | ||||
| 		if ( texture.compareFunction ) { | ||||
|  | ||||
| 			gl.texParameteri( textureType, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE ); | ||||
| 			gl.texParameteri( textureType, gl.TEXTURE_COMPARE_FUNC, compareToGL[ texture.compareFunction ] ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { | ||||
|  | ||||
| 			if ( texture.magFilter === NearestFilter ) return; | ||||
| 			if ( texture.minFilter !== NearestMipmapLinearFilter && texture.minFilter !== LinearMipmapLinearFilter ) return; | ||||
| 			if ( texture.type === FloatType && extensions.has( 'OES_texture_float_linear' ) === false ) return; // verify extension for WebGL 1 and WebGL 2 | ||||
|  | ||||
| 			if ( texture.anisotropy > 1 || currentAnisotropy !== texture.anisotropy ) { | ||||
|  | ||||
| 				const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); | ||||
| 				gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, backend.getMaxAnisotropy() ) ); | ||||
| 				backend.get( texture ).currentAnisotropy = texture.anisotropy; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createDefaultTexture( texture ) { | ||||
|  | ||||
| 		const { gl, backend, defaultTextures } = this; | ||||
|  | ||||
|  | ||||
| 		const glTextureType = this.getGLTextureType( texture ); | ||||
|  | ||||
| 		let textureGPU = defaultTextures[ glTextureType ]; | ||||
|  | ||||
| 		if ( textureGPU === undefined ) { | ||||
|  | ||||
| 			textureGPU = gl.createTexture(); | ||||
|  | ||||
| 			backend.state.bindTexture( glTextureType, textureGPU ); | ||||
| 			gl.texParameteri( glTextureType, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); | ||||
| 			gl.texParameteri( glTextureType, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); | ||||
|  | ||||
| 			// gl.texImage2D( glTextureType, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); | ||||
|  | ||||
| 			defaultTextures[ glTextureType ] = textureGPU; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		backend.set( texture, { | ||||
| 			textureGPU, | ||||
| 			glTextureType, | ||||
| 			isDefault: true | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createTexture( texture, options ) { | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
| 		const { levels, width, height, depth } = options; | ||||
|  | ||||
| 		const glFormat = backend.utils.convert( texture.format, texture.colorSpace ); | ||||
| 		const glType = backend.utils.convert( texture.type ); | ||||
| 		const glInternalFormat = this.getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture ); | ||||
|  | ||||
| 		const textureGPU = gl.createTexture(); | ||||
| 		const glTextureType = this.getGLTextureType( texture ); | ||||
|  | ||||
| 		backend.state.bindTexture( glTextureType, textureGPU ); | ||||
|  | ||||
| 		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); | ||||
| 		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); | ||||
| 		gl.pixelStorei( gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); | ||||
| 		gl.pixelStorei( gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE ); | ||||
|  | ||||
| 		this.setTextureParameters( glTextureType, texture ); | ||||
|  | ||||
| 		if ( texture.isDataArrayTexture ) { | ||||
|  | ||||
| 			gl.texStorage3D( gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, width, height, depth ); | ||||
|  | ||||
| 		} else if ( ! texture.isVideoTexture ) { | ||||
|  | ||||
| 			gl.texStorage2D( glTextureType, levels, glInternalFormat, width, height ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		backend.set( texture, { | ||||
| 			textureGPU, | ||||
| 			glTextureType, | ||||
| 			glFormat, | ||||
| 			glType, | ||||
| 			glInternalFormat | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copyBufferToTexture( buffer, texture ) { | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
|  | ||||
| 		const { textureGPU, glTextureType, glFormat, glType } = backend.get( texture ); | ||||
|  | ||||
| 		const { width, height } = texture.source.data; | ||||
|  | ||||
| 		gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, buffer ); | ||||
|  | ||||
| 		backend.state.bindTexture( glTextureType, textureGPU ); | ||||
|  | ||||
| 		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, false ); | ||||
| 		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); | ||||
| 		gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, 0 ); | ||||
|  | ||||
| 		gl.bindBuffer( gl.PIXEL_UNPACK_BUFFER, null ); | ||||
|  | ||||
| 		backend.state.unbindTexture(); | ||||
| 		// debug | ||||
| 		// const framebuffer = gl.createFramebuffer(); | ||||
| 		// gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer ); | ||||
| 		// gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, glTextureType, textureGPU, 0 ); | ||||
|  | ||||
| 		// const readout = new Float32Array( width * height * 4 ); | ||||
|  | ||||
| 		// const altFormat = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_FORMAT ); | ||||
| 		// const altType = gl.getParameter( gl.IMPLEMENTATION_COLOR_READ_TYPE ); | ||||
|  | ||||
| 		// gl.readPixels( 0, 0, width, height, altFormat, altType, readout ); | ||||
| 		// gl.bindFramebuffer( gl.FRAMEBUFFER, null ); | ||||
| 		// console.log( readout ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateTexture( texture, options ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
| 		const { width, height } = options; | ||||
| 		const { textureGPU, glTextureType, glFormat, glType, glInternalFormat } = this.backend.get( texture ); | ||||
|  | ||||
| 		if ( texture.isRenderTargetTexture || ( textureGPU === undefined /* unsupported texture format */ ) ) | ||||
| 			return; | ||||
|  | ||||
| 		const getImage = ( source ) => { | ||||
|  | ||||
| 			if ( source.isDataTexture ) { | ||||
|  | ||||
| 				return source.image.data; | ||||
|  | ||||
| 			} else if ( source instanceof ImageBitmap || source instanceof OffscreenCanvas || source instanceof HTMLImageElement || source instanceof HTMLCanvasElement ) { | ||||
|  | ||||
| 				return source; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return source.data; | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		this.backend.state.bindTexture( glTextureType, textureGPU ); | ||||
|  | ||||
| 		if ( texture.isCompressedTexture ) { | ||||
|  | ||||
| 			const mipmaps = texture.mipmaps; | ||||
|  | ||||
| 			for ( let i = 0; i < mipmaps.length; i ++ ) { | ||||
|  | ||||
| 				const mipmap = mipmaps[ i ]; | ||||
|  | ||||
| 				if ( texture.isCompressedArrayTexture ) { | ||||
|  | ||||
| 					const image = options.image; | ||||
|  | ||||
| 					if ( texture.format !== gl.RGBA ) { | ||||
|  | ||||
| 						if ( glFormat !== null ) { | ||||
|  | ||||
| 							gl.compressedTexSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 ); | ||||
|  | ||||
|  | ||||
| 						} else { | ||||
|  | ||||
| 							console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); | ||||
|  | ||||
| 						} | ||||
|  | ||||
| 					} else { | ||||
|  | ||||
| 						gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					if ( glFormat !== null ) { | ||||
|  | ||||
| 						gl.compressedTexSubImage2D( gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); | ||||
|  | ||||
| 					} else { | ||||
|  | ||||
| 						console.warn( 'Unsupported compressed texture format' ); | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( texture.isCubeTexture ) { | ||||
|  | ||||
| 			const images = options.images; | ||||
|  | ||||
| 			for ( let i = 0; i < 6; i ++ ) { | ||||
|  | ||||
| 				const image = getImage( images[ i ] ); | ||||
|  | ||||
| 				gl.texSubImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, width, height, glFormat, glType, image ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else if ( texture.isDataArrayTexture ) { | ||||
|  | ||||
| 			const image = options.image; | ||||
|  | ||||
| 			gl.texSubImage3D( gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); | ||||
|  | ||||
| 		} else if ( texture.isVideoTexture ) { | ||||
|  | ||||
| 			texture.update(); | ||||
|  | ||||
| 			gl.texImage2D( glTextureType, 0, glInternalFormat, glFormat, glType, options.image ); | ||||
|  | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const image = getImage( options.image ); | ||||
|  | ||||
| 			gl.texSubImage2D( glTextureType, 0, 0, 0, width, height, glFormat, glType, image ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateMipmaps( texture ) { | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
| 		const { textureGPU, glTextureType } = backend.get( texture ); | ||||
|  | ||||
| 		backend.state.bindTexture( glTextureType, textureGPU ); | ||||
| 		gl.generateMipmap( glTextureType ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	deallocateRenderBuffers( renderTarget ) { | ||||
|  | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
|  | ||||
| 		// remove framebuffer reference | ||||
| 		if ( renderTarget ) { | ||||
|  | ||||
| 			const renderContextData = backend.get( renderTarget ); | ||||
|  | ||||
| 			renderContextData.renderBufferStorageSetup = undefined; | ||||
|  | ||||
| 			if ( renderContextData.framebuffer ) { | ||||
|  | ||||
| 				gl.deleteFramebuffer( renderContextData.framebuffer ); | ||||
| 				renderContextData.framebuffer = undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( renderContextData.depthRenderbuffer ) { | ||||
|  | ||||
| 				gl.deleteRenderbuffer( renderContextData.depthRenderbuffer ); | ||||
| 				renderContextData.depthRenderbuffer = undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( renderContextData.stencilRenderbuffer ) { | ||||
|  | ||||
| 				gl.deleteRenderbuffer( renderContextData.stencilRenderbuffer ); | ||||
| 				renderContextData.stencilRenderbuffer = undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( renderContextData.msaaFrameBuffer ) { | ||||
|  | ||||
| 				gl.deleteFramebuffer( renderContextData.msaaFrameBuffer ); | ||||
| 				renderContextData.msaaFrameBuffer = undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			if ( renderContextData.msaaRenderbuffers ) { | ||||
|  | ||||
| 				for ( let i = 0; i < renderContextData.msaaRenderbuffers.length; i ++ ) { | ||||
|  | ||||
| 					gl.deleteRenderbuffer( renderContextData.msaaRenderbuffers[ i ] ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				renderContextData.msaaRenderbuffers = undefined; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	destroyTexture( texture ) { | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
| 		const { textureGPU, renderTarget } = backend.get( texture ); | ||||
|  | ||||
| 		this.deallocateRenderBuffers( renderTarget ); | ||||
| 		gl.deleteTexture( textureGPU ); | ||||
|  | ||||
| 		backend.delete( texture ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copyTextureToTexture( position, srcTexture, dstTexture, level = 0 ) { | ||||
|  | ||||
| 		const { gl, backend } = this; | ||||
| 		const { state } = this.backend; | ||||
|  | ||||
| 		const width = srcTexture.image.width; | ||||
| 		const height = srcTexture.image.height; | ||||
| 		const { textureGPU: dstTextureGPU, glTextureType, glType, glFormat } = backend.get( dstTexture ); | ||||
|  | ||||
| 		state.bindTexture( glTextureType, dstTextureGPU ); | ||||
|  | ||||
| 		// As another texture upload may have changed pixelStorei | ||||
| 		// parameters, make sure they are correct for the dstTexture | ||||
| 		gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY ); | ||||
| 		gl.pixelStorei( gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha ); | ||||
| 		gl.pixelStorei( gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment ); | ||||
|  | ||||
| 		if ( srcTexture.isDataTexture ) { | ||||
|  | ||||
| 			gl.texSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			if ( srcTexture.isCompressedTexture ) { | ||||
|  | ||||
| 				gl.compressedTexSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				gl.texSubImage2D( gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// Generate mipmaps only when copying level 0 | ||||
| 		if ( level === 0 && dstTexture.generateMipmaps ) gl.generateMipmap( gl.TEXTURE_2D ); | ||||
|  | ||||
| 		state.unbindTexture(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	copyFramebufferToTexture( texture, renderContext ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
| 		const { state } = this.backend; | ||||
|  | ||||
| 		const { textureGPU } = this.backend.get( texture ); | ||||
|  | ||||
| 		const width = texture.image.width; | ||||
| 		const height = texture.image.height; | ||||
|  | ||||
| 		const requireDrawFrameBuffer = texture.isDepthTexture === true || ( renderContext.renderTarget && renderContext.renderTarget.samples > 0 ); | ||||
|  | ||||
| 		if ( requireDrawFrameBuffer ) { | ||||
|  | ||||
| 			let mask; | ||||
| 			let attachment; | ||||
|  | ||||
| 			if ( texture.isDepthTexture === true ) { | ||||
|  | ||||
| 				mask = gl.DEPTH_BUFFER_BIT; | ||||
| 				attachment = gl.DEPTH_ATTACHMENT; | ||||
|  | ||||
| 				if ( renderContext.stencil ) { | ||||
|  | ||||
| 					mask |= gl.STENCIL_BUFFER_BIT; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				mask = gl.COLOR_BUFFER_BIT; | ||||
| 				attachment = gl.COLOR_ATTACHMENT0; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const fb = gl.createFramebuffer(); | ||||
| 			state.bindFramebuffer( gl.DRAW_FRAMEBUFFER, fb ); | ||||
|  | ||||
| 			gl.framebufferTexture2D( gl.DRAW_FRAMEBUFFER, attachment, gl.TEXTURE_2D, textureGPU, 0 ); | ||||
|  | ||||
| 			gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, gl.NEAREST ); | ||||
|  | ||||
| 			gl.deleteFramebuffer( fb ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			state.bindTexture( gl.TEXTURE_2D, textureGPU ); | ||||
| 			gl.copyTexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, 0, 0, width, height ); | ||||
|  | ||||
| 			state.unbindTexture(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( texture.generateMipmaps ) this.generateMipmaps( texture ); | ||||
|  | ||||
| 		this.backend._setFramebuffer( renderContext ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	// Setup storage for internal depth/stencil buffers and bind to correct framebuffer | ||||
| 	setupRenderBufferStorage( renderbuffer, renderContext ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
| 		const renderTarget = renderContext.renderTarget; | ||||
|  | ||||
| 		const { samples, depthTexture, depthBuffer, stencilBuffer, width, height } = renderTarget; | ||||
|  | ||||
| 		gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer ); | ||||
|  | ||||
| 		if ( depthBuffer && ! stencilBuffer ) { | ||||
|  | ||||
| 			let glInternalFormat = gl.DEPTH_COMPONENT24; | ||||
|  | ||||
| 			if ( samples > 0 ) { | ||||
|  | ||||
| 				if ( depthTexture && depthTexture.isDepthTexture ) { | ||||
|  | ||||
| 					if ( depthTexture.type === gl.FLOAT ) { | ||||
|  | ||||
| 						glInternalFormat = gl.DEPTH_COMPONENT32F; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, glInternalFormat, width, height ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				gl.renderbufferStorage( gl.RENDERBUFFER, glInternalFormat, width, height ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); | ||||
|  | ||||
| 		} else if ( depthBuffer && stencilBuffer ) { | ||||
|  | ||||
| 			if ( samples > 0 ) { | ||||
|  | ||||
| 				gl.renderbufferStorageMultisample( gl.RENDERBUFFER, samples, gl.DEPTH24_STENCIL8, width, height ); | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
|  | ||||
| 			gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	async copyTextureToBuffer( texture, x, y, width, height ) { | ||||
|  | ||||
| 		const { backend, gl } = this; | ||||
|  | ||||
| 		const { textureGPU, glFormat, glType } = this.backend.get( texture ); | ||||
|  | ||||
| 		const fb = gl.createFramebuffer(); | ||||
|  | ||||
| 		gl.bindFramebuffer( gl.READ_FRAMEBUFFER, fb ); | ||||
| 		gl.framebufferTexture2D( gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureGPU, 0 ); | ||||
|  | ||||
| 		const typedArrayType = this._getTypedArrayType( glType ); | ||||
| 		const bytesPerTexel = this._getBytesPerTexel( glFormat ); | ||||
|  | ||||
| 		const elementCount = width * height; | ||||
| 		const byteLength = elementCount * bytesPerTexel; | ||||
|  | ||||
| 		const buffer = gl.createBuffer(); | ||||
|  | ||||
| 		gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer ); | ||||
| 		gl.bufferData( gl.PIXEL_PACK_BUFFER, byteLength, gl.STREAM_READ ); | ||||
| 		gl.readPixels( x, y, width, height, glFormat, glType, 0 ); | ||||
| 		gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null ); | ||||
|  | ||||
| 		await backend.utils._clientWaitAsync(); | ||||
|  | ||||
| 		const dstBuffer = new typedArrayType( byteLength / typedArrayType.BYTES_PER_ELEMENT ); | ||||
|  | ||||
| 		gl.bindBuffer( gl.PIXEL_PACK_BUFFER, buffer ); | ||||
| 		gl.getBufferSubData( gl.PIXEL_PACK_BUFFER, 0, dstBuffer ); | ||||
| 		gl.bindBuffer( gl.PIXEL_PACK_BUFFER, null ); | ||||
|  | ||||
| 		gl.deleteFramebuffer( fb ); | ||||
|  | ||||
| 		return dstBuffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getTypedArrayType( glType ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( glType === gl.UNSIGNED_BYTE ) return Uint8Array; | ||||
|  | ||||
| 		if ( glType === gl.UNSIGNED_SHORT_4_4_4_4 ) return Uint16Array; | ||||
| 		if ( glType === gl.UNSIGNED_SHORT_5_5_5_1 ) return Uint16Array; | ||||
| 		if ( glType === gl.UNSIGNED_SHORT_5_6_5 ) return Uint16Array; | ||||
| 		if ( glType === gl.UNSIGNED_SHORT ) return Uint16Array; | ||||
| 		if ( glType === gl.UNSIGNED_INT ) return Uint32Array; | ||||
|  | ||||
| 		if ( glType === gl.FLOAT ) return Float32Array; | ||||
|  | ||||
| 		throw new Error( `Unsupported WebGL type: ${glType}` ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBytesPerTexel( glFormat ) { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		if ( glFormat === gl.RGBA ) return 4; | ||||
| 		if ( glFormat === gl.RGB ) return 3; | ||||
| 		if ( glFormat === gl.ALPHA ) return 1; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLTextureUtils; | ||||
							
								
								
									
										268
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								public/sdk/three/jsm/renderers/webgl/utils/WebGLUtils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,268 @@ | ||||
| import { RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT5_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT1_Format, RGB_S3TC_DXT1_Format, DepthFormat, DepthStencilFormat, LuminanceAlphaFormat, LuminanceFormat, RedFormat, RGBFormat, RGBAFormat, AlphaFormat, RedIntegerFormat, RGFormat, RGIntegerFormat, RGBAIntegerFormat, HalfFloatType, FloatType, UnsignedIntType, IntType, UnsignedShortType, ShortType, ByteType, UnsignedInt248Type, UnsignedInt5999Type, UnsignedShort5551Type, UnsignedShort4444Type, UnsignedByteType, RGBA_BPTC_Format, RED_RGTC1_Format, SIGNED_RED_RGTC1_Format, RED_GREEN_RGTC2_Format, SIGNED_RED_GREEN_RGTC2_Format, SRGBColorSpace, NoColorSpace } from 'three'; | ||||
|  | ||||
| class WebGLUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 		this.gl = this.backend.gl; | ||||
| 		this.extensions = backend.extensions; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	convert( p, colorSpace = NoColorSpace ) { | ||||
|  | ||||
| 		const { gl, extensions } = this; | ||||
|  | ||||
| 		let extension; | ||||
|  | ||||
| 		if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE; | ||||
| 		if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4; | ||||
| 		if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1; | ||||
| 		if ( p === UnsignedInt5999Type ) return gl.UNSIGNED_INT_5_9_9_9_REV; | ||||
|  | ||||
| 		if ( p === ByteType ) return gl.BYTE; | ||||
| 		if ( p === ShortType ) return gl.SHORT; | ||||
| 		if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT; | ||||
| 		if ( p === IntType ) return gl.INT; | ||||
| 		if ( p === UnsignedIntType ) return gl.UNSIGNED_INT; | ||||
| 		if ( p === FloatType ) return gl.FLOAT; | ||||
|  | ||||
| 		if ( p === HalfFloatType ) { | ||||
|  | ||||
| 			return gl.HALF_FLOAT; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( p === AlphaFormat ) return gl.ALPHA; | ||||
| 		if ( p === RGBFormat ) return gl.RGB; | ||||
| 		if ( p === RGBAFormat ) return gl.RGBA; | ||||
| 		if ( p === LuminanceFormat ) return gl.LUMINANCE; | ||||
| 		if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA; | ||||
| 		if ( p === DepthFormat ) return gl.DEPTH_COMPONENT; | ||||
| 		if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL; | ||||
|  | ||||
| 		// WebGL2 formats. | ||||
|  | ||||
| 		if ( p === RedFormat ) return gl.RED; | ||||
| 		if ( p === RedIntegerFormat ) return gl.RED_INTEGER; | ||||
| 		if ( p === RGFormat ) return gl.RG; | ||||
| 		if ( p === RGIntegerFormat ) return gl.RG_INTEGER; | ||||
| 		if ( p === RGBAIntegerFormat ) return gl.RGBA_INTEGER; | ||||
|  | ||||
| 		// S3TC | ||||
|  | ||||
| 		if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { | ||||
|  | ||||
| 			if ( colorSpace === SRGBColorSpace ) { | ||||
|  | ||||
| 				extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' ); | ||||
|  | ||||
| 				if ( extension !== null ) { | ||||
|  | ||||
| 					if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_S3TC_DXT1_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					return null; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); | ||||
|  | ||||
| 				if ( extension !== null ) { | ||||
|  | ||||
| 					if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; | ||||
| 					if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					return null; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// PVRTC | ||||
|  | ||||
| 		if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { | ||||
|  | ||||
| 			extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); | ||||
|  | ||||
| 			if ( extension !== null ) { | ||||
|  | ||||
| 				if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; | ||||
| 				if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; | ||||
| 				if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; | ||||
| 				if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return null; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// ETC | ||||
|  | ||||
| 		if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { | ||||
|  | ||||
| 			extension = extensions.get( 'WEBGL_compressed_texture_etc' ); | ||||
|  | ||||
| 			if ( extension !== null ) { | ||||
|  | ||||
| 				if ( p === RGB_ETC1_Format || p === RGB_ETC2_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; | ||||
| 				if ( p === RGBA_ETC2_EAC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return null; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// ASTC | ||||
|  | ||||
| 		if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format || | ||||
| 			p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format || | ||||
| 			p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format || | ||||
| 			p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format || | ||||
| 			p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) { | ||||
|  | ||||
| 			extension = extensions.get( 'WEBGL_compressed_texture_astc' ); | ||||
|  | ||||
| 			if ( extension !== null ) { | ||||
|  | ||||
| 				if ( p === RGBA_ASTC_4x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; | ||||
| 				if ( p === RGBA_ASTC_5x4_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; | ||||
| 				if ( p === RGBA_ASTC_5x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; | ||||
| 				if ( p === RGBA_ASTC_6x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; | ||||
| 				if ( p === RGBA_ASTC_6x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; | ||||
| 				if ( p === RGBA_ASTC_8x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; | ||||
| 				if ( p === RGBA_ASTC_8x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; | ||||
| 				if ( p === RGBA_ASTC_8x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; | ||||
| 				if ( p === RGBA_ASTC_10x5_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; | ||||
| 				if ( p === RGBA_ASTC_10x6_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; | ||||
| 				if ( p === RGBA_ASTC_10x8_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; | ||||
| 				if ( p === RGBA_ASTC_10x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; | ||||
| 				if ( p === RGBA_ASTC_12x10_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; | ||||
| 				if ( p === RGBA_ASTC_12x12_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return null; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// BPTC | ||||
|  | ||||
| 		if ( p === RGBA_BPTC_Format ) { | ||||
|  | ||||
| 			extension = extensions.get( 'EXT_texture_compression_bptc' ); | ||||
|  | ||||
| 			if ( extension !== null ) { | ||||
|  | ||||
| 				if ( p === RGBA_BPTC_Format ) return ( colorSpace === SRGBColorSpace ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return null; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// RGTC | ||||
|  | ||||
| 		if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { | ||||
|  | ||||
| 			extension = extensions.get( 'EXT_texture_compression_rgtc' ); | ||||
|  | ||||
| 			if ( extension !== null ) { | ||||
|  | ||||
| 				if ( p === RGBA_BPTC_Format ) return extension.COMPRESSED_RED_RGTC1_EXT; | ||||
| 				if ( p === SIGNED_RED_RGTC1_Format ) return extension.COMPRESSED_SIGNED_RED_RGTC1_EXT; | ||||
| 				if ( p === RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_RED_GREEN_RGTC2_EXT; | ||||
| 				if ( p === SIGNED_RED_GREEN_RGTC2_Format ) return extension.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				return null; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		if ( p === UnsignedInt248Type ) { | ||||
|  | ||||
| 			return gl.UNSIGNED_INT_24_8; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// if "p" can't be resolved, assume the user defines a WebGL constant as a string (fallback/workaround for packed RGB formats) | ||||
|  | ||||
| 		return ( gl[ p ] !== undefined ) ? gl[ p ] : null; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_clientWaitAsync() { | ||||
|  | ||||
| 		const { gl } = this; | ||||
|  | ||||
| 		const sync = gl.fenceSync( gl.SYNC_GPU_COMMANDS_COMPLETE, 0 ); | ||||
|  | ||||
| 		gl.flush(); | ||||
|  | ||||
| 		return new Promise( ( resolve, reject ) => { | ||||
|  | ||||
| 			function test() { | ||||
|  | ||||
| 				const res = gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ); | ||||
|  | ||||
| 				if ( res === gl.WAIT_FAILED ) { | ||||
|  | ||||
| 					gl.deleteSync( sync ); | ||||
|  | ||||
| 					reject(); | ||||
| 					return; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( res === gl.TIMEOUT_EXPIRED ) { | ||||
|  | ||||
| 					requestAnimationFrame( test ); | ||||
| 					return; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				gl.deleteSync( sync ); | ||||
|  | ||||
| 				resolve(); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			test(); | ||||
|  | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGLUtils; | ||||
							
								
								
									
										1339
									
								
								public/sdk/three/jsm/renderers/webgpu/WebGPUBackend.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1339
									
								
								public/sdk/three/jsm/renderers/webgpu/WebGPUBackend.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										53
									
								
								public/sdk/three/jsm/renderers/webgpu/WebGPURenderer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								public/sdk/three/jsm/renderers/webgpu/WebGPURenderer.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | ||||
| import WebGPU from '../../capabilities/WebGPU.js'; | ||||
|  | ||||
| import Renderer from '../common/Renderer.js'; | ||||
| import WebGLBackend from '../webgl/WebGLBackend.js'; | ||||
| import WebGPUBackend from './WebGPUBackend.js'; | ||||
| /* | ||||
| const debugHandler = { | ||||
|  | ||||
| 	get: function ( target, name ) { | ||||
|  | ||||
| 		// Add |update | ||||
| 		if ( /^(create|destroy)/.test( name ) ) console.log( 'WebGPUBackend.' + name ); | ||||
|  | ||||
| 		return target[ name ]; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| }; | ||||
| */ | ||||
| class WebGPURenderer extends Renderer { | ||||
|  | ||||
| 	constructor( parameters = {} ) { | ||||
|  | ||||
| 		let BackendClass; | ||||
|  | ||||
| 		if ( parameters.forceWebGL ) { | ||||
|  | ||||
| 			BackendClass = WebGLBackend; | ||||
|  | ||||
| 		} else if ( WebGPU.isAvailable() ) { | ||||
|  | ||||
| 			BackendClass = WebGPUBackend; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			BackendClass = WebGLBackend; | ||||
|  | ||||
| 			console.warn( 'THREE.WebGPURenderer: WebGPU is not available, running under WebGL2 backend.' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const backend = new BackendClass( parameters ); | ||||
|  | ||||
| 		//super( new Proxy( backend, debugHandler ) ); | ||||
| 		super( backend, parameters ); | ||||
|  | ||||
| 		this.isWebGPURenderer = true; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPURenderer; | ||||
							
								
								
									
										1095
									
								
								public/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1095
									
								
								public/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										104
									
								
								public/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								public/sdk/three/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | ||||
| import NodeFunction from '../../../nodes/core/NodeFunction.js'; | ||||
| import NodeFunctionInput from '../../../nodes/core/NodeFunctionInput.js'; | ||||
|  | ||||
| const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i; | ||||
| const propertiesRegexp = /[a-z_0-9]+|<(.*?)>+/ig; | ||||
|  | ||||
| const wgslTypeLib = { | ||||
| 	f32: 'float' | ||||
| }; | ||||
|  | ||||
| const parse = ( source ) => { | ||||
|  | ||||
| 	source = source.trim(); | ||||
|  | ||||
| 	const declaration = source.match( declarationRegexp ); | ||||
|  | ||||
| 	if ( declaration !== null && declaration.length === 4 ) { | ||||
|  | ||||
| 		// tokenizer | ||||
|  | ||||
| 		const inputsCode = declaration[ 2 ]; | ||||
| 		const propsMatches = []; | ||||
|  | ||||
| 		let nameMatch = null; | ||||
|  | ||||
| 		while ( ( nameMatch = propertiesRegexp.exec( inputsCode ) ) !== null ) { | ||||
|  | ||||
| 			propsMatches.push( nameMatch ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// parser | ||||
|  | ||||
| 		const inputs = []; | ||||
|  | ||||
| 		let i = 0; | ||||
|  | ||||
| 		while ( i < propsMatches.length ) { | ||||
|  | ||||
| 			// default | ||||
|  | ||||
| 			const name = propsMatches[ i ++ ][ 0 ]; | ||||
| 			let type = propsMatches[ i ++ ][ 0 ]; | ||||
|  | ||||
| 			type = wgslTypeLib[ type ] || type; | ||||
|  | ||||
| 			// precision | ||||
|  | ||||
| 			if ( i < propsMatches.length && propsMatches[ i ][ 0 ].startsWith( '<' ) === true ) | ||||
| 				i ++; | ||||
|  | ||||
| 			// add input | ||||
|  | ||||
| 			inputs.push( new NodeFunctionInput( type, name ) ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// | ||||
|  | ||||
| 		const blockCode = source.substring( declaration[ 0 ].length ); | ||||
|  | ||||
| 		const name = declaration[ 1 ] !== undefined ? declaration[ 1 ] : ''; | ||||
| 		const type = declaration[ 3 ] || 'void'; | ||||
|  | ||||
| 		return { | ||||
| 			type, | ||||
| 			inputs, | ||||
| 			name, | ||||
| 			inputsCode, | ||||
| 			blockCode | ||||
| 		}; | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		throw new Error( 'FunctionNode: Function is not a WGSL code.' ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| }; | ||||
|  | ||||
| class WGSLNodeFunction extends NodeFunction { | ||||
|  | ||||
| 	constructor( source ) { | ||||
|  | ||||
| 		const { type, inputs, name, inputsCode, blockCode } = parse( source ); | ||||
|  | ||||
| 		super( type, inputs, name ); | ||||
|  | ||||
| 		this.inputsCode = inputsCode; | ||||
| 		this.blockCode = blockCode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCode( name = this.name ) { | ||||
|  | ||||
| 		const type = this.type !== 'void' ? '-> ' + this.type : ''; | ||||
|  | ||||
| 		return `fn ${ name } ( ${ this.inputsCode.trim() } ) ${ type }` + this.blockCode; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WGSLNodeFunction; | ||||
| @ -0,0 +1,14 @@ | ||||
| import NodeParser from '../../../nodes/core/NodeParser.js'; | ||||
| import WGSLNodeFunction from './WGSLNodeFunction.js'; | ||||
|  | ||||
| class WGSLNodeParser extends NodeParser { | ||||
|  | ||||
| 	parseFunction( source ) { | ||||
|  | ||||
| 		return new WGSLNodeFunction( source ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WGSLNodeParser; | ||||
| @ -0,0 +1,321 @@ | ||||
| import { Float16BufferAttribute } from 'three'; | ||||
| import { GPUInputStepMode } from './WebGPUConstants.js'; | ||||
|  | ||||
| const typedArraysToVertexFormatPrefix = new Map( [ | ||||
| 	[ Int8Array, [ 'sint8', 'snorm8' ]], | ||||
| 	[ Uint8Array, [ 'uint8', 'unorm8' ]], | ||||
| 	[ Int16Array, [ 'sint16', 'snorm16' ]], | ||||
| 	[ Uint16Array, [ 'uint16', 'unorm16' ]], | ||||
| 	[ Int32Array, [ 'sint32', 'snorm32' ]], | ||||
| 	[ Uint32Array, [ 'uint32', 'unorm32' ]], | ||||
| 	[ Float32Array, [ 'float32', ]], | ||||
| ] ); | ||||
|  | ||||
| const typedAttributeToVertexFormatPrefix = new Map( [ | ||||
| 	[ Float16BufferAttribute, [ 'float16', ]], | ||||
| ] ); | ||||
|  | ||||
| const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [ | ||||
| 	[ Int32Array, 'sint32' ], | ||||
| 	[ Int16Array, 'sint32' ], // patch for INT16 | ||||
| 	[ Uint32Array, 'uint32' ], | ||||
| 	[ Uint16Array, 'uint32' ], // patch for UINT16 | ||||
| 	[ Float32Array, 'float32' ] | ||||
| ] ); | ||||
|  | ||||
| class WebGPUAttributeUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createAttribute( attribute, usage ) { | ||||
|  | ||||
| 		const bufferAttribute = this._getBufferAttribute( attribute ); | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const bufferData = backend.get( bufferAttribute ); | ||||
|  | ||||
| 		let buffer = bufferData.buffer; | ||||
|  | ||||
| 		if ( buffer === undefined ) { | ||||
|  | ||||
| 			const device = backend.device; | ||||
|  | ||||
| 			let array = bufferAttribute.array; | ||||
|  | ||||
| 			// patch for INT16 and UINT16 | ||||
| 			if ( attribute.normalized === false && ( array.constructor === Int16Array || array.constructor === Uint16Array ) ) { | ||||
|  | ||||
| 				const tempArray = new Uint32Array( array.length ); | ||||
| 				for ( let i = 0; i < array.length; i ++ ) { | ||||
|  | ||||
| 					tempArray[ i ] = array[ i ]; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				array = tempArray; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			bufferAttribute.array = array; | ||||
|  | ||||
| 			if ( ( bufferAttribute.isStorageBufferAttribute || bufferAttribute.isStorageInstancedBufferAttribute ) && bufferAttribute.itemSize === 3 ) { | ||||
|  | ||||
| 				array = new array.constructor( bufferAttribute.count * 4 ); | ||||
|  | ||||
| 				for ( let i = 0; i < bufferAttribute.count; i ++ ) { | ||||
|  | ||||
| 					array.set( bufferAttribute.array.subarray( i * 3, i * 3 + 3 ), i * 4 ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				// Update BufferAttribute | ||||
| 				bufferAttribute.itemSize = 4; | ||||
| 				bufferAttribute.array = array; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441 | ||||
|  | ||||
| 			buffer = device.createBuffer( { | ||||
| 				label: bufferAttribute.name, | ||||
| 				size: size, | ||||
| 				usage: usage, | ||||
| 				mappedAtCreation: true | ||||
| 			} ); | ||||
|  | ||||
| 			new array.constructor( buffer.getMappedRange() ).set( array ); | ||||
|  | ||||
| 			buffer.unmap(); | ||||
|  | ||||
| 			bufferData.buffer = buffer; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateAttribute( attribute ) { | ||||
|  | ||||
| 		const bufferAttribute = this._getBufferAttribute( attribute ); | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		const buffer = backend.get( bufferAttribute ).buffer; | ||||
|  | ||||
| 		const array = bufferAttribute.array; | ||||
| 		const updateRanges = bufferAttribute.updateRanges; | ||||
|  | ||||
| 		if ( updateRanges.length === 0 ) { | ||||
|  | ||||
| 			// Not using update ranges | ||||
|  | ||||
| 			device.queue.writeBuffer( | ||||
| 				buffer, | ||||
| 				0, | ||||
| 				array, | ||||
| 				0 | ||||
| 			); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			for ( let i = 0, l = updateRanges.length; i < l; i ++ ) { | ||||
|  | ||||
| 				const range = updateRanges[ i ]; | ||||
| 				device.queue.writeBuffer( | ||||
| 					buffer, | ||||
| 					0, | ||||
| 					array, | ||||
| 					range.start * array.BYTES_PER_ELEMENT, | ||||
| 					range.count * array.BYTES_PER_ELEMENT | ||||
| 				); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			bufferAttribute.clearUpdateRanges(); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createShaderVertexBuffers( renderObject ) { | ||||
|  | ||||
| 		const attributes = renderObject.getAttributes(); | ||||
| 		const vertexBuffers = new Map(); | ||||
|  | ||||
| 		for ( let slot = 0; slot < attributes.length; slot ++ ) { | ||||
|  | ||||
| 			const geometryAttribute = attributes[ slot ]; | ||||
| 			const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT; | ||||
| 			const bufferAttribute = this._getBufferAttribute( geometryAttribute ); | ||||
|  | ||||
| 			let vertexBufferLayout = vertexBuffers.get( bufferAttribute ); | ||||
|  | ||||
| 			if ( vertexBufferLayout === undefined ) { | ||||
|  | ||||
| 				let arrayStride, stepMode; | ||||
|  | ||||
| 				if ( geometryAttribute.isInterleavedBufferAttribute === true ) { | ||||
|  | ||||
| 					arrayStride = geometryAttribute.data.stride * bytesPerElement; | ||||
| 					stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					arrayStride = geometryAttribute.itemSize * bytesPerElement; | ||||
| 					stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				// patch for INT16 and UINT16 | ||||
| 				if ( geometryAttribute.normalized === false && ( geometryAttribute.array.constructor === Int16Array || geometryAttribute.array.constructor === Uint16Array ) ) { | ||||
|  | ||||
| 					arrayStride = 4; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				vertexBufferLayout = { | ||||
| 					arrayStride, | ||||
| 					attributes: [], | ||||
| 					stepMode | ||||
| 				}; | ||||
|  | ||||
| 				vertexBuffers.set( bufferAttribute, vertexBufferLayout ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			const format = this._getVertexFormat( geometryAttribute ); | ||||
| 			const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0; | ||||
|  | ||||
| 			vertexBufferLayout.attributes.push( { | ||||
| 				shaderLocation: slot, | ||||
| 				offset, | ||||
| 				format | ||||
| 			} ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return Array.from( vertexBuffers.values() ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	destroyAttribute( attribute ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const data = backend.get( this._getBufferAttribute( attribute ) ); | ||||
|  | ||||
| 		data.buffer.destroy(); | ||||
|  | ||||
| 		backend.delete( attribute ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	async getArrayBufferAsync( attribute ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		const data = backend.get( this._getBufferAttribute( attribute ) ); | ||||
|  | ||||
| 		const bufferGPU = data.buffer; | ||||
| 		const size = bufferGPU.size; | ||||
|  | ||||
| 		let readBufferGPU = data.readBuffer; | ||||
| 		let needsUnmap = true; | ||||
|  | ||||
| 		if ( readBufferGPU === undefined ) { | ||||
|  | ||||
| 			readBufferGPU = device.createBuffer( { | ||||
| 				label: attribute.name, | ||||
| 				size, | ||||
| 				usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ | ||||
| 			} ); | ||||
|  | ||||
| 			needsUnmap = false; | ||||
|  | ||||
| 			data.readBuffer = readBufferGPU; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const cmdEncoder = device.createCommandEncoder( {} ); | ||||
|  | ||||
| 		cmdEncoder.copyBufferToBuffer( | ||||
| 			bufferGPU, | ||||
| 			0, | ||||
| 			readBufferGPU, | ||||
| 			0, | ||||
| 			size | ||||
| 		); | ||||
|  | ||||
| 		if ( needsUnmap ) readBufferGPU.unmap(); | ||||
|  | ||||
| 		const gpuCommands = cmdEncoder.finish(); | ||||
| 		device.queue.submit( [ gpuCommands ] ); | ||||
|  | ||||
| 		await readBufferGPU.mapAsync( GPUMapMode.READ ); | ||||
|  | ||||
| 		const arrayBuffer = readBufferGPU.getMappedRange(); | ||||
|  | ||||
| 		return arrayBuffer; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getVertexFormat( geometryAttribute ) { | ||||
|  | ||||
| 		const { itemSize, normalized } = geometryAttribute; | ||||
| 		const ArrayType = geometryAttribute.array.constructor; | ||||
| 		const AttributeType = geometryAttribute.constructor; | ||||
|  | ||||
| 		let format; | ||||
|  | ||||
| 		if ( itemSize == 1 ) { | ||||
|  | ||||
| 			format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType ); | ||||
| 			const prefix = prefixOptions[ normalized ? 1 : 0 ]; | ||||
|  | ||||
| 			if ( prefix ) { | ||||
|  | ||||
| 				const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize; | ||||
| 				const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4; | ||||
| 				const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT; | ||||
|  | ||||
| 				if ( paddedItemSize % 1 ) { | ||||
|  | ||||
| 					throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				format = `${prefix}x${paddedItemSize}`; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( ! format ) { | ||||
|  | ||||
| 			console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return format; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBufferAttribute( attribute ) { | ||||
|  | ||||
| 		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; | ||||
|  | ||||
| 		return attribute; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPUAttributeUtils; | ||||
| @ -0,0 +1,258 @@ | ||||
| import { | ||||
| 	GPUTextureAspect, GPUTextureViewDimension, GPUBufferBindingType, GPUTextureSampleType | ||||
| } from './WebGPUConstants.js'; | ||||
| import { FloatType, IntType, UnsignedIntType } from 'three'; | ||||
|  | ||||
| class WebGPUBindingUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createBindingsLayout( bindings ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		const entries = []; | ||||
|  | ||||
| 		let index = 0; | ||||
|  | ||||
| 		for ( const binding of bindings ) { | ||||
|  | ||||
| 			const bindingGPU = { | ||||
| 				binding: index ++, | ||||
| 				visibility: binding.visibility | ||||
| 			}; | ||||
|  | ||||
| 			if ( binding.isUniformBuffer || binding.isStorageBuffer ) { | ||||
|  | ||||
| 				const buffer = {}; // GPUBufferBindingLayout | ||||
|  | ||||
| 				if ( binding.isStorageBuffer ) { | ||||
|  | ||||
| 					buffer.type = GPUBufferBindingType.Storage; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				bindingGPU.buffer = buffer; | ||||
|  | ||||
| 			} else if ( binding.isSampler ) { | ||||
|  | ||||
| 				const sampler = {}; // GPUSamplerBindingLayout | ||||
|  | ||||
| 				if ( binding.texture.isDepthTexture ) { | ||||
|  | ||||
| 					if ( binding.texture.compareFunction !== null ) { | ||||
|  | ||||
| 						sampler.type = 'comparison'; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				bindingGPU.sampler = sampler; | ||||
|  | ||||
| 			} else if ( binding.isSampledTexture && binding.texture.isVideoTexture ) { | ||||
|  | ||||
| 				bindingGPU.externalTexture = {}; // GPUExternalTextureBindingLayout | ||||
|  | ||||
| 			} else if ( binding.isSampledTexture && binding.store ) { | ||||
|  | ||||
| 				const format = this.backend.get( binding.texture ).texture.format; | ||||
|  | ||||
| 				bindingGPU.storageTexture = { format }; // GPUStorageTextureBindingLayout | ||||
|  | ||||
| 			} else if ( binding.isSampledTexture ) { | ||||
|  | ||||
| 				const texture = {}; // GPUTextureBindingLayout | ||||
|  | ||||
| 				if ( binding.texture.isDepthTexture ) { | ||||
|  | ||||
| 					texture.sampleType = GPUTextureSampleType.Depth; | ||||
|  | ||||
| 				} else if ( binding.texture.isDataTexture ) { | ||||
|  | ||||
| 					const type = binding.texture.type; | ||||
|  | ||||
| 					if ( type === IntType ) { | ||||
|  | ||||
| 						texture.sampleType = GPUTextureSampleType.SInt; | ||||
|  | ||||
| 					} else if ( type === UnsignedIntType ) { | ||||
|  | ||||
| 						texture.sampleType = GPUTextureSampleType.UInt; | ||||
|  | ||||
| 					} else if ( type === FloatType ) { | ||||
|  | ||||
| 						// @TODO: Add support for this soon: backend.hasFeature( 'float32-filterable' ) | ||||
|  | ||||
| 						texture.sampleType = GPUTextureSampleType.UnfilterableFloat; | ||||
|  | ||||
| 					} | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				if ( binding.isSampledCubeTexture ) { | ||||
|  | ||||
| 					texture.viewDimension = GPUTextureViewDimension.Cube; | ||||
|  | ||||
| 				} else if ( binding.texture.isDataArrayTexture ) { | ||||
|  | ||||
| 					texture.viewDimension = GPUTextureViewDimension.TwoDArray; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				bindingGPU.texture = texture; | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				console.error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			entries.push( bindingGPU ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return device.createBindGroupLayout( { entries } ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createBindings( bindings ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const bindingsData = backend.get( bindings ); | ||||
|  | ||||
| 		// setup (static) binding layout and (dynamic) binding group | ||||
|  | ||||
| 		const bindLayoutGPU = this.createBindingsLayout( bindings ); | ||||
| 		const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU ); | ||||
|  | ||||
| 		bindingsData.layout = bindLayoutGPU; | ||||
| 		bindingsData.group = bindGroupGPU; | ||||
| 		bindingsData.bindings = bindings; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	updateBinding( binding ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		const buffer = binding.buffer; | ||||
| 		const bufferGPU = backend.get( binding ).buffer; | ||||
|  | ||||
| 		device.queue.writeBuffer( bufferGPU, 0, buffer, 0 ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createBindGroup( bindings, layoutGPU ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		let bindingPoint = 0; | ||||
| 		const entriesGPU = []; | ||||
|  | ||||
| 		for ( const binding of bindings ) { | ||||
|  | ||||
| 			if ( binding.isUniformBuffer ) { | ||||
|  | ||||
| 				const bindingData = backend.get( binding ); | ||||
|  | ||||
| 				if ( bindingData.buffer === undefined ) { | ||||
|  | ||||
| 					const byteLength = binding.byteLength; | ||||
|  | ||||
| 					const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST; | ||||
|  | ||||
| 					const bufferGPU = device.createBuffer( { | ||||
| 						label: 'bindingBuffer_' + binding.name, | ||||
| 						size: byteLength, | ||||
| 						usage: usage | ||||
| 					} ); | ||||
|  | ||||
| 					bindingData.buffer = bufferGPU; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } ); | ||||
|  | ||||
| 			} else if ( binding.isStorageBuffer ) { | ||||
|  | ||||
| 				const bindingData = backend.get( binding ); | ||||
|  | ||||
| 				if ( bindingData.buffer === undefined ) { | ||||
|  | ||||
| 					const attribute = binding.attribute; | ||||
| 					//const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST; | ||||
|  | ||||
| 					//backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer | ||||
|  | ||||
| 					bindingData.buffer = backend.get( attribute ).buffer; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } ); | ||||
|  | ||||
| 			} else if ( binding.isSampler ) { | ||||
|  | ||||
| 				const textureGPU = backend.get( binding.texture ); | ||||
|  | ||||
| 				entriesGPU.push( { binding: bindingPoint, resource: textureGPU.sampler } ); | ||||
|  | ||||
| 			} else if ( binding.isSampledTexture ) { | ||||
|  | ||||
| 				const textureData = backend.get( binding.texture ); | ||||
|  | ||||
| 				let dimensionViewGPU; | ||||
|  | ||||
| 				if ( binding.isSampledCubeTexture ) { | ||||
|  | ||||
| 					dimensionViewGPU = GPUTextureViewDimension.Cube; | ||||
|  | ||||
| 				} else if ( binding.texture.isDataArrayTexture ) { | ||||
|  | ||||
| 					dimensionViewGPU = GPUTextureViewDimension.TwoDArray; | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					dimensionViewGPU = GPUTextureViewDimension.TwoD; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				let resourceGPU; | ||||
|  | ||||
| 				if ( textureData.externalTexture !== undefined ) { | ||||
|  | ||||
| 					resourceGPU = device.importExternalTexture( { source: textureData.externalTexture } ); | ||||
|  | ||||
| 				} else { | ||||
|  | ||||
| 					const aspectGPU = GPUTextureAspect.All; | ||||
|  | ||||
| 					resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU, mipLevelCount: binding.store ? 1 : textureData.mipLevelCount } ); | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 				entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			bindingPoint ++; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return device.createBindGroup( { | ||||
| 			layout: layoutGPU, | ||||
| 			entries: entriesGPU | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPUBindingUtils; | ||||
							
								
								
									
										324
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUConstants.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUConstants.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,324 @@ | ||||
| export const GPUPrimitiveTopology = { | ||||
| 	PointList: 'point-list', | ||||
| 	LineList: 'line-list', | ||||
| 	LineStrip: 'line-strip', | ||||
| 	TriangleList: 'triangle-list', | ||||
| 	TriangleStrip: 'triangle-strip', | ||||
| }; | ||||
|  | ||||
| export const GPUCompareFunction = { | ||||
| 	Never: 'never', | ||||
| 	Less: 'less', | ||||
| 	Equal: 'equal', | ||||
| 	LessEqual: 'less-equal', | ||||
| 	Greater: 'greater', | ||||
| 	NotEqual: 'not-equal', | ||||
| 	GreaterEqual: 'greater-equal', | ||||
| 	Always: 'always' | ||||
| }; | ||||
|  | ||||
| export const GPUStoreOp = { | ||||
| 	Store: 'store', | ||||
| 	Discard: 'discard' | ||||
| }; | ||||
|  | ||||
| export const GPULoadOp = { | ||||
| 	Load: 'load', | ||||
| 	Clear: 'clear' | ||||
| }; | ||||
|  | ||||
| export const GPUFrontFace = { | ||||
| 	CCW: 'ccw', | ||||
| 	CW: 'cw' | ||||
| }; | ||||
|  | ||||
| export const GPUCullMode = { | ||||
| 	None: 'none', | ||||
| 	Front: 'front', | ||||
| 	Back: 'back' | ||||
| }; | ||||
|  | ||||
| export const GPUIndexFormat = { | ||||
| 	Uint16: 'uint16', | ||||
| 	Uint32: 'uint32' | ||||
| }; | ||||
|  | ||||
| export const GPUVertexFormat = { | ||||
| 	Uint8x2: 'uint8x2', | ||||
| 	Uint8x4: 'uint8x4', | ||||
| 	Sint8x2: 'sint8x2', | ||||
| 	Sint8x4: 'sint8x4', | ||||
| 	Unorm8x2: 'unorm8x2', | ||||
| 	Unorm8x4: 'unorm8x4', | ||||
| 	Snorm8x2: 'snorm8x2', | ||||
| 	Snorm8x4: 'snorm8x4', | ||||
| 	Uint16x2: 'uint16x2', | ||||
| 	Uint16x4: 'uint16x4', | ||||
| 	Sint16x2: 'sint16x2', | ||||
| 	Sint16x4: 'sint16x4', | ||||
| 	Unorm16x2: 'unorm16x2', | ||||
| 	Unorm16x4: 'unorm16x4', | ||||
| 	Snorm16x2: 'snorm16x2', | ||||
| 	Snorm16x4: 'snorm16x4', | ||||
| 	Float16x2: 'float16x2', | ||||
| 	Float16x4: 'float16x4', | ||||
| 	Float32: 'float32', | ||||
| 	Float32x2: 'float32x2', | ||||
| 	Float32x3: 'float32x3', | ||||
| 	Float32x4: 'float32x4', | ||||
| 	Uint32: 'uint32', | ||||
| 	Uint32x2: 'uint32x2', | ||||
| 	Uint32x3: 'uint32x3', | ||||
| 	Uint32x4: 'uint32x4', | ||||
| 	Sint32: 'sint32', | ||||
| 	Sint32x2: 'sint32x2', | ||||
| 	Sint32x3: 'sint32x3', | ||||
| 	Sint32x4: 'sint32x4' | ||||
| }; | ||||
|  | ||||
| export const GPUTextureFormat = { | ||||
|  | ||||
| 	// 8-bit formats | ||||
|  | ||||
| 	R8Unorm: 'r8unorm', | ||||
| 	R8Snorm: 'r8snorm', | ||||
| 	R8Uint: 'r8uint', | ||||
| 	R8Sint: 'r8sint', | ||||
|  | ||||
| 	// 16-bit formats | ||||
|  | ||||
| 	R16Uint: 'r16uint', | ||||
| 	R16Sint: 'r16sint', | ||||
| 	R16Float: 'r16float', | ||||
| 	RG8Unorm: 'rg8unorm', | ||||
| 	RG8Snorm: 'rg8snorm', | ||||
| 	RG8Uint: 'rg8uint', | ||||
| 	RG8Sint: 'rg8sint', | ||||
|  | ||||
| 	// 32-bit formats | ||||
|  | ||||
| 	R32Uint: 'r32uint', | ||||
| 	R32Sint: 'r32sint', | ||||
| 	R32Float: 'r32float', | ||||
| 	RG16Uint: 'rg16uint', | ||||
| 	RG16Sint: 'rg16sint', | ||||
| 	RG16Float: 'rg16float', | ||||
| 	RGBA8Unorm: 'rgba8unorm', | ||||
| 	RGBA8UnormSRGB: 'rgba8unorm-srgb', | ||||
| 	RGBA8Snorm: 'rgba8snorm', | ||||
| 	RGBA8Uint: 'rgba8uint', | ||||
| 	RGBA8Sint: 'rgba8sint', | ||||
| 	BGRA8Unorm: 'bgra8unorm', | ||||
| 	BGRA8UnormSRGB: 'bgra8unorm-srgb', | ||||
| 	// Packed 32-bit formats | ||||
| 	RGB9E5UFloat: 'rgb9e5ufloat', | ||||
| 	RGB10A2Unorm: 'rgb10a2unorm', | ||||
| 	RG11B10uFloat: 'rgb10a2unorm', | ||||
|  | ||||
| 	// 64-bit formats | ||||
|  | ||||
| 	RG32Uint: 'rg32uint', | ||||
| 	RG32Sint: 'rg32sint', | ||||
| 	RG32Float: 'rg32float', | ||||
| 	RGBA16Uint: 'rgba16uint', | ||||
| 	RGBA16Sint: 'rgba16sint', | ||||
| 	RGBA16Float: 'rgba16float', | ||||
|  | ||||
| 	// 128-bit formats | ||||
|  | ||||
| 	RGBA32Uint: 'rgba32uint', | ||||
| 	RGBA32Sint: 'rgba32sint', | ||||
| 	RGBA32Float: 'rgba32float', | ||||
|  | ||||
| 	// Depth and stencil formats | ||||
|  | ||||
| 	Stencil8: 'stencil8', | ||||
| 	Depth16Unorm: 'depth16unorm', | ||||
| 	Depth24Plus: 'depth24plus', | ||||
| 	Depth24PlusStencil8: 'depth24plus-stencil8', | ||||
| 	Depth32Float: 'depth32float', | ||||
|  | ||||
| 	// 'depth32float-stencil8' extension | ||||
|  | ||||
| 	Depth32FloatStencil8: 'depth32float-stencil8', | ||||
|  | ||||
| 	// BC compressed formats usable if 'texture-compression-bc' is both | ||||
| 	// supported by the device/user agent and enabled in requestDevice. | ||||
|  | ||||
| 	BC1RGBAUnorm: 'bc1-rgba-unorm', | ||||
| 	BC1RGBAUnormSRGB: 'bc1-rgba-unorm-srgb', | ||||
| 	BC2RGBAUnorm: 'bc2-rgba-unorm', | ||||
| 	BC2RGBAUnormSRGB: 'bc2-rgba-unorm-srgb', | ||||
| 	BC3RGBAUnorm: 'bc3-rgba-unorm', | ||||
| 	BC3RGBAUnormSRGB: 'bc3-rgba-unorm-srgb', | ||||
| 	BC4RUnorm: 'bc4-r-unorm', | ||||
| 	BC4RSnorm: 'bc4-r-snorm', | ||||
| 	BC5RGUnorm: 'bc5-rg-unorm', | ||||
| 	BC5RGSnorm: 'bc5-rg-snorm', | ||||
| 	BC6HRGBUFloat: 'bc6h-rgb-ufloat', | ||||
| 	BC6HRGBFloat: 'bc6h-rgb-float', | ||||
| 	BC7RGBAUnorm: 'bc7-rgba-unorm', | ||||
| 	BC7RGBAUnormSRGB: 'bc7-rgba-srgb', | ||||
|  | ||||
| 	// ETC2 compressed formats usable if 'texture-compression-etc2' is both | ||||
| 	// supported by the device/user agent and enabled in requestDevice. | ||||
|  | ||||
| 	ETC2RGB8Unorm: 'etc2-rgb8unorm', | ||||
| 	ETC2RGB8UnormSRGB: 'etc2-rgb8unorm-srgb', | ||||
| 	ETC2RGB8A1Unorm: 'etc2-rgb8a1unorm', | ||||
| 	ETC2RGB8A1UnormSRGB: 'etc2-rgb8a1unorm-srgb', | ||||
| 	ETC2RGBA8Unorm: 'etc2-rgba8unorm', | ||||
| 	ETC2RGBA8UnormSRGB: 'etc2-rgba8unorm-srgb', | ||||
| 	EACR11Unorm: 'eac-r11unorm', | ||||
| 	EACR11Snorm: 'eac-r11snorm', | ||||
| 	EACRG11Unorm: 'eac-rg11unorm', | ||||
| 	EACRG11Snorm: 'eac-rg11snorm', | ||||
|  | ||||
| 	// ASTC compressed formats usable if 'texture-compression-astc' is both | ||||
| 	// supported by the device/user agent and enabled in requestDevice. | ||||
|  | ||||
| 	ASTC4x4Unorm: 'astc-4x4-unorm', | ||||
| 	ASTC4x4UnormSRGB: 'astc-4x4-unorm-srgb', | ||||
| 	ASTC5x4Unorm: 'astc-5x4-unorm', | ||||
| 	ASTC5x4UnormSRGB: 'astc-5x4-unorm-srgb', | ||||
| 	ASTC5x5Unorm: 'astc-5x5-unorm', | ||||
| 	ASTC5x5UnormSRGB: 'astc-5x5-unorm-srgb', | ||||
| 	ASTC6x5Unorm: 'astc-6x5-unorm', | ||||
| 	ASTC6x5UnormSRGB: 'astc-6x5-unorm-srgb', | ||||
| 	ASTC6x6Unorm: 'astc-6x6-unorm', | ||||
| 	ASTC6x6UnormSRGB: 'astc-6x6-unorm-srgb', | ||||
| 	ASTC8x5Unorm: 'astc-8x5-unorm', | ||||
| 	ASTC8x5UnormSRGB: 'astc-8x5-unorm-srgb', | ||||
| 	ASTC8x6Unorm: 'astc-8x6-unorm', | ||||
| 	ASTC8x6UnormSRGB: 'astc-8x6-unorm-srgb', | ||||
| 	ASTC8x8Unorm: 'astc-8x8-unorm', | ||||
| 	ASTC8x8UnormSRGB: 'astc-8x8-unorm-srgb', | ||||
| 	ASTC10x5Unorm: 'astc-10x5-unorm', | ||||
| 	ASTC10x5UnormSRGB: 'astc-10x5-unorm-srgb', | ||||
| 	ASTC10x6Unorm: 'astc-10x6-unorm', | ||||
| 	ASTC10x6UnormSRGB: 'astc-10x6-unorm-srgb', | ||||
| 	ASTC10x8Unorm: 'astc-10x8-unorm', | ||||
| 	ASTC10x8UnormSRGB: 'astc-10x8-unorm-srgb', | ||||
| 	ASTC10x10Unorm: 'astc-10x10-unorm', | ||||
| 	ASTC10x10UnormSRGB: 'astc-10x10-unorm-srgb', | ||||
| 	ASTC12x10Unorm: 'astc-12x10-unorm', | ||||
| 	ASTC12x10UnormSRGB: 'astc-12x10-unorm-srgb', | ||||
| 	ASTC12x12Unorm: 'astc-12x12-unorm', | ||||
| 	ASTC12x12UnormSRGB: 'astc-12x12-unorm-srgb', | ||||
|  | ||||
| }; | ||||
|  | ||||
| export const GPUAddressMode = { | ||||
| 	ClampToEdge: 'clamp-to-edge', | ||||
| 	Repeat: 'repeat', | ||||
| 	MirrorRepeat: 'mirror-repeat' | ||||
| }; | ||||
|  | ||||
| export const GPUFilterMode = { | ||||
| 	Linear: 'linear', | ||||
| 	Nearest: 'nearest' | ||||
| }; | ||||
|  | ||||
| export const GPUBlendFactor = { | ||||
| 	Zero: 'zero', | ||||
| 	One: 'one', | ||||
| 	Src: 'src', | ||||
| 	OneMinusSrc: 'one-minus-src', | ||||
| 	SrcAlpha: 'src-alpha', | ||||
| 	OneMinusSrcAlpha: 'one-minus-src-alpha', | ||||
| 	Dst: 'dst', | ||||
| 	OneMinusDstColor: 'one-minus-dst', | ||||
| 	DstAlpha: 'dst-alpha', | ||||
| 	OneMinusDstAlpha: 'one-minus-dst-alpha', | ||||
| 	SrcAlphaSaturated: 'src-alpha-saturated', | ||||
| 	Constant: 'constant', | ||||
| 	OneMinusConstant: 'one-minus-constant' | ||||
| }; | ||||
|  | ||||
| export const GPUBlendOperation = { | ||||
| 	Add: 'add', | ||||
| 	Subtract: 'subtract', | ||||
| 	ReverseSubtract: 'reverse-subtract', | ||||
| 	Min: 'min', | ||||
| 	Max: 'max' | ||||
| }; | ||||
|  | ||||
| export const GPUColorWriteFlags = { | ||||
| 	None: 0, | ||||
| 	Red: 0x1, | ||||
| 	Green: 0x2, | ||||
| 	Blue: 0x4, | ||||
| 	Alpha: 0x8, | ||||
| 	All: 0xF | ||||
| }; | ||||
|  | ||||
| export const GPUStencilOperation = { | ||||
| 	Keep: 'keep', | ||||
| 	Zero: 'zero', | ||||
| 	Replace: 'replace', | ||||
| 	Invert: 'invert', | ||||
| 	IncrementClamp: 'increment-clamp', | ||||
| 	DecrementClamp: 'decrement-clamp', | ||||
| 	IncrementWrap: 'increment-wrap', | ||||
| 	DecrementWrap: 'decrement-wrap' | ||||
| }; | ||||
|  | ||||
| export const GPUBufferBindingType = { | ||||
| 	Uniform: 'uniform', | ||||
| 	Storage: 'storage', | ||||
| 	ReadOnlyStorage: 'read-only-storage' | ||||
| }; | ||||
|  | ||||
| export const GPUSamplerBindingType = { | ||||
| 	Filtering: 'filtering', | ||||
| 	NonFiltering: 'non-filtering', | ||||
| 	Comparison: 'comparison' | ||||
| }; | ||||
|  | ||||
| export const GPUTextureSampleType = { | ||||
| 	Float: 'float', | ||||
| 	UnfilterableFloat: 'unfilterable-float', | ||||
| 	Depth: 'depth', | ||||
| 	SInt: 'sint', | ||||
| 	UInt: 'uint' | ||||
| }; | ||||
|  | ||||
| export const GPUTextureDimension = { | ||||
| 	OneD: '1d', | ||||
| 	TwoD: '2d', | ||||
| 	ThreeD: '3d' | ||||
| }; | ||||
|  | ||||
| export const GPUTextureViewDimension = { | ||||
| 	OneD: '1d', | ||||
| 	TwoD: '2d', | ||||
| 	TwoDArray: '2d-array', | ||||
| 	Cube: 'cube', | ||||
| 	CubeArray: 'cube-array', | ||||
| 	ThreeD: '3d' | ||||
| }; | ||||
|  | ||||
| export const GPUTextureAspect = { | ||||
| 	All: 'all', | ||||
| 	StencilOnly: 'stencil-only', | ||||
| 	DepthOnly: 'depth-only' | ||||
| }; | ||||
|  | ||||
| export const GPUInputStepMode = { | ||||
| 	Vertex: 'vertex', | ||||
| 	Instance: 'instance' | ||||
| }; | ||||
|  | ||||
| export const GPUFeatureName = { | ||||
| 	DepthClipControl: 'depth-clip-control', | ||||
| 	Depth32FloatStencil8: 'depth32float-stencil8', | ||||
| 	TextureCompressionBC: 'texture-compression-bc', | ||||
| 	TextureCompressionETC2: 'texture-compression-etc2', | ||||
| 	TextureCompressionASTC: 'texture-compression-astc', | ||||
| 	TimestampQuery: 'timestamp-query', | ||||
| 	IndirectFirstInstance: 'indirect-first-instance', | ||||
| 	ShaderF16: 'shader-f16', | ||||
| 	RG11B10UFloat: 'rg11b10ufloat-renderable', | ||||
| 	BGRA8UNormStorage: 'bgra8unorm-storage', | ||||
| 	Float32Filterable: 'float32-filterable' | ||||
| }; | ||||
| @ -0,0 +1,591 @@ | ||||
| import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js'; | ||||
|  | ||||
| import { | ||||
| 	GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation | ||||
| } from './WebGPUConstants.js'; | ||||
|  | ||||
| import { | ||||
| 	FrontSide, BackSide, DoubleSide, | ||||
| 	NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth, | ||||
| 	NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending, | ||||
| 	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor, | ||||
| 	OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor, | ||||
| 	AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation, | ||||
| 	KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp, | ||||
| 	NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc | ||||
| } from 'three'; | ||||
|  | ||||
| class WebGPUPipelineUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createRenderPipeline( renderObject, promises ) { | ||||
|  | ||||
| 		const { object, material, geometry, pipeline } = renderObject; | ||||
| 		const { vertexProgram, fragmentProgram } = pipeline; | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
| 		const utils = backend.utils; | ||||
|  | ||||
| 		const pipelineData = backend.get( pipeline ); | ||||
| 		const bindingsData = backend.get( renderObject.getBindings() ); | ||||
|  | ||||
| 		// vertex buffers | ||||
|  | ||||
| 		const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers( renderObject ); | ||||
|  | ||||
| 		// blending | ||||
|  | ||||
| 		let blending; | ||||
|  | ||||
| 		if ( material.transparent === true && material.blending !== NoBlending ) { | ||||
|  | ||||
| 			blending = this._getBlending( material ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		// stencil | ||||
|  | ||||
| 		let stencilFront = {}; | ||||
|  | ||||
| 		if ( material.stencilWrite === true ) { | ||||
|  | ||||
| 			stencilFront = { | ||||
| 				compare: this._getStencilCompare( material ), | ||||
| 				failOp: this._getStencilOperation( material.stencilFail ), | ||||
| 				depthFailOp: this._getStencilOperation( material.stencilZFail ), | ||||
| 				passOp: this._getStencilOperation( material.stencilZPass ) | ||||
| 			}; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const colorWriteMask = this._getColorWriteMask( material ); | ||||
|  | ||||
| 		const targets = []; | ||||
|  | ||||
| 		if ( renderObject.context.textures !== null ) { | ||||
|  | ||||
| 			const textures = renderObject.context.textures; | ||||
|  | ||||
| 			for ( let i = 0; i < textures.length; i ++ ) { | ||||
|  | ||||
| 				const colorFormat = utils.getTextureFormatGPU( textures[ i ] ); | ||||
|  | ||||
| 				targets.push( { | ||||
| 					format: colorFormat, | ||||
| 					blend: blending, | ||||
| 					writeMask: colorWriteMask | ||||
| 				} ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const colorFormat = utils.getCurrentColorFormat( renderObject.context ); | ||||
|  | ||||
| 			targets.push( { | ||||
| 				format: colorFormat, | ||||
| 				blend: blending, | ||||
| 				writeMask: colorWriteMask | ||||
| 			} ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const vertexModule = backend.get( vertexProgram ).module; | ||||
| 		const fragmentModule = backend.get( fragmentProgram ).module; | ||||
|  | ||||
| 		const primitiveState = this._getPrimitiveState( object, geometry, material ); | ||||
| 		const depthCompare = this._getDepthCompare( material ); | ||||
| 		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context ); | ||||
| 		let sampleCount = utils.getSampleCount( renderObject.context ); | ||||
|  | ||||
| 		if ( sampleCount > 1 ) { | ||||
|  | ||||
| 			// WebGPU only supports power-of-two sample counts and 2 is not a valid value | ||||
| 			sampleCount = Math.pow( 2, Math.floor( Math.log2( sampleCount ) ) ); | ||||
|  | ||||
| 			if ( sampleCount === 2 ) { | ||||
|  | ||||
| 				sampleCount = 4; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		const pipelineDescriptor = { | ||||
| 			vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ), | ||||
| 			fragment: Object.assign( {}, fragmentModule, { targets } ), | ||||
| 			primitive: primitiveState, | ||||
| 			depthStencil: { | ||||
| 				format: depthStencilFormat, | ||||
| 				depthWriteEnabled: material.depthWrite, | ||||
| 				depthCompare: depthCompare, | ||||
| 				stencilFront: stencilFront, | ||||
| 				stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used) | ||||
| 				stencilReadMask: material.stencilFuncMask, | ||||
| 				stencilWriteMask: material.stencilWriteMask | ||||
| 			}, | ||||
| 			multisample: { | ||||
| 				count: sampleCount, | ||||
| 				alphaToCoverageEnabled: material.alphaToCoverage | ||||
| 			}, | ||||
| 			layout: device.createPipelineLayout( { | ||||
| 				bindGroupLayouts: [ bindingsData.layout ] | ||||
| 			} ) | ||||
| 		}; | ||||
|  | ||||
| 		if ( promises === null ) { | ||||
|  | ||||
| 			pipelineData.pipeline = device.createRenderPipeline( pipelineDescriptor ); | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const p = new Promise( ( resolve /*, reject*/ ) => { | ||||
|  | ||||
| 				device.createRenderPipelineAsync( pipelineDescriptor ).then( pipeline => { | ||||
|  | ||||
| 					pipelineData.pipeline = pipeline; | ||||
| 					resolve(); | ||||
|  | ||||
| 				} ); | ||||
|  | ||||
| 			} ); | ||||
|  | ||||
| 			promises.push( p ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	createComputePipeline( pipeline, bindings ) { | ||||
|  | ||||
| 		const backend = this.backend; | ||||
| 		const device = backend.device; | ||||
|  | ||||
| 		const computeProgram = backend.get( pipeline.computeProgram ).module; | ||||
|  | ||||
| 		const pipelineGPU = backend.get( pipeline ); | ||||
| 		const bindingsData = backend.get( bindings ); | ||||
|  | ||||
| 		pipelineGPU.pipeline = device.createComputePipeline( { | ||||
| 			compute: computeProgram, | ||||
| 			layout: device.createPipelineLayout( { | ||||
| 				bindGroupLayouts: [ bindingsData.layout ] | ||||
| 			} ) | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBlending( material ) { | ||||
|  | ||||
| 		let color, alpha; | ||||
|  | ||||
| 		const blending = material.blending; | ||||
|  | ||||
| 		if ( blending === CustomBlending ) { | ||||
|  | ||||
| 			const blendSrcAlpha = material.blendSrcAlpha !== null ? material.blendSrcAlpha : GPUBlendFactor.One; | ||||
| 			const blendDstAlpha = material.blendDstAlpha !== null ? material.blendDstAlpha : GPUBlendFactor.Zero; | ||||
| 			const blendEquationAlpha = material.blendEquationAlpha !== null ? material.blendEquationAlpha : GPUBlendFactor.Add; | ||||
|  | ||||
| 			color = { | ||||
| 				srcFactor: this._getBlendFactor( material.blendSrc ), | ||||
| 				dstFactor: this._getBlendFactor( material.blendDst ), | ||||
| 				operation: this._getBlendOperation( material.blendEquation ) | ||||
| 			}; | ||||
|  | ||||
| 			alpha = { | ||||
| 				srcFactor: this._getBlendFactor( blendSrcAlpha ), | ||||
| 				dstFactor: this._getBlendFactor( blendDstAlpha ), | ||||
| 				operation: this._getBlendOperation( blendEquationAlpha ) | ||||
| 			}; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const premultipliedAlpha = material.premultipliedAlpha; | ||||
|  | ||||
| 			const setBlend = ( srcRGB, dstRGB, srcAlpha, dstAlpha ) => { | ||||
|  | ||||
| 				color = { | ||||
| 					srcFactor: srcRGB, | ||||
| 					dstFactor: dstRGB, | ||||
| 					operation: GPUBlendOperation.Add | ||||
| 				}; | ||||
|  | ||||
| 				alpha = { | ||||
| 					srcFactor: srcAlpha, | ||||
| 					dstFactor: dstAlpha, | ||||
| 					operation: GPUBlendOperation.Add | ||||
| 				}; | ||||
|  | ||||
| 			}; | ||||
|  | ||||
| 			if ( premultipliedAlpha ) { | ||||
|  | ||||
| 				switch ( blending ) { | ||||
|  | ||||
| 					case NormalBlending: | ||||
| 						setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha ); | ||||
| 						break; | ||||
|  | ||||
| 					case AdditiveBlending: | ||||
| 						setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.One, GPUBlendFactor.One ); | ||||
| 						break; | ||||
|  | ||||
| 					case SubtractiveBlending: | ||||
| 						setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One ); | ||||
| 						break; | ||||
|  | ||||
| 					case MultiplyBlending: | ||||
| 						setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.SrcAlpha ); | ||||
| 						break; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} else { | ||||
|  | ||||
| 				switch ( blending ) { | ||||
|  | ||||
| 					case NormalBlending: | ||||
| 						setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.OneMinusSrcAlpha, GPUBlendFactor.One, GPUBlendFactor.OneMinusSrcAlpha ); | ||||
| 						break; | ||||
|  | ||||
| 					case AdditiveBlending: | ||||
| 						setBlend( GPUBlendFactor.SrcAlpha, GPUBlendFactor.One, GPUBlendFactor.SrcAlpha, GPUBlendFactor.One ); | ||||
| 						break; | ||||
|  | ||||
| 					case SubtractiveBlending: | ||||
| 						setBlend( GPUBlendFactor.Zero, GPUBlendFactor.OneMinusSrc, GPUBlendFactor.Zero, GPUBlendFactor.One ); | ||||
| 						break; | ||||
|  | ||||
| 					case MultiplyBlending: | ||||
| 						setBlend( GPUBlendFactor.Zero, GPUBlendFactor.Src, GPUBlendFactor.Zero, GPUBlendFactor.Src ); | ||||
| 						break; | ||||
|  | ||||
| 				} | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if ( color !== undefined && alpha !== undefined ) { | ||||
|  | ||||
| 			return { color, alpha }; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			console.error( 'THREE.WebGPURenderer: Invalid blending: ', blending ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBlendFactor( blend ) { | ||||
|  | ||||
| 		let blendFactor; | ||||
|  | ||||
| 		switch ( blend ) { | ||||
|  | ||||
| 			case ZeroFactor: | ||||
| 				blendFactor = GPUBlendFactor.Zero; | ||||
| 				break; | ||||
|  | ||||
| 			case OneFactor: | ||||
| 				blendFactor = GPUBlendFactor.One; | ||||
| 				break; | ||||
|  | ||||
| 			case SrcColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.Src; | ||||
| 				break; | ||||
|  | ||||
| 			case OneMinusSrcColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.OneMinusSrc; | ||||
| 				break; | ||||
|  | ||||
| 			case SrcAlphaFactor: | ||||
| 				blendFactor = GPUBlendFactor.SrcAlpha; | ||||
| 				break; | ||||
|  | ||||
| 			case OneMinusSrcAlphaFactor: | ||||
| 				blendFactor = GPUBlendFactor.OneMinusSrcAlpha; | ||||
| 				break; | ||||
|  | ||||
| 			case DstColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.Dst; | ||||
| 				break; | ||||
|  | ||||
| 			case OneMinusDstColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.OneMinusDstColor; | ||||
| 				break; | ||||
|  | ||||
| 			case DstAlphaFactor: | ||||
| 				blendFactor = GPUBlendFactor.DstAlpha; | ||||
| 				break; | ||||
|  | ||||
| 			case OneMinusDstAlphaFactor: | ||||
| 				blendFactor = GPUBlendFactor.OneMinusDstAlpha; | ||||
| 				break; | ||||
|  | ||||
| 			case SrcAlphaSaturateFactor: | ||||
| 				blendFactor = GPUBlendFactor.SrcAlphaSaturated; | ||||
| 				break; | ||||
|  | ||||
| 			case BlendColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.Constant; | ||||
| 				break; | ||||
|  | ||||
| 			case OneMinusBlendColorFactor: | ||||
| 				blendFactor = GPUBlendFactor.OneMinusConstant; | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return blendFactor; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getStencilCompare( material ) { | ||||
|  | ||||
| 		let stencilCompare; | ||||
|  | ||||
| 		const stencilFunc = material.stencilFunc; | ||||
|  | ||||
| 		switch ( stencilFunc ) { | ||||
|  | ||||
| 			case NeverStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.Never; | ||||
| 				break; | ||||
|  | ||||
| 			case AlwaysStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.Always; | ||||
| 				break; | ||||
|  | ||||
| 			case LessStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.Less; | ||||
| 				break; | ||||
|  | ||||
| 			case LessEqualStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.LessEqual; | ||||
| 				break; | ||||
|  | ||||
| 			case EqualStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.Equal; | ||||
| 				break; | ||||
|  | ||||
| 			case GreaterEqualStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.GreaterEqual; | ||||
| 				break; | ||||
|  | ||||
| 			case GreaterStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.Greater; | ||||
| 				break; | ||||
|  | ||||
| 			case NotEqualStencilFunc: | ||||
| 				stencilCompare = GPUCompareFunction.NotEqual; | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return stencilCompare; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getStencilOperation( op ) { | ||||
|  | ||||
| 		let stencilOperation; | ||||
|  | ||||
| 		switch ( op ) { | ||||
|  | ||||
| 			case KeepStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.Keep; | ||||
| 				break; | ||||
|  | ||||
| 			case ZeroStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.Zero; | ||||
| 				break; | ||||
|  | ||||
| 			case ReplaceStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.Replace; | ||||
| 				break; | ||||
|  | ||||
| 			case InvertStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.Invert; | ||||
| 				break; | ||||
|  | ||||
| 			case IncrementStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.IncrementClamp; | ||||
| 				break; | ||||
|  | ||||
| 			case DecrementStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.DecrementClamp; | ||||
| 				break; | ||||
|  | ||||
| 			case IncrementWrapStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.IncrementWrap; | ||||
| 				break; | ||||
|  | ||||
| 			case DecrementWrapStencilOp: | ||||
| 				stencilOperation = GPUStencilOperation.DecrementWrap; | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return stencilOperation; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getBlendOperation( blendEquation ) { | ||||
|  | ||||
| 		let blendOperation; | ||||
|  | ||||
| 		switch ( blendEquation ) { | ||||
|  | ||||
| 			case AddEquation: | ||||
| 				blendOperation = GPUBlendOperation.Add; | ||||
| 				break; | ||||
|  | ||||
| 			case SubtractEquation: | ||||
| 				blendOperation = GPUBlendOperation.Subtract; | ||||
| 				break; | ||||
|  | ||||
| 			case ReverseSubtractEquation: | ||||
| 				blendOperation = GPUBlendOperation.ReverseSubtract; | ||||
| 				break; | ||||
|  | ||||
| 			case MinEquation: | ||||
| 				blendOperation = GPUBlendOperation.Min; | ||||
| 				break; | ||||
|  | ||||
| 			case MaxEquation: | ||||
| 				blendOperation = GPUBlendOperation.Max; | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation ); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return blendOperation; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getPrimitiveState( object, geometry, material ) { | ||||
|  | ||||
| 		const descriptor = {}; | ||||
| 		const utils = this.backend.utils; | ||||
|  | ||||
| 		descriptor.topology = utils.getPrimitiveTopology( object, material ); | ||||
|  | ||||
| 		if ( geometry.index !== null && object.isLine === true && object.isLineSegments !== true ) { | ||||
|  | ||||
| 			descriptor.stripIndexFormat = ( geometry.index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		switch ( material.side ) { | ||||
|  | ||||
| 			case FrontSide: | ||||
| 				descriptor.frontFace = GPUFrontFace.CCW; | ||||
| 				descriptor.cullMode = GPUCullMode.Back; | ||||
| 				break; | ||||
|  | ||||
| 			case BackSide: | ||||
| 				descriptor.frontFace = GPUFrontFace.CCW; | ||||
| 				descriptor.cullMode = GPUCullMode.Front; | ||||
| 				break; | ||||
|  | ||||
| 			case DoubleSide: | ||||
| 				descriptor.frontFace = GPUFrontFace.CCW; | ||||
| 				descriptor.cullMode = GPUCullMode.None; | ||||
| 				break; | ||||
|  | ||||
| 			default: | ||||
| 				console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side ); | ||||
| 				break; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return descriptor; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getColorWriteMask( material ) { | ||||
|  | ||||
| 		return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	_getDepthCompare( material ) { | ||||
|  | ||||
| 		let depthCompare; | ||||
|  | ||||
| 		if ( material.depthTest === false ) { | ||||
|  | ||||
| 			depthCompare = GPUCompareFunction.Always; | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			const depthFunc = material.depthFunc; | ||||
|  | ||||
| 			switch ( depthFunc ) { | ||||
|  | ||||
| 				case NeverDepth: | ||||
| 					depthCompare = GPUCompareFunction.Never; | ||||
| 					break; | ||||
|  | ||||
| 				case AlwaysDepth: | ||||
| 					depthCompare = GPUCompareFunction.Always; | ||||
| 					break; | ||||
|  | ||||
| 				case LessDepth: | ||||
| 					depthCompare = GPUCompareFunction.Less; | ||||
| 					break; | ||||
|  | ||||
| 				case LessEqualDepth: | ||||
| 					depthCompare = GPUCompareFunction.LessEqual; | ||||
| 					break; | ||||
|  | ||||
| 				case EqualDepth: | ||||
| 					depthCompare = GPUCompareFunction.Equal; | ||||
| 					break; | ||||
|  | ||||
| 				case GreaterEqualDepth: | ||||
| 					depthCompare = GPUCompareFunction.GreaterEqual; | ||||
| 					break; | ||||
|  | ||||
| 				case GreaterDepth: | ||||
| 					depthCompare = GPUCompareFunction.Greater; | ||||
| 					break; | ||||
|  | ||||
| 				case NotEqualDepth: | ||||
| 					depthCompare = GPUCompareFunction.NotEqual; | ||||
| 					break; | ||||
|  | ||||
| 				default: | ||||
| 					console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc ); | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return depthCompare; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPUPipelineUtils; | ||||
| @ -0,0 +1,285 @@ | ||||
| import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js'; | ||||
|  | ||||
| class WebGPUTexturePassUtils { | ||||
|  | ||||
| 	constructor( device ) { | ||||
|  | ||||
| 		this.device = device; | ||||
|  | ||||
| 		const mipmapVertexSource = ` | ||||
| struct VarysStruct { | ||||
| 	@builtin( position ) Position: vec4<f32>, | ||||
| 	@location( 0 ) vTex : vec2<f32> | ||||
| }; | ||||
|  | ||||
| @vertex | ||||
| fn main( @builtin( vertex_index ) vertexIndex : u32 ) -> VarysStruct { | ||||
|  | ||||
| 	var Varys : VarysStruct; | ||||
|  | ||||
| 	var pos = array< vec2<f32>, 4 >( | ||||
| 		vec2<f32>( -1.0,  1.0 ), | ||||
| 		vec2<f32>(  1.0,  1.0 ), | ||||
| 		vec2<f32>( -1.0, -1.0 ), | ||||
| 		vec2<f32>(  1.0, -1.0 ) | ||||
| 	); | ||||
|  | ||||
| 	var tex = array< vec2<f32>, 4 >( | ||||
| 		vec2<f32>( 0.0, 0.0 ), | ||||
| 		vec2<f32>( 1.0, 0.0 ), | ||||
| 		vec2<f32>( 0.0, 1.0 ), | ||||
| 		vec2<f32>( 1.0, 1.0 ) | ||||
| 	); | ||||
|  | ||||
| 	Varys.vTex = tex[ vertexIndex ]; | ||||
| 	Varys.Position = vec4<f32>( pos[ vertexIndex ], 0.0, 1.0 ); | ||||
|  | ||||
| 	return Varys; | ||||
|  | ||||
| } | ||||
| `; | ||||
|  | ||||
| 		const mipmapFragmentSource = ` | ||||
| @group( 0 ) @binding( 0 ) | ||||
| var imgSampler : sampler; | ||||
|  | ||||
| @group( 0 ) @binding( 1 ) | ||||
| var img : texture_2d<f32>; | ||||
|  | ||||
| @fragment | ||||
| fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> { | ||||
|  | ||||
| 	return textureSample( img, imgSampler, vTex ); | ||||
|  | ||||
| } | ||||
| `; | ||||
|  | ||||
| 		const flipYFragmentSource = ` | ||||
| @group( 0 ) @binding( 0 ) | ||||
| var imgSampler : sampler; | ||||
|  | ||||
| @group( 0 ) @binding( 1 ) | ||||
| var img : texture_2d<f32>; | ||||
|  | ||||
| @fragment | ||||
| fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> { | ||||
|  | ||||
| 	return textureSample( img, imgSampler, vec2( vTex.x, 1.0 - vTex.y ) ); | ||||
|  | ||||
| } | ||||
| `; | ||||
| 		this.mipmapSampler = device.createSampler( { minFilter: GPUFilterMode.Linear } ); | ||||
| 		this.flipYSampler = device.createSampler( { minFilter: GPUFilterMode.Nearest } ); //@TODO?: Consider using textureLoad() | ||||
|  | ||||
| 		// We'll need a new pipeline for every texture format used. | ||||
| 		this.transferPipelines = {}; | ||||
| 		this.flipYPipelines = {}; | ||||
|  | ||||
| 		this.mipmapVertexShaderModule = device.createShaderModule( { | ||||
| 			label: 'mipmapVertex', | ||||
| 			code: mipmapVertexSource | ||||
| 		} ); | ||||
|  | ||||
| 		this.mipmapFragmentShaderModule = device.createShaderModule( { | ||||
| 			label: 'mipmapFragment', | ||||
| 			code: mipmapFragmentSource | ||||
| 		} ); | ||||
|  | ||||
| 		this.flipYFragmentShaderModule = device.createShaderModule( { | ||||
| 			label: 'flipYFragment', | ||||
| 			code: flipYFragmentSource | ||||
| 		} ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getTransferPipeline( format ) { | ||||
|  | ||||
| 		let pipeline = this.transferPipelines[ format ]; | ||||
|  | ||||
| 		if ( pipeline === undefined ) { | ||||
|  | ||||
| 			pipeline = this.device.createRenderPipeline( { | ||||
| 				vertex: { | ||||
| 					module: this.mipmapVertexShaderModule, | ||||
| 					entryPoint: 'main' | ||||
| 				}, | ||||
| 				fragment: { | ||||
| 					module: this.mipmapFragmentShaderModule, | ||||
| 					entryPoint: 'main', | ||||
| 					targets: [ { format } ] | ||||
| 				}, | ||||
| 				primitive: { | ||||
| 					topology: GPUPrimitiveTopology.TriangleStrip, | ||||
| 					stripIndexFormat: GPUIndexFormat.Uint32 | ||||
| 				}, | ||||
| 				layout: 'auto' | ||||
| 			} ); | ||||
|  | ||||
| 			this.transferPipelines[ format ] = pipeline; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getFlipYPipeline( format ) { | ||||
|  | ||||
| 		let pipeline = this.flipYPipelines[ format ]; | ||||
|  | ||||
| 		if ( pipeline === undefined ) { | ||||
|  | ||||
| 			pipeline = this.device.createRenderPipeline( { | ||||
| 				vertex: { | ||||
| 					module: this.mipmapVertexShaderModule, | ||||
| 					entryPoint: 'main' | ||||
| 				}, | ||||
| 				fragment: { | ||||
| 					module: this.flipYFragmentShaderModule, | ||||
| 					entryPoint: 'main', | ||||
| 					targets: [ { format } ] | ||||
| 				}, | ||||
| 				primitive: { | ||||
| 					topology: GPUPrimitiveTopology.TriangleStrip, | ||||
| 					stripIndexFormat: GPUIndexFormat.Uint32 | ||||
| 				}, | ||||
| 				layout: 'auto' | ||||
| 			} ); | ||||
|  | ||||
| 			this.flipYPipelines[ format ] = pipeline; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return pipeline; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	flipY( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { | ||||
|  | ||||
| 		const format = textureGPUDescriptor.format; | ||||
| 		const { width, height } = textureGPUDescriptor.size; | ||||
|  | ||||
| 		const transferPipeline = this.getTransferPipeline( format ); | ||||
| 		const flipYPipeline = this.getFlipYPipeline( format ); | ||||
|  | ||||
| 		const tempTexture = this.device.createTexture( { | ||||
| 			size: { width, height, depthOrArrayLayers: 1 }, | ||||
| 			format, | ||||
| 			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | ||||
| 		} ); | ||||
|  | ||||
| 		const srcView = textureGPU.createView( { | ||||
| 			baseMipLevel: 0, | ||||
| 			mipLevelCount: 1, | ||||
| 			dimension: GPUTextureViewDimension.TwoD, | ||||
| 			baseArrayLayer | ||||
| 		} ); | ||||
|  | ||||
| 		const dstView = tempTexture.createView( { | ||||
| 			baseMipLevel: 0, | ||||
| 			mipLevelCount: 1, | ||||
| 			dimension: GPUTextureViewDimension.TwoD, | ||||
| 			baseArrayLayer: 0 | ||||
| 		} ); | ||||
|  | ||||
| 		const commandEncoder = this.device.createCommandEncoder( {} ); | ||||
|  | ||||
| 		const pass = ( pipeline, sourceView, destinationView ) => { | ||||
|  | ||||
| 			const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. | ||||
|  | ||||
| 			const bindGroup = this.device.createBindGroup( { | ||||
| 				layout: bindGroupLayout, | ||||
| 				entries: [ { | ||||
| 					binding: 0, | ||||
| 					resource: this.flipYSampler | ||||
| 				}, { | ||||
| 					binding: 1, | ||||
| 					resource: sourceView | ||||
| 				} ] | ||||
| 			} ); | ||||
|  | ||||
| 			const passEncoder = commandEncoder.beginRenderPass( { | ||||
| 				colorAttachments: [ { | ||||
| 					view: destinationView, | ||||
| 					loadOp: GPULoadOp.Clear, | ||||
| 					storeOp: GPUStoreOp.Store, | ||||
| 					clearValue: [ 0, 0, 0, 0 ] | ||||
| 				} ] | ||||
| 			} ); | ||||
|  | ||||
| 			passEncoder.setPipeline( pipeline ); | ||||
| 			passEncoder.setBindGroup( 0, bindGroup ); | ||||
| 			passEncoder.draw( 4, 1, 0, 0 ); | ||||
| 			passEncoder.end(); | ||||
|  | ||||
| 		}; | ||||
|  | ||||
| 		pass( transferPipeline, srcView, dstView ); | ||||
| 		pass( flipYPipeline, dstView, srcView ); | ||||
|  | ||||
| 		this.device.queue.submit( [ commandEncoder.finish() ] ); | ||||
|  | ||||
| 		tempTexture.destroy(); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer = 0 ) { | ||||
|  | ||||
| 		const pipeline = this.getTransferPipeline( textureGPUDescriptor.format ); | ||||
|  | ||||
| 		const commandEncoder = this.device.createCommandEncoder( {} ); | ||||
| 		const bindGroupLayout = pipeline.getBindGroupLayout( 0 ); // @TODO: Consider making this static. | ||||
|  | ||||
| 		let srcView = textureGPU.createView( { | ||||
| 			baseMipLevel: 0, | ||||
| 			mipLevelCount: 1, | ||||
| 			dimension: GPUTextureViewDimension.TwoD, | ||||
| 			baseArrayLayer | ||||
| 		} ); | ||||
|  | ||||
| 		for ( let i = 1; i < textureGPUDescriptor.mipLevelCount; i ++ ) { | ||||
|  | ||||
| 			const bindGroup = this.device.createBindGroup( { | ||||
| 				layout: bindGroupLayout, | ||||
| 				entries: [ { | ||||
| 					binding: 0, | ||||
| 					resource: this.mipmapSampler | ||||
| 				}, { | ||||
| 					binding: 1, | ||||
| 					resource: srcView | ||||
| 				} ] | ||||
| 			} ); | ||||
|  | ||||
| 			const dstView = textureGPU.createView( { | ||||
| 				baseMipLevel: i, | ||||
| 				mipLevelCount: 1, | ||||
| 				dimension: GPUTextureViewDimension.TwoD, | ||||
| 				baseArrayLayer | ||||
| 			} ); | ||||
|  | ||||
| 			const passEncoder = commandEncoder.beginRenderPass( { | ||||
| 				colorAttachments: [ { | ||||
| 					view: dstView, | ||||
| 					loadOp: GPULoadOp.Clear, | ||||
| 					storeOp: GPUStoreOp.Store, | ||||
| 					clearValue: [ 0, 0, 0, 0 ] | ||||
| 				} ] | ||||
| 			} ); | ||||
|  | ||||
| 			passEncoder.setPipeline( pipeline ); | ||||
| 			passEncoder.setBindGroup( 0, bindGroup ); | ||||
| 			passEncoder.draw( 4, 1, 0, 0 ); | ||||
| 			passEncoder.end(); | ||||
|  | ||||
| 			srcView = dstView; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		this.device.queue.submit( [ commandEncoder.finish() ] ); | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPUTexturePassUtils; | ||||
							
								
								
									
										1114
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1114
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										93
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUUtils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								public/sdk/three/jsm/renderers/webgpu/utils/WebGPUUtils.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js'; | ||||
|  | ||||
| class WebGPUUtils { | ||||
|  | ||||
| 	constructor( backend ) { | ||||
|  | ||||
| 		this.backend = backend; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCurrentDepthStencilFormat( renderContext ) { | ||||
|  | ||||
| 		let format; | ||||
|  | ||||
| 		if ( renderContext.depthTexture !== null ) { | ||||
|  | ||||
| 			format = this.getTextureFormatGPU( renderContext.depthTexture ); | ||||
|  | ||||
| 		} else if ( renderContext.depth && renderContext.stencil ) { | ||||
|  | ||||
| 			format = GPUTextureFormat.Depth24PlusStencil8; | ||||
|  | ||||
| 		} else if ( renderContext.depth ) { | ||||
|  | ||||
| 			format = GPUTextureFormat.Depth24Plus; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return format; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getTextureFormatGPU( texture ) { | ||||
|  | ||||
| 		return this.backend.get( texture ).texture.format; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCurrentColorFormat( renderContext ) { | ||||
|  | ||||
| 		let format; | ||||
|  | ||||
| 		if ( renderContext.textures !== null ) { | ||||
|  | ||||
| 			format = this.getTextureFormatGPU( renderContext.textures[ 0 ] ); | ||||
|  | ||||
|  | ||||
| 		} else { | ||||
|  | ||||
| 			format = GPUTextureFormat.BGRA8Unorm; // default context format | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return format; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getCurrentColorSpace( renderContext ) { | ||||
|  | ||||
| 		if ( renderContext.textures !== null ) { | ||||
|  | ||||
| 			return renderContext.textures[ 0 ].colorSpace; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return this.backend.renderer.outputColorSpace; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getPrimitiveTopology( object, material ) { | ||||
|  | ||||
| 		if ( object.isPoints ) return GPUPrimitiveTopology.PointList; | ||||
| 		else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList; | ||||
| 		else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip; | ||||
| 		else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	getSampleCount( renderContext ) { | ||||
|  | ||||
| 		if ( renderContext.textures !== null ) { | ||||
|  | ||||
| 			return renderContext.sampleCount; | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return this.backend.parameters.sampleCount; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| export default WebGPUUtils; | ||||
		Reference in New Issue
	
	Block a user