diff --git a/README.md b/README.md index fd46e14..5f55853 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,10 @@ A 3D procedural skyscraper generator with shaders in Three.js. -## Running +## Running on local -Run using `serve.sh`. +Run using `python3 -m http.server` or with your favourite static site server tool. ## Requirements -Working internet connection, Python, a WebGL2 capable browser - -Note: make sure broswer is updated and use it preferably in incognito mode if making changes to files (files serverd by python server are cached by default is some Python versions). +Working internet connection, Python, a modern browser diff --git a/index.html b/index.html index 129a057..7e83e2b 100644 --- a/index.html +++ b/index.html @@ -1,15 +1,15 @@ - - - My first three.js app - - - - - - - - - \ No newline at end of file + + + + My first three.js app + + + + + + + + diff --git a/lib/orbitcontrols.js b/lib/orbitcontrols.js new file mode 100644 index 0000000..0214546 --- /dev/null +++ b/lib/orbitcontrols.js @@ -0,0 +1,1396 @@ +import { + EventDispatcher, + MOUSE, + Quaternion, + Spherical, + TOUCH, + Vector2, + Vector3, + Plane, + Ray, + MathUtils +} from './three.js'; + +// OrbitControls performs orbiting, dollying (zooming), and panning. +// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default). +// +// Orbit - left mouse / touch: one-finger move +// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish +// Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move + +const _changeEvent = { type: 'change' }; +const _startEvent = { type: 'start' }; +const _endEvent = { type: 'end' }; +const _ray = new Ray(); +const _plane = new Plane(); +const TILT_LIMIT = Math.cos( 70 * MathUtils.DEG2RAD ); + +class OrbitControls extends EventDispatcher { + + constructor( object, domElement ) { + + super(); + + this.object = object; + this.domElement = domElement; + this.domElement.style.touchAction = 'none'; // disable touch scroll + + // Set to false to disable this control + this.enabled = true; + + // "target" sets the location of focus, where the object orbits around + this.target = new Vector3(); + + // How far you can dolly in and out ( PerspectiveCamera only ) + this.minDistance = 0; + this.maxDistance = Infinity; + + // How far you can zoom in and out ( OrthographicCamera only ) + this.minZoom = 0; + this.maxZoom = Infinity; + + // How far you can orbit vertically, upper and lower limits. + // Range is 0 to Math.PI radians. + this.minPolarAngle = 0; // radians + this.maxPolarAngle = Math.PI; // radians + + // How far you can orbit horizontally, upper and lower limits. + // If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ], with ( max - min < 2 PI ) + this.minAzimuthAngle = - Infinity; // radians + this.maxAzimuthAngle = Infinity; // radians + + // Set to true to enable damping (inertia) + // If damping is enabled, you must call controls.update() in your animation loop + this.enableDamping = false; + this.dampingFactor = 0.05; + + // This option actually enables dollying in and out; left as "zoom" for backwards compatibility. + // Set to false to disable zooming + this.enableZoom = true; + this.zoomSpeed = 1.0; + + // Set to false to disable rotating + this.enableRotate = true; + this.rotateSpeed = 1.0; + + // Set to false to disable panning + this.enablePan = true; + this.panSpeed = 1.0; + this.screenSpacePanning = true; // if false, pan orthogonal to world-space direction camera.up + this.keyPanSpeed = 7.0; // pixels moved per arrow key push + this.zoomToCursor = false; + + // Set to true to automatically rotate around the target + // If auto-rotate is enabled, you must call controls.update() in your animation loop + this.autoRotate = false; + this.autoRotateSpeed = 2.0; // 30 seconds per orbit when fps is 60 + + // The four arrow keys + this.keys = { LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }; + + // Mouse buttons + this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }; + + // Touch fingers + this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }; + + // for reset + this.target0 = this.target.clone(); + this.position0 = this.object.position.clone(); + this.zoom0 = this.object.zoom; + + // the target DOM element for key events + this._domElementKeyEvents = null; + + // + // public methods + // + + this.getPolarAngle = function () { + + return spherical.phi; + + }; + + this.getAzimuthalAngle = function () { + + return spherical.theta; + + }; + + this.getDistance = function () { + + return this.object.position.distanceTo( this.target ); + + }; + + this.listenToKeyEvents = function ( domElement ) { + + domElement.addEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = domElement; + + }; + + this.stopListenToKeyEvents = function () { + + this._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + this._domElementKeyEvents = null; + + }; + + this.saveState = function () { + + scope.target0.copy( scope.target ); + scope.position0.copy( scope.object.position ); + scope.zoom0 = scope.object.zoom; + + }; + + this.reset = function () { + + scope.target.copy( scope.target0 ); + scope.object.position.copy( scope.position0 ); + scope.object.zoom = scope.zoom0; + + scope.object.updateProjectionMatrix(); + scope.dispatchEvent( _changeEvent ); + + scope.update(); + + state = STATE.NONE; + + }; + + // this method is exposed, but perhaps it would be better if we can make it private... + this.update = function () { + + const offset = new Vector3(); + + // so camera.up is the orbit axis + const quat = new Quaternion().setFromUnitVectors( object.up, new Vector3( 0, 1, 0 ) ); + const quatInverse = quat.clone().invert(); + + const lastPosition = new Vector3(); + const lastQuaternion = new Quaternion(); + const lastTargetPosition = new Vector3(); + + const twoPI = 2 * Math.PI; + + return function update( deltaTime = null ) { + + const position = scope.object.position; + + offset.copy( position ).sub( scope.target ); + + // rotate offset to "y-axis-is-up" space + offset.applyQuaternion( quat ); + + // angle from z-axis around y-axis + spherical.setFromVector3( offset ); + + if ( scope.autoRotate && state === STATE.NONE ) { + + rotateLeft( getAutoRotationAngle( deltaTime ) ); + + } + + if ( scope.enableDamping ) { + + spherical.theta += sphericalDelta.theta * scope.dampingFactor; + spherical.phi += sphericalDelta.phi * scope.dampingFactor; + + } else { + + spherical.theta += sphericalDelta.theta; + spherical.phi += sphericalDelta.phi; + + } + + // restrict theta to be between desired limits + + let min = scope.minAzimuthAngle; + let max = scope.maxAzimuthAngle; + + if ( isFinite( min ) && isFinite( max ) ) { + + if ( min < - Math.PI ) min += twoPI; else if ( min > Math.PI ) min -= twoPI; + + if ( max < - Math.PI ) max += twoPI; else if ( max > Math.PI ) max -= twoPI; + + if ( min <= max ) { + + spherical.theta = Math.max( min, Math.min( max, spherical.theta ) ); + + } else { + + spherical.theta = ( spherical.theta > ( min + max ) / 2 ) ? + Math.max( min, spherical.theta ) : + Math.min( max, spherical.theta ); + + } + + } + + // restrict phi to be between desired limits + spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) ); + + spherical.makeSafe(); + + + // move target to panned location + + if ( scope.enableDamping === true ) { + + scope.target.addScaledVector( panOffset, scope.dampingFactor ); + + } else { + + scope.target.add( panOffset ); + + } + + // adjust the camera position based on zoom only if we're not zooming to the cursor or if it's an ortho camera + // we adjust zoom later in these cases + if ( scope.zoomToCursor && performCursorZoom || scope.object.isOrthographicCamera ) { + + spherical.radius = clampDistance( spherical.radius ); + + } else { + + spherical.radius = clampDistance( spherical.radius * scale ); + + } + + + offset.setFromSpherical( spherical ); + + // rotate offset back to "camera-up-vector-is-up" space + offset.applyQuaternion( quatInverse ); + + position.copy( scope.target ).add( offset ); + + scope.object.lookAt( scope.target ); + + if ( scope.enableDamping === true ) { + + sphericalDelta.theta *= ( 1 - scope.dampingFactor ); + sphericalDelta.phi *= ( 1 - scope.dampingFactor ); + + panOffset.multiplyScalar( 1 - scope.dampingFactor ); + + } else { + + sphericalDelta.set( 0, 0, 0 ); + + panOffset.set( 0, 0, 0 ); + + } + + // adjust camera position + let zoomChanged = false; + if ( scope.zoomToCursor && performCursorZoom ) { + + let newRadius = null; + if ( scope.object.isPerspectiveCamera ) { + + // move the camera down the pointer ray + // this method avoids floating point error + const prevRadius = offset.length(); + newRadius = clampDistance( prevRadius * scale ); + + const radiusDelta = prevRadius - newRadius; + scope.object.position.addScaledVector( dollyDirection, radiusDelta ); + scope.object.updateMatrixWorld(); + + } else if ( scope.object.isOrthographicCamera ) { + + // adjust the ortho camera position based on zoom changes + const mouseBefore = new Vector3( mouse.x, mouse.y, 0 ); + mouseBefore.unproject( scope.object ); + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + const mouseAfter = new Vector3( mouse.x, mouse.y, 0 ); + mouseAfter.unproject( scope.object ); + + scope.object.position.sub( mouseAfter ).add( mouseBefore ); + scope.object.updateMatrixWorld(); + + newRadius = offset.length(); + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled.' ); + scope.zoomToCursor = false; + + } + + // handle the placement of the target + if ( newRadius !== null ) { + + if ( this.screenSpacePanning ) { + + // position the orbit target in front of the new camera position + scope.target.set( 0, 0, - 1 ) + .transformDirection( scope.object.matrix ) + .multiplyScalar( newRadius ) + .add( scope.object.position ); + + } else { + + // get the ray and translation plane to compute target + _ray.origin.copy( scope.object.position ); + _ray.direction.set( 0, 0, - 1 ).transformDirection( scope.object.matrix ); + + // if the camera is 20 degrees above the horizon then don't adjust the focus target to avoid + // extremely large values + if ( Math.abs( scope.object.up.dot( _ray.direction ) ) < TILT_LIMIT ) { + + object.lookAt( scope.target ); + + } else { + + _plane.setFromNormalAndCoplanarPoint( scope.object.up, scope.target ); + _ray.intersectPlane( _plane, scope.target ); + + } + + } + + } + + } else if ( scope.object.isOrthographicCamera ) { + + scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / scale ) ); + scope.object.updateProjectionMatrix(); + zoomChanged = true; + + } + + scale = 1; + performCursorZoom = false; + + // update condition is: + // min(camera displacement, camera rotation in radians)^2 > EPS + // using small-angle approximation cos(x/2) = 1 - x^2 / 8 + + if ( zoomChanged || + lastPosition.distanceToSquared( scope.object.position ) > EPS || + 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS || + lastTargetPosition.distanceToSquared( scope.target ) > 0 ) { + + scope.dispatchEvent( _changeEvent ); + + lastPosition.copy( scope.object.position ); + lastQuaternion.copy( scope.object.quaternion ); + lastTargetPosition.copy( scope.target ); + + zoomChanged = false; + + return true; + + } + + return false; + + }; + + }(); + + this.dispose = function () { + + scope.domElement.removeEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.removeEventListener( 'pointerdown', onPointerDown ); + scope.domElement.removeEventListener( 'pointercancel', onPointerUp ); + scope.domElement.removeEventListener( 'wheel', onMouseWheel ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + + if ( scope._domElementKeyEvents !== null ) { + + scope._domElementKeyEvents.removeEventListener( 'keydown', onKeyDown ); + scope._domElementKeyEvents = null; + + } + + //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here? + + }; + + // + // internals + // + + const scope = this; + + const STATE = { + NONE: - 1, + ROTATE: 0, + DOLLY: 1, + PAN: 2, + TOUCH_ROTATE: 3, + TOUCH_PAN: 4, + TOUCH_DOLLY_PAN: 5, + TOUCH_DOLLY_ROTATE: 6 + }; + + let state = STATE.NONE; + + const EPS = 0.000001; + + // current position in spherical coordinates + const spherical = new Spherical(); + const sphericalDelta = new Spherical(); + + let scale = 1; + const panOffset = new Vector3(); + + const rotateStart = new Vector2(); + const rotateEnd = new Vector2(); + const rotateDelta = new Vector2(); + + const panStart = new Vector2(); + const panEnd = new Vector2(); + const panDelta = new Vector2(); + + const dollyStart = new Vector2(); + const dollyEnd = new Vector2(); + const dollyDelta = new Vector2(); + + const dollyDirection = new Vector3(); + const mouse = new Vector2(); + let performCursorZoom = false; + + const pointers = []; + const pointerPositions = {}; + + function getAutoRotationAngle( deltaTime ) { + + if ( deltaTime !== null ) { + + return ( 2 * Math.PI / 60 * scope.autoRotateSpeed ) * deltaTime; + + } else { + + return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; + + } + + } + + function getZoomScale() { + + return Math.pow( 0.95, scope.zoomSpeed ); + + } + + function rotateLeft( angle ) { + + sphericalDelta.theta -= angle; + + } + + function rotateUp( angle ) { + + sphericalDelta.phi -= angle; + + } + + const panLeft = function () { + + const v = new Vector3(); + + return function panLeft( distance, objectMatrix ) { + + v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix + v.multiplyScalar( - distance ); + + panOffset.add( v ); + + }; + + }(); + + const panUp = function () { + + const v = new Vector3(); + + return function panUp( distance, objectMatrix ) { + + if ( scope.screenSpacePanning === true ) { + + v.setFromMatrixColumn( objectMatrix, 1 ); + + } else { + + v.setFromMatrixColumn( objectMatrix, 0 ); + v.crossVectors( scope.object.up, v ); + + } + + v.multiplyScalar( distance ); + + panOffset.add( v ); + + }; + + }(); + + // deltaX and deltaY are in pixels; right and down are positive + const pan = function () { + + const offset = new Vector3(); + + return function pan( deltaX, deltaY ) { + + const element = scope.domElement; + + if ( scope.object.isPerspectiveCamera ) { + + // perspective + const position = scope.object.position; + offset.copy( position ).sub( scope.target ); + let targetDistance = offset.length(); + + // half of the fov is center to top of screen + targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); + + // we use only clientHeight here so aspect ratio does not distort speed + panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix ); + panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix ); + + } else if ( scope.object.isOrthographicCamera ) { + + // orthographic + panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix ); + panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix ); + + } else { + + // camera neither orthographic nor perspective + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); + scope.enablePan = false; + + } + + }; + + }(); + + function dollyOut( dollyScale ) { + + if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) { + + scale /= dollyScale; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function dollyIn( dollyScale ) { + + if ( scope.object.isPerspectiveCamera || scope.object.isOrthographicCamera ) { + + scale *= dollyScale; + + } else { + + console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); + scope.enableZoom = false; + + } + + } + + function updateMouseParameters( event ) { + + if ( ! scope.zoomToCursor ) { + + return; + + } + + performCursorZoom = true; + + const rect = scope.domElement.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + const w = rect.width; + const h = rect.height; + + mouse.x = ( x / w ) * 2 - 1; + mouse.y = - ( y / h ) * 2 + 1; + + dollyDirection.set( mouse.x, mouse.y, 1 ).unproject( scope.object ).sub( scope.object.position ).normalize(); + + } + + function clampDistance( dist ) { + + return Math.max( scope.minDistance, Math.min( scope.maxDistance, dist ) ); + + } + + // + // event callbacks - update the object state + // + + function handleMouseDownRotate( event ) { + + rotateStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownDolly( event ) { + + updateMouseParameters( event ); + dollyStart.set( event.clientX, event.clientY ); + + } + + function handleMouseDownPan( event ) { + + panStart.set( event.clientX, event.clientY ); + + } + + function handleMouseMoveRotate( event ) { + + rotateEnd.set( event.clientX, event.clientY ); + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + scope.update(); + + } + + function handleMouseMoveDolly( event ) { + + dollyEnd.set( event.clientX, event.clientY ); + + dollyDelta.subVectors( dollyEnd, dollyStart ); + + if ( dollyDelta.y > 0 ) { + + dollyOut( getZoomScale() ); + + } else if ( dollyDelta.y < 0 ) { + + dollyIn( getZoomScale() ); + + } + + dollyStart.copy( dollyEnd ); + + scope.update(); + + } + + function handleMouseMovePan( event ) { + + panEnd.set( event.clientX, event.clientY ); + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + scope.update(); + + } + + function handleMouseWheel( event ) { + + updateMouseParameters( event ); + + if ( event.deltaY < 0 ) { + + dollyIn( getZoomScale() ); + + } else if ( event.deltaY > 0 ) { + + dollyOut( getZoomScale() ); + + } + + scope.update(); + + } + + function handleKeyDown( event ) { + + let needsUpdate = false; + + switch ( event.code ) { + + case scope.keys.UP: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.BOTTOM: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateUp( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( 0, - scope.keyPanSpeed ); + + } + + needsUpdate = true; + break; + + case scope.keys.LEFT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + case scope.keys.RIGHT: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + rotateLeft( - 2 * Math.PI * scope.rotateSpeed / scope.domElement.clientHeight ); + + } else { + + pan( - scope.keyPanSpeed, 0 ); + + } + + needsUpdate = true; + break; + + } + + if ( needsUpdate ) { + + // prevent the browser from scrolling on cursor keys + event.preventDefault(); + + scope.update(); + + } + + + } + + function handleTouchStartRotate() { + + if ( pointers.length === 1 ) { + + rotateStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + rotateStart.set( x, y ); + + } + + } + + function handleTouchStartPan() { + + if ( pointers.length === 1 ) { + + panStart.set( pointers[ 0 ].pageX, pointers[ 0 ].pageY ); + + } else { + + const x = 0.5 * ( pointers[ 0 ].pageX + pointers[ 1 ].pageX ); + const y = 0.5 * ( pointers[ 0 ].pageY + pointers[ 1 ].pageY ); + + panStart.set( x, y ); + + } + + } + + function handleTouchStartDolly() { + + const dx = pointers[ 0 ].pageX - pointers[ 1 ].pageX; + const dy = pointers[ 0 ].pageY - pointers[ 1 ].pageY; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyStart.set( 0, distance ); + + } + + function handleTouchStartDollyPan() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enablePan ) handleTouchStartPan(); + + } + + function handleTouchStartDollyRotate() { + + if ( scope.enableZoom ) handleTouchStartDolly(); + + if ( scope.enableRotate ) handleTouchStartRotate(); + + } + + function handleTouchMoveRotate( event ) { + + if ( pointers.length == 1 ) { + + rotateEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + rotateEnd.set( x, y ); + + } + + rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed ); + + const element = scope.domElement; + + rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height + + rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight ); + + rotateStart.copy( rotateEnd ); + + } + + function handleTouchMovePan( event ) { + + if ( pointers.length === 1 ) { + + panEnd.set( event.pageX, event.pageY ); + + } else { + + const position = getSecondPointerPosition( event ); + + const x = 0.5 * ( event.pageX + position.x ); + const y = 0.5 * ( event.pageY + position.y ); + + panEnd.set( x, y ); + + } + + panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed ); + + pan( panDelta.x, panDelta.y ); + + panStart.copy( panEnd ); + + } + + function handleTouchMoveDolly( event ) { + + const position = getSecondPointerPosition( event ); + + const dx = event.pageX - position.x; + const dy = event.pageY - position.y; + + const distance = Math.sqrt( dx * dx + dy * dy ); + + dollyEnd.set( 0, distance ); + + dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) ); + + dollyOut( dollyDelta.y ); + + dollyStart.copy( dollyEnd ); + + } + + function handleTouchMoveDollyPan( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enablePan ) handleTouchMovePan( event ); + + } + + function handleTouchMoveDollyRotate( event ) { + + if ( scope.enableZoom ) handleTouchMoveDolly( event ); + + if ( scope.enableRotate ) handleTouchMoveRotate( event ); + + } + + // + // event handlers - FSM: listen for events and reset state + // + + function onPointerDown( event ) { + + if ( scope.enabled === false ) return; + + if ( pointers.length === 0 ) { + + scope.domElement.setPointerCapture( event.pointerId ); + + scope.domElement.addEventListener( 'pointermove', onPointerMove ); + scope.domElement.addEventListener( 'pointerup', onPointerUp ); + + } + + // + + addPointer( event ); + + if ( event.pointerType === 'touch' ) { + + onTouchStart( event ); + + } else { + + onMouseDown( event ); + + } + + } + + function onPointerMove( event ) { + + if ( scope.enabled === false ) return; + + if ( event.pointerType === 'touch' ) { + + onTouchMove( event ); + + } else { + + onMouseMove( event ); + + } + + } + + function onPointerUp( event ) { + + removePointer( event ); + + if ( pointers.length === 0 ) { + + scope.domElement.releasePointerCapture( event.pointerId ); + + scope.domElement.removeEventListener( 'pointermove', onPointerMove ); + scope.domElement.removeEventListener( 'pointerup', onPointerUp ); + + } + + scope.dispatchEvent( _endEvent ); + + state = STATE.NONE; + + } + + function onMouseDown( event ) { + + let mouseAction; + + switch ( event.button ) { + + case 0: + + mouseAction = scope.mouseButtons.LEFT; + break; + + case 1: + + mouseAction = scope.mouseButtons.MIDDLE; + break; + + case 2: + + mouseAction = scope.mouseButtons.RIGHT; + break; + + default: + + mouseAction = - 1; + + } + + switch ( mouseAction ) { + + case MOUSE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseDownDolly( event ); + + state = STATE.DOLLY; + + break; + + case MOUSE.ROTATE: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } else { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } + + break; + + case MOUSE.PAN: + + if ( event.ctrlKey || event.metaKey || event.shiftKey ) { + + if ( scope.enableRotate === false ) return; + + handleMouseDownRotate( event ); + + state = STATE.ROTATE; + + } else { + + if ( scope.enablePan === false ) return; + + handleMouseDownPan( event ); + + state = STATE.PAN; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onMouseMove( event ) { + + switch ( state ) { + + case STATE.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleMouseMoveRotate( event ); + + break; + + case STATE.DOLLY: + + if ( scope.enableZoom === false ) return; + + handleMouseMoveDolly( event ); + + break; + + case STATE.PAN: + + if ( scope.enablePan === false ) return; + + handleMouseMovePan( event ); + + break; + + } + + } + + function onMouseWheel( event ) { + + if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return; + + event.preventDefault(); + + scope.dispatchEvent( _startEvent ); + + handleMouseWheel( event ); + + scope.dispatchEvent( _endEvent ); + + } + + function onKeyDown( event ) { + + if ( scope.enabled === false || scope.enablePan === false ) return; + + handleKeyDown( event ); + + } + + function onTouchStart( event ) { + + trackPointer( event ); + + switch ( pointers.length ) { + + case 1: + + switch ( scope.touches.ONE ) { + + case TOUCH.ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchStartRotate(); + + state = STATE.TOUCH_ROTATE; + + break; + + case TOUCH.PAN: + + if ( scope.enablePan === false ) return; + + handleTouchStartPan(); + + state = STATE.TOUCH_PAN; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + case 2: + + switch ( scope.touches.TWO ) { + + case TOUCH.DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchStartDollyPan(); + + state = STATE.TOUCH_DOLLY_PAN; + + break; + + case TOUCH.DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchStartDollyRotate(); + + state = STATE.TOUCH_DOLLY_ROTATE; + + break; + + default: + + state = STATE.NONE; + + } + + break; + + default: + + state = STATE.NONE; + + } + + if ( state !== STATE.NONE ) { + + scope.dispatchEvent( _startEvent ); + + } + + } + + function onTouchMove( event ) { + + trackPointer( event ); + + switch ( state ) { + + case STATE.TOUCH_ROTATE: + + if ( scope.enableRotate === false ) return; + + handleTouchMoveRotate( event ); + + scope.update(); + + break; + + case STATE.TOUCH_PAN: + + if ( scope.enablePan === false ) return; + + handleTouchMovePan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_PAN: + + if ( scope.enableZoom === false && scope.enablePan === false ) return; + + handleTouchMoveDollyPan( event ); + + scope.update(); + + break; + + case STATE.TOUCH_DOLLY_ROTATE: + + if ( scope.enableZoom === false && scope.enableRotate === false ) return; + + handleTouchMoveDollyRotate( event ); + + scope.update(); + + break; + + default: + + state = STATE.NONE; + + } + + } + + function onContextMenu( event ) { + + if ( scope.enabled === false ) return; + + event.preventDefault(); + + } + + function addPointer( event ) { + + pointers.push( event ); + + } + + function removePointer( event ) { + + delete pointerPositions[ event.pointerId ]; + + for ( let i = 0; i < pointers.length; i ++ ) { + + if ( pointers[ i ].pointerId == event.pointerId ) { + + pointers.splice( i, 1 ); + return; + + } + + } + + } + + function trackPointer( event ) { + + let position = pointerPositions[ event.pointerId ]; + + if ( position === undefined ) { + + position = new Vector2(); + pointerPositions[ event.pointerId ] = position; + + } + + position.set( event.pageX, event.pageY ); + + } + + function getSecondPointerPosition( event ) { + + const pointer = ( event.pointerId === pointers[ 0 ].pointerId ) ? pointers[ 1 ] : pointers[ 0 ]; + + return pointerPositions[ pointer.pointerId ]; + + } + + // + + scope.domElement.addEventListener( 'contextmenu', onContextMenu ); + + scope.domElement.addEventListener( 'pointerdown', onPointerDown ); + scope.domElement.addEventListener( 'pointercancel', onPointerUp ); + scope.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } ); + + // force an update at start + + this.update(); + + } + +} + +export { OrbitControls }; \ No newline at end of file diff --git a/lib/three.js b/lib/three.js index 00aeed2..89ca5e9 100644 --- a/lib/three.js +++ b/lib/three.js @@ -1,6639 +1,6725 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.THREE = {})); -}(this, function (exports) { 'use strict'; +/** + * @license + * Copyright 2010-2023 Three.js Authors + * SPDX-License-Identifier: MIT + */ +const REVISION = '157'; + +const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; +const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; +const CullFaceNone = 0; +const CullFaceBack = 1; +const CullFaceFront = 2; +const CullFaceFrontBack = 3; +const BasicShadowMap = 0; +const PCFShadowMap = 1; +const PCFSoftShadowMap = 2; +const VSMShadowMap = 3; +const FrontSide = 0; +const BackSide = 1; +const DoubleSide = 2; +const TwoPassDoubleSide = 2; // r149 +const NoBlending = 0; +const NormalBlending = 1; +const AdditiveBlending = 2; +const SubtractiveBlending = 3; +const MultiplyBlending = 4; +const CustomBlending = 5; +const AddEquation = 100; +const SubtractEquation = 101; +const ReverseSubtractEquation = 102; +const MinEquation = 103; +const MaxEquation = 104; +const ZeroFactor = 200; +const OneFactor = 201; +const SrcColorFactor = 202; +const OneMinusSrcColorFactor = 203; +const SrcAlphaFactor = 204; +const OneMinusSrcAlphaFactor = 205; +const DstAlphaFactor = 206; +const OneMinusDstAlphaFactor = 207; +const DstColorFactor = 208; +const OneMinusDstColorFactor = 209; +const SrcAlphaSaturateFactor = 210; +const NeverDepth = 0; +const AlwaysDepth = 1; +const LessDepth = 2; +const LessEqualDepth = 3; +const EqualDepth = 4; +const GreaterEqualDepth = 5; +const GreaterDepth = 6; +const NotEqualDepth = 7; +const MultiplyOperation = 0; +const MixOperation = 1; +const AddOperation = 2; +const NoToneMapping = 0; +const LinearToneMapping = 1; +const ReinhardToneMapping = 2; +const CineonToneMapping = 3; +const ACESFilmicToneMapping = 4; +const CustomToneMapping = 5; + +const UVMapping = 300; +const CubeReflectionMapping = 301; +const CubeRefractionMapping = 302; +const EquirectangularReflectionMapping = 303; +const EquirectangularRefractionMapping = 304; +const CubeUVReflectionMapping = 306; +const RepeatWrapping = 1000; +const ClampToEdgeWrapping = 1001; +const MirroredRepeatWrapping = 1002; +const NearestFilter = 1003; +const NearestMipmapNearestFilter = 1004; +const NearestMipMapNearestFilter = 1004; +const NearestMipmapLinearFilter = 1005; +const NearestMipMapLinearFilter = 1005; +const LinearFilter = 1006; +const LinearMipmapNearestFilter = 1007; +const LinearMipMapNearestFilter = 1007; +const LinearMipmapLinearFilter = 1008; +const LinearMipMapLinearFilter = 1008; +const UnsignedByteType = 1009; +const ByteType = 1010; +const ShortType = 1011; +const UnsignedShortType = 1012; +const IntType = 1013; +const UnsignedIntType = 1014; +const FloatType = 1015; +const HalfFloatType = 1016; +const UnsignedShort4444Type = 1017; +const UnsignedShort5551Type = 1018; +const UnsignedInt248Type = 1020; +const AlphaFormat = 1021; +const RGBAFormat = 1023; +const LuminanceFormat = 1024; +const LuminanceAlphaFormat = 1025; +const DepthFormat = 1026; +const DepthStencilFormat = 1027; +const RedFormat = 1028; +const RedIntegerFormat = 1029; +const RGFormat = 1030; +const RGIntegerFormat = 1031; +const RGBAIntegerFormat = 1033; + +const RGB_S3TC_DXT1_Format = 33776; +const RGBA_S3TC_DXT1_Format = 33777; +const RGBA_S3TC_DXT3_Format = 33778; +const RGBA_S3TC_DXT5_Format = 33779; +const RGB_PVRTC_4BPPV1_Format = 35840; +const RGB_PVRTC_2BPPV1_Format = 35841; +const RGBA_PVRTC_4BPPV1_Format = 35842; +const RGBA_PVRTC_2BPPV1_Format = 35843; +const RGB_ETC1_Format = 36196; +const RGB_ETC2_Format = 37492; +const RGBA_ETC2_EAC_Format = 37496; +const RGBA_ASTC_4x4_Format = 37808; +const RGBA_ASTC_5x4_Format = 37809; +const RGBA_ASTC_5x5_Format = 37810; +const RGBA_ASTC_6x5_Format = 37811; +const RGBA_ASTC_6x6_Format = 37812; +const RGBA_ASTC_8x5_Format = 37813; +const RGBA_ASTC_8x6_Format = 37814; +const RGBA_ASTC_8x8_Format = 37815; +const RGBA_ASTC_10x5_Format = 37816; +const RGBA_ASTC_10x6_Format = 37817; +const RGBA_ASTC_10x8_Format = 37818; +const RGBA_ASTC_10x10_Format = 37819; +const RGBA_ASTC_12x10_Format = 37820; +const RGBA_ASTC_12x12_Format = 37821; +const RGBA_BPTC_Format = 36492; +const RGB_BPTC_SIGNED_Format = 36494; +const RGB_BPTC_UNSIGNED_Format = 36495; +const RED_RGTC1_Format = 36283; +const SIGNED_RED_RGTC1_Format = 36284; +const RED_GREEN_RGTC2_Format = 36285; +const SIGNED_RED_GREEN_RGTC2_Format = 36286; +const LoopOnce = 2200; +const LoopRepeat = 2201; +const LoopPingPong = 2202; +const InterpolateDiscrete = 2300; +const InterpolateLinear = 2301; +const InterpolateSmooth = 2302; +const ZeroCurvatureEnding = 2400; +const ZeroSlopeEnding = 2401; +const WrapAroundEnding = 2402; +const NormalAnimationBlendMode = 2500; +const AdditiveAnimationBlendMode = 2501; +const TrianglesDrawMode = 0; +const TriangleStripDrawMode = 1; +const TriangleFanDrawMode = 2; +/** @deprecated Use LinearSRGBColorSpace or NoColorSpace in three.js r152+. */ +const LinearEncoding = 3000; +/** @deprecated Use SRGBColorSpace in three.js r152+. */ +const sRGBEncoding = 3001; +const BasicDepthPacking = 3200; +const RGBADepthPacking = 3201; +const TangentSpaceNormalMap = 0; +const ObjectSpaceNormalMap = 1; + +// Color space string identifiers, matching CSS Color Module Level 4 and WebGPU names where available. +const NoColorSpace = ''; +const SRGBColorSpace = 'srgb'; +const LinearSRGBColorSpace = 'srgb-linear'; +const DisplayP3ColorSpace = 'display-p3'; +const LinearDisplayP3ColorSpace = 'display-p3-linear'; + +const LinearTransfer = 'linear'; +const SRGBTransfer = 'srgb'; + +const Rec709Primaries = 'rec709'; +const P3Primaries = 'p3'; + +const ZeroStencilOp = 0; +const KeepStencilOp = 7680; +const ReplaceStencilOp = 7681; +const IncrementStencilOp = 7682; +const DecrementStencilOp = 7683; +const IncrementWrapStencilOp = 34055; +const DecrementWrapStencilOp = 34056; +const InvertStencilOp = 5386; + +const NeverStencilFunc = 512; +const LessStencilFunc = 513; +const EqualStencilFunc = 514; +const LessEqualStencilFunc = 515; +const GreaterStencilFunc = 516; +const NotEqualStencilFunc = 517; +const GreaterEqualStencilFunc = 518; +const AlwaysStencilFunc = 519; + +const NeverCompare = 512; +const LessCompare = 513; +const EqualCompare = 514; +const LessEqualCompare = 515; +const GreaterCompare = 516; +const NotEqualCompare = 517; +const GreaterEqualCompare = 518; +const AlwaysCompare = 519; + +const StaticDrawUsage = 35044; +const DynamicDrawUsage = 35048; +const StreamDrawUsage = 35040; +const StaticReadUsage = 35045; +const DynamicReadUsage = 35049; +const StreamReadUsage = 35041; +const StaticCopyUsage = 35046; +const DynamicCopyUsage = 35050; +const StreamCopyUsage = 35042; + +const GLSL1 = '100'; +const GLSL3 = '300 es'; + +const _SRGBAFormat = 1035; // fallback for WebGL 1 - // Polyfills +const WebGLCoordinateSystem = 2000; +const WebGPUCoordinateSystem = 2001; - if ( Number.EPSILON === undefined ) { +/** + * https://github.com/mrdoob/eventdispatcher.js/ + */ - Number.EPSILON = Math.pow( 2, - 52 ); +class EventDispatcher { - } - - if ( Number.isInteger === undefined ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger - - Number.isInteger = function ( value ) { + addEventListener( type, listener ) { - return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value; + if ( this._listeners === undefined ) this._listeners = {}; - }; - - } + const listeners = this._listeners; - // + if ( listeners[ type ] === undefined ) { - if ( Math.sign === undefined ) { + listeners[ type ] = []; - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign + } - Math.sign = function ( x ) { + if ( listeners[ type ].indexOf( listener ) === - 1 ) { - return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; + listeners[ type ].push( listener ); - }; + } } - if ( 'name' in Function.prototype === false ) { - - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name + hasEventListener( type, listener ) { - Object.defineProperty( Function.prototype, 'name', { + if ( this._listeners === undefined ) return false; - get: function () { + const listeners = this._listeners; - return this.toString().match( /^\s*function\s*([^\(\s]*)/ )[ 1 ]; + return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; - } + } - } ); + removeEventListener( type, listener ) { - } + if ( this._listeners === undefined ) return; - if ( Object.assign === undefined ) { + const listeners = this._listeners; + const listenerArray = listeners[ type ]; - // Missing in IE - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + if ( listenerArray !== undefined ) { - Object.assign = function ( target ) { + const index = listenerArray.indexOf( listener ); - if ( target === undefined || target === null ) { + if ( index !== - 1 ) { - throw new TypeError( 'Cannot convert undefined or null to object' ); + listenerArray.splice( index, 1 ); } - var output = Object( target ); + } - for ( var index = 1; index < arguments.length; index ++ ) { + } - var source = arguments[ index ]; + dispatchEvent( event ) { - if ( source !== undefined && source !== null ) { + if ( this._listeners === undefined ) return; - for ( var nextKey in source ) { + const listeners = this._listeners; + const listenerArray = listeners[ event.type ]; - if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) { + if ( listenerArray !== undefined ) { - output[ nextKey ] = source[ nextKey ]; + event.target = this; - } + // Make a copy, in case listeners are removed while iterating. + const array = listenerArray.slice( 0 ); - } + for ( let i = 0, l = array.length; i < l; i ++ ) { - } + array[ i ].call( this, event ); } - return output; + event.target = null; - }; + } } - /** - * https://github.com/mrdoob/eventdispatcher.js/ - */ +} - function EventDispatcher() {} +const _lut = [ '00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c', '0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d', '4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a', '5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e', '8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b', '9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc', 'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9', 'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff' ]; - Object.assign( EventDispatcher.prototype, { +let _seed = 1234567; - addEventListener: function ( type, listener ) { - if ( this._listeners === undefined ) this._listeners = {}; +const DEG2RAD = Math.PI / 180; +const RAD2DEG = 180 / Math.PI; - var listeners = this._listeners; +// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 +function generateUUID() { - if ( listeners[ type ] === undefined ) { + const d0 = Math.random() * 0xffffffff | 0; + const d1 = Math.random() * 0xffffffff | 0; + const d2 = Math.random() * 0xffffffff | 0; + const d3 = Math.random() * 0xffffffff | 0; + const uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + + _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + + _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + + _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; - listeners[ type ] = []; + // .toLowerCase() here flattens concatenated strings to save heap memory space. + return uuid.toLowerCase(); - } +} - if ( listeners[ type ].indexOf( listener ) === - 1 ) { +function clamp( value, min, max ) { - listeners[ type ].push( listener ); + return Math.max( min, Math.min( max, value ) ); - } +} - }, +// compute euclidean modulo of m % n +// https://en.wikipedia.org/wiki/Modulo_operation +function euclideanModulo( n, m ) { - hasEventListener: function ( type, listener ) { + return ( ( n % m ) + m ) % m; - if ( this._listeners === undefined ) return false; +} - var listeners = this._listeners; +// Linear mapping from range to range +function mapLinear( x, a1, a2, b1, b2 ) { - return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1; + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); - }, +} + +// https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/inverse-lerp-a-super-useful-yet-often-overlooked-function-r5230/ +function inverseLerp( x, y, value ) { - removeEventListener: function ( type, listener ) { + if ( x !== y ) { - if ( this._listeners === undefined ) return; + return ( value - x ) / ( y - x ); - var listeners = this._listeners; - var listenerArray = listeners[ type ]; + } else { - if ( listenerArray !== undefined ) { + return 0; - var index = listenerArray.indexOf( listener ); + } - if ( index !== - 1 ) { +} - listenerArray.splice( index, 1 ); +// https://en.wikipedia.org/wiki/Linear_interpolation +function lerp( x, y, t ) { - } + return ( 1 - t ) * x + t * y; - } +} - }, +// http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ +function damp( x, y, lambda, dt ) { - dispatchEvent: function ( event ) { + return lerp( x, y, 1 - Math.exp( - lambda * dt ) ); - if ( this._listeners === undefined ) return; +} - var listeners = this._listeners; - var listenerArray = listeners[ event.type ]; +// https://www.desmos.com/calculator/vcsjnyz7x4 +function pingpong( x, length = 1 ) { - if ( listenerArray !== undefined ) { + return length - Math.abs( euclideanModulo( x, length * 2 ) - length ); - event.target = this; +} - var array = listenerArray.slice( 0 ); +// http://en.wikipedia.org/wiki/Smoothstep +function smoothstep( x, min, max ) { - for ( var i = 0, l = array.length; i < l; i ++ ) { + if ( x <= min ) return 0; + if ( x >= max ) return 1; - array[ i ].call( this, event ); + x = ( x - min ) / ( max - min ); - } + return x * x * ( 3 - 2 * x ); - } +} - } +function smootherstep( x, min, max ) { - } ); + if ( x <= min ) return 0; + if ( x >= max ) return 1; - var REVISION = '108'; - var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; - var TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; - var CullFaceNone = 0; - var CullFaceBack = 1; - var CullFaceFront = 2; - var CullFaceFrontBack = 3; - var FrontFaceDirectionCW = 0; - var FrontFaceDirectionCCW = 1; - var BasicShadowMap = 0; - var PCFShadowMap = 1; - var PCFSoftShadowMap = 2; - var VSMShadowMap = 3; - var FrontSide = 0; - var BackSide = 1; - var DoubleSide = 2; - var FlatShading = 1; - var SmoothShading = 2; - var NoColors = 0; - var FaceColors = 1; - var VertexColors = 2; - var NoBlending = 0; - var NormalBlending = 1; - var AdditiveBlending = 2; - var SubtractiveBlending = 3; - var MultiplyBlending = 4; - var CustomBlending = 5; - var AddEquation = 100; - var SubtractEquation = 101; - var ReverseSubtractEquation = 102; - var MinEquation = 103; - var MaxEquation = 104; - var ZeroFactor = 200; - var OneFactor = 201; - var SrcColorFactor = 202; - var OneMinusSrcColorFactor = 203; - var SrcAlphaFactor = 204; - var OneMinusSrcAlphaFactor = 205; - var DstAlphaFactor = 206; - var OneMinusDstAlphaFactor = 207; - var DstColorFactor = 208; - var OneMinusDstColorFactor = 209; - var SrcAlphaSaturateFactor = 210; - var NeverDepth = 0; - var AlwaysDepth = 1; - var LessDepth = 2; - var LessEqualDepth = 3; - var EqualDepth = 4; - var GreaterEqualDepth = 5; - var GreaterDepth = 6; - var NotEqualDepth = 7; - var MultiplyOperation = 0; - var MixOperation = 1; - var AddOperation = 2; - var NoToneMapping = 0; - var LinearToneMapping = 1; - var ReinhardToneMapping = 2; - var Uncharted2ToneMapping = 3; - var CineonToneMapping = 4; - var ACESFilmicToneMapping = 5; - - var UVMapping = 300; - var CubeReflectionMapping = 301; - var CubeRefractionMapping = 302; - var EquirectangularReflectionMapping = 303; - var EquirectangularRefractionMapping = 304; - var SphericalReflectionMapping = 305; - var CubeUVReflectionMapping = 306; - var CubeUVRefractionMapping = 307; - var RepeatWrapping = 1000; - var ClampToEdgeWrapping = 1001; - var MirroredRepeatWrapping = 1002; - var NearestFilter = 1003; - var NearestMipmapNearestFilter = 1004; - var NearestMipMapNearestFilter = 1004; - var NearestMipmapLinearFilter = 1005; - var NearestMipMapLinearFilter = 1005; - var LinearFilter = 1006; - var LinearMipmapNearestFilter = 1007; - var LinearMipMapNearestFilter = 1007; - var LinearMipmapLinearFilter = 1008; - var LinearMipMapLinearFilter = 1008; - var UnsignedByteType = 1009; - var ByteType = 1010; - var ShortType = 1011; - var UnsignedShortType = 1012; - var IntType = 1013; - var UnsignedIntType = 1014; - var FloatType = 1015; - var HalfFloatType = 1016; - var UnsignedShort4444Type = 1017; - var UnsignedShort5551Type = 1018; - var UnsignedShort565Type = 1019; - var UnsignedInt248Type = 1020; - var AlphaFormat = 1021; - var RGBFormat = 1022; - var RGBAFormat = 1023; - var LuminanceFormat = 1024; - var LuminanceAlphaFormat = 1025; - var RGBEFormat = RGBAFormat; - var DepthFormat = 1026; - var DepthStencilFormat = 1027; - var RedFormat = 1028; - var RGB_S3TC_DXT1_Format = 33776; - var RGBA_S3TC_DXT1_Format = 33777; - var RGBA_S3TC_DXT3_Format = 33778; - var RGBA_S3TC_DXT5_Format = 33779; - var RGB_PVRTC_4BPPV1_Format = 35840; - var RGB_PVRTC_2BPPV1_Format = 35841; - var RGBA_PVRTC_4BPPV1_Format = 35842; - var RGBA_PVRTC_2BPPV1_Format = 35843; - var RGB_ETC1_Format = 36196; - var RGBA_ASTC_4x4_Format = 37808; - var RGBA_ASTC_5x4_Format = 37809; - var RGBA_ASTC_5x5_Format = 37810; - var RGBA_ASTC_6x5_Format = 37811; - var RGBA_ASTC_6x6_Format = 37812; - var RGBA_ASTC_8x5_Format = 37813; - var RGBA_ASTC_8x6_Format = 37814; - var RGBA_ASTC_8x8_Format = 37815; - var RGBA_ASTC_10x5_Format = 37816; - var RGBA_ASTC_10x6_Format = 37817; - var RGBA_ASTC_10x8_Format = 37818; - var RGBA_ASTC_10x10_Format = 37819; - var RGBA_ASTC_12x10_Format = 37820; - var RGBA_ASTC_12x12_Format = 37821; - var LoopOnce = 2200; - var LoopRepeat = 2201; - var LoopPingPong = 2202; - var InterpolateDiscrete = 2300; - var InterpolateLinear = 2301; - var InterpolateSmooth = 2302; - var ZeroCurvatureEnding = 2400; - var ZeroSlopeEnding = 2401; - var WrapAroundEnding = 2402; - var TrianglesDrawMode = 0; - var TriangleStripDrawMode = 1; - var TriangleFanDrawMode = 2; - var LinearEncoding = 3000; - var sRGBEncoding = 3001; - var GammaEncoding = 3007; - var RGBEEncoding = 3002; - var LogLuvEncoding = 3003; - var RGBM7Encoding = 3004; - var RGBM16Encoding = 3005; - var RGBDEncoding = 3006; - var BasicDepthPacking = 3200; - var RGBADepthPacking = 3201; - var TangentSpaceNormalMap = 0; - var ObjectSpaceNormalMap = 1; - - var ZeroStencilOp = 0; - var KeepStencilOp = 7680; - var ReplaceStencilOp = 7681; - var IncrementStencilOp = 7682; - var DecrementStencilOp = 7683; - var IncrementWrapStencilOp = 34055; - var DecrementWrapStencilOp = 34056; - var InvertStencilOp = 5386; - - var NeverStencilFunc = 512; - var LessStencilFunc = 513; - var EqualStencilFunc = 514; - var LessEqualStencilFunc = 515; - var GreaterStencilFunc = 516; - var NotEqualStencilFunc = 517; - var GreaterEqualStencilFunc = 518; - var AlwaysStencilFunc = 519; + x = ( x - min ) / ( max - min ); - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); - var _lut = []; +} - for ( var i = 0; i < 256; i ++ ) { +// Random integer from interval +function randInt( low, high ) { - _lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ); + return low + Math.floor( Math.random() * ( high - low + 1 ) ); - } +} - var _Math = { +// Random float from interval +function randFloat( low, high ) { - DEG2RAD: Math.PI / 180, - RAD2DEG: 180 / Math.PI, + return low + Math.random() * ( high - low ); - generateUUID: function () { +} - // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136 +// Random float from <-range/2, range/2> interval +function randFloatSpread( range ) { - var d0 = Math.random() * 0xffffffff | 0; - var d1 = Math.random() * 0xffffffff | 0; - var d2 = Math.random() * 0xffffffff | 0; - var d3 = Math.random() * 0xffffffff | 0; - var uuid = _lut[ d0 & 0xff ] + _lut[ d0 >> 8 & 0xff ] + _lut[ d0 >> 16 & 0xff ] + _lut[ d0 >> 24 & 0xff ] + '-' + - _lut[ d1 & 0xff ] + _lut[ d1 >> 8 & 0xff ] + '-' + _lut[ d1 >> 16 & 0x0f | 0x40 ] + _lut[ d1 >> 24 & 0xff ] + '-' + - _lut[ d2 & 0x3f | 0x80 ] + _lut[ d2 >> 8 & 0xff ] + '-' + _lut[ d2 >> 16 & 0xff ] + _lut[ d2 >> 24 & 0xff ] + - _lut[ d3 & 0xff ] + _lut[ d3 >> 8 & 0xff ] + _lut[ d3 >> 16 & 0xff ] + _lut[ d3 >> 24 & 0xff ]; + return range * ( 0.5 - Math.random() ); - // .toUpperCase() here flattens concatenated strings to save heap memory space. - return uuid.toUpperCase(); +} - }, +// Deterministic pseudo-random float in the interval [ 0, 1 ] +function seededRandom( s ) { - clamp: function ( value, min, max ) { + if ( s !== undefined ) _seed = s; - return Math.max( min, Math.min( max, value ) ); + // Mulberry32 generator - }, + let t = _seed += 0x6D2B79F5; - // compute euclidian modulo of m % n - // https://en.wikipedia.org/wiki/Modulo_operation + t = Math.imul( t ^ t >>> 15, t | 1 ); - euclideanModulo: function ( n, m ) { + t ^= t + Math.imul( t ^ t >>> 7, t | 61 ); - return ( ( n % m ) + m ) % m; + return ( ( t ^ t >>> 14 ) >>> 0 ) / 4294967296; - }, +} - // Linear mapping from range to range +function degToRad( degrees ) { - mapLinear: function ( x, a1, a2, b1, b2 ) { + return degrees * DEG2RAD; - return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); +} - }, +function radToDeg( radians ) { - // https://en.wikipedia.org/wiki/Linear_interpolation + return radians * RAD2DEG; - lerp: function ( x, y, t ) { +} - return ( 1 - t ) * x + t * y; +function isPowerOfTwo( value ) { - }, + return ( value & ( value - 1 ) ) === 0 && value !== 0; - // http://en.wikipedia.org/wiki/Smoothstep +} - smoothstep: function ( x, min, max ) { +function ceilPowerOfTwo( value ) { - if ( x <= min ) return 0; - if ( x >= max ) return 1; + return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); - x = ( x - min ) / ( max - min ); +} - return x * x * ( 3 - 2 * x ); +function floorPowerOfTwo( value ) { - }, + return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); - smootherstep: function ( x, min, max ) { +} - if ( x <= min ) return 0; - if ( x >= max ) return 1; +function setQuaternionFromProperEuler( q, a, b, c, order ) { - x = ( x - min ) / ( max - min ); + // Intrinsic Proper Euler Angles - see https://en.wikipedia.org/wiki/Euler_angles - return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); + // rotations are applied to the axes in the order specified by 'order' + // rotation by angle 'a' is applied first, then by angle 'b', then by angle 'c' + // angles are in radians - }, + const cos = Math.cos; + const sin = Math.sin; - // Random integer from interval + const c2 = cos( b / 2 ); + const s2 = sin( b / 2 ); - randInt: function ( low, high ) { + const c13 = cos( ( a + c ) / 2 ); + const s13 = sin( ( a + c ) / 2 ); - return low + Math.floor( Math.random() * ( high - low + 1 ) ); + const c1_3 = cos( ( a - c ) / 2 ); + const s1_3 = sin( ( a - c ) / 2 ); - }, + const c3_1 = cos( ( c - a ) / 2 ); + const s3_1 = sin( ( c - a ) / 2 ); - // Random float from interval + switch ( order ) { - randFloat: function ( low, high ) { + case 'XYX': + q.set( c2 * s13, s2 * c1_3, s2 * s1_3, c2 * c13 ); + break; - return low + Math.random() * ( high - low ); + case 'YZY': + q.set( s2 * s1_3, c2 * s13, s2 * c1_3, c2 * c13 ); + break; - }, + case 'ZXZ': + q.set( s2 * c1_3, s2 * s1_3, c2 * s13, c2 * c13 ); + break; - // Random float from <-range/2, range/2> interval + case 'XZX': + q.set( c2 * s13, s2 * s3_1, s2 * c3_1, c2 * c13 ); + break; - randFloatSpread: function ( range ) { + case 'YXY': + q.set( s2 * c3_1, c2 * s13, s2 * s3_1, c2 * c13 ); + break; - return range * ( 0.5 - Math.random() ); + case 'ZYZ': + q.set( s2 * s3_1, s2 * c3_1, c2 * s13, c2 * c13 ); + break; - }, + default: + console.warn( 'THREE.MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: ' + order ); - degToRad: function ( degrees ) { + } - return degrees * _Math.DEG2RAD; +} - }, +function denormalize( value, array ) { - radToDeg: function ( radians ) { + switch ( array.constructor ) { - return radians * _Math.RAD2DEG; + case Float32Array: - }, + return value; - isPowerOfTwo: function ( value ) { + case Uint32Array: - return ( value & ( value - 1 ) ) === 0 && value !== 0; + return value / 4294967295.0; - }, + case Uint16Array: - ceilPowerOfTwo: function ( value ) { + return value / 65535.0; - return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) ); + case Uint8Array: - }, + return value / 255.0; - floorPowerOfTwo: function ( value ) { + case Int32Array: - return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) ); + return Math.max( value / 2147483647.0, - 1.0 ); - } + case Int16Array: - }; + return Math.max( value / 32767.0, - 1.0 ); - /** - * @author mrdoob / http://mrdoob.com/ - * @author philogb / http://blog.thejit.org/ - * @author egraether / http://egraether.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - */ + case Int8Array: - function Vector2( x, y ) { + return Math.max( value / 127.0, - 1.0 ); - this.x = x || 0; - this.y = y || 0; + default: + + throw new Error( 'Invalid component type.' ); } - Object.defineProperties( Vector2.prototype, { +} - "width": { +function normalize( value, array ) { - get: function () { + switch ( array.constructor ) { - return this.x; + case Float32Array: - }, + return value; - set: function ( value ) { + case Uint32Array: - this.x = value; + return Math.round( value * 4294967295.0 ); - } + case Uint16Array: - }, + return Math.round( value * 65535.0 ); - "height": { + case Uint8Array: - get: function () { + return Math.round( value * 255.0 ); - return this.y; + case Int32Array: - }, + return Math.round( value * 2147483647.0 ); - set: function ( value ) { + case Int16Array: - this.y = value; + return Math.round( value * 32767.0 ); - } + case Int8Array: - } + return Math.round( value * 127.0 ); - } ); + default: - Object.assign( Vector2.prototype, { + throw new Error( 'Invalid component type.' ); - isVector2: true, + } - set: function ( x, y ) { +} - this.x = x; - this.y = y; +const MathUtils = { + DEG2RAD: DEG2RAD, + RAD2DEG: RAD2DEG, + generateUUID: generateUUID, + clamp: clamp, + euclideanModulo: euclideanModulo, + mapLinear: mapLinear, + inverseLerp: inverseLerp, + lerp: lerp, + damp: damp, + pingpong: pingpong, + smoothstep: smoothstep, + smootherstep: smootherstep, + randInt: randInt, + randFloat: randFloat, + randFloatSpread: randFloatSpread, + seededRandom: seededRandom, + degToRad: degToRad, + radToDeg: radToDeg, + isPowerOfTwo: isPowerOfTwo, + ceilPowerOfTwo: ceilPowerOfTwo, + floorPowerOfTwo: floorPowerOfTwo, + setQuaternionFromProperEuler: setQuaternionFromProperEuler, + normalize: normalize, + denormalize: denormalize +}; - return this; +class Vector2 { - }, + constructor( x = 0, y = 0 ) { - setScalar: function ( scalar ) { + Vector2.prototype.isVector2 = true; - this.x = scalar; - this.y = scalar; + this.x = x; + this.y = y; - return this; + } - }, + get width() { - setX: function ( x ) { + return this.x; - this.x = x; + } - return this; + set width( value ) { - }, + this.x = value; - setY: function ( y ) { + } - this.y = y; + get height() { - return this; + return this.y; - }, + } - setComponent: function ( index, value ) { + set height( value ) { - switch ( index ) { + this.y = value; - case 0: this.x = value; break; - case 1: this.y = value; break; - default: throw new Error( 'index is out of range: ' + index ); + } - } + set( x, y ) { - return this; + this.x = x; + this.y = y; - }, + return this; - getComponent: function ( index ) { + } - switch ( index ) { + setScalar( scalar ) { - case 0: return this.x; - case 1: return this.y; - default: throw new Error( 'index is out of range: ' + index ); + this.x = scalar; + this.y = scalar; - } + return this; - }, + } - clone: function () { + setX( x ) { - return new this.constructor( this.x, this.y ); + this.x = x; - }, + return this; - copy: function ( v ) { + } - this.x = v.x; - this.y = v.y; + setY( y ) { - return this; + this.y = y; - }, + return this; - add: function ( v, w ) { + } - if ( w !== undefined ) { + setComponent( index, value ) { - console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + switch ( index ) { - } + case 0: this.x = value; break; + case 1: this.y = value; break; + default: throw new Error( 'index is out of range: ' + index ); - this.x += v.x; - this.y += v.y; + } - return this; + return this; - }, + } - addScalar: function ( s ) { + getComponent( index ) { - this.x += s; - this.y += s; + switch ( index ) { - return this; + case 0: return this.x; + case 1: return this.y; + default: throw new Error( 'index is out of range: ' + index ); - }, + } - addVectors: function ( a, b ) { + } - this.x = a.x + b.x; - this.y = a.y + b.y; + clone() { - return this; + return new this.constructor( this.x, this.y ); - }, + } - addScaledVector: function ( v, s ) { + copy( v ) { - this.x += v.x * s; - this.y += v.y * s; + this.x = v.x; + this.y = v.y; - return this; + return this; - }, + } - sub: function ( v, w ) { + add( v ) { - if ( w !== undefined ) { + this.x += v.x; + this.y += v.y; - console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + return this; - } + } - this.x -= v.x; - this.y -= v.y; + addScalar( s ) { - return this; + this.x += s; + this.y += s; - }, + return this; - subScalar: function ( s ) { + } - this.x -= s; - this.y -= s; + addVectors( a, b ) { - return this; + this.x = a.x + b.x; + this.y = a.y + b.y; - }, + return this; - subVectors: function ( a, b ) { + } - this.x = a.x - b.x; - this.y = a.y - b.y; + addScaledVector( v, s ) { - return this; + this.x += v.x * s; + this.y += v.y * s; - }, + return this; - multiply: function ( v ) { + } - this.x *= v.x; - this.y *= v.y; + sub( v ) { - return this; + this.x -= v.x; + this.y -= v.y; - }, + return this; - multiplyScalar: function ( scalar ) { + } - this.x *= scalar; - this.y *= scalar; + subScalar( s ) { - return this; + this.x -= s; + this.y -= s; - }, + return this; - divide: function ( v ) { + } - this.x /= v.x; - this.y /= v.y; + subVectors( a, b ) { - return this; + this.x = a.x - b.x; + this.y = a.y - b.y; - }, + return this; - divideScalar: function ( scalar ) { + } - return this.multiplyScalar( 1 / scalar ); + multiply( v ) { - }, + this.x *= v.x; + this.y *= v.y; - applyMatrix3: function ( m ) { + return this; - var x = this.x, y = this.y; - var e = m.elements; + } - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; + multiplyScalar( scalar ) { - return this; + this.x *= scalar; + this.y *= scalar; - }, + return this; - min: function ( v ) { + } - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); + divide( v ) { - return this; + this.x /= v.x; + this.y /= v.y; - }, + return this; - max: function ( v ) { + } - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); + divideScalar( scalar ) { - return this; + return this.multiplyScalar( 1 / scalar ); - }, + } - clamp: function ( min, max ) { + applyMatrix3( m ) { - // assumes min < max, componentwise + const x = this.x, y = this.y; + const e = m.elements; - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ]; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ]; - return this; + return this; - }, + } - clampScalar: function ( minVal, maxVal ) { + min( v ) { - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); - return this; + return this; - }, + } - clampLength: function ( min, max ) { + max( v ) { - var length = this.length(); + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + return this; - }, + } - floor: function () { + clamp( min, max ) { - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); + // assumes min < max, componentwise - return this; + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - }, + return this; - ceil: function () { + } - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); + clampScalar( minVal, maxVal ) { - return this; + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - }, + return this; - round: function () { + } - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); + clampLength( min, max ) { - return this; + const length = this.length(); - }, + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - roundToZero: function () { + } - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); + floor() { - return this; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); - }, + return this; - negate: function () { + } - this.x = - this.x; - this.y = - this.y; + ceil() { - return this; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); - }, + return this; - dot: function ( v ) { + } - return this.x * v.x + this.y * v.y; + round() { - }, + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); - cross: function ( v ) { + return this; - return this.x * v.y - this.y * v.x; + } - }, + roundToZero() { - lengthSq: function () { + this.x = Math.trunc( this.x ); + this.y = Math.trunc( this.y ); - return this.x * this.x + this.y * this.y; + return this; - }, + } - length: function () { + negate() { - return Math.sqrt( this.x * this.x + this.y * this.y ); + this.x = - this.x; + this.y = - this.y; - }, + return this; - manhattanLength: function () { + } - return Math.abs( this.x ) + Math.abs( this.y ); + dot( v ) { - }, + return this.x * v.x + this.y * v.y; - normalize: function () { + } - return this.divideScalar( this.length() || 1 ); + cross( v ) { - }, + return this.x * v.y - this.y * v.x; - angle: function () { + } - // computes the angle in radians with respect to the positive x-axis + lengthSq() { - var angle = Math.atan2( this.y, this.x ); + return this.x * this.x + this.y * this.y; - if ( angle < 0 ) angle += 2 * Math.PI; + } - return angle; + length() { - }, + return Math.sqrt( this.x * this.x + this.y * this.y ); - distanceTo: function ( v ) { + } - return Math.sqrt( this.distanceToSquared( v ) ); + manhattanLength() { - }, + return Math.abs( this.x ) + Math.abs( this.y ); - distanceToSquared: function ( v ) { + } - var dx = this.x - v.x, dy = this.y - v.y; - return dx * dx + dy * dy; + normalize() { - }, + return this.divideScalar( this.length() || 1 ); - manhattanDistanceTo: function ( v ) { + } - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); + angle() { - }, + // computes the angle in radians with respect to the positive x-axis - setLength: function ( length ) { + const angle = Math.atan2( - this.y, - this.x ) + Math.PI; - return this.normalize().multiplyScalar( length ); + return angle; - }, + } - lerp: function ( v, alpha ) { + angleTo( v ) { - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; + const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - return this; + if ( denominator === 0 ) return Math.PI / 2; - }, + const theta = this.dot( v ) / denominator; - lerpVectors: function ( v1, v2, alpha ) { + // clamp, to handle numerical problems - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + return Math.acos( clamp( theta, - 1, 1 ) ); - }, + } - equals: function ( v ) { + distanceTo( v ) { - return ( ( v.x === this.x ) && ( v.y === this.y ) ); + return Math.sqrt( this.distanceToSquared( v ) ); - }, + } - fromArray: function ( array, offset ) { + distanceToSquared( v ) { - if ( offset === undefined ) offset = 0; + const dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; + } - return this; + manhattanDistanceTo( v ) { - }, + return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ); - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + setLength( length ) { - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; + return this.normalize().multiplyScalar( length ); - return array; + } - }, + lerp( v, alpha ) { - fromBufferAttribute: function ( attribute, index, offset ) { + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; - if ( offset !== undefined ) { + return this; - console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' ); + } - } + lerpVectors( v1, v2, alpha ) { - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; - return this; + return this; - }, + } - rotateAround: function ( center, angle ) { + equals( v ) { - var c = Math.cos( angle ), s = Math.sin( angle ); + return ( ( v.x === this.x ) && ( v.y === this.y ) ); - var x = this.x - center.x; - var y = this.y - center.y; + } - this.x = x * c - y * s + center.x; - this.y = x * s + y * c + center.y; + fromArray( array, offset = 0 ) { - return this; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; - } + return this; - } ); + } - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ + toArray( array = [], offset = 0 ) { - function Quaternion( x, y, z, w ) { + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._w = ( w !== undefined ) ? w : 1; + return array; } - Object.assign( Quaternion, { + fromBufferAttribute( attribute, index ) { - slerp: function ( qa, qb, qm, t ) { + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); - return qm.copy( qa ).slerp( qb, t ); + return this; - }, + } - slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { + rotateAround( center, angle ) { - // fuzz-free, array-based Quaternion SLERP operation + const c = Math.cos( angle ), s = Math.sin( angle ); - var x0 = src0[ srcOffset0 + 0 ], - y0 = src0[ srcOffset0 + 1 ], - z0 = src0[ srcOffset0 + 2 ], - w0 = src0[ srcOffset0 + 3 ], + const x = this.x - center.x; + const y = this.y - center.y; - x1 = src1[ srcOffset1 + 0 ], - y1 = src1[ srcOffset1 + 1 ], - z1 = src1[ srcOffset1 + 2 ], - w1 = src1[ srcOffset1 + 3 ]; + this.x = x * c - y * s + center.x; + this.y = x * s + y * c + center.y; - if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { + return this; - var s = 1 - t, + } - cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + random() { - dir = ( cos >= 0 ? 1 : - 1 ), - sqrSin = 1 - cos * cos; + this.x = Math.random(); + this.y = Math.random(); - // Skip the Slerp for tiny steps to avoid numeric problems: - if ( sqrSin > Number.EPSILON ) { + return this; - var sin = Math.sqrt( sqrSin ), - len = Math.atan2( sin, cos * dir ); + } - s = Math.sin( s * len ) / sin; - t = Math.sin( t * len ) / sin; + *[ Symbol.iterator ]() { - } + yield this.x; + yield this.y; - var tDir = t * dir; + } - x0 = x0 * s + x1 * tDir; - y0 = y0 * s + y1 * tDir; - z0 = z0 * s + z1 * tDir; - w0 = w0 * s + w1 * tDir; +} - // Normalize in case we just did a lerp: - if ( s === 1 - t ) { +class Matrix3 { - var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); + constructor( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - x0 *= f; - y0 *= f; - z0 *= f; - w0 *= f; + Matrix3.prototype.isMatrix3 = true; - } + this.elements = [ - } + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 - dst[ dstOffset ] = x0; - dst[ dstOffset + 1 ] = y0; - dst[ dstOffset + 2 ] = z0; - dst[ dstOffset + 3 ] = w0; + ]; + + if ( n11 !== undefined ) { + + this.set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ); } - } ); + } - Object.defineProperties( Quaternion.prototype, { + set( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { - x: { + const te = this.elements; - get: function () { + te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; + te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; + te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; - return this._x; + return this; - }, + } - set: function ( value ) { + identity() { - this._x = value; - this._onChangeCallback(); + this.set( - } + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 - }, + ); - y: { + return this; - get: function () { + } - return this._y; + copy( m ) { - }, + const te = this.elements; + const me = m.elements; - set: function ( value ) { + te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; + te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; + te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; - this._y = value; - this._onChangeCallback(); + return this; - } + } - }, + extractBasis( xAxis, yAxis, zAxis ) { - z: { + xAxis.setFromMatrix3Column( this, 0 ); + yAxis.setFromMatrix3Column( this, 1 ); + zAxis.setFromMatrix3Column( this, 2 ); - get: function () { + return this; - return this._z; + } - }, + setFromMatrix4( m ) { - set: function ( value ) { + const me = m.elements; - this._z = value; - this._onChangeCallback(); + this.set( - } + me[ 0 ], me[ 4 ], me[ 8 ], + me[ 1 ], me[ 5 ], me[ 9 ], + me[ 2 ], me[ 6 ], me[ 10 ] - }, + ); - w: { + return this; - get: function () { + } - return this._w; + multiply( m ) { - }, + return this.multiplyMatrices( this, m ); - set: function ( value ) { + } - this._w = value; - this._onChangeCallback(); + premultiply( m ) { - } + return this.multiplyMatrices( m, this ); - } + } - } ); + multiplyMatrices( a, b ) { - Object.assign( Quaternion.prototype, { + const ae = a.elements; + const be = b.elements; + const te = this.elements; - isQuaternion: true, + const a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; + const a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; + const a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; - set: function ( x, y, z, w ) { + const b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; + const b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; + const b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; - this._x = x; - this._y = y; - this._z = z; - this._w = w; + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; + te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; + te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; - this._onChangeCallback(); + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; + te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; + te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; - return this; + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; + te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; + te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; - }, + return this; - clone: function () { + } - return new this.constructor( this._x, this._y, this._z, this._w ); + multiplyScalar( s ) { - }, + const te = this.elements; - copy: function ( quaternion ) { + te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; + te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; + te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; - this._x = quaternion.x; - this._y = quaternion.y; - this._z = quaternion.z; - this._w = quaternion.w; + return this; - this._onChangeCallback(); + } - return this; + determinant() { - }, + const te = this.elements; + + const a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], + d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], + g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; - setFromEuler: function ( euler, update ) { + return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; - if ( ! ( euler && euler.isEuler ) ) { + } - throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + invert() { - } + const te = this.elements, - var x = euler._x, y = euler._y, z = euler._z, order = euler.order; + n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], + n12 = te[ 3 ], n22 = te[ 4 ], n32 = te[ 5 ], + n13 = te[ 6 ], n23 = te[ 7 ], n33 = te[ 8 ], - // http://www.mathworks.com/matlabcentral/fileexchange/ - // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ - // content/SpinCalc.m + t11 = n33 * n22 - n32 * n23, + t12 = n32 * n13 - n33 * n12, + t13 = n23 * n12 - n22 * n13, - var cos = Math.cos; - var sin = Math.sin; + det = n11 * t11 + n21 * t12 + n31 * t13; - var c1 = cos( x / 2 ); - var c2 = cos( y / 2 ); - var c3 = cos( z / 2 ); + if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - var s1 = sin( x / 2 ); - var s2 = sin( y / 2 ); - var s3 = sin( z / 2 ); + const detInv = 1 / det; - if ( order === 'XYZ' ) { + te[ 0 ] = t11 * detInv; + te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; + te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; + te[ 3 ] = t12 * detInv; + te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; + te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; - } else if ( order === 'YXZ' ) { + te[ 6 ] = t13 * detInv; + te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; + te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; + return this; - } else if ( order === 'ZXY' ) { + } - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; + transpose() { - } else if ( order === 'ZYX' ) { + let tmp; + const m = this.elements; - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; + tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; + tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; + tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; - } else if ( order === 'YZX' ) { + return this; - this._x = s1 * c2 * c3 + c1 * s2 * s3; - this._y = c1 * s2 * c3 + s1 * c2 * s3; - this._z = c1 * c2 * s3 - s1 * s2 * c3; - this._w = c1 * c2 * c3 - s1 * s2 * s3; + } - } else if ( order === 'XZY' ) { + getNormalMatrix( matrix4 ) { - this._x = s1 * c2 * c3 - c1 * s2 * s3; - this._y = c1 * s2 * c3 - s1 * c2 * s3; - this._z = c1 * c2 * s3 + s1 * s2 * c3; - this._w = c1 * c2 * c3 + s1 * s2 * s3; + return this.setFromMatrix4( matrix4 ).invert().transpose(); - } + } - if ( update !== false ) this._onChangeCallback(); + transposeIntoArray( r ) { - return this; + const m = this.elements; - }, + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; - setFromAxisAngle: function ( axis, angle ) { + return this; - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + } - // assumes axis is normalized + setUvTransform( tx, ty, sx, sy, rotation, cx, cy ) { - var halfAngle = angle / 2, s = Math.sin( halfAngle ); + const c = Math.cos( rotation ); + const s = Math.sin( rotation ); - this._x = axis.x * s; - this._y = axis.y * s; - this._z = axis.z * s; - this._w = Math.cos( halfAngle ); + this.set( + sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, + - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, + 0, 0, 1 + ); - this._onChangeCallback(); + return this; - return this; + } - }, + // - setFromRotationMatrix: function ( m ) { + scale( sx, sy ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + this.premultiply( _m3.makeScale( sx, sy ) ); - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + return this; + + } - var te = m.elements, + rotate( theta ) { - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], + this.premultiply( _m3.makeRotation( - theta ) ); - trace = m11 + m22 + m33, - s; + return this; - if ( trace > 0 ) { + } - s = 0.5 / Math.sqrt( trace + 1.0 ); + translate( tx, ty ) { - this._w = 0.25 / s; - this._x = ( m32 - m23 ) * s; - this._y = ( m13 - m31 ) * s; - this._z = ( m21 - m12 ) * s; + this.premultiply( _m3.makeTranslation( tx, ty ) ); - } else if ( m11 > m22 && m11 > m33 ) { + return this; - s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + } - this._w = ( m32 - m23 ) / s; - this._x = 0.25 * s; - this._y = ( m12 + m21 ) / s; - this._z = ( m13 + m31 ) / s; + // for 2D Transforms - } else if ( m22 > m33 ) { + makeTranslation( x, y ) { - s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + if ( x.isVector2 ) { - this._w = ( m13 - m31 ) / s; - this._x = ( m12 + m21 ) / s; - this._y = 0.25 * s; - this._z = ( m23 + m32 ) / s; + this.set( - } else { + 1, 0, x.x, + 0, 1, x.y, + 0, 0, 1 - s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + ); - this._w = ( m21 - m12 ) / s; - this._x = ( m13 + m31 ) / s; - this._y = ( m23 + m32 ) / s; - this._z = 0.25 * s; + } else { - } + this.set( - this._onChangeCallback(); + 1, 0, x, + 0, 1, y, + 0, 0, 1 - return this; + ); - }, + } - setFromUnitVectors: function ( vFrom, vTo ) { + return this; - // assumes direction vectors vFrom and vTo are normalized + } - var EPS = 0.000001; + makeRotation( theta ) { - var r = vFrom.dot( vTo ) + 1; + // counterclockwise - if ( r < EPS ) { + const c = Math.cos( theta ); + const s = Math.sin( theta ); - r = 0; + this.set( - if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { + c, - s, 0, + s, c, 0, + 0, 0, 1 - this._x = - vFrom.y; - this._y = vFrom.x; - this._z = 0; - this._w = r; + ); - } else { + return this; - this._x = 0; - this._y = - vFrom.z; - this._z = vFrom.y; - this._w = r; + } - } + makeScale( x, y ) { - } else { + this.set( - // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 + x, 0, 0, + 0, y, 0, + 0, 0, 1 - this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; - this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; - this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; - this._w = r; + ); - } + return this; - return this.normalize(); + } - }, + // - angleTo: function ( q ) { + equals( matrix ) { - return 2 * Math.acos( Math.abs( _Math.clamp( this.dot( q ), - 1, 1 ) ) ); + const te = this.elements; + const me = matrix.elements; - }, + for ( let i = 0; i < 9; i ++ ) { - rotateTowards: function ( q, step ) { + if ( te[ i ] !== me[ i ] ) return false; - var angle = this.angleTo( q ); + } - if ( angle === 0 ) return this; + return true; - var t = Math.min( 1, step / angle ); + } - this.slerp( q, t ); + fromArray( array, offset = 0 ) { - return this; + for ( let i = 0; i < 9; i ++ ) { - }, + this.elements[ i ] = array[ i + offset ]; - inverse: function () { + } - // quaternion is assumed to have unit length + return this; - return this.conjugate(); + } - }, + toArray( array = [], offset = 0 ) { - conjugate: function () { + const te = this.elements; - this._x *= - 1; - this._y *= - 1; - this._z *= - 1; + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; - this._onChangeCallback(); + array[ offset + 3 ] = te[ 3 ]; + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; - return this; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; + array[ offset + 8 ] = te[ 8 ]; - }, + return array; - dot: function ( v ) { + } - return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; + clone() { - }, + return new this.constructor().fromArray( this.elements ); - lengthSq: function () { + } - return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; +} - }, +const _m3 = /*@__PURE__*/ new Matrix3(); - length: function () { +function arrayNeedsUint32( array ) { - return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); + // assumes larger values usually on last - }, + for ( let i = array.length - 1; i >= 0; -- i ) { - normalize: function () { + if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565 - var l = this.length(); + } - if ( l === 0 ) { + return false; - this._x = 0; - this._y = 0; - this._z = 0; - this._w = 1; +} - } else { +const TYPED_ARRAYS = { + Int8Array: Int8Array, + Uint8Array: Uint8Array, + Uint8ClampedArray: Uint8ClampedArray, + Int16Array: Int16Array, + Uint16Array: Uint16Array, + Int32Array: Int32Array, + Uint32Array: Uint32Array, + Float32Array: Float32Array, + Float64Array: Float64Array +}; - l = 1 / l; +function getTypedArray( type, buffer ) { - this._x = this._x * l; - this._y = this._y * l; - this._z = this._z * l; - this._w = this._w * l; + return new TYPED_ARRAYS[ type ]( buffer ); - } +} - this._onChangeCallback(); +function createElementNS( name ) { - return this; + return document.createElementNS( 'http://www.w3.org/1999/xhtml', name ); - }, +} - multiply: function ( q, p ) { +function createCanvasElement() { - if ( p !== undefined ) { + const canvas = createElementNS( 'canvas' ); + canvas.style.display = 'block'; + return canvas; - console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); - return this.multiplyQuaternions( q, p ); +} - } +const _cache = {}; - return this.multiplyQuaternions( this, q ); +function warnOnce( message ) { - }, + if ( message in _cache ) return; - premultiply: function ( q ) { + _cache[ message ] = true; - return this.multiplyQuaternions( q, this ); + console.warn( message ); - }, +} - multiplyQuaternions: function ( a, b ) { +/** + * Matrices converting P3 <-> Rec. 709 primaries, without gamut mapping + * or clipping. Based on W3C specifications for sRGB and Display P3, + * and ICC specifications for the D50 connection space. Values in/out + * are _linear_ sRGB and _linear_ Display P3. + * + * Note that both sRGB and Display P3 use the sRGB transfer functions. + * + * Reference: + * - http://www.russellcottrell.com/photo/matrixCalculator.htm + */ - // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm +const LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = /*@__PURE__*/ new Matrix3().set( + 0.8224621, 0.177538, 0.0, + 0.0331941, 0.9668058, 0.0, + 0.0170827, 0.0723974, 0.9105199, +); - var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; - var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; +const LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = /*@__PURE__*/ new Matrix3().set( + 1.2249401, - 0.2249404, 0.0, + - 0.0420569, 1.0420571, 0.0, + - 0.0196376, - 0.0786361, 1.0982735 +); - this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; - this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; - this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; - this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; +/** + * Defines supported color spaces by transfer function and primaries, + * and provides conversions to/from the Linear-sRGB reference space. + */ +const COLOR_SPACES = { + [ LinearSRGBColorSpace ]: { + transfer: LinearTransfer, + primaries: Rec709Primaries, + toReference: ( color ) => color, + fromReference: ( color ) => color, + }, + [ SRGBColorSpace ]: { + transfer: SRGBTransfer, + primaries: Rec709Primaries, + toReference: ( color ) => color.convertSRGBToLinear(), + fromReference: ( color ) => color.convertLinearToSRGB(), + }, + [ LinearDisplayP3ColorSpace ]: { + transfer: LinearTransfer, + primaries: P3Primaries, + toReference: ( color ) => color.applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ), + fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ), + }, + [ DisplayP3ColorSpace ]: { + transfer: SRGBTransfer, + primaries: P3Primaries, + toReference: ( color ) => color.convertSRGBToLinear().applyMatrix3( LINEAR_DISPLAY_P3_TO_LINEAR_SRGB ), + fromReference: ( color ) => color.applyMatrix3( LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 ).convertLinearToSRGB(), + }, +}; - this._onChangeCallback(); +const SUPPORTED_WORKING_COLOR_SPACES = new Set( [ LinearSRGBColorSpace, LinearDisplayP3ColorSpace ] ); - return this; +const ColorManagement = { - }, + enabled: true, - slerp: function ( qb, t ) { + _workingColorSpace: LinearSRGBColorSpace, - if ( t === 0 ) return this; - if ( t === 1 ) return this.copy( qb ); + get legacyMode() { - var x = this._x, y = this._y, z = this._z, w = this._w; + console.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' ); - // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + return ! this.enabled; - var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; + }, - if ( cosHalfTheta < 0 ) { + set legacyMode( legacyMode ) { - this._w = - qb._w; - this._x = - qb._x; - this._y = - qb._y; - this._z = - qb._z; + console.warn( 'THREE.ColorManagement: .legacyMode=false renamed to .enabled=true in r150.' ); - cosHalfTheta = - cosHalfTheta; + this.enabled = ! legacyMode; - } else { + }, - this.copy( qb ); + get workingColorSpace() { - } + return this._workingColorSpace; - if ( cosHalfTheta >= 1.0 ) { + }, - this._w = w; - this._x = x; - this._y = y; - this._z = z; + set workingColorSpace( colorSpace ) { - return this; + if ( ! SUPPORTED_WORKING_COLOR_SPACES.has( colorSpace ) ) { - } + throw new Error( `Unsupported working color space, "${ colorSpace }".` ); - var sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; + } - if ( sqrSinHalfTheta <= Number.EPSILON ) { + this._workingColorSpace = colorSpace; - var s = 1 - t; - this._w = s * w + t * this._w; - this._x = s * x + t * this._x; - this._y = s * y + t * this._y; - this._z = s * z + t * this._z; + }, - this.normalize(); - this._onChangeCallback(); + convert: function ( color, sourceColorSpace, targetColorSpace ) { - return this; + if ( this.enabled === false || sourceColorSpace === targetColorSpace || ! sourceColorSpace || ! targetColorSpace ) { - } + return color; - var sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); - var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); - var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, - ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + } - this._w = ( w * ratioA + this._w * ratioB ); - this._x = ( x * ratioA + this._x * ratioB ); - this._y = ( y * ratioA + this._y * ratioB ); - this._z = ( z * ratioA + this._z * ratioB ); + const sourceToReference = COLOR_SPACES[ sourceColorSpace ].toReference; + const targetFromReference = COLOR_SPACES[ targetColorSpace ].fromReference; - this._onChangeCallback(); + return targetFromReference( sourceToReference( color ) ); - return this; + }, - }, + fromWorkingColorSpace: function ( color, targetColorSpace ) { - equals: function ( quaternion ) { + return this.convert( color, this._workingColorSpace, targetColorSpace ); - return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); + }, - }, + toWorkingColorSpace: function ( color, sourceColorSpace ) { - fromArray: function ( array, offset ) { + return this.convert( color, sourceColorSpace, this._workingColorSpace ); - if ( offset === undefined ) offset = 0; + }, - this._x = array[ offset ]; - this._y = array[ offset + 1 ]; - this._z = array[ offset + 2 ]; - this._w = array[ offset + 3 ]; + getPrimaries: function ( colorSpace ) { - this._onChangeCallback(); + return COLOR_SPACES[ colorSpace ].primaries; - return this; + }, - }, + getTransfer: function ( colorSpace ) { - toArray: function ( array, offset ) { + if ( colorSpace === NoColorSpace ) return LinearTransfer; - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + return COLOR_SPACES[ colorSpace ].transfer; - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._w; + }, - return array; +}; - }, - _onChange: function ( callback ) { +function SRGBToLinear( c ) { - this._onChangeCallback = callback; + return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - return this; +} - }, +function LinearToSRGB( c ) { - _onChangeCallback: function () {} + return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; - } ); +} - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ +let _canvas; - var _vector = new Vector3(); - var _quaternion = new Quaternion(); +class ImageUtils { - function Vector3( x, y, z ) { + static getDataURL( image ) { - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; + if ( /^data:/i.test( image.src ) ) { - } + return image.src; - Object.assign( Vector3.prototype, { + } - isVector3: true, + if ( typeof HTMLCanvasElement === 'undefined' ) { - set: function ( x, y, z ) { + return image.src; - this.x = x; - this.y = y; - this.z = z; + } - return this; + let canvas; - }, + if ( image instanceof HTMLCanvasElement ) { - setScalar: function ( scalar ) { + canvas = image; - this.x = scalar; - this.y = scalar; - this.z = scalar; + } else { - return this; + if ( _canvas === undefined ) _canvas = createElementNS( 'canvas' ); - }, + _canvas.width = image.width; + _canvas.height = image.height; - setX: function ( x ) { + const context = _canvas.getContext( '2d' ); - this.x = x; + if ( image instanceof ImageData ) { - return this; + context.putImageData( image, 0, 0 ); - }, + } else { - setY: function ( y ) { + context.drawImage( image, 0, 0, image.width, image.height ); - this.y = y; + } - return this; + canvas = _canvas; - }, + } - setZ: function ( z ) { + if ( canvas.width > 2048 || canvas.height > 2048 ) { - this.z = z; + console.warn( 'THREE.ImageUtils.getDataURL: Image converted to jpg for performance reasons', image ); - return this; + return canvas.toDataURL( 'image/jpeg', 0.6 ); - }, + } else { - setComponent: function ( index, value ) { + return canvas.toDataURL( 'image/png' ); - switch ( index ) { + } - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - default: throw new Error( 'index is out of range: ' + index ); + } - } + static sRGBToLinear( image ) { - return this; + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - }, + const canvas = createElementNS( 'canvas' ); + + canvas.width = image.width; + canvas.height = image.height; - getComponent: function ( index ) { + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height ); - switch ( index ) { + const imageData = context.getImageData( 0, 0, image.width, image.height ); + const data = imageData.data; - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - default: throw new Error( 'index is out of range: ' + index ); + for ( let i = 0; i < data.length; i ++ ) { + + data[ i ] = SRGBToLinear( data[ i ] / 255 ) * 255; } - }, + context.putImageData( imageData, 0, 0 ); - clone: function () { + return canvas; - return new this.constructor( this.x, this.y, this.z ); + } else if ( image.data ) { - }, + const data = image.data.slice( 0 ); - copy: function ( v ) { + for ( let i = 0; i < data.length; i ++ ) { - this.x = v.x; - this.y = v.y; - this.z = v.z; + if ( data instanceof Uint8Array || data instanceof Uint8ClampedArray ) { - return this; + data[ i ] = Math.floor( SRGBToLinear( data[ i ] / 255 ) * 255 ); - }, + } else { - add: function ( v, w ) { + // assuming float - if ( w !== undefined ) { + data[ i ] = SRGBToLinear( data[ i ] ); - console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + } } - this.x += v.x; - this.y += v.y; - this.z += v.z; + return { + data: data, + width: image.width, + height: image.height + }; - return this; + } else { - }, + console.warn( 'THREE.ImageUtils.sRGBToLinear(): Unsupported image type. No color space conversion applied.' ); + return image; - addScalar: function ( s ) { + } - this.x += s; - this.y += s; - this.z += s; + } - return this; +} - }, +let _sourceId = 0; - addVectors: function ( a, b ) { +class Source { - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; + constructor( data = null ) { - return this; + this.isSource = true; - }, + Object.defineProperty( this, 'id', { value: _sourceId ++ } ); - addScaledVector: function ( v, s ) { + this.uuid = generateUUID(); - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; + this.data = data; - return this; + this.version = 0; - }, + } - sub: function ( v, w ) { + set needsUpdate( value ) { - if ( w !== undefined ) { + if ( value === true ) this.version ++; - console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + } - } + toJSON( meta ) { - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - return this; + if ( ! isRootObject && meta.images[ this.uuid ] !== undefined ) { - }, + return meta.images[ this.uuid ]; - subScalar: function ( s ) { + } - this.x -= s; - this.y -= s; - this.z -= s; + const output = { + uuid: this.uuid, + url: '' + }; - return this; + const data = this.data; - }, + if ( data !== null ) { - subVectors: function ( a, b ) { + let url; - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; + if ( Array.isArray( data ) ) { - return this; + // cube texture - }, + url = []; - multiply: function ( v, w ) { + for ( let i = 0, l = data.length; i < l; i ++ ) { - if ( w !== undefined ) { + if ( data[ i ].isDataTexture ) { - console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); - return this.multiplyVectors( v, w ); + url.push( serializeImage( data[ i ].image ) ); - } - - this.x *= v.x; - this.y *= v.y; - this.z *= v.z; + } else { - return this; + url.push( serializeImage( data[ i ] ) ); - }, + } - multiplyScalar: function ( scalar ) { + } - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; + } else { - return this; + // texture - }, + url = serializeImage( data ); - multiplyVectors: function ( a, b ) { + } - this.x = a.x * b.x; - this.y = a.y * b.y; - this.z = a.z * b.z; + output.url = url; - return this; + } - }, + if ( ! isRootObject ) { - applyEuler: function ( euler ) { + meta.images[ this.uuid ] = output; - if ( ! ( euler && euler.isEuler ) ) { + } - console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); + return output; - } + } - return this.applyQuaternion( _quaternion.setFromEuler( euler ) ); +} - }, +function serializeImage( image ) { - applyAxisAngle: function ( axis, angle ) { + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - return this.applyQuaternion( _quaternion.setFromAxisAngle( axis, angle ) ); + // default images - }, + return ImageUtils.getDataURL( image ); - applyMatrix3: function ( m ) { + } else { - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + if ( image.data ) { - this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; - this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; - this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; + // images of DataTexture - return this; + return { + data: Array.from( image.data ), + width: image.width, + height: image.height, + type: image.data.constructor.name + }; - }, + } else { - applyMatrix4: function ( m ) { + console.warn( 'THREE.Texture: Unable to serialize Texture.' ); + return {}; - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + } - var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); + } - this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; - this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; - this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; +} - return this; +let _textureId = 0; - }, +class Texture extends EventDispatcher { - applyQuaternion: function ( q ) { + constructor( image = Texture.DEFAULT_IMAGE, mapping = Texture.DEFAULT_MAPPING, wrapS = ClampToEdgeWrapping, wrapT = ClampToEdgeWrapping, magFilter = LinearFilter, minFilter = LinearMipmapLinearFilter, format = RGBAFormat, type = UnsignedByteType, anisotropy = Texture.DEFAULT_ANISOTROPY, colorSpace = NoColorSpace ) { - var x = this.x, y = this.y, z = this.z; - var qx = q.x, qy = q.y, qz = q.z, qw = q.w; + super(); - // calculate quat * vector + this.isTexture = true; - var ix = qw * x + qy * z - qz * y; - var iy = qw * y + qz * x - qx * z; - var iz = qw * z + qx * y - qy * x; - var iw = - qx * x - qy * y - qz * z; + Object.defineProperty( this, 'id', { value: _textureId ++ } ); - // calculate result * inverse quat + this.uuid = generateUUID(); - this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; - this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; - this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; + this.name = ''; - return this; + this.source = new Source( image ); + this.mipmaps = []; - }, + this.mapping = mapping; + this.channel = 0; - project: function ( camera ) { + this.wrapS = wrapS; + this.wrapT = wrapT; - return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); + this.magFilter = magFilter; + this.minFilter = minFilter; - }, + this.anisotropy = anisotropy; - unproject: function ( camera ) { + this.format = format; + this.internalFormat = null; + this.type = type; - return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); + this.offset = new Vector2( 0, 0 ); + this.repeat = new Vector2( 1, 1 ); + this.center = new Vector2( 0, 0 ); + this.rotation = 0; - }, + this.matrixAutoUpdate = true; + this.matrix = new Matrix3(); - transformDirection: function ( m ) { + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - // input: THREE.Matrix4 affine matrix - // vector interpreted as a direction + if ( typeof colorSpace === 'string' ) { - var x = this.x, y = this.y, z = this.z; - var e = m.elements; + this.colorSpace = colorSpace; - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; + } else { // @deprecated, r152 - return this.normalize(); + warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' ); + this.colorSpace = colorSpace === sRGBEncoding ? SRGBColorSpace : NoColorSpace; - }, + } - divide: function ( v ) { - this.x /= v.x; - this.y /= v.y; - this.z /= v.z; + this.userData = {}; - return this; + this.version = 0; + this.onUpdate = null; - }, + this.isRenderTargetTexture = false; // indicates whether a texture belongs to a render target or not + this.needsPMREMUpdate = false; // indicates whether this texture should be processed by PMREMGenerator or not (only relevant for render target textures) - divideScalar: function ( scalar ) { + } - return this.multiplyScalar( 1 / scalar ); + get image() { - }, + return this.source.data; - min: function ( v ) { + } - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); + set image( value = null ) { - return this; + this.source.data = value; - }, + } - max: function ( v ) { + updateMatrix() { - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); + this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); - return this; + } - }, + clone() { - clamp: function ( min, max ) { + return new this.constructor().copy( this ); - // assumes min < max, componentwise + } - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + copy( source ) { - return this; + this.name = source.name; - }, + this.source = source.source; + this.mipmaps = source.mipmaps.slice( 0 ); - clampScalar: function ( minVal, maxVal ) { + this.mapping = source.mapping; + this.channel = source.channel; - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; - return this; + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; - }, + this.anisotropy = source.anisotropy; - clampLength: function ( min, max ) { + this.format = source.format; + this.internalFormat = source.internalFormat; + this.type = source.type; - var length = this.length(); + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); + this.center.copy( source.center ); + this.rotation = source.rotation; - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrix.copy( source.matrix ); - }, + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.colorSpace = source.colorSpace; - floor: function () { + this.userData = JSON.parse( JSON.stringify( source.userData ) ); - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); + this.needsUpdate = true; - return this; + return this; - }, + } - ceil: function () { + toJSON( meta ) { - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - return this; + if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { - }, + return meta.textures[ this.uuid ]; - round: function () { + } - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); + const output = { - return this; + metadata: { + version: 4.6, + type: 'Texture', + generator: 'Texture.toJSON' + }, - }, + uuid: this.uuid, + name: this.name, - roundToZero: function () { + image: this.source.toJSON( meta ).uuid, - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); + mapping: this.mapping, + channel: this.channel, - return this; + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + center: [ this.center.x, this.center.y ], + rotation: this.rotation, - }, + wrap: [ this.wrapS, this.wrapT ], - negate: function () { + format: this.format, + internalFormat: this.internalFormat, + type: this.type, + colorSpace: this.colorSpace, - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, - return this; + flipY: this.flipY, - }, + generateMipmaps: this.generateMipmaps, + premultiplyAlpha: this.premultiplyAlpha, + unpackAlignment: this.unpackAlignment - dot: function ( v ) { + }; - return this.x * v.x + this.y * v.y + this.z * v.z; + if ( Object.keys( this.userData ).length > 0 ) output.userData = this.userData; - }, + if ( ! isRootObject ) { - // TODO lengthSquared? + meta.textures[ this.uuid ] = output; - lengthSq: function () { + } - return this.x * this.x + this.y * this.y + this.z * this.z; + return output; - }, + } - length: function () { + dispose() { - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); + this.dispatchEvent( { type: 'dispose' } ); - }, + } - manhattanLength: function () { + transformUv( uv ) { - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + if ( this.mapping !== UVMapping ) return uv; - }, + uv.applyMatrix3( this.matrix ); - normalize: function () { + if ( uv.x < 0 || uv.x > 1 ) { - return this.divideScalar( this.length() || 1 ); + switch ( this.wrapS ) { - }, + case RepeatWrapping: - setLength: function ( length ) { + uv.x = uv.x - Math.floor( uv.x ); + break; - return this.normalize().multiplyScalar( length ); + case ClampToEdgeWrapping: - }, + uv.x = uv.x < 0 ? 0 : 1; + break; - lerp: function ( v, alpha ) { + case MirroredRepeatWrapping: - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - return this; + uv.x = Math.ceil( uv.x ) - uv.x; - }, + } else { - lerpVectors: function ( v1, v2, alpha ) { + uv.x = uv.x - Math.floor( uv.x ); - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + } - }, + break; - cross: function ( v, w ) { + } - if ( w !== undefined ) { + } - console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); - return this.crossVectors( v, w ); + if ( uv.y < 0 || uv.y > 1 ) { - } + switch ( this.wrapT ) { - return this.crossVectors( this, v ); + case RepeatWrapping: - }, + uv.y = uv.y - Math.floor( uv.y ); + break; - crossVectors: function ( a, b ) { + case ClampToEdgeWrapping: - var ax = a.x, ay = a.y, az = a.z; - var bx = b.x, by = b.y, bz = b.z; + uv.y = uv.y < 0 ? 0 : 1; + break; - this.x = ay * bz - az * by; - this.y = az * bx - ax * bz; - this.z = ax * by - ay * bx; + case MirroredRepeatWrapping: - return this; + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - }, + uv.y = Math.ceil( uv.y ) - uv.y; - projectOnVector: function ( vector ) { + } else { - var scalar = vector.dot( this ) / vector.lengthSq(); + uv.y = uv.y - Math.floor( uv.y ); - return this.copy( vector ).multiplyScalar( scalar ); + } - }, + break; - projectOnPlane: function ( planeNormal ) { + } - _vector.copy( this ).projectOnVector( planeNormal ); + } - return this.sub( _vector ); + if ( this.flipY ) { - }, + uv.y = 1 - uv.y; - reflect: function ( normal ) { + } - // reflect incident vector off plane orthogonal to normal - // normal is assumed to have unit length + return uv; - return this.sub( _vector.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); + } - }, + set needsUpdate( value ) { - angleTo: function ( v ) { + if ( value === true ) { - var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); + this.version ++; + this.source.needsUpdate = true; - // clamp, to handle numerical problems + } - return Math.acos( _Math.clamp( theta, - 1, 1 ) ); + } - }, + get encoding() { // @deprecated, r152 - distanceTo: function ( v ) { + warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' ); + return this.colorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; - return Math.sqrt( this.distanceToSquared( v ) ); + } - }, + set encoding( encoding ) { // @deprecated, r152 - distanceToSquared: function ( v ) { + warnOnce( 'THREE.Texture: Property .encoding has been replaced by .colorSpace.' ); + this.colorSpace = encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; - var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; + } - return dx * dx + dy * dy + dz * dz; +} - }, +Texture.DEFAULT_IMAGE = null; +Texture.DEFAULT_MAPPING = UVMapping; +Texture.DEFAULT_ANISOTROPY = 1; - manhattanDistanceTo: function ( v ) { +class Vector4 { - return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); + constructor( x = 0, y = 0, z = 0, w = 1 ) { - }, + Vector4.prototype.isVector4 = true; - setFromSpherical: function ( s ) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; - return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); + } - }, + get width() { - setFromSphericalCoords: function ( radius, phi, theta ) { + return this.z; - var sinPhiRadius = Math.sin( phi ) * radius; + } - this.x = sinPhiRadius * Math.sin( theta ); - this.y = Math.cos( phi ) * radius; - this.z = sinPhiRadius * Math.cos( theta ); + set width( value ) { - return this; + this.z = value; - }, + } - setFromCylindrical: function ( c ) { + get height() { - return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); + return this.w; - }, + } - setFromCylindricalCoords: function ( radius, theta, y ) { + set height( value ) { - this.x = radius * Math.sin( theta ); - this.y = y; - this.z = radius * Math.cos( theta ); + this.w = value; - return this; + } - }, + set( x, y, z, w ) { - setFromMatrixPosition: function ( m ) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; - var e = m.elements; + return this; - this.x = e[ 12 ]; - this.y = e[ 13 ]; - this.z = e[ 14 ]; + } - return this; + setScalar( scalar ) { - }, + this.x = scalar; + this.y = scalar; + this.z = scalar; + this.w = scalar; - setFromMatrixScale: function ( m ) { + return this; - var sx = this.setFromMatrixColumn( m, 0 ).length(); - var sy = this.setFromMatrixColumn( m, 1 ).length(); - var sz = this.setFromMatrixColumn( m, 2 ).length(); + } - this.x = sx; - this.y = sy; - this.z = sz; + setX( x ) { - return this; + this.x = x; - }, + return this; - setFromMatrixColumn: function ( m, index ) { + } - return this.fromArray( m.elements, index * 4 ); + setY( y ) { - }, + this.y = y; - equals: function ( v ) { + return this; - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + } - }, + setZ( z ) { - fromArray: function ( array, offset ) { + this.z = z; - if ( offset === undefined ) offset = 0; + return this; - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; + } - return this; + setW( w ) { - }, + this.w = w; - toArray: function ( array, offset ) { + return this; - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + } - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; + setComponent( index, value ) { - return array; + switch ( index ) { - }, + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + case 3: this.w = value; break; + default: throw new Error( 'index is out of range: ' + index ); - fromBufferAttribute: function ( attribute, index, offset ) { + } - if ( offset !== undefined ) { + return this; - console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' ); + } - } + getComponent( index ) { - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); + switch ( index ) { - return this; + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + case 3: return this.w; + default: throw new Error( 'index is out of range: ' + index ); } - } ); + } - /** - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - * @author tschw - */ + clone() { - var _vector$1 = new Vector3(); + return new this.constructor( this.x, this.y, this.z, this.w ); - function Matrix3() { + } - this.elements = [ + copy( v ) { - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; - ]; + return this; + + } - if ( arguments.length > 0 ) { + add( v ) { - console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; - } + return this; } - Object.assign( Matrix3.prototype, { + addScalar( s ) { - isMatrix3: true, + this.x += s; + this.y += s; + this.z += s; + this.w += s; - set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { + return this; - var te = this.elements; + } - te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31; - te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32; - te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33; + addVectors( a, b ) { - return this; + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; - }, + return this; - identity: function () { + } - this.set( + addScaledVector( v, s ) { - 1, 0, 0, - 0, 1, 0, - 0, 0, 1 + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; + this.w += v.w * s; - ); + return this; - return this; + } - }, + sub( v ) { - clone: function () { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; - return new this.constructor().fromArray( this.elements ); + return this; - }, + } - copy: function ( m ) { + subScalar( s ) { - var te = this.elements; - var me = m.elements; + this.x -= s; + this.y -= s; + this.z -= s; + this.w -= s; - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; - te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; - te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ]; + return this; - return this; + } - }, + subVectors( a, b ) { - setFromMatrix4: function ( m ) { + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; - var me = m.elements; + return this; - this.set( + } - me[ 0 ], me[ 4 ], me[ 8 ], - me[ 1 ], me[ 5 ], me[ 9 ], - me[ 2 ], me[ 6 ], me[ 10 ] + multiply( v ) { - ); + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + this.w *= v.w; - return this; + return this; - }, + } - applyToBufferAttribute: function ( attribute ) { + multiplyScalar( scalar ) { - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + this.w *= scalar; - _vector$1.x = attribute.getX( i ); - _vector$1.y = attribute.getY( i ); - _vector$1.z = attribute.getZ( i ); + return this; - _vector$1.applyMatrix3( this ); + } - attribute.setXYZ( i, _vector$1.x, _vector$1.y, _vector$1.z ); + applyMatrix4( m ) { - } + const x = this.x, y = this.y, z = this.z, w = this.w; + const e = m.elements; - return attribute; + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; + this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; - }, + return this; - multiply: function ( m ) { + } - return this.multiplyMatrices( this, m ); + divideScalar( scalar ) { - }, + return this.multiplyScalar( 1 / scalar ); - premultiply: function ( m ) { + } - return this.multiplyMatrices( m, this ); + setAxisAngleFromQuaternion( q ) { - }, + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - multiplyMatrices: function ( a, b ) { + // q is assumed to be normalized - var ae = a.elements; - var be = b.elements; - var te = this.elements; + this.w = 2 * Math.acos( q.w ); - var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ]; - var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ]; - var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ]; + const s = Math.sqrt( 1 - q.w * q.w ); - var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ]; - var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ]; - var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ]; + if ( s < 0.0001 ) { - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31; - te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32; - te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33; + this.x = 1; + this.y = 0; + this.z = 0; - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31; - te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32; - te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33; + } else { - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31; - te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32; - te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33; + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; - return this; + } - }, + return this; - multiplyScalar: function ( s ) { + } - var te = this.elements; + setAxisAngleFromRotationMatrix( m ) { - te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; - te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; - te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm - return this; + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - }, + let angle, x, y, z; // variables for result + const epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees - determinant: function () { + te = m.elements, - var te = this.elements; + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], - d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], - g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; + if ( ( Math.abs( m12 - m21 ) < epsilon ) && + ( Math.abs( m13 - m31 ) < epsilon ) && + ( Math.abs( m23 - m32 ) < epsilon ) ) { - return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms - }, + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && + ( Math.abs( m13 + m31 ) < epsilon2 ) && + ( Math.abs( m23 + m32 ) < epsilon2 ) && + ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { - getInverse: function ( matrix, throwOnDegenerate ) { + // this singularity is identity matrix so angle = 0 - if ( matrix && matrix.isMatrix4 ) { + this.set( 1, 0, 0, 0 ); - console.error( "THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument." ); + return this; // zero angle, arbitrary axis } - var me = matrix.elements, - te = this.elements, - - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], - n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ], - n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ], + // otherwise this singularity is angle = 180 - t11 = n33 * n22 - n32 * n23, - t12 = n32 * n13 - n33 * n12, - t13 = n23 * n12 - n22 * n13, + angle = Math.PI; - det = n11 * t11 + n21 * t12 + n31 * t13; + const xx = ( m11 + 1 ) / 2; + const yy = ( m22 + 1 ) / 2; + const zz = ( m33 + 1 ) / 2; + const xy = ( m12 + m21 ) / 4; + const xz = ( m13 + m31 ) / 4; + const yz = ( m23 + m32 ) / 4; - if ( det === 0 ) { + if ( ( xx > yy ) && ( xx > zz ) ) { - var msg = "THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0"; + // m11 is the largest diagonal term - if ( throwOnDegenerate === true ) { + if ( xx < epsilon ) { - throw new Error( msg ); + x = 0; + y = 0.707106781; + z = 0.707106781; } else { - console.warn( msg ); + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; } - return this.identity(); + } else if ( yy > zz ) { - } + // m22 is the largest diagonal term - var detInv = 1 / det; + if ( yy < epsilon ) { - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv; - te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv; + x = 0.707106781; + y = 0; + z = 0.707106781; - te[ 3 ] = t12 * detInv; - te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv; - te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv; + } else { - te[ 6 ] = t13 * detInv; - te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv; - te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv; + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; - return this; + } - }, + } else { - transpose: function () { + // m33 is the largest diagonal term so base result on this - var tmp, m = this.elements; + if ( zz < epsilon ) { - tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; - tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; - tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; + x = 0.707106781; + y = 0.707106781; + z = 0; - return this; + } else { - }, + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; - getNormalMatrix: function ( matrix4 ) { + } - return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose(); + } - }, + this.set( x, y, z, angle ); - transposeIntoArray: function ( r ) { + return this; // return 180 deg rotation - var m = this.elements; + } - r[ 0 ] = m[ 0 ]; - r[ 1 ] = m[ 3 ]; - r[ 2 ] = m[ 6 ]; - r[ 3 ] = m[ 1 ]; - r[ 4 ] = m[ 4 ]; - r[ 5 ] = m[ 7 ]; - r[ 6 ] = m[ 2 ]; - r[ 7 ] = m[ 5 ]; - r[ 8 ] = m[ 8 ]; + // as we have reached here there are no singularities so we can handle normally - return this; + let s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize - }, + if ( Math.abs( s ) < 0.001 ) s = 1; - setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) { + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case - var c = Math.cos( rotation ); - var s = Math.sin( rotation ); + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); - this.set( - sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx, - - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty, - 0, 0, 1 - ); + return this; - }, + } - scale: function ( sx, sy ) { + min( v ) { - var te = this.elements; + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); + this.w = Math.min( this.w, v.w ); - te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx; - te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy; + return this; - return this; - - }, - - rotate: function ( theta ) { - - var c = Math.cos( theta ); - var s = Math.sin( theta ); - - var te = this.elements; + } - var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ]; - var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ]; + max( v ) { - te[ 0 ] = c * a11 + s * a21; - te[ 3 ] = c * a12 + s * a22; - te[ 6 ] = c * a13 + s * a23; + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); + this.w = Math.max( this.w, v.w ); - te[ 1 ] = - s * a11 + c * a21; - te[ 4 ] = - s * a12 + c * a22; - te[ 7 ] = - s * a13 + c * a23; + return this; - return this; + } - }, + clamp( min, max ) { - translate: function ( tx, ty ) { + // assumes min < max, componentwise - var te = this.elements; + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); + this.w = Math.max( min.w, Math.min( max.w, this.w ) ); - te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ]; - te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ]; + return this; - return this; + } - }, + clampScalar( minVal, maxVal ) { - equals: function ( matrix ) { + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); + this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); - var te = this.elements; - var me = matrix.elements; + return this; - for ( var i = 0; i < 9; i ++ ) { + } - if ( te[ i ] !== me[ i ] ) return false; + clampLength( min, max ) { - } + const length = this.length(); - return true; + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - }, + } - fromArray: function ( array, offset ) { + floor() { - if ( offset === undefined ) offset = 0; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); + this.w = Math.floor( this.w ); - for ( var i = 0; i < 9; i ++ ) { + return this; - this.elements[ i ] = array[ i + offset ]; + } - } + ceil() { - return this; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); + this.w = Math.ceil( this.w ); - }, + return this; - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + round() { - var te = this.elements; + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); + this.w = Math.round( this.w ); - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; + return this; - array[ offset + 3 ] = te[ 3 ]; - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; + } - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; - array[ offset + 8 ] = te[ 8 ]; + roundToZero() { - return array; + this.x = Math.trunc( this.x ); + this.y = Math.trunc( this.y ); + this.z = Math.trunc( this.z ); + this.w = Math.trunc( this.w ); - } + return this; - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ + negate() { - var _canvas; + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; + this.w = - this.w; - var ImageUtils = { + return this; - getDataURL: function ( image ) { + } - var canvas; + dot( v ) { - if ( typeof HTMLCanvasElement == 'undefined' ) { + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; - return image.src; + } - } else if ( image instanceof HTMLCanvasElement ) { + lengthSq() { - canvas = image; + return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; - } else { + } - if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + length() { - _canvas.width = image.width; - _canvas.height = image.height; + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); - var context = _canvas.getContext( '2d' ); + } - if ( image instanceof ImageData ) { + manhattanLength() { - context.putImageData( image, 0, 0 ); + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); - } else { + } - context.drawImage( image, 0, 0, image.width, image.height ); + normalize() { - } + return this.divideScalar( this.length() || 1 ); - canvas = _canvas; + } - } + setLength( length ) { - if ( canvas.width > 2048 || canvas.height > 2048 ) { + return this.normalize().multiplyScalar( length ); - return canvas.toDataURL( 'image/jpeg', 0.6 ); + } - } else { + lerp( v, alpha ) { - return canvas.toDataURL( 'image/png' ); + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; - } + return this; - } + } - }; + lerpVectors( v1, v2, alpha ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; + this.w = v1.w + ( v2.w - v1.w ) * alpha; - var textureId = 0; + return this; - function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { + } - Object.defineProperty( this, 'id', { value: textureId ++ } ); + equals( v ) { - this.uuid = _Math.generateUUID(); + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); - this.name = ''; + } - this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE; - this.mipmaps = []; + fromArray( array, offset = 0 ) { - this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; - this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping; + return this; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : LinearMipmapLinearFilter; + } - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + toArray( array = [], offset = 0 ) { - this.format = format !== undefined ? format : RGBAFormat; - this.type = type !== undefined ? type : UnsignedByteType; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; - this.offset = new Vector2( 0, 0 ); - this.repeat = new Vector2( 1, 1 ); - this.center = new Vector2( 0, 0 ); - this.rotation = 0; + return array; - this.matrixAutoUpdate = true; - this.matrix = new Matrix3(); + } - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + fromBufferAttribute( attribute, index ) { - // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. - // - // Also changing the encoding after already used by a Material will not automatically make the Material - // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. - this.encoding = encoding !== undefined ? encoding : LinearEncoding; + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); + this.w = attribute.getW( index ); - this.version = 0; - this.onUpdate = null; + return this; } - Texture.DEFAULT_IMAGE = undefined; - Texture.DEFAULT_MAPPING = UVMapping; - - Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + random() { - constructor: Texture, + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); + this.w = Math.random(); - isTexture: true, - - updateMatrix: function () { - - this.matrix.setUvTransform( this.offset.x, this.offset.y, this.repeat.x, this.repeat.y, this.rotation, this.center.x, this.center.y ); + return this; - }, + } - clone: function () { + *[ Symbol.iterator ]() { - return new this.constructor().copy( this ); + yield this.x; + yield this.y; + yield this.z; + yield this.w; - }, + } - copy: function ( source ) { +} - this.name = source.name; +/* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers +*/ +class RenderTarget extends EventDispatcher { - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); + constructor( width = 1, height = 1, options = {} ) { - this.mapping = source.mapping; + super(); - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + this.isRenderTarget = true; - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + this.width = width; + this.height = height; + this.depth = 1; - this.anisotropy = source.anisotropy; + this.scissor = new Vector4( 0, 0, width, height ); + this.scissorTest = false; - this.format = source.format; - this.type = source.type; + this.viewport = new Vector4( 0, 0, width, height ); - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); - this.center.copy( source.center ); - this.rotation = source.rotation; + const image = { width: width, height: height, depth: 1 }; - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrix.copy( source.matrix ); + if ( options.encoding !== undefined ) { - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; - this.encoding = source.encoding; + // @deprecated, r152 + warnOnce( 'THREE.WebGLRenderTarget: option.encoding has been replaced by option.colorSpace.' ); + options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; - return this; + } - }, + options = Object.assign( { + generateMipmaps: false, + internalFormat: null, + minFilter: LinearFilter, + depthBuffer: true, + stencilBuffer: false, + depthTexture: null, + samples: 0 + }, options ); - toJSON: function ( meta ) { + this.texture = new Texture( image, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); + this.texture.isRenderTargetTexture = true; - var isRootObject = ( meta === undefined || typeof meta === 'string' ); + this.texture.flipY = false; + this.texture.generateMipmaps = options.generateMipmaps; + this.texture.internalFormat = options.internalFormat; - if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) { + this.depthBuffer = options.depthBuffer; + this.stencilBuffer = options.stencilBuffer; - return meta.textures[ this.uuid ]; + this.depthTexture = options.depthTexture; - } + this.samples = options.samples; - var output = { + } - metadata: { - version: 4.5, - type: 'Texture', - generator: 'Texture.toJSON' - }, + setSize( width, height, depth = 1 ) { - uuid: this.uuid, - name: this.name, + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - mapping: this.mapping, + this.width = width; + this.height = height; + this.depth = depth; - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - center: [ this.center.x, this.center.y ], - rotation: this.rotation, + this.texture.image.width = width; + this.texture.image.height = height; + this.texture.image.depth = depth; - wrap: [ this.wrapS, this.wrapT ], + this.dispose(); - format: this.format, - type: this.type, - encoding: this.encoding, + } - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy, + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - flipY: this.flipY, + } - premultiplyAlpha: this.premultiplyAlpha, - unpackAlignment: this.unpackAlignment + clone() { - }; + return new this.constructor().copy( this ); - if ( this.image !== undefined ) { + } - // TODO: Move to THREE.Image + copy( source ) { - var image = this.image; + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - if ( image.uuid === undefined ) { + this.scissor.copy( source.scissor ); + this.scissorTest = source.scissorTest; - image.uuid = _Math.generateUUID(); // UGH + this.viewport.copy( source.viewport ); - } + this.texture = source.texture.clone(); + this.texture.isRenderTargetTexture = true; - if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) { + // ensure image object is not shared, see #20328 - var url; + const image = Object.assign( {}, source.texture.image ); + this.texture.source = new Source( image ); - if ( Array.isArray( image ) ) { + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - // process array of images e.g. CubeTexture + if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); - url = []; + this.samples = source.samples; - for ( var i = 0, l = image.length; i < l; i ++ ) { + return this; - url.push( ImageUtils.getDataURL( image[ i ] ) ); + } - } + dispose() { - } else { + this.dispatchEvent( { type: 'dispose' } ); - // process single image + } - url = ImageUtils.getDataURL( image ); +} - } +class WebGLRenderTarget extends RenderTarget { - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: url - }; + constructor( width = 1, height = 1, options = {} ) { - } + super( width, height, options ); - output.image = image.uuid; + this.isWebGLRenderTarget = true; - } + } - if ( ! isRootObject ) { +} - meta.textures[ this.uuid ] = output; +class DataArrayTexture extends Texture { - } + constructor( data = null, width = 1, height = 1, depth = 1 ) { - return output; + super( null ); - }, + this.isDataArrayTexture = true; - dispose: function () { + this.image = { data, width, height, depth }; - this.dispatchEvent( { type: 'dispose' } ); + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - }, + this.wrapR = ClampToEdgeWrapping; - transformUv: function ( uv ) { + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - if ( this.mapping !== UVMapping ) return uv; + } - uv.applyMatrix3( this.matrix ); +} - if ( uv.x < 0 || uv.x > 1 ) { +class WebGLArrayRenderTarget extends WebGLRenderTarget { - switch ( this.wrapS ) { + constructor( width = 1, height = 1, depth = 1 ) { - case RepeatWrapping: + super( width, height ); - uv.x = uv.x - Math.floor( uv.x ); - break; + this.isWebGLArrayRenderTarget = true; - case ClampToEdgeWrapping: + this.depth = depth; - uv.x = uv.x < 0 ? 0 : 1; - break; + this.texture = new DataArrayTexture( null, width, height, depth ); - case MirroredRepeatWrapping: + this.texture.isRenderTargetTexture = true; - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + } - uv.x = Math.ceil( uv.x ) - uv.x; +} - } else { +class Data3DTexture extends Texture { - uv.x = uv.x - Math.floor( uv.x ); + constructor( data = null, width = 1, height = 1, depth = 1 ) { - } - break; + // We're going to add .setXXX() methods for setting properties later. + // Users can still set in DataTexture3D directly. + // + // const texture = new THREE.DataTexture3D( data, width, height, depth ); + // texture.anisotropy = 16; + // + // See #14839 - } + super( null ); - } + this.isData3DTexture = true; - if ( uv.y < 0 || uv.y > 1 ) { + this.image = { data, width, height, depth }; - switch ( this.wrapT ) { + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - case RepeatWrapping: + this.wrapR = ClampToEdgeWrapping; - uv.y = uv.y - Math.floor( uv.y ); - break; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - case ClampToEdgeWrapping: + } - uv.y = uv.y < 0 ? 0 : 1; - break; +} - case MirroredRepeatWrapping: +class WebGL3DRenderTarget extends WebGLRenderTarget { - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { + constructor( width = 1, height = 1, depth = 1 ) { - uv.y = Math.ceil( uv.y ) - uv.y; + super( width, height ); - } else { + this.isWebGL3DRenderTarget = true; - uv.y = uv.y - Math.floor( uv.y ); + this.depth = depth; - } - break; + this.texture = new Data3DTexture( null, width, height, depth ); - } + this.texture.isRenderTargetTexture = true; - } + } - if ( this.flipY ) { +} - uv.y = 1 - uv.y; +class WebGLMultipleRenderTargets extends WebGLRenderTarget { - } + constructor( width = 1, height = 1, count = 1, options = {} ) { - return uv; + super( width, height, options ); - } + this.isWebGLMultipleRenderTargets = true; - } ); + const texture = this.texture; - Object.defineProperty( Texture.prototype, "needsUpdate", { + this.texture = []; - set: function ( value ) { + for ( let i = 0; i < count; i ++ ) { - if ( value === true ) this.version ++; + this.texture[ i ] = texture.clone(); + this.texture[ i ].isRenderTargetTexture = true; } - } ); - - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author mikael emtinger / http://gomo.se/ - * @author egraether / http://egraether.com/ - * @author WestLangley / http://github.com/WestLangley - */ - - function Vector4( x, y, z, w ) { - - this.x = x || 0; - this.y = y || 0; - this.z = z || 0; - this.w = ( w !== undefined ) ? w : 1; - } - Object.defineProperties( Vector4.prototype, { - - "width": { - - get: function () { + setSize( width, height, depth = 1 ) { - return this.z; + if ( this.width !== width || this.height !== height || this.depth !== depth ) { - }, + this.width = width; + this.height = height; + this.depth = depth; - set: function ( value ) { + for ( let i = 0, il = this.texture.length; i < il; i ++ ) { - this.z = value; + this.texture[ i ].image.width = width; + this.texture[ i ].image.height = height; + this.texture[ i ].image.depth = depth; } - }, - - "height": { + this.dispose(); - get: function () { + } - return this.w; + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - }, + } - set: function ( value ) { + copy( source ) { - this.w = value; + this.dispose(); - } + this.width = source.width; + this.height = source.height; + this.depth = source.depth; - } + this.scissor.copy( source.scissor ); + this.scissorTest = source.scissorTest; - } ); + this.viewport.copy( source.viewport ); - Object.assign( Vector4.prototype, { + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - isVector4: true, + if ( source.depthTexture !== null ) this.depthTexture = source.depthTexture.clone(); - set: function ( x, y, z, w ) { + this.texture.length = 0; - this.x = x; - this.y = y; - this.z = z; - this.w = w; + for ( let i = 0, il = source.texture.length; i < il; i ++ ) { - return this; + this.texture[ i ] = source.texture[ i ].clone(); + this.texture[ i ].isRenderTargetTexture = true; - }, + } - setScalar: function ( scalar ) { + return this; - this.x = scalar; - this.y = scalar; - this.z = scalar; - this.w = scalar; + } - return this; +} - }, +class Quaternion { - setX: function ( x ) { + constructor( x = 0, y = 0, z = 0, w = 1 ) { - this.x = x; + this.isQuaternion = true; - return this; + this._x = x; + this._y = y; + this._z = z; + this._w = w; - }, + } - setY: function ( y ) { + static slerpFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { - this.y = y; + // fuzz-free, array-based Quaternion SLERP operation - return this; + let x0 = src0[ srcOffset0 + 0 ], + y0 = src0[ srcOffset0 + 1 ], + z0 = src0[ srcOffset0 + 2 ], + w0 = src0[ srcOffset0 + 3 ]; - }, + const x1 = src1[ srcOffset1 + 0 ], + y1 = src1[ srcOffset1 + 1 ], + z1 = src1[ srcOffset1 + 2 ], + w1 = src1[ srcOffset1 + 3 ]; - setZ: function ( z ) { + if ( t === 0 ) { - this.z = z; + dst[ dstOffset + 0 ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; + return; - return this; + } - }, + if ( t === 1 ) { - setW: function ( w ) { + dst[ dstOffset + 0 ] = x1; + dst[ dstOffset + 1 ] = y1; + dst[ dstOffset + 2 ] = z1; + dst[ dstOffset + 3 ] = w1; + return; - this.w = w; + } - return this; + if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { - }, + let s = 1 - t; + const cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, + dir = ( cos >= 0 ? 1 : - 1 ), + sqrSin = 1 - cos * cos; - setComponent: function ( index, value ) { + // Skip the Slerp for tiny steps to avoid numeric problems: + if ( sqrSin > Number.EPSILON ) { - switch ( index ) { + const sin = Math.sqrt( sqrSin ), + len = Math.atan2( sin, cos * dir ); - case 0: this.x = value; break; - case 1: this.y = value; break; - case 2: this.z = value; break; - case 3: this.w = value; break; - default: throw new Error( 'index is out of range: ' + index ); + s = Math.sin( s * len ) / sin; + t = Math.sin( t * len ) / sin; } - return this; + const tDir = t * dir; - }, + x0 = x0 * s + x1 * tDir; + y0 = y0 * s + y1 * tDir; + z0 = z0 * s + z1 * tDir; + w0 = w0 * s + w1 * tDir; - getComponent: function ( index ) { + // Normalize in case we just did a lerp: + if ( s === 1 - t ) { - switch ( index ) { + const f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); - case 0: return this.x; - case 1: return this.y; - case 2: return this.z; - case 3: return this.w; - default: throw new Error( 'index is out of range: ' + index ); + x0 *= f; + y0 *= f; + z0 *= f; + w0 *= f; } - }, + } - clone: function () { + dst[ dstOffset ] = x0; + dst[ dstOffset + 1 ] = y0; + dst[ dstOffset + 2 ] = z0; + dst[ dstOffset + 3 ] = w0; - return new this.constructor( this.x, this.y, this.z, this.w ); + } - }, + static multiplyQuaternionsFlat( dst, dstOffset, src0, srcOffset0, src1, srcOffset1 ) { - copy: function ( v ) { + const x0 = src0[ srcOffset0 ]; + const y0 = src0[ srcOffset0 + 1 ]; + const z0 = src0[ srcOffset0 + 2 ]; + const w0 = src0[ srcOffset0 + 3 ]; - this.x = v.x; - this.y = v.y; - this.z = v.z; - this.w = ( v.w !== undefined ) ? v.w : 1; + const x1 = src1[ srcOffset1 ]; + const y1 = src1[ srcOffset1 + 1 ]; + const z1 = src1[ srcOffset1 + 2 ]; + const w1 = src1[ srcOffset1 + 3 ]; - return this; + dst[ dstOffset ] = x0 * w1 + w0 * x1 + y0 * z1 - z0 * y1; + dst[ dstOffset + 1 ] = y0 * w1 + w0 * y1 + z0 * x1 - x0 * z1; + dst[ dstOffset + 2 ] = z0 * w1 + w0 * z1 + x0 * y1 - y0 * x1; + dst[ dstOffset + 3 ] = w0 * w1 - x0 * x1 - y0 * y1 - z0 * z1; - }, + return dst; - add: function ( v, w ) { + } - if ( w !== undefined ) { + get x() { - console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); - return this.addVectors( v, w ); + return this._x; - } + } - this.x += v.x; - this.y += v.y; - this.z += v.z; - this.w += v.w; + set x( value ) { - return this; + this._x = value; + this._onChangeCallback(); - }, + } - addScalar: function ( s ) { + get y() { - this.x += s; - this.y += s; - this.z += s; - this.w += s; + return this._y; - return this; + } - }, + set y( value ) { - addVectors: function ( a, b ) { + this._y = value; + this._onChangeCallback(); - this.x = a.x + b.x; - this.y = a.y + b.y; - this.z = a.z + b.z; - this.w = a.w + b.w; + } - return this; + get z() { - }, + return this._z; - addScaledVector: function ( v, s ) { + } - this.x += v.x * s; - this.y += v.y * s; - this.z += v.z * s; - this.w += v.w * s; + set z( value ) { - return this; + this._z = value; + this._onChangeCallback(); - }, + } - sub: function ( v, w ) { + get w() { - if ( w !== undefined ) { + return this._w; - console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); - return this.subVectors( v, w ); + } - } + set w( value ) { - this.x -= v.x; - this.y -= v.y; - this.z -= v.z; - this.w -= v.w; + this._w = value; + this._onChangeCallback(); - return this; + } - }, + set( x, y, z, w ) { - subScalar: function ( s ) { + this._x = x; + this._y = y; + this._z = z; + this._w = w; - this.x -= s; - this.y -= s; - this.z -= s; - this.w -= s; + this._onChangeCallback(); - return this; + return this; - }, + } - subVectors: function ( a, b ) { + clone() { - this.x = a.x - b.x; - this.y = a.y - b.y; - this.z = a.z - b.z; - this.w = a.w - b.w; + return new this.constructor( this._x, this._y, this._z, this._w ); - return this; + } - }, + copy( quaternion ) { - multiplyScalar: function ( scalar ) { + this._x = quaternion.x; + this._y = quaternion.y; + this._z = quaternion.z; + this._w = quaternion.w; - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - this.w *= scalar; + this._onChangeCallback(); - return this; + return this; - }, + } - applyMatrix4: function ( m ) { + setFromEuler( euler, update ) { - var x = this.x, y = this.y, z = this.z, w = this.w; - var e = m.elements; + const x = euler._x, y = euler._y, z = euler._z, order = euler._order; - this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; - this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; - this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; - this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m - return this; + const cos = Math.cos; + const sin = Math.sin; - }, + const c1 = cos( x / 2 ); + const c2 = cos( y / 2 ); + const c3 = cos( z / 2 ); - divideScalar: function ( scalar ) { + const s1 = sin( x / 2 ); + const s2 = sin( y / 2 ); + const s3 = sin( z / 2 ); - return this.multiplyScalar( 1 / scalar ); + switch ( order ) { - }, + case 'XYZ': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; - setAxisAngleFromQuaternion: function ( q ) { + case 'YXZ': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm - - // q is assumed to be normalized - - this.w = 2 * Math.acos( q.w ); + case 'ZXY': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; - var s = Math.sqrt( 1 - q.w * q.w ); + case 'ZYX': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; - if ( s < 0.0001 ) { + case 'YZX': + this._x = s1 * c2 * c3 + c1 * s2 * s3; + this._y = c1 * s2 * c3 + s1 * c2 * s3; + this._z = c1 * c2 * s3 - s1 * s2 * c3; + this._w = c1 * c2 * c3 - s1 * s2 * s3; + break; - this.x = 1; - this.y = 0; - this.z = 0; + case 'XZY': + this._x = s1 * c2 * c3 - c1 * s2 * s3; + this._y = c1 * s2 * c3 - s1 * c2 * s3; + this._z = c1 * c2 * s3 + s1 * s2 * c3; + this._w = c1 * c2 * c3 + s1 * s2 * s3; + break; - } else { + default: + console.warn( 'THREE.Quaternion: .setFromEuler() encountered an unknown order: ' + order ); - this.x = q.x / s; - this.y = q.y / s; - this.z = q.z / s; + } - } + if ( update !== false ) this._onChangeCallback(); - return this; + return this; - }, + } - setAxisAngleFromRotationMatrix: function ( m ) { + setFromAxisAngle( axis, angle ) { - // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + // assumes axis is normalized - var angle, x, y, z, // variables for result - epsilon = 0.01, // margin to allow for rounding errors - epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + const halfAngle = angle / 2, s = Math.sin( halfAngle ); - te = m.elements, + this._x = axis.x * s; + this._y = axis.y * s; + this._z = axis.z * s; + this._w = Math.cos( halfAngle ); - m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], - m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], - m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + this._onChangeCallback(); - if ( ( Math.abs( m12 - m21 ) < epsilon ) && - ( Math.abs( m13 - m31 ) < epsilon ) && - ( Math.abs( m23 - m32 ) < epsilon ) ) { + return this; - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms + } - if ( ( Math.abs( m12 + m21 ) < epsilon2 ) && - ( Math.abs( m13 + m31 ) < epsilon2 ) && - ( Math.abs( m23 + m32 ) < epsilon2 ) && - ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + setFromRotationMatrix( m ) { - // this singularity is identity matrix so angle = 0 + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm - this.set( 1, 0, 0, 0 ); + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - return this; // zero angle, arbitrary axis + const te = m.elements, - } + m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], + m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], + m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], - // otherwise this singularity is angle = 180 + trace = m11 + m22 + m33; - angle = Math.PI; + if ( trace > 0 ) { - var xx = ( m11 + 1 ) / 2; - var yy = ( m22 + 1 ) / 2; - var zz = ( m33 + 1 ) / 2; - var xy = ( m12 + m21 ) / 4; - var xz = ( m13 + m31 ) / 4; - var yz = ( m23 + m32 ) / 4; + const s = 0.5 / Math.sqrt( trace + 1.0 ); - if ( ( xx > yy ) && ( xx > zz ) ) { + this._w = 0.25 / s; + this._x = ( m32 - m23 ) * s; + this._y = ( m13 - m31 ) * s; + this._z = ( m21 - m12 ) * s; - // m11 is the largest diagonal term + } else if ( m11 > m22 && m11 > m33 ) { - if ( xx < epsilon ) { + const s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); - x = 0; - y = 0.707106781; - z = 0.707106781; + this._w = ( m32 - m23 ) / s; + this._x = 0.25 * s; + this._y = ( m12 + m21 ) / s; + this._z = ( m13 + m31 ) / s; - } else { + } else if ( m22 > m33 ) { - x = Math.sqrt( xx ); - y = xy / x; - z = xz / x; + const s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); - } + this._w = ( m13 - m31 ) / s; + this._x = ( m12 + m21 ) / s; + this._y = 0.25 * s; + this._z = ( m23 + m32 ) / s; - } else if ( yy > zz ) { + } else { - // m22 is the largest diagonal term + const s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); - if ( yy < epsilon ) { + this._w = ( m21 - m12 ) / s; + this._x = ( m13 + m31 ) / s; + this._y = ( m23 + m32 ) / s; + this._z = 0.25 * s; - x = 0.707106781; - y = 0; - z = 0.707106781; + } - } else { + this._onChangeCallback(); - y = Math.sqrt( yy ); - x = xy / y; - z = yz / y; + return this; - } + } - } else { + setFromUnitVectors( vFrom, vTo ) { - // m33 is the largest diagonal term so base result on this + // assumes direction vectors vFrom and vTo are normalized - if ( zz < epsilon ) { + let r = vFrom.dot( vTo ) + 1; - x = 0.707106781; - y = 0.707106781; - z = 0; + if ( r < Number.EPSILON ) { - } else { + // vFrom and vTo point in opposite directions - z = Math.sqrt( zz ); - x = xz / z; - y = yz / z; + r = 0; - } + if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { - } + this._x = - vFrom.y; + this._y = vFrom.x; + this._z = 0; + this._w = r; - this.set( x, y, z, angle ); + } else { - return this; // return 180 deg rotation + this._x = 0; + this._y = - vFrom.z; + this._z = vFrom.y; + this._w = r; } - // as we have reached here there are no singularities so we can handle normally + } else { - var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + - ( m13 - m31 ) * ( m13 - m31 ) + - ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + // crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 - if ( Math.abs( s ) < 0.001 ) s = 1; + this._x = vFrom.y * vTo.z - vFrom.z * vTo.y; + this._y = vFrom.z * vTo.x - vFrom.x * vTo.z; + this._z = vFrom.x * vTo.y - vFrom.y * vTo.x; + this._w = r; - // prevent divide by zero, should not happen if matrix is orthogonal and should be - // caught by singularity test above, but I've left it in just in case + } - this.x = ( m32 - m23 ) / s; - this.y = ( m13 - m31 ) / s; - this.z = ( m21 - m12 ) / s; - this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + return this.normalize(); - return this; + } - }, + angleTo( q ) { - min: function ( v ) { + return 2 * Math.acos( Math.abs( clamp( this.dot( q ), - 1, 1 ) ) ); - this.x = Math.min( this.x, v.x ); - this.y = Math.min( this.y, v.y ); - this.z = Math.min( this.z, v.z ); - this.w = Math.min( this.w, v.w ); + } - return this; + rotateTowards( q, step ) { - }, + const angle = this.angleTo( q ); - max: function ( v ) { + if ( angle === 0 ) return this; - this.x = Math.max( this.x, v.x ); - this.y = Math.max( this.y, v.y ); - this.z = Math.max( this.z, v.z ); - this.w = Math.max( this.w, v.w ); + const t = Math.min( 1, step / angle ); - return this; + this.slerp( q, t ); - }, + return this; - clamp: function ( min, max ) { + } - // assumes min < max, componentwise + identity() { - this.x = Math.max( min.x, Math.min( max.x, this.x ) ); - this.y = Math.max( min.y, Math.min( max.y, this.y ) ); - this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - this.w = Math.max( min.w, Math.min( max.w, this.w ) ); + return this.set( 0, 0, 0, 1 ); - return this; + } - }, + invert() { - clampScalar: function ( minVal, maxVal ) { + // quaternion is assumed to have unit length - this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); - this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); - this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - this.w = Math.max( minVal, Math.min( maxVal, this.w ) ); + return this.conjugate(); - return this; + } - }, + conjugate() { - clampLength: function ( min, max ) { + this._x *= - 1; + this._y *= - 1; + this._z *= - 1; - var length = this.length(); + this._onChangeCallback(); - return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); + return this; - }, + } - floor: function () { + dot( v ) { - this.x = Math.floor( this.x ); - this.y = Math.floor( this.y ); - this.z = Math.floor( this.z ); - this.w = Math.floor( this.w ); + return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; - return this; + } - }, + lengthSq() { - ceil: function () { + return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; - this.x = Math.ceil( this.x ); - this.y = Math.ceil( this.y ); - this.z = Math.ceil( this.z ); - this.w = Math.ceil( this.w ); + } - return this; + length() { - }, + return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); - round: function () { + } - this.x = Math.round( this.x ); - this.y = Math.round( this.y ); - this.z = Math.round( this.z ); - this.w = Math.round( this.w ); + normalize() { - return this; + let l = this.length(); - }, + if ( l === 0 ) { - roundToZero: function () { + this._x = 0; + this._y = 0; + this._z = 0; + this._w = 1; - this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); - this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); - this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); - this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); + } else { - return this; + l = 1 / l; - }, + this._x = this._x * l; + this._y = this._y * l; + this._z = this._z * l; + this._w = this._w * l; - negate: function () { + } - this.x = - this.x; - this.y = - this.y; - this.z = - this.z; - this.w = - this.w; + this._onChangeCallback(); - return this; + return this; - }, + } - dot: function ( v ) { + multiply( q ) { - return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + return this.multiplyQuaternions( this, q ); - }, + } - lengthSq: function () { + premultiply( q ) { - return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; + return this.multiplyQuaternions( q, this ); - }, + } - length: function () { + multiplyQuaternions( a, b ) { - return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm - }, + const qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; + const qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; - manhattanLength: function () { + this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; - return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + this._onChangeCallback(); - }, + return this; + + } - normalize: function () { + slerp( qb, t ) { - return this.divideScalar( this.length() || 1 ); + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); - }, + const x = this._x, y = this._y, z = this._z, w = this._w; - setLength: function ( length ) { + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ - return this.normalize().multiplyScalar( length ); + let cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; - }, + if ( cosHalfTheta < 0 ) { - lerp: function ( v, alpha ) { + this._w = - qb._w; + this._x = - qb._x; + this._y = - qb._y; + this._z = - qb._z; - this.x += ( v.x - this.x ) * alpha; - this.y += ( v.y - this.y ) * alpha; - this.z += ( v.z - this.z ) * alpha; - this.w += ( v.w - this.w ) * alpha; + cosHalfTheta = - cosHalfTheta; - return this; + } else { - }, + this.copy( qb ); - lerpVectors: function ( v1, v2, alpha ) { + } - return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); + if ( cosHalfTheta >= 1.0 ) { - }, + this._w = w; + this._x = x; + this._y = y; + this._z = z; - equals: function ( v ) { + return this; - return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); + } - }, + const sqrSinHalfTheta = 1.0 - cosHalfTheta * cosHalfTheta; - fromArray: function ( array, offset ) { + if ( sqrSinHalfTheta <= Number.EPSILON ) { - if ( offset === undefined ) offset = 0; + const s = 1 - t; + this._w = s * w + t * this._w; + this._x = s * x + t * this._x; + this._y = s * y + t * this._y; + this._z = s * z + t * this._z; - this.x = array[ offset ]; - this.y = array[ offset + 1 ]; - this.z = array[ offset + 2 ]; - this.w = array[ offset + 3 ]; + this.normalize(); + this._onChangeCallback(); return this; - }, - - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + const sinHalfTheta = Math.sqrt( sqrSinHalfTheta ); + const halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); + const ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; - array[ offset ] = this.x; - array[ offset + 1 ] = this.y; - array[ offset + 2 ] = this.z; - array[ offset + 3 ] = this.w; + this._w = ( w * ratioA + this._w * ratioB ); + this._x = ( x * ratioA + this._x * ratioB ); + this._y = ( y * ratioA + this._y * ratioB ); + this._z = ( z * ratioA + this._z * ratioB ); - return array; + this._onChangeCallback(); - }, + return this; - fromBufferAttribute: function ( attribute, index, offset ) { + } - if ( offset !== undefined ) { + slerpQuaternions( qa, qb, t ) { - console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' ); + return this.copy( qa ).slerp( qb, t ); - } + } - this.x = attribute.getX( index ); - this.y = attribute.getY( index ); - this.z = attribute.getZ( index ); - this.w = attribute.getW( index ); + random() { - return this; + // Derived from http://planning.cs.uiuc.edu/node198.html + // Note, this source uses w, x, y, z ordering, + // so we swap the order below. - } + const u1 = Math.random(); + const sqrt1u1 = Math.sqrt( 1 - u1 ); + const sqrtu1 = Math.sqrt( u1 ); - } ); + const u2 = 2 * Math.PI * Math.random(); - /** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - * @author Marius Kintel / https://github.com/kintel - */ + const u3 = 2 * Math.PI * Math.random(); - /* - In options, we can specify: - * Texture parameters for an auto-generated target texture - * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers - */ - function WebGLRenderTarget( width, height, options ) { + return this.set( + sqrt1u1 * Math.cos( u2 ), + sqrtu1 * Math.sin( u3 ), + sqrtu1 * Math.cos( u3 ), + sqrt1u1 * Math.sin( u2 ), + ); - this.width = width; - this.height = height; + } - this.scissor = new Vector4( 0, 0, width, height ); - this.scissorTest = false; + equals( quaternion ) { - this.viewport = new Vector4( 0, 0, width, height ); + return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); - options = options || {}; + } - this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); + fromArray( array, offset = 0 ) { - this.texture.image = {}; - this.texture.image.width = width; - this.texture.image.height = height; + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; - this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; - this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; + this._onChangeCallback(); - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null; + return this; } - WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + toArray( array = [], offset = 0 ) { - constructor: WebGLRenderTarget, + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; - isWebGLRenderTarget: true, + return array; - setSize: function ( width, height ) { + } - if ( this.width !== width || this.height !== height ) { + fromBufferAttribute( attribute, index ) { - this.width = width; - this.height = height; + this._x = attribute.getX( index ); + this._y = attribute.getY( index ); + this._z = attribute.getZ( index ); + this._w = attribute.getW( index ); - this.texture.image.width = width; - this.texture.image.height = height; + return this; - this.dispose(); + } - } + toJSON() { - this.viewport.set( 0, 0, width, height ); - this.scissor.set( 0, 0, width, height ); + return this.toArray(); - }, + } - clone: function () { + _onChange( callback ) { - return new this.constructor().copy( this ); + this._onChangeCallback = callback; - }, + return this; - copy: function ( source ) { + } - this.width = source.width; - this.height = source.height; + _onChangeCallback() {} - this.viewport.copy( source.viewport ); + *[ Symbol.iterator ]() { - this.texture = source.texture.clone(); + yield this._x; + yield this._y; + yield this._z; + yield this._w; - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; - this.depthTexture = source.depthTexture; + } - return this; +} - }, +class Vector3 { - dispose: function () { + constructor( x = 0, y = 0, z = 0 ) { - this.dispatchEvent( { type: 'dispose' } ); + Vector3.prototype.isVector3 = true; - } + this.x = x; + this.y = y; + this.z = z; - } ); + } - /** - * @author Mugen87 / https://github.com/Mugen87 - * @author Matt DesLauriers / @mattdesl - */ + set( x, y, z ) { - function WebGLMultisampleRenderTarget( width, height, options ) { + if ( z === undefined ) z = this.z; // sprite.scale.set(x,y) - WebGLRenderTarget.call( this, width, height, options ); + this.x = x; + this.y = y; + this.z = z; - this.samples = 4; + return this; } - WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { + setScalar( scalar ) { - constructor: WebGLMultisampleRenderTarget, + this.x = scalar; + this.y = scalar; + this.z = scalar; - isWebGLMultisampleRenderTarget: true, + return this; - copy: function ( source ) { + } - WebGLRenderTarget.prototype.copy.call( this, source ); + setX( x ) { - this.samples = source.samples; + this.x = x; - return this; + return this; - } + } - } ); + setY( y ) { - var _v1 = new Vector3(); - var _m1 = new Matrix4(); - var _zero = new Vector3( 0, 0, 0 ); - var _one = new Vector3( 1, 1, 1 ); - var _x = new Vector3(); - var _y = new Vector3(); - var _z = new Vector3(); + this.y = y; - /** - * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author philogb / http://blog.thejit.org/ - * @author jordi_ros / http://plattsoft.com - * @author D1plo1d / http://github.com/D1plo1d - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ + return this; - function Matrix4() { + } - this.elements = [ + setZ( z ) { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + this.z = z; - ]; + return this; - if ( arguments.length > 0 ) { + } + + setComponent( index, value ) { - console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); + switch ( index ) { + + case 0: this.x = value; break; + case 1: this.y = value; break; + case 2: this.z = value; break; + default: throw new Error( 'index is out of range: ' + index ); } + return this; + } - Object.assign( Matrix4.prototype, { + getComponent( index ) { - isMatrix4: true, + switch ( index ) { - set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + case 0: return this.x; + case 1: return this.y; + case 2: return this.z; + default: throw new Error( 'index is out of range: ' + index ); - var te = this.elements; + } - te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; - te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; - te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; - te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; + } - return this; + clone() { - }, + return new this.constructor( this.x, this.y, this.z ); - identity: function () { + } - this.set( + copy( v ) { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + this.x = v.x; + this.y = v.y; + this.z = v.z; - ); + return this; - return this; + } - }, + add( v ) { - clone: function () { + this.x += v.x; + this.y += v.y; + this.z += v.z; - return new Matrix4().fromArray( this.elements ); + return this; - }, + } - copy: function ( m ) { + addScalar( s ) { - var te = this.elements; - var me = m.elements; + this.x += s; + this.y += s; + this.z += s; - te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; - te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; - te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; - te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; + return this; - return this; + } - }, + addVectors( a, b ) { - copyPosition: function ( m ) { + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; - var te = this.elements, me = m.elements; + return this; - te[ 12 ] = me[ 12 ]; - te[ 13 ] = me[ 13 ]; - te[ 14 ] = me[ 14 ]; + } - return this; + addScaledVector( v, s ) { - }, + this.x += v.x * s; + this.y += v.y * s; + this.z += v.z * s; - extractBasis: function ( xAxis, yAxis, zAxis ) { + return this; - xAxis.setFromMatrixColumn( this, 0 ); - yAxis.setFromMatrixColumn( this, 1 ); - zAxis.setFromMatrixColumn( this, 2 ); + } - return this; + sub( v ) { - }, + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; - makeBasis: function ( xAxis, yAxis, zAxis ) { + return this; - this.set( - xAxis.x, yAxis.x, zAxis.x, 0, - xAxis.y, yAxis.y, zAxis.y, 0, - xAxis.z, yAxis.z, zAxis.z, 0, - 0, 0, 0, 1 - ); + } - return this; + subScalar( s ) { - }, + this.x -= s; + this.y -= s; + this.z -= s; - extractRotation: function ( m ) { + return this; - // this method does not support reflection matrices + } - var te = this.elements; - var me = m.elements; + subVectors( a, b ) { - var scaleX = 1 / _v1.setFromMatrixColumn( m, 0 ).length(); - var scaleY = 1 / _v1.setFromMatrixColumn( m, 1 ).length(); - var scaleZ = 1 / _v1.setFromMatrixColumn( m, 2 ).length(); + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; - te[ 0 ] = me[ 0 ] * scaleX; - te[ 1 ] = me[ 1 ] * scaleX; - te[ 2 ] = me[ 2 ] * scaleX; - te[ 3 ] = 0; + return this; - te[ 4 ] = me[ 4 ] * scaleY; - te[ 5 ] = me[ 5 ] * scaleY; - te[ 6 ] = me[ 6 ] * scaleY; - te[ 7 ] = 0; + } - te[ 8 ] = me[ 8 ] * scaleZ; - te[ 9 ] = me[ 9 ] * scaleZ; - te[ 10 ] = me[ 10 ] * scaleZ; - te[ 11 ] = 0; + multiply( v ) { - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; - return this; + return this; - }, + } - makeRotationFromEuler: function ( euler ) { + multiplyScalar( scalar ) { - if ( ! ( euler && euler.isEuler ) ) { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; - console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); + return this; - } + } - var te = this.elements; + multiplyVectors( a, b ) { - var x = euler.x, y = euler.y, z = euler.z; - var a = Math.cos( x ), b = Math.sin( x ); - var c = Math.cos( y ), d = Math.sin( y ); - var e = Math.cos( z ), f = Math.sin( z ); + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; - if ( euler.order === 'XYZ' ) { + return this; - var ae = a * e, af = a * f, be = b * e, bf = b * f; + } - te[ 0 ] = c * e; - te[ 4 ] = - c * f; - te[ 8 ] = d; + applyEuler( euler ) { - te[ 1 ] = af + be * d; - te[ 5 ] = ae - bf * d; - te[ 9 ] = - b * c; + return this.applyQuaternion( _quaternion$4.setFromEuler( euler ) ); - te[ 2 ] = bf - ae * d; - te[ 6 ] = be + af * d; - te[ 10 ] = a * c; + } - } else if ( euler.order === 'YXZ' ) { + applyAxisAngle( axis, angle ) { - var ce = c * e, cf = c * f, de = d * e, df = d * f; + return this.applyQuaternion( _quaternion$4.setFromAxisAngle( axis, angle ) ); - te[ 0 ] = ce + df * b; - te[ 4 ] = de * b - cf; - te[ 8 ] = a * d; + } - te[ 1 ] = a * f; - te[ 5 ] = a * e; - te[ 9 ] = - b; + applyMatrix3( m ) { - te[ 2 ] = cf * b - de; - te[ 6 ] = df + ce * b; - te[ 10 ] = a * c; + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - } else if ( euler.order === 'ZXY' ) { + this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; + this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; + this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; - var ce = c * e, cf = c * f, de = d * e, df = d * f; + return this; - te[ 0 ] = ce - df * b; - te[ 4 ] = - a * f; - te[ 8 ] = de + cf * b; + } - te[ 1 ] = cf + de * b; - te[ 5 ] = a * e; - te[ 9 ] = df - ce * b; + applyNormalMatrix( m ) { - te[ 2 ] = - a * d; - te[ 6 ] = b; - te[ 10 ] = a * c; + return this.applyMatrix3( m ).normalize(); - } else if ( euler.order === 'ZYX' ) { + } - var ae = a * e, af = a * f, be = b * e, bf = b * f; + applyMatrix4( m ) { - te[ 0 ] = c * e; - te[ 4 ] = be * d - af; - te[ 8 ] = ae * d + bf; + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - te[ 1 ] = c * f; - te[ 5 ] = bf * d + ae; - te[ 9 ] = af * d - be; + const w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); - te[ 2 ] = - d; - te[ 6 ] = b * c; - te[ 10 ] = a * c; + this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w; + this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w; + this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w; - } else if ( euler.order === 'YZX' ) { + return this; - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + } - te[ 0 ] = c * e; - te[ 4 ] = bd - ac * f; - te[ 8 ] = bc * f + ad; + applyQuaternion( q ) { - te[ 1 ] = f; - te[ 5 ] = a * e; - te[ 9 ] = - b * e; + const x = this.x, y = this.y, z = this.z; + const qx = q.x, qy = q.y, qz = q.z, qw = q.w; - te[ 2 ] = - d * e; - te[ 6 ] = ad * f + bc; - te[ 10 ] = ac - bd * f; + // calculate quat * vector - } else if ( euler.order === 'XZY' ) { + const ix = qw * x + qy * z - qz * y; + const iy = qw * y + qz * x - qx * z; + const iz = qw * z + qx * y - qy * x; + const iw = - qx * x - qy * y - qz * z; - var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + // calculate result * inverse quat - te[ 0 ] = c * e; - te[ 4 ] = - f; - te[ 8 ] = d * e; + this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; + this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; + this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; - te[ 1 ] = ac * f + bd; - te[ 5 ] = a * e; - te[ 9 ] = ad * f - bc; + return this; - te[ 2 ] = bc * f - ad; - te[ 6 ] = b * e; - te[ 10 ] = bd * f + ac; + } - } + project( camera ) { - // bottom row - te[ 3 ] = 0; - te[ 7 ] = 0; - te[ 11 ] = 0; + return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix ); - // last column - te[ 12 ] = 0; - te[ 13 ] = 0; - te[ 14 ] = 0; - te[ 15 ] = 1; + } - return this; + unproject( camera ) { - }, + return this.applyMatrix4( camera.projectionMatrixInverse ).applyMatrix4( camera.matrixWorld ); - makeRotationFromQuaternion: function ( q ) { + } - return this.compose( _zero, q, _one ); + transformDirection( m ) { - }, + // input: THREE.Matrix4 affine matrix + // vector interpreted as a direction - lookAt: function ( eye, target, up ) { + const x = this.x, y = this.y, z = this.z; + const e = m.elements; - var te = this.elements; + this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; + this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; + this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; - _z.subVectors( eye, target ); + return this.normalize(); - if ( _z.lengthSq() === 0 ) { + } - // eye and target are in the same position + divide( v ) { - _z.z = 1; + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; - } + return this; - _z.normalize(); - _x.crossVectors( up, _z ); + } - if ( _x.lengthSq() === 0 ) { + divideScalar( scalar ) { - // up and z are parallel + return this.multiplyScalar( 1 / scalar ); - if ( Math.abs( up.z ) === 1 ) { + } - _z.x += 0.0001; + min( v ) { - } else { + this.x = Math.min( this.x, v.x ); + this.y = Math.min( this.y, v.y ); + this.z = Math.min( this.z, v.z ); - _z.z += 0.0001; + return this; - } + } - _z.normalize(); - _x.crossVectors( up, _z ); + max( v ) { - } + this.x = Math.max( this.x, v.x ); + this.y = Math.max( this.y, v.y ); + this.z = Math.max( this.z, v.z ); - _x.normalize(); - _y.crossVectors( _z, _x ); + return this; - te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; - te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; - te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; + } - return this; + clamp( min, max ) { - }, + // assumes min < max, componentwise - multiply: function ( m, n ) { + this.x = Math.max( min.x, Math.min( max.x, this.x ) ); + this.y = Math.max( min.y, Math.min( max.y, this.y ) ); + this.z = Math.max( min.z, Math.min( max.z, this.z ) ); - if ( n !== undefined ) { + return this; - console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); - return this.multiplyMatrices( m, n ); + } - } + clampScalar( minVal, maxVal ) { - return this.multiplyMatrices( this, m ); + this.x = Math.max( minVal, Math.min( maxVal, this.x ) ); + this.y = Math.max( minVal, Math.min( maxVal, this.y ) ); + this.z = Math.max( minVal, Math.min( maxVal, this.z ) ); - }, + return this; - premultiply: function ( m ) { + } - return this.multiplyMatrices( m, this ); + clampLength( min, max ) { - }, + const length = this.length(); - multiplyMatrices: function ( a, b ) { + return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) ); - var ae = a.elements; - var be = b.elements; - var te = this.elements; + } - var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; - var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; - var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; - var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; + floor() { - var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; - var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; - var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; - var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; + this.x = Math.floor( this.x ); + this.y = Math.floor( this.y ); + this.z = Math.floor( this.z ); - te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; - te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; - te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; - te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + return this; - te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; - te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; - te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; - te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + } - te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; - te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; - te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; - te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + ceil() { - te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; - te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; - te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; - te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + this.x = Math.ceil( this.x ); + this.y = Math.ceil( this.y ); + this.z = Math.ceil( this.z ); - return this; + return this; - }, + } - multiplyScalar: function ( s ) { + round() { - var te = this.elements; + this.x = Math.round( this.x ); + this.y = Math.round( this.y ); + this.z = Math.round( this.z ); - te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; - te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; - te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; - te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; + return this; - return this; + } - }, + roundToZero() { - applyToBufferAttribute: function ( attribute ) { + this.x = Math.trunc( this.x ); + this.y = Math.trunc( this.y ); + this.z = Math.trunc( this.z ); - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + return this; - _v1.x = attribute.getX( i ); - _v1.y = attribute.getY( i ); - _v1.z = attribute.getZ( i ); + } - _v1.applyMatrix4( this ); + negate() { - attribute.setXYZ( i, _v1.x, _v1.y, _v1.z ); + this.x = - this.x; + this.y = - this.y; + this.z = - this.z; - } + return this; - return attribute; + } - }, + dot( v ) { - determinant: function () { - - var te = this.elements; - - var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; - var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; - var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; - var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; - - //TODO: make this more efficient - //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) - - return ( - n41 * ( - + n14 * n23 * n32 - - n13 * n24 * n32 - - n14 * n22 * n33 - + n12 * n24 * n33 - + n13 * n22 * n34 - - n12 * n23 * n34 - ) + - n42 * ( - + n11 * n23 * n34 - - n11 * n24 * n33 - + n14 * n21 * n33 - - n13 * n21 * n34 - + n13 * n24 * n31 - - n14 * n23 * n31 - ) + - n43 * ( - + n11 * n24 * n32 - - n11 * n22 * n34 - - n14 * n21 * n32 - + n12 * n21 * n34 - + n14 * n22 * n31 - - n12 * n24 * n31 - ) + - n44 * ( - - n13 * n22 * n31 - - n11 * n23 * n32 - + n11 * n22 * n33 - + n13 * n21 * n32 - - n12 * n21 * n33 - + n12 * n23 * n31 - ) + return this.x * v.x + this.y * v.y + this.z * v.z; - ); + } - }, + // TODO lengthSquared? - transpose: function () { + lengthSq() { - var te = this.elements; - var tmp; + return this.x * this.x + this.y * this.y + this.z * this.z; - tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; - tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; - tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; + } - tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; - tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; - tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; + length() { - return this; + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); - }, + } - setPosition: function ( x, y, z ) { + manhattanLength() { - var te = this.elements; + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); - if ( x.isVector3 ) { + } - te[ 12 ] = x.x; - te[ 13 ] = x.y; - te[ 14 ] = x.z; + normalize() { - } else { + return this.divideScalar( this.length() || 1 ); - te[ 12 ] = x; - te[ 13 ] = y; - te[ 14 ] = z; + } - } + setLength( length ) { - return this; + return this.normalize().multiplyScalar( length ); - }, + } - getInverse: function ( m, throwOnDegenerate ) { + lerp( v, alpha ) { - // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm - var te = this.elements, - me = m.elements, + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; - n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ], - n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ], - n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ], - n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ], + return this; - t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, - t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, - t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, - t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; + } - var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; + lerpVectors( v1, v2, alpha ) { - if ( det === 0 ) { + this.x = v1.x + ( v2.x - v1.x ) * alpha; + this.y = v1.y + ( v2.y - v1.y ) * alpha; + this.z = v1.z + ( v2.z - v1.z ) * alpha; - var msg = "THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0"; + return this; - if ( throwOnDegenerate === true ) { + } - throw new Error( msg ); + cross( v ) { - } else { + return this.crossVectors( this, v ); - console.warn( msg ); + } - } + crossVectors( a, b ) { - return this.identity(); + const ax = a.x, ay = a.y, az = a.z; + const bx = b.x, by = b.y, bz = b.z; - } + this.x = ay * bz - az * by; + this.y = az * bx - ax * bz; + this.z = ax * by - ay * bx; - var detInv = 1 / det; + return this; - te[ 0 ] = t11 * detInv; - te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; - te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; - te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; + } - te[ 4 ] = t12 * detInv; - te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; - te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; - te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; + projectOnVector( v ) { - te[ 8 ] = t13 * detInv; - te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; - te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; - te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; + const denominator = v.lengthSq(); - te[ 12 ] = t14 * detInv; - te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; - te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; - te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; + if ( denominator === 0 ) return this.set( 0, 0, 0 ); - return this; + const scalar = v.dot( this ) / denominator; - }, + return this.copy( v ).multiplyScalar( scalar ); - scale: function ( v ) { + } - var te = this.elements; - var x = v.x, y = v.y, z = v.z; + projectOnPlane( planeNormal ) { - te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; - te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; - te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; - te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; + _vector$b.copy( this ).projectOnVector( planeNormal ); - return this; + return this.sub( _vector$b ); - }, + } - getMaxScaleOnAxis: function () { + reflect( normal ) { - var te = this.elements; + // reflect incident vector off plane orthogonal to normal + // normal is assumed to have unit length - var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; - var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; - var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; + return this.sub( _vector$b.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); - return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); + } - }, + angleTo( v ) { - makeTranslation: function ( x, y, z ) { + const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() ); - this.set( + if ( denominator === 0 ) return Math.PI / 2; - 1, 0, 0, x, - 0, 1, 0, y, - 0, 0, 1, z, - 0, 0, 0, 1 + const theta = this.dot( v ) / denominator; - ); + // clamp, to handle numerical problems - return this; + return Math.acos( clamp( theta, - 1, 1 ) ); - }, + } - makeRotationX: function ( theta ) { + distanceTo( v ) { - var c = Math.cos( theta ), s = Math.sin( theta ); + return Math.sqrt( this.distanceToSquared( v ) ); - this.set( + } - 1, 0, 0, 0, - 0, c, - s, 0, - 0, s, c, 0, - 0, 0, 0, 1 + distanceToSquared( v ) { - ); + const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z; - return this; + return dx * dx + dy * dy + dz * dz; - }, + } - makeRotationY: function ( theta ) { + manhattanDistanceTo( v ) { - var c = Math.cos( theta ), s = Math.sin( theta ); + return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z ); - this.set( + } - c, 0, s, 0, - 0, 1, 0, 0, - - s, 0, c, 0, - 0, 0, 0, 1 + setFromSpherical( s ) { - ); + return this.setFromSphericalCoords( s.radius, s.phi, s.theta ); - return this; + } - }, + setFromSphericalCoords( radius, phi, theta ) { - makeRotationZ: function ( theta ) { + const sinPhiRadius = Math.sin( phi ) * radius; - var c = Math.cos( theta ), s = Math.sin( theta ); + this.x = sinPhiRadius * Math.sin( theta ); + this.y = Math.cos( phi ) * radius; + this.z = sinPhiRadius * Math.cos( theta ); - this.set( + return this; - c, - s, 0, 0, - s, c, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 + } - ); + setFromCylindrical( c ) { - return this; + return this.setFromCylindricalCoords( c.radius, c.theta, c.y ); - }, + } - makeRotationAxis: function ( axis, angle ) { + setFromCylindricalCoords( radius, theta, y ) { - // Based on http://www.gamedev.net/reference/articles/article1199.asp + this.x = radius * Math.sin( theta ); + this.y = y; + this.z = radius * Math.cos( theta ); - var c = Math.cos( angle ); - var s = Math.sin( angle ); - var t = 1 - c; - var x = axis.x, y = axis.y, z = axis.z; - var tx = t * x, ty = t * y; + return this; - this.set( + } - tx * x + c, tx * y - s * z, tx * z + s * y, 0, - tx * y + s * z, ty * y + c, ty * z - s * x, 0, - tx * z - s * y, ty * z + s * x, t * z * z + c, 0, - 0, 0, 0, 1 + setFromMatrixPosition( m ) { - ); + const e = m.elements; - return this; + this.x = e[ 12 ]; + this.y = e[ 13 ]; + this.z = e[ 14 ]; - }, + return this; - makeScale: function ( x, y, z ) { + } - this.set( + setFromMatrixScale( m ) { - x, 0, 0, 0, - 0, y, 0, 0, - 0, 0, z, 0, - 0, 0, 0, 1 + const sx = this.setFromMatrixColumn( m, 0 ).length(); + const sy = this.setFromMatrixColumn( m, 1 ).length(); + const sz = this.setFromMatrixColumn( m, 2 ).length(); - ); + this.x = sx; + this.y = sy; + this.z = sz; - return this; + return this; - }, + } - makeShear: function ( x, y, z ) { + setFromMatrixColumn( m, index ) { - this.set( + return this.fromArray( m.elements, index * 4 ); - 1, y, z, 0, - x, 1, z, 0, - x, y, 1, 0, - 0, 0, 0, 1 + } - ); + setFromMatrix3Column( m, index ) { - return this; + return this.fromArray( m.elements, index * 3 ); - }, + } - compose: function ( position, quaternion, scale ) { + setFromEuler( e ) { - var te = this.elements; + this.x = e._x; + this.y = e._y; + this.z = e._z; - var x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; - var x2 = x + x, y2 = y + y, z2 = z + z; - var xx = x * x2, xy = x * y2, xz = x * z2; - var yy = y * y2, yz = y * z2, zz = z * z2; - var wx = w * x2, wy = w * y2, wz = w * z2; + return this; - var sx = scale.x, sy = scale.y, sz = scale.z; + } - te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; - te[ 1 ] = ( xy + wz ) * sx; - te[ 2 ] = ( xz - wy ) * sx; - te[ 3 ] = 0; + setFromColor( c ) { - te[ 4 ] = ( xy - wz ) * sy; - te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; - te[ 6 ] = ( yz + wx ) * sy; - te[ 7 ] = 0; + this.x = c.r; + this.y = c.g; + this.z = c.b; - te[ 8 ] = ( xz + wy ) * sz; - te[ 9 ] = ( yz - wx ) * sz; - te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; - te[ 11 ] = 0; + return this; - te[ 12 ] = position.x; - te[ 13 ] = position.y; - te[ 14 ] = position.z; - te[ 15 ] = 1; + } - return this; + equals( v ) { - }, + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); - decompose: function ( position, quaternion, scale ) { + } - var te = this.elements; + fromArray( array, offset = 0 ) { - var sx = _v1.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); - var sy = _v1.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); - var sz = _v1.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; - // if determine is negative, we need to invert one scale - var det = this.determinant(); - if ( det < 0 ) sx = - sx; + return this; - position.x = te[ 12 ]; - position.y = te[ 13 ]; - position.z = te[ 14 ]; + } - // scale the rotation part - _m1.copy( this ); + toArray( array = [], offset = 0 ) { - var invSX = 1 / sx; - var invSY = 1 / sy; - var invSZ = 1 / sz; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; - _m1.elements[ 0 ] *= invSX; - _m1.elements[ 1 ] *= invSX; - _m1.elements[ 2 ] *= invSX; + return array; - _m1.elements[ 4 ] *= invSY; - _m1.elements[ 5 ] *= invSY; - _m1.elements[ 6 ] *= invSY; + } - _m1.elements[ 8 ] *= invSZ; - _m1.elements[ 9 ] *= invSZ; - _m1.elements[ 10 ] *= invSZ; + fromBufferAttribute( attribute, index ) { - quaternion.setFromRotationMatrix( _m1 ); + this.x = attribute.getX( index ); + this.y = attribute.getY( index ); + this.z = attribute.getZ( index ); - scale.x = sx; - scale.y = sy; - scale.z = sz; + return this; - return this; + } - }, + random() { - makePerspective: function ( left, right, top, bottom, near, far ) { + this.x = Math.random(); + this.y = Math.random(); + this.z = Math.random(); - if ( far === undefined ) { + return this; - console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' ); + } - } + randomDirection() { - var te = this.elements; - var x = 2 * near / ( right - left ); - var y = 2 * near / ( top - bottom ); + // Derived from https://mathworld.wolfram.com/SpherePointPicking.html - var a = ( right + left ) / ( right - left ); - var b = ( top + bottom ) / ( top - bottom ); - var c = - ( far + near ) / ( far - near ); - var d = - 2 * far * near / ( far - near ); + const u = ( Math.random() - 0.5 ) * 2; + const t = Math.random() * Math.PI * 2; + const f = Math.sqrt( 1 - u ** 2 ); - te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; - te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; + this.x = f * Math.cos( t ); + this.y = f * Math.sin( t ); + this.z = u; - return this; + return this; - }, + } - makeOrthographic: function ( left, right, top, bottom, near, far ) { + *[ Symbol.iterator ]() { - var te = this.elements; - var w = 1.0 / ( right - left ); - var h = 1.0 / ( top - bottom ); - var p = 1.0 / ( far - near ); + yield this.x; + yield this.y; + yield this.z; - var x = ( right + left ) * w; - var y = ( top + bottom ) * h; - var z = ( far + near ) * p; + } - te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; - te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; - te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z; - te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; +} - return this; +const _vector$b = /*@__PURE__*/ new Vector3(); +const _quaternion$4 = /*@__PURE__*/ new Quaternion(); - }, +class Box3 { - equals: function ( matrix ) { + constructor( min = new Vector3( + Infinity, + Infinity, + Infinity ), max = new Vector3( - Infinity, - Infinity, - Infinity ) ) { - var te = this.elements; - var me = matrix.elements; + this.isBox3 = true; - for ( var i = 0; i < 16; i ++ ) { + this.min = min; + this.max = max; - if ( te[ i ] !== me[ i ] ) return false; + } - } + set( min, max ) { - return true; + this.min.copy( min ); + this.max.copy( max ); - }, + return this; - fromArray: function ( array, offset ) { + } - if ( offset === undefined ) offset = 0; + setFromArray( array ) { - for ( var i = 0; i < 16; i ++ ) { + this.makeEmpty(); - this.elements[ i ] = array[ i + offset ]; + for ( let i = 0, il = array.length; i < il; i += 3 ) { - } + this.expandByPoint( _vector$a.fromArray( array, i ) ); - return this; + } - }, + return this; - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + setFromBufferAttribute( attribute ) { - var te = this.elements; + this.makeEmpty(); - array[ offset ] = te[ 0 ]; - array[ offset + 1 ] = te[ 1 ]; - array[ offset + 2 ] = te[ 2 ]; - array[ offset + 3 ] = te[ 3 ]; + for ( let i = 0, il = attribute.count; i < il; i ++ ) { - array[ offset + 4 ] = te[ 4 ]; - array[ offset + 5 ] = te[ 5 ]; - array[ offset + 6 ] = te[ 6 ]; - array[ offset + 7 ] = te[ 7 ]; + this.expandByPoint( _vector$a.fromBufferAttribute( attribute, i ) ); - array[ offset + 8 ] = te[ 8 ]; - array[ offset + 9 ] = te[ 9 ]; - array[ offset + 10 ] = te[ 10 ]; - array[ offset + 11 ] = te[ 11 ]; + } - array[ offset + 12 ] = te[ 12 ]; - array[ offset + 13 ] = te[ 13 ]; - array[ offset + 14 ] = te[ 14 ]; - array[ offset + 15 ] = te[ 15 ]; + return this; - return array; + } - } + setFromPoints( points ) { - } ); + this.makeEmpty(); - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ + for ( let i = 0, il = points.length; i < il; i ++ ) { - var _matrix = new Matrix4(); - var _quaternion$1 = new Quaternion(); + this.expandByPoint( points[ i ] ); - function Euler( x, y, z, order ) { + } - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._order = order || Euler.DefaultOrder; + return this; } - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + setFromCenterAndSize( center, size ) { - Euler.DefaultOrder = 'XYZ'; + const halfSize = _vector$a.copy( size ).multiplyScalar( 0.5 ); - Object.defineProperties( Euler.prototype, { + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); - x: { + return this; - get: function () { + } - return this._x; + setFromObject( object, precise = false ) { - }, + this.makeEmpty(); - set: function ( value ) { + return this.expandByObject( object, precise ); - this._x = value; - this._onChangeCallback(); + } - } + clone() { - }, + return new this.constructor().copy( this ); - y: { + } - get: function () { + copy( box ) { - return this._y; + this.min.copy( box.min ); + this.max.copy( box.max ); - }, + return this; - set: function ( value ) { + } - this._y = value; - this._onChangeCallback(); + makeEmpty() { - } + this.min.x = this.min.y = this.min.z = + Infinity; + this.max.x = this.max.y = this.max.z = - Infinity; - }, + return this; - z: { + } - get: function () { + isEmpty() { - return this._z; + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - }, + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); - set: function ( value ) { + } - this._z = value; - this._onChangeCallback(); + getCenter( target ) { - } + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - }, + } - order: { + getSize( target ) { - get: function () { + return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); - return this._order; + } - }, + expandByPoint( point ) { - set: function ( value ) { + this.min.min( point ); + this.max.max( point ); - this._order = value; - this._onChangeCallback(); + return this; - } + } - } + expandByVector( vector ) { - } ); + this.min.sub( vector ); + this.max.add( vector ); - Object.assign( Euler.prototype, { + return this; - isEuler: true, + } - set: function ( x, y, z, order ) { + expandByScalar( scalar ) { - this._x = x; - this._y = y; - this._z = z; - this._order = order || this._order; + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); - this._onChangeCallback(); + return this; - return this; + } - }, + expandByObject( object, precise = false ) { - clone: function () { + // Computes the world-axis-aligned bounding box of an object (including its children), + // accounting for both the object's, and children's, world transforms - return new this.constructor( this._x, this._y, this._z, this._order ); + object.updateWorldMatrix( false, false ); - }, + if ( object.boundingBox !== undefined ) { - copy: function ( euler ) { + if ( object.boundingBox === null ) { - this._x = euler._x; - this._y = euler._y; - this._z = euler._z; - this._order = euler._order; + object.computeBoundingBox(); - this._onChangeCallback(); + } - return this; + _box$3.copy( object.boundingBox ); + _box$3.applyMatrix4( object.matrixWorld ); - }, + this.union( _box$3 ); + + } else { - setFromRotationMatrix: function ( m, order, update ) { + const geometry = object.geometry; - var clamp = _Math.clamp; + if ( geometry !== undefined ) { - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + if ( precise && geometry.attributes !== undefined && geometry.attributes.position !== undefined ) { - var te = m.elements; - var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; - var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; - var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; + const position = geometry.attributes.position; + for ( let i = 0, l = position.count; i < l; i ++ ) { - order = order || this._order; + _vector$a.fromBufferAttribute( position, i ).applyMatrix4( object.matrixWorld ); + this.expandByPoint( _vector$a ); - if ( order === 'XYZ' ) { + } - this._y = Math.asin( clamp( m13, - 1, 1 ) ); + } else { - if ( Math.abs( m13 ) < 0.9999999 ) { + if ( geometry.boundingBox === null ) { - this._x = Math.atan2( - m23, m33 ); - this._z = Math.atan2( - m12, m11 ); + geometry.computeBoundingBox(); - } else { + } - this._x = Math.atan2( m32, m22 ); - this._z = 0; + _box$3.copy( geometry.boundingBox ); + _box$3.applyMatrix4( object.matrixWorld ); + + this.union( _box$3 ); } - } else if ( order === 'YXZ' ) { + } - this._x = Math.asin( - clamp( m23, - 1, 1 ) ); + } - if ( Math.abs( m23 ) < 0.9999999 ) { + const children = object.children; - this._y = Math.atan2( m13, m33 ); - this._z = Math.atan2( m21, m22 ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - } else { + this.expandByObject( children[ i ], precise ); - this._y = Math.atan2( - m31, m11 ); - this._z = 0; + } - } + return this; - } else if ( order === 'ZXY' ) { + } - this._x = Math.asin( clamp( m32, - 1, 1 ) ); + containsPoint( point ) { - if ( Math.abs( m32 ) < 0.9999999 ) { + return point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y || + point.z < this.min.z || point.z > this.max.z ? false : true; - this._y = Math.atan2( - m31, m33 ); - this._z = Math.atan2( - m12, m22 ); + } - } else { + containsBox( box ) { - this._y = 0; - this._z = Math.atan2( m21, m11 ); + return this.min.x <= box.min.x && box.max.x <= this.max.x && + this.min.y <= box.min.y && box.max.y <= this.max.y && + this.min.z <= box.min.z && box.max.z <= this.max.z; - } + } - } else if ( order === 'ZYX' ) { + getParameter( point, target ) { - this._y = Math.asin( - clamp( m31, - 1, 1 ) ); + // This can potentially have a divide by zero if the box + // has a size dimension of 0. - if ( Math.abs( m31 ) < 0.9999999 ) { + return target.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ), + ( point.z - this.min.z ) / ( this.max.z - this.min.z ) + ); - this._x = Math.atan2( m32, m33 ); - this._z = Math.atan2( m21, m11 ); + } - } else { + intersectsBox( box ) { - this._x = 0; - this._z = Math.atan2( - m12, m22 ); + // using 6 splitting planes to rule out intersections. + return box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y || + box.max.z < this.min.z || box.min.z > this.max.z ? false : true; - } + } - } else if ( order === 'YZX' ) { + intersectsSphere( sphere ) { - this._z = Math.asin( clamp( m21, - 1, 1 ) ); + // Find the point on the AABB closest to the sphere center. + this.clampPoint( sphere.center, _vector$a ); - if ( Math.abs( m21 ) < 0.9999999 ) { + // If that point is inside the sphere, the AABB and sphere intersect. + return _vector$a.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); - this._x = Math.atan2( - m23, m22 ); - this._y = Math.atan2( - m31, m11 ); + } - } else { + intersectsPlane( plane ) { - this._x = 0; - this._y = Math.atan2( m13, m33 ); + // We compute the minimum and maximum dot product values. If those values + // are on the same side (back or front) of the plane, then there is no intersection. - } + let min, max; - } else if ( order === 'XZY' ) { + if ( plane.normal.x > 0 ) { - this._z = Math.asin( - clamp( m12, - 1, 1 ) ); + min = plane.normal.x * this.min.x; + max = plane.normal.x * this.max.x; - if ( Math.abs( m12 ) < 0.9999999 ) { + } else { - this._x = Math.atan2( m32, m22 ); - this._y = Math.atan2( m13, m11 ); + min = plane.normal.x * this.max.x; + max = plane.normal.x * this.min.x; - } else { + } - this._x = Math.atan2( - m23, m33 ); - this._y = 0; + if ( plane.normal.y > 0 ) { - } + min += plane.normal.y * this.min.y; + max += plane.normal.y * this.max.y; - } else { + } else { - console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ); + min += plane.normal.y * this.max.y; + max += plane.normal.y * this.min.y; - } + } - this._order = order; + if ( plane.normal.z > 0 ) { - if ( update !== false ) this._onChangeCallback(); + min += plane.normal.z * this.min.z; + max += plane.normal.z * this.max.z; - return this; + } else { - }, + min += plane.normal.z * this.max.z; + max += plane.normal.z * this.min.z; - setFromQuaternion: function ( q, order, update ) { + } - _matrix.makeRotationFromQuaternion( q ); + return ( min <= - plane.constant && max >= - plane.constant ); - return this.setFromRotationMatrix( _matrix, order, update ); + } - }, + intersectsTriangle( triangle ) { - setFromVector3: function ( v, order ) { + if ( this.isEmpty() ) { - return this.set( v.x, v.y, v.z, order || this._order ); + return false; - }, + } + + // compute box center and extents + this.getCenter( _center ); + _extents.subVectors( this.max, _center ); - reorder: function ( newOrder ) { + // translate triangle to aabb origin + _v0$2.subVectors( triangle.a, _center ); + _v1$7.subVectors( triangle.b, _center ); + _v2$4.subVectors( triangle.c, _center ); - // WARNING: this discards revolution information -bhouston + // compute edge vectors for triangle + _f0.subVectors( _v1$7, _v0$2 ); + _f1.subVectors( _v2$4, _v1$7 ); + _f2.subVectors( _v0$2, _v2$4 ); - _quaternion$1.setFromEuler( this ); + // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb + // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation + // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) + let axes = [ + 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, + _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, + - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 + ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { - return this.setFromQuaternion( _quaternion$1, newOrder ); + return false; - }, + } - equals: function ( euler ) { + // test 3 face normals from the aabb + axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; + if ( ! satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ) ) { - return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); + return false; - }, + } - fromArray: function ( array ) { + // finally testing the face normal of the triangle + // use already existing triangle edge vectors here + _triangleNormal.crossVectors( _f0, _f1 ); + axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; + return satForAxes( axes, _v0$2, _v1$7, _v2$4, _extents ); - this._onChangeCallback(); + } - return this; + clampPoint( point, target ) { - }, + return target.copy( point ).clamp( this.min, this.max ); - toArray: function ( array, offset ) { + } - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + distanceToPoint( point ) { - array[ offset ] = this._x; - array[ offset + 1 ] = this._y; - array[ offset + 2 ] = this._z; - array[ offset + 3 ] = this._order; + return this.clampPoint( point, _vector$a ).distanceTo( point ); - return array; + } - }, + getBoundingSphere( target ) { - toVector3: function ( optionalResult ) { + if ( this.isEmpty() ) { - if ( optionalResult ) { + target.makeEmpty(); - return optionalResult.set( this._x, this._y, this._z ); + } else { - } else { + this.getCenter( target.center ); - return new Vector3( this._x, this._y, this._z ); + target.radius = this.getSize( _vector$a ).length() * 0.5; - } + } - }, + return target; - _onChange: function ( callback ) { + } - this._onChangeCallback = callback; + intersect( box ) { - return this; + this.min.max( box.min ); + this.max.min( box.max ); - }, + // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. + if ( this.isEmpty() ) this.makeEmpty(); - _onChangeCallback: function () {} + return this; - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + union( box ) { - function Layers() { + this.min.min( box.min ); + this.max.max( box.max ); - this.mask = 1 | 0; + return this; } - Object.assign( Layers.prototype, { + applyMatrix4( matrix ) { - set: function ( channel ) { + // transform of empty box is an empty box. + if ( this.isEmpty() ) return this; - this.mask = 1 << channel | 0; + // NOTE: I am using a binary pattern to specify all 2^3 combinations below + _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 + _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 + _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 + _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 + _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 + _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 + _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 + _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 - }, + this.setFromPoints( _points ); - enable: function ( channel ) { + return this; - this.mask |= 1 << channel | 0; + } - }, + translate( offset ) { - enableAll: function () { + this.min.add( offset ); + this.max.add( offset ); - this.mask = 0xffffffff | 0; + return this; - }, + } - toggle: function ( channel ) { + equals( box ) { - this.mask ^= 1 << channel | 0; + return box.min.equals( this.min ) && box.max.equals( this.max ); - }, + } - disable: function ( channel ) { +} - this.mask &= ~ ( 1 << channel | 0 ); +const _points = [ + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3(), + /*@__PURE__*/ new Vector3() +]; - }, +const _vector$a = /*@__PURE__*/ new Vector3(); - disableAll: function () { +const _box$3 = /*@__PURE__*/ new Box3(); - this.mask = 0; +// triangle centered vertices - }, +const _v0$2 = /*@__PURE__*/ new Vector3(); +const _v1$7 = /*@__PURE__*/ new Vector3(); +const _v2$4 = /*@__PURE__*/ new Vector3(); - test: function ( layers ) { +// triangle edge vectors - return ( this.mask & layers.mask ) !== 0; +const _f0 = /*@__PURE__*/ new Vector3(); +const _f1 = /*@__PURE__*/ new Vector3(); +const _f2 = /*@__PURE__*/ new Vector3(); - } +const _center = /*@__PURE__*/ new Vector3(); +const _extents = /*@__PURE__*/ new Vector3(); +const _triangleNormal = /*@__PURE__*/ new Vector3(); +const _testAxis = /*@__PURE__*/ new Vector3(); - } ); +function satForAxes( axes, v0, v1, v2, extents ) { - var _object3DId = 0; + for ( let i = 0, j = axes.length - 3; i <= j; i += 3 ) { - var _v1$1 = new Vector3(); - var _q1 = new Quaternion(); - var _m1$1 = new Matrix4(); - var _target = new Vector3(); + _testAxis.fromArray( axes, i ); + // project the aabb onto the separating axis + const r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); + // project all 3 vertices of the triangle onto the separating axis + const p0 = v0.dot( _testAxis ); + const p1 = v1.dot( _testAxis ); + const p2 = v2.dot( _testAxis ); + // actual test, basically see if either of the most extreme of the triangle points intersects r + if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { - var _position = new Vector3(); - var _scale = new Vector3(); - var _quaternion$2 = new Quaternion(); + // points of the projected triangle are outside the projected half-length of the aabb + // the axis is separating and we can exit + return false; - var _xAxis = new Vector3( 1, 0, 0 ); - var _yAxis = new Vector3( 0, 1, 0 ); - var _zAxis = new Vector3( 0, 0, 1 ); + } - var _addedEvent = { type: 'added' }; - var _removedEvent = { type: 'removed' }; + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author WestLangley / http://github.com/WestLangley - * @author elephantatwork / www.elephantatwork.ch - */ + return true; - function Object3D() { +} - Object.defineProperty( this, 'id', { value: _object3DId ++ } ); +const _box$2 = /*@__PURE__*/ new Box3(); +const _v1$6 = /*@__PURE__*/ new Vector3(); +const _v2$3 = /*@__PURE__*/ new Vector3(); - this.uuid = _Math.generateUUID(); +class Sphere { - this.name = ''; - this.type = 'Object3D'; + constructor( center = new Vector3(), radius = - 1 ) { - this.parent = null; - this.children = []; + this.center = center; + this.radius = radius; - this.up = Object3D.DefaultUp.clone(); + } - var position = new Vector3(); - var rotation = new Euler(); - var quaternion = new Quaternion(); - var scale = new Vector3( 1, 1, 1 ); + set( center, radius ) { - function onRotationChange() { + this.center.copy( center ); + this.radius = radius; - quaternion.setFromEuler( rotation, false ); + return this; - } + } - function onQuaternionChange() { + setFromPoints( points, optionalCenter ) { - rotation.setFromQuaternion( quaternion, undefined, false ); + const center = this.center; - } + if ( optionalCenter !== undefined ) { - rotation._onChange( onRotationChange ); - quaternion._onChange( onQuaternionChange ); + center.copy( optionalCenter ); - Object.defineProperties( this, { - position: { - configurable: true, - enumerable: true, - value: position - }, - rotation: { - configurable: true, - enumerable: true, - value: rotation - }, - quaternion: { - configurable: true, - enumerable: true, - value: quaternion - }, - scale: { - configurable: true, - enumerable: true, - value: scale - }, - modelViewMatrix: { - value: new Matrix4() - }, - normalMatrix: { - value: new Matrix3() - } - } ); + } else { - this.matrix = new Matrix4(); - this.matrixWorld = new Matrix4(); + _box$2.setFromPoints( points ).getCenter( center ); - this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate; - this.matrixWorldNeedsUpdate = false; + } - this.layers = new Layers(); - this.visible = true; + let maxRadiusSq = 0; - this.castShadow = false; - this.receiveShadow = false; + for ( let i = 0, il = points.length; i < il; i ++ ) { - this.frustumCulled = true; - this.renderOrder = 0; + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); - this.userData = {}; + } + + this.radius = Math.sqrt( maxRadiusSq ); + + return this; } - Object3D.DefaultUp = new Vector3( 0, 1, 0 ); - Object3D.DefaultMatrixAutoUpdate = true; + copy( sphere ) { - Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this.center.copy( sphere.center ); + this.radius = sphere.radius; - constructor: Object3D, + return this; - isObject3D: true, + } - onBeforeRender: function () {}, - onAfterRender: function () {}, + isEmpty() { - applyMatrix: function ( matrix ) { + return ( this.radius < 0 ); - if ( this.matrixAutoUpdate ) this.updateMatrix(); + } - this.matrix.premultiply( matrix ); + makeEmpty() { - this.matrix.decompose( this.position, this.quaternion, this.scale ); + this.center.set( 0, 0, 0 ); + this.radius = - 1; - }, + return this; - applyQuaternion: function ( q ) { + } - this.quaternion.premultiply( q ); + containsPoint( point ) { - return this; + return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); - }, + } - setRotationFromAxisAngle: function ( axis, angle ) { + distanceToPoint( point ) { - // assumes axis is normalized + return ( point.distanceTo( this.center ) - this.radius ); - this.quaternion.setFromAxisAngle( axis, angle ); + } - }, + intersectsSphere( sphere ) { - setRotationFromEuler: function ( euler ) { + const radiusSum = this.radius + sphere.radius; - this.quaternion.setFromEuler( euler, true ); + return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); - }, + } - setRotationFromMatrix: function ( m ) { + intersectsBox( box ) { - // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + return box.intersectsSphere( this ); - this.quaternion.setFromRotationMatrix( m ); + } - }, + intersectsPlane( plane ) { - setRotationFromQuaternion: function ( q ) { + return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; - // assumes q is normalized + } - this.quaternion.copy( q ); + clampPoint( point, target ) { - }, + const deltaLengthSq = this.center.distanceToSquared( point ); - rotateOnAxis: function ( axis, angle ) { + target.copy( point ); - // rotate object on axis in object space - // axis is assumed to be normalized + if ( deltaLengthSq > ( this.radius * this.radius ) ) { - _q1.setFromAxisAngle( axis, angle ); + target.sub( this.center ).normalize(); + target.multiplyScalar( this.radius ).add( this.center ); - this.quaternion.multiply( _q1 ); + } - return this; + return target; - }, + } - rotateOnWorldAxis: function ( axis, angle ) { + getBoundingBox( target ) { - // rotate object on axis in world space - // axis is assumed to be normalized - // method assumes no rotated parent + if ( this.isEmpty() ) { - _q1.setFromAxisAngle( axis, angle ); + // Empty sphere produces empty bounding box + target.makeEmpty(); + return target; - this.quaternion.premultiply( _q1 ); + } - return this; + target.set( this.center, this.center ); + target.expandByScalar( this.radius ); - }, + return target; - rotateX: function ( angle ) { + } - return this.rotateOnAxis( _xAxis, angle ); + applyMatrix4( matrix ) { - }, + this.center.applyMatrix4( matrix ); + this.radius = this.radius * matrix.getMaxScaleOnAxis(); - rotateY: function ( angle ) { + return this; - return this.rotateOnAxis( _yAxis, angle ); + } - }, + translate( offset ) { - rotateZ: function ( angle ) { + this.center.add( offset ); - return this.rotateOnAxis( _zAxis, angle ); + return this; - }, + } - translateOnAxis: function ( axis, distance ) { + expandByPoint( point ) { - // translate object by distance along axis in object space - // axis is assumed to be normalized + if ( this.isEmpty() ) { - _v1$1.copy( axis ).applyQuaternion( this.quaternion ); + this.center.copy( point ); - this.position.add( _v1$1.multiplyScalar( distance ) ); + this.radius = 0; return this; - }, + } - translateX: function ( distance ) { + _v1$6.subVectors( point, this.center ); - return this.translateOnAxis( _xAxis, distance ); + const lengthSq = _v1$6.lengthSq(); - }, + if ( lengthSq > ( this.radius * this.radius ) ) { - translateY: function ( distance ) { + // calculate the minimal sphere - return this.translateOnAxis( _yAxis, distance ); + const length = Math.sqrt( lengthSq ); - }, + const delta = ( length - this.radius ) * 0.5; - translateZ: function ( distance ) { + this.center.addScaledVector( _v1$6, delta / length ); - return this.translateOnAxis( _zAxis, distance ); + this.radius += delta; - }, + } - localToWorld: function ( vector ) { + return this; - return vector.applyMatrix4( this.matrixWorld ); + } - }, + union( sphere ) { - worldToLocal: function ( vector ) { + if ( sphere.isEmpty() ) { - return vector.applyMatrix4( _m1$1.getInverse( this.matrixWorld ) ); + return this; - }, + } - lookAt: function ( x, y, z ) { + if ( this.isEmpty() ) { - // This method does not support objects having non-uniformly-scaled parent(s) + this.copy( sphere ); - if ( x.isVector3 ) { + return this; - _target.copy( x ); + } - } else { + if ( this.center.equals( sphere.center ) === true ) { - _target.set( x, y, z ); + this.radius = Math.max( this.radius, sphere.radius ); - } + } else { - var parent = this.parent; + _v2$3.subVectors( sphere.center, this.center ).setLength( sphere.radius ); - this.updateWorldMatrix( true, false ); + this.expandByPoint( _v1$6.copy( sphere.center ).add( _v2$3 ) ); - _position.setFromMatrixPosition( this.matrixWorld ); + this.expandByPoint( _v1$6.copy( sphere.center ).sub( _v2$3 ) ); - if ( this.isCamera || this.isLight ) { + } - _m1$1.lookAt( _position, _target, this.up ); + return this; - } else { + } - _m1$1.lookAt( _target, _position, this.up ); + equals( sphere ) { - } + return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); - this.quaternion.setFromRotationMatrix( _m1$1 ); + } - if ( parent ) { + clone() { - _m1$1.extractRotation( parent.matrixWorld ); - _q1.setFromRotationMatrix( _m1$1 ); - this.quaternion.premultiply( _q1.inverse() ); + return new this.constructor().copy( this ); - } + } - }, +} - add: function ( object ) { +const _vector$9 = /*@__PURE__*/ new Vector3(); +const _segCenter = /*@__PURE__*/ new Vector3(); +const _segDir = /*@__PURE__*/ new Vector3(); +const _diff = /*@__PURE__*/ new Vector3(); - if ( arguments.length > 1 ) { +const _edge1 = /*@__PURE__*/ new Vector3(); +const _edge2 = /*@__PURE__*/ new Vector3(); +const _normal$1 = /*@__PURE__*/ new Vector3(); - for ( var i = 0; i < arguments.length; i ++ ) { +class Ray { - this.add( arguments[ i ] ); + constructor( origin = new Vector3(), direction = new Vector3( 0, 0, - 1 ) ) { - } + this.origin = origin; + this.direction = direction; - return this; + } - } + set( origin, direction ) { - if ( object === this ) { + this.origin.copy( origin ); + this.direction.copy( direction ); - console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); - return this; + return this; - } + } - if ( ( object && object.isObject3D ) ) { + copy( ray ) { - if ( object.parent !== null ) { + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); - object.parent.remove( object ); + return this; - } + } - object.parent = this; - this.children.push( object ); + at( t, target ) { - object.dispatchEvent( _addedEvent ); + return target.copy( this.origin ).addScaledVector( this.direction, t ); - } else { + } - console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); + lookAt( v ) { - } + this.direction.copy( v ).sub( this.origin ).normalize(); - return this; + return this; - }, + } - remove: function ( object ) { + recast( t ) { - if ( arguments.length > 1 ) { + this.origin.copy( this.at( t, _vector$9 ) ); - for ( var i = 0; i < arguments.length; i ++ ) { + return this; - this.remove( arguments[ i ] ); + } - } + closestPointToPoint( point, target ) { - return this; + target.subVectors( point, this.origin ); - } + const directionDistance = target.dot( this.direction ); - var index = this.children.indexOf( object ); + if ( directionDistance < 0 ) { - if ( index !== - 1 ) { + return target.copy( this.origin ); - object.parent = null; - this.children.splice( index, 1 ); + } - object.dispatchEvent( _removedEvent ); + return target.copy( this.origin ).addScaledVector( this.direction, directionDistance ); - } + } - return this; + distanceToPoint( point ) { - }, + return Math.sqrt( this.distanceSqToPoint( point ) ); - attach: function ( object ) { + } - // adds object as a child of this, while maintaining the object's world transform + distanceSqToPoint( point ) { - this.updateWorldMatrix( true, false ); + const directionDistance = _vector$9.subVectors( point, this.origin ).dot( this.direction ); - _m1$1.getInverse( this.matrixWorld ); + // point behind the ray - if ( object.parent !== null ) { + if ( directionDistance < 0 ) { - object.parent.updateWorldMatrix( true, false ); + return this.origin.distanceToSquared( point ); - _m1$1.multiply( object.parent.matrixWorld ); + } - } + _vector$9.copy( this.origin ).addScaledVector( this.direction, directionDistance ); - object.applyMatrix( _m1$1 ); + return _vector$9.distanceToSquared( point ); - object.updateWorldMatrix( false, false ); + } - this.add( object ); + distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - return this; + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment - }, + _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + _segDir.copy( v1 ).sub( v0 ).normalize(); + _diff.copy( this.origin ).sub( _segCenter ); - getObjectById: function ( id ) { + const segExtent = v0.distanceTo( v1 ) * 0.5; + const a01 = - this.direction.dot( _segDir ); + const b0 = _diff.dot( this.direction ); + const b1 = - _diff.dot( _segDir ); + const c = _diff.lengthSq(); + const det = Math.abs( 1 - a01 * a01 ); + let s0, s1, sqrDist, extDet; - return this.getObjectByProperty( 'id', id ); + if ( det > 0 ) { - }, + // The ray and segment are not parallel. - getObjectByName: function ( name ) { + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; - return this.getObjectByProperty( 'name', name ); + if ( s0 >= 0 ) { - }, + if ( s1 >= - extDet ) { - getObjectByProperty: function ( name, value ) { + if ( s1 <= extDet ) { - if ( this[ name ] === value ) return this; + // region 0 + // Minimum at interior points of ray and segment. - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + const invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - var child = this.children[ i ]; - var object = child.getObjectByProperty( name, value ); + } else { - if ( object !== undefined ) { + // region 1 - return object; + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - } + } else { - return undefined; + // region 5 - }, + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - getWorldPosition: function ( target ) { + } - if ( target === undefined ) { + } else { - console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' ); - target = new Vector3(); + if ( s1 <= - extDet ) { - } + // region 4 - this.updateMatrixWorld( true ); + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - return target.setFromMatrixPosition( this.matrixWorld ); + } else if ( s1 <= extDet ) { - }, + // region 3 - getWorldQuaternion: function ( target ) { + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; - if ( target === undefined ) { + } else { - console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' ); - target = new Quaternion(); + // region 2 - } + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - this.updateMatrixWorld( true ); + } - this.matrixWorld.decompose( _position, target, _scale ); + } - return target; + } else { - }, + // Ray and segment are parallel. - getWorldScale: function ( target ) { + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - if ( target === undefined ) { - - console.warn( 'THREE.Object3D: .getWorldScale() target is now required' ); - target = new Vector3(); - - } + } - this.updateMatrixWorld( true ); + if ( optionalPointOnRay ) { - this.matrixWorld.decompose( _position, _quaternion$2, target ); + optionalPointOnRay.copy( this.origin ).addScaledVector( this.direction, s0 ); - return target; + } - }, + if ( optionalPointOnSegment ) { - getWorldDirection: function ( target ) { + optionalPointOnSegment.copy( _segCenter ).addScaledVector( _segDir, s1 ); - if ( target === undefined ) { + } - console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' ); - target = new Vector3(); + return sqrDist; - } + } - this.updateMatrixWorld( true ); + intersectSphere( sphere, target ) { - var e = this.matrixWorld.elements; + _vector$9.subVectors( sphere.center, this.origin ); + const tca = _vector$9.dot( this.direction ); + const d2 = _vector$9.dot( _vector$9 ) - tca * tca; + const radius2 = sphere.radius * sphere.radius; - return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); + if ( d2 > radius2 ) return null; - }, + const thc = Math.sqrt( radius2 - d2 ); - raycast: function () {}, + // t0 = first intersect point - entrance on front of sphere + const t0 = tca - thc; - traverse: function ( callback ) { + // t1 = second intersect point - exit point on back of sphere + const t1 = tca + thc; - callback( this ); + // test to see if t1 is behind the ray - if so, return null + if ( t1 < 0 ) return null; - var children = this.children; + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, target ); - for ( var i = 0, l = children.length; i < l; i ++ ) { + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, target ); - children[ i ].traverse( callback ); + } - } + intersectsSphere( sphere ) { - }, + return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); - traverseVisible: function ( callback ) { + } - if ( this.visible === false ) return; + distanceToPlane( plane ) { - callback( this ); + const denominator = plane.normal.dot( this.direction ); - var children = this.children; + if ( denominator === 0 ) { - for ( var i = 0, l = children.length; i < l; i ++ ) { + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { - children[ i ].traverseVisible( callback ); + return 0; } - }, + // Null is preferable to undefined since undefined means.... it is undefined - traverseAncestors: function ( callback ) { + return null; - var parent = this.parent; + } - if ( parent !== null ) { + const t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - callback( parent ); + // Return if the ray never intersects the plane - parent.traverseAncestors( callback ); + return t >= 0 ? t : null; - } + } - }, + intersectPlane( plane, target ) { - updateMatrix: function () { + const t = this.distanceToPlane( plane ); - this.matrix.compose( this.position, this.quaternion, this.scale ); + if ( t === null ) { - this.matrixWorldNeedsUpdate = true; + return null; - }, + } - updateMatrixWorld: function ( force ) { + return this.at( t, target ); - if ( this.matrixAutoUpdate ) this.updateMatrix(); + } - if ( this.matrixWorldNeedsUpdate || force ) { + intersectsPlane( plane ) { - if ( this.parent === null ) { + // check if the ray lies on the plane first - this.matrixWorld.copy( this.matrix ); + const distToPoint = plane.distanceToPoint( this.origin ); - } else { + if ( distToPoint === 0 ) { - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + return true; - } + } - this.matrixWorldNeedsUpdate = false; + const denominator = plane.normal.dot( this.direction ); - force = true; + if ( denominator * distToPoint < 0 ) { - } + return true; - // update children + } - var children = this.children; + // ray origin is behind the plane (and is pointing behind it) - for ( var i = 0, l = children.length; i < l; i ++ ) { + return false; - children[ i ].updateMatrixWorld( force ); + } - } + intersectBox( box, target ) { - }, + let tmin, tmax, tymin, tymax, tzmin, tzmax; - updateWorldMatrix: function ( updateParents, updateChildren ) { + const invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; - var parent = this.parent; + const origin = this.origin; - if ( updateParents === true && parent !== null ) { + if ( invdirx >= 0 ) { - parent.updateWorldMatrix( true, false ); + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; - } + } else { - if ( this.matrixAutoUpdate ) this.updateMatrix(); + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; - if ( this.parent === null ) { + } - this.matrixWorld.copy( this.matrix ); + if ( invdiry >= 0 ) { - } else { + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + } else { - } + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; - // update children + } - if ( updateChildren === true ) { + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - var children = this.children; + if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin; - for ( var i = 0, l = children.length; i < l; i ++ ) { + if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax; - children[ i ].updateWorldMatrix( false, true ); + if ( invdirz >= 0 ) { - } + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; - } + } else { - }, + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; - toJSON: function ( meta ) { + } - // meta is a string when called from JSON.stringify - var isRootObject = ( meta === undefined || typeof meta === 'string' ); + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - var output = {}; + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - // meta is a hash used to collect geometries, materials. - // not providing it implies that this is the root object - // being serialized. - if ( isRootObject ) { + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - // initialize meta obj - meta = { - geometries: {}, - materials: {}, - textures: {}, - images: {}, - shapes: {} - }; + //return point closest to the ray (positive side) - output.metadata = { - version: 4.5, - type: 'Object', - generator: 'Object3D.toJSON' - }; + if ( tmax < 0 ) return null; - } + return this.at( tmin >= 0 ? tmin : tmax, target ); - // standard Object3D serialization + } - var object = {}; + intersectsBox( box ) { - object.uuid = this.uuid; - object.type = this.type; + return this.intersectBox( box, _vector$9 ) !== null; - if ( this.name !== '' ) object.name = this.name; - if ( this.castShadow === true ) object.castShadow = true; - if ( this.receiveShadow === true ) object.receiveShadow = true; - if ( this.visible === false ) object.visible = false; - if ( this.frustumCulled === false ) object.frustumCulled = false; - if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; - if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; + } - object.layers = this.layers.mask; - object.matrix = this.matrix.toArray(); + intersectTriangle( a, b, c, backfaceCulling, target ) { - if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; + // Compute the offset origin, edges, and normal. - // object specific properties + // from https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - if ( this.isMesh && this.drawMode !== TrianglesDrawMode ) object.drawMode = this.drawMode; + _edge1.subVectors( b, a ); + _edge2.subVectors( c, a ); + _normal$1.crossVectors( _edge1, _edge2 ); - // + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + let DdN = this.direction.dot( _normal$1 ); + let sign; - function serialize( library, element ) { + if ( DdN > 0 ) { - if ( library[ element.uuid ] === undefined ) { + if ( backfaceCulling ) return null; + sign = 1; - library[ element.uuid ] = element.toJSON( meta ); + } else if ( DdN < 0 ) { - } + sign = - 1; + DdN = - DdN; - return element.uuid; + } else { - } + return null; - if ( this.isMesh || this.isLine || this.isPoints ) { + } - object.geometry = serialize( meta.geometries, this.geometry ); + _diff.subVectors( this.origin, a ); + const DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); - var parameters = this.geometry.parameters; + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { - if ( parameters !== undefined && parameters.shapes !== undefined ) { + return null; - var shapes = parameters.shapes; + } - if ( Array.isArray( shapes ) ) { + const DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { - var shape = shapes[ i ]; + return null; - serialize( meta.shapes, shape ); + } - } + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { - } else { + return null; - serialize( meta.shapes, shapes ); + } - } + // Line intersects triangle, check if ray does. + const QdN = - sign * _diff.dot( _normal$1 ); - } + // t < 0, no intersection + if ( QdN < 0 ) { - } + return null; - if ( this.material !== undefined ) { + } - if ( Array.isArray( this.material ) ) { + // Ray intersects triangle. + return this.at( QdN / DdN, target ); - var uuids = []; + } - for ( var i = 0, l = this.material.length; i < l; i ++ ) { + applyMatrix4( matrix4 ) { - uuids.push( serialize( meta.materials, this.material[ i ] ) ); + this.origin.applyMatrix4( matrix4 ); + this.direction.transformDirection( matrix4 ); - } + return this; - object.material = uuids; + } - } else { + equals( ray ) { - object.material = serialize( meta.materials, this.material ); + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); - } + } - } + clone() { - // + return new this.constructor().copy( this ); - if ( this.children.length > 0 ) { + } - object.children = []; +} - for ( var i = 0; i < this.children.length; i ++ ) { +class Matrix4 { - object.children.push( this.children[ i ].toJSON( meta ).object ); + constructor( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - } + Matrix4.prototype.isMatrix4 = true; - } + this.elements = [ + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - if ( isRootObject ) { + ]; - var geometries = extractFromCache( meta.geometries ); - var materials = extractFromCache( meta.materials ); - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); - var shapes = extractFromCache( meta.shapes ); + if ( n11 !== undefined ) { - if ( geometries.length > 0 ) output.geometries = geometries; - if ( materials.length > 0 ) output.materials = materials; - if ( textures.length > 0 ) output.textures = textures; - if ( images.length > 0 ) output.images = images; - if ( shapes.length > 0 ) output.shapes = shapes; + this.set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ); - } + } - output.object = object; + } - return output; + set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { - // extract data from the cache hash - // remove metadata on each item - // and return as array - function extractFromCache( cache ) { + const te = this.elements; - var values = []; - for ( var key in cache ) { + te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; + te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; + te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; + te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; - var data = cache[ key ]; - delete data.metadata; - values.push( data ); + return this; - } - return values; + } - } + identity() { - }, + this.set( - clone: function ( recursive ) { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - return new this.constructor().copy( this, recursive ); + ); - }, + return this; - copy: function ( source, recursive ) { + } - if ( recursive === undefined ) recursive = true; + clone() { - this.name = source.name; + return new Matrix4().fromArray( this.elements ); - this.up.copy( source.up ); + } - this.position.copy( source.position ); - this.quaternion.copy( source.quaternion ); - this.scale.copy( source.scale ); + copy( m ) { - this.matrix.copy( source.matrix ); - this.matrixWorld.copy( source.matrixWorld ); + const te = this.elements; + const me = m.elements; - this.matrixAutoUpdate = source.matrixAutoUpdate; - this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ]; + te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; + te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ]; + te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ]; - this.layers.mask = source.layers.mask; - this.visible = source.visible; + return this; - this.castShadow = source.castShadow; - this.receiveShadow = source.receiveShadow; + } - this.frustumCulled = source.frustumCulled; - this.renderOrder = source.renderOrder; + copyPosition( m ) { - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + const te = this.elements, me = m.elements; - if ( recursive === true ) { + te[ 12 ] = me[ 12 ]; + te[ 13 ] = me[ 13 ]; + te[ 14 ] = me[ 14 ]; - for ( var i = 0; i < source.children.length; i ++ ) { + return this; - var child = source.children[ i ]; - this.add( child.clone() ); + } - } + setFromMatrix3( m ) { - } + const me = m.elements; - return this; + this.set( - } + me[ 0 ], me[ 3 ], me[ 6 ], 0, + me[ 1 ], me[ 4 ], me[ 7 ], 0, + me[ 2 ], me[ 5 ], me[ 8 ], 0, + 0, 0, 0, 1 - } ); + ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + return this; - function Scene() { + } - Object3D.call( this ); + extractBasis( xAxis, yAxis, zAxis ) { - this.type = 'Scene'; + xAxis.setFromMatrixColumn( this, 0 ); + yAxis.setFromMatrixColumn( this, 1 ); + zAxis.setFromMatrixColumn( this, 2 ); - this.background = null; - this.fog = null; - this.overrideMaterial = null; + return this; - this.autoUpdate = true; // checked by the renderer + } - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + makeBasis( xAxis, yAxis, zAxis ) { - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + this.set( + xAxis.x, yAxis.x, zAxis.x, 0, + xAxis.y, yAxis.y, zAxis.y, 0, + xAxis.z, yAxis.z, zAxis.z, 0, + 0, 0, 0, 1 + ); - } + return this; } - Scene.prototype = Object.assign( Object.create( Object3D.prototype ), { + extractRotation( m ) { - constructor: Scene, + // this method does not support reflection matrices - isScene: true, + const te = this.elements; + const me = m.elements; - copy: function ( source, recursive ) { + const scaleX = 1 / _v1$5.setFromMatrixColumn( m, 0 ).length(); + const scaleY = 1 / _v1$5.setFromMatrixColumn( m, 1 ).length(); + const scaleZ = 1 / _v1$5.setFromMatrixColumn( m, 2 ).length(); - Object3D.prototype.copy.call( this, source, recursive ); + te[ 0 ] = me[ 0 ] * scaleX; + te[ 1 ] = me[ 1 ] * scaleX; + te[ 2 ] = me[ 2 ] * scaleX; + te[ 3 ] = 0; - if ( source.background !== null ) this.background = source.background.clone(); - if ( source.fog !== null ) this.fog = source.fog.clone(); - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + te[ 4 ] = me[ 4 ] * scaleY; + te[ 5 ] = me[ 5 ] * scaleY; + te[ 6 ] = me[ 6 ] * scaleY; + te[ 7 ] = 0; - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; + te[ 8 ] = me[ 8 ] * scaleZ; + te[ 9 ] = me[ 9 ] * scaleZ; + te[ 10 ] = me[ 10 ] * scaleZ; + te[ 11 ] = 0; - return this; + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - }, + return this; - toJSON: function ( meta ) { + } - var data = Object3D.prototype.toJSON.call( this, meta ); + makeRotationFromEuler( euler ) { - if ( this.background !== null ) data.object.background = this.background.toJSON( meta ); - if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); + const te = this.elements; - return data; + const x = euler.x, y = euler.y, z = euler.z; + const a = Math.cos( x ), b = Math.sin( x ); + const c = Math.cos( y ), d = Math.sin( y ); + const e = Math.cos( z ), f = Math.sin( z ); - }, + if ( euler.order === 'XYZ' ) { - dispose: function () { + const ae = a * e, af = a * f, be = b * e, bf = b * f; - this.dispatchEvent( { type: 'dispose' } ); + te[ 0 ] = c * e; + te[ 4 ] = - c * f; + te[ 8 ] = d; - } + te[ 1 ] = af + be * d; + te[ 5 ] = ae - bf * d; + te[ 9 ] = - b * c; - } ); + te[ 2 ] = bf - ae * d; + te[ 6 ] = be + af * d; + te[ 10 ] = a * c; - var _points = [ - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3(), - new Vector3() - ]; - var _vector$2 = new Vector3(); + } else if ( euler.order === 'YXZ' ) { - // triangle centered vertices + const ce = c * e, cf = c * f, de = d * e, df = d * f; - var _v0 = new Vector3(); - var _v1$2 = new Vector3(); - var _v2 = new Vector3(); + te[ 0 ] = ce + df * b; + te[ 4 ] = de * b - cf; + te[ 8 ] = a * d; - // triangle edge vectors + te[ 1 ] = a * f; + te[ 5 ] = a * e; + te[ 9 ] = - b; - var _f0 = new Vector3(); - var _f1 = new Vector3(); - var _f2 = new Vector3(); + te[ 2 ] = cf * b - de; + te[ 6 ] = df + ce * b; + te[ 10 ] = a * c; - var _center = new Vector3(); - var _extents = new Vector3(); - var _triangleNormal = new Vector3(); - var _testAxis = new Vector3(); + } else if ( euler.order === 'ZXY' ) { - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - */ + const ce = c * e, cf = c * f, de = d * e, df = d * f; - function Box3( min, max ) { + te[ 0 ] = ce - df * b; + te[ 4 ] = - a * f; + te[ 8 ] = de + cf * b; - this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity ); + te[ 1 ] = cf + de * b; + te[ 5 ] = a * e; + te[ 9 ] = df - ce * b; - } + te[ 2 ] = - a * d; + te[ 6 ] = b; + te[ 10 ] = a * c; - Object.assign( Box3.prototype, { + } else if ( euler.order === 'ZYX' ) { - isBox3: true, + const ae = a * e, af = a * f, be = b * e, bf = b * f; - set: function ( min, max ) { + te[ 0 ] = c * e; + te[ 4 ] = be * d - af; + te[ 8 ] = ae * d + bf; - this.min.copy( min ); - this.max.copy( max ); + te[ 1 ] = c * f; + te[ 5 ] = bf * d + ae; + te[ 9 ] = af * d - be; - return this; + te[ 2 ] = - d; + te[ 6 ] = b * c; + te[ 10 ] = a * c; - }, + } else if ( euler.order === 'YZX' ) { - setFromArray: function ( array ) { + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; + te[ 0 ] = c * e; + te[ 4 ] = bd - ac * f; + te[ 8 ] = bc * f + ad; - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; + te[ 1 ] = f; + te[ 5 ] = a * e; + te[ 9 ] = - b * e; - for ( var i = 0, l = array.length; i < l; i += 3 ) { + te[ 2 ] = - d * e; + te[ 6 ] = ad * f + bc; + te[ 10 ] = ac - bd * f; - var x = array[ i ]; - var y = array[ i + 1 ]; - var z = array[ i + 2 ]; + } else if ( euler.order === 'XZY' ) { - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + const ac = a * c, ad = a * d, bc = b * c, bd = b * d; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + te[ 0 ] = c * e; + te[ 4 ] = - f; + te[ 8 ] = d * e; - } + te[ 1 ] = ac * f + bd; + te[ 5 ] = a * e; + te[ 9 ] = ad * f - bc; - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + te[ 2 ] = bc * f - ad; + te[ 6 ] = b * e; + te[ 10 ] = bd * f + ac; - return this; + } - }, + // bottom row + te[ 3 ] = 0; + te[ 7 ] = 0; + te[ 11 ] = 0; - setFromBufferAttribute: function ( attribute ) { + // last column + te[ 12 ] = 0; + te[ 13 ] = 0; + te[ 14 ] = 0; + te[ 15 ] = 1; - var minX = + Infinity; - var minY = + Infinity; - var minZ = + Infinity; + return this; - var maxX = - Infinity; - var maxY = - Infinity; - var maxZ = - Infinity; + } - for ( var i = 0, l = attribute.count; i < l; i ++ ) { + makeRotationFromQuaternion( q ) { - var x = attribute.getX( i ); - var y = attribute.getY( i ); - var z = attribute.getZ( i ); + return this.compose( _zero, q, _one ); - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( z < minZ ) minZ = z; + } - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; - if ( z > maxZ ) maxZ = z; + lookAt( eye, target, up ) { - } + const te = this.elements; - this.min.set( minX, minY, minZ ); - this.max.set( maxX, maxY, maxZ ); + _z.subVectors( eye, target ); - return this; + if ( _z.lengthSq() === 0 ) { - }, + // eye and target are in the same position - setFromPoints: function ( points ) { + _z.z = 1; - this.makeEmpty(); + } - for ( var i = 0, il = points.length; i < il; i ++ ) { + _z.normalize(); + _x.crossVectors( up, _z ); - this.expandByPoint( points[ i ] ); + if ( _x.lengthSq() === 0 ) { - } + // up and z are parallel - return this; + if ( Math.abs( up.z ) === 1 ) { - }, + _z.x += 0.0001; - setFromCenterAndSize: function ( center, size ) { + } else { - var halfSize = _vector$2.copy( size ).multiplyScalar( 0.5 ); + _z.z += 0.0001; - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); + } - return this; + _z.normalize(); + _x.crossVectors( up, _z ); - }, + } - setFromObject: function ( object ) { + _x.normalize(); + _y.crossVectors( _z, _x ); - this.makeEmpty(); + te[ 0 ] = _x.x; te[ 4 ] = _y.x; te[ 8 ] = _z.x; + te[ 1 ] = _x.y; te[ 5 ] = _y.y; te[ 9 ] = _z.y; + te[ 2 ] = _x.z; te[ 6 ] = _y.z; te[ 10 ] = _z.z; - return this.expandByObject( object ); + return this; - }, + } - clone: function () { + multiply( m ) { - return new this.constructor().copy( this ); + return this.multiplyMatrices( this, m ); - }, + } - copy: function ( box ) { + premultiply( m ) { - this.min.copy( box.min ); - this.max.copy( box.max ); + return this.multiplyMatrices( m, this ); - return this; + } - }, + multiplyMatrices( a, b ) { - makeEmpty: function () { + const ae = a.elements; + const be = b.elements; + const te = this.elements; - this.min.x = this.min.y = this.min.z = + Infinity; - this.max.x = this.max.y = this.max.z = - Infinity; + const a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; + const a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; + const a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; + const a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; - return this; + const b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; + const b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; + const b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; + const b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; - }, + te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; - isEmpty: function () { + te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); + te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; - }, + return this; - getCenter: function ( target ) { + } - if ( target === undefined ) { + multiplyScalar( s ) { - console.warn( 'THREE.Box3: .getCenter() target is now required' ); - target = new Vector3(); + const te = this.elements; - } + te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; + te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; + te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; + te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + return this; - }, + } - getSize: function ( target ) { + determinant() { + + const te = this.elements; + + const n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; + const n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; + const n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; + const n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n41 * ( + + n14 * n23 * n32 + - n13 * n24 * n32 + - n14 * n22 * n33 + + n12 * n24 * n33 + + n13 * n22 * n34 + - n12 * n23 * n34 + ) + + n42 * ( + + n11 * n23 * n34 + - n11 * n24 * n33 + + n14 * n21 * n33 + - n13 * n21 * n34 + + n13 * n24 * n31 + - n14 * n23 * n31 + ) + + n43 * ( + + n11 * n24 * n32 + - n11 * n22 * n34 + - n14 * n21 * n32 + + n12 * n21 * n34 + + n14 * n22 * n31 + - n12 * n24 * n31 + ) + + n44 * ( + - n13 * n22 * n31 + - n11 * n23 * n32 + + n11 * n22 * n33 + + n13 * n21 * n32 + - n12 * n21 * n33 + + n12 * n23 * n31 + ) - if ( target === undefined ) { + ); - console.warn( 'THREE.Box3: .getSize() target is now required' ); - target = new Vector3(); + } - } + transpose() { - return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min ); + const te = this.elements; + let tmp; - }, + tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; + tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; + tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; - expandByPoint: function ( point ) { + tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; + tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; + tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; - this.min.min( point ); - this.max.max( point ); + return this; - return this; + } - }, + setPosition( x, y, z ) { - expandByVector: function ( vector ) { + const te = this.elements; - this.min.sub( vector ); - this.max.add( vector ); + if ( x.isVector3 ) { - return this; + te[ 12 ] = x.x; + te[ 13 ] = x.y; + te[ 14 ] = x.z; - }, + } else { - expandByScalar: function ( scalar ) { + te[ 12 ] = x; + te[ 13 ] = y; + te[ 14 ] = z; - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); + } - return this; + return this; - }, + } - expandByObject: function ( object ) { + invert() { - var i, l; + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + const te = this.elements, - // Computes the world-axis-aligned bounding box of an object (including its children), - // accounting for both the object's, and children's, world transforms + n11 = te[ 0 ], n21 = te[ 1 ], n31 = te[ 2 ], n41 = te[ 3 ], + n12 = te[ 4 ], n22 = te[ 5 ], n32 = te[ 6 ], n42 = te[ 7 ], + n13 = te[ 8 ], n23 = te[ 9 ], n33 = te[ 10 ], n43 = te[ 11 ], + n14 = te[ 12 ], n24 = te[ 13 ], n34 = te[ 14 ], n44 = te[ 15 ], - object.updateWorldMatrix( false, false ); + t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44, + t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44, + t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44, + t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; - var geometry = object.geometry; + const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14; - if ( geometry !== undefined ) { + if ( det === 0 ) return this.set( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); - if ( geometry.isGeometry ) { + const detInv = 1 / det; - var vertices = geometry.vertices; + te[ 0 ] = t11 * detInv; + te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv; + te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv; + te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv; - for ( i = 0, l = vertices.length; i < l; i ++ ) { + te[ 4 ] = t12 * detInv; + te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv; + te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv; + te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv; - _vector$2.copy( vertices[ i ] ); - _vector$2.applyMatrix4( object.matrixWorld ); + te[ 8 ] = t13 * detInv; + te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv; + te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv; + te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv; - this.expandByPoint( _vector$2 ); + te[ 12 ] = t14 * detInv; + te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv; + te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv; + te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv; - } + return this; - } else if ( geometry.isBufferGeometry ) { + } - var attribute = geometry.attributes.position; + scale( v ) { - if ( attribute !== undefined ) { + const te = this.elements; + const x = v.x, y = v.y, z = v.z; - for ( i = 0, l = attribute.count; i < l; i ++ ) { + te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; + te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; + te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; + te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; - _vector$2.fromBufferAttribute( attribute, i ).applyMatrix4( object.matrixWorld ); + return this; - this.expandByPoint( _vector$2 ); + } - } + getMaxScaleOnAxis() { - } + const te = this.elements; - } + const scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; + const scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; + const scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; - } + return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); - // + } - var children = object.children; + makeTranslation( x, y, z ) { - for ( i = 0, l = children.length; i < l; i ++ ) { + if ( x.isVector3 ) { - this.expandByObject( children[ i ] ); + this.set( - } + 1, 0, 0, x.x, + 0, 1, 0, x.y, + 0, 0, 1, x.z, + 0, 0, 0, 1 - return this; + ); - }, + } else { - containsPoint: function ( point ) { + this.set( - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y || - point.z < this.min.z || point.z > this.max.z ? false : true; + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 - }, + ); - containsBox: function ( box ) { + } - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y && - this.min.z <= box.min.z && box.max.z <= this.max.z; + return this; - }, + } - getParameter: function ( point, target ) { + makeRotationX( theta ) { - // This can potentially have a divide by zero if the box - // has a size dimension of 0. + const c = Math.cos( theta ), s = Math.sin( theta ); - if ( target === undefined ) { + this.set( - console.warn( 'THREE.Box3: .getParameter() target is now required' ); - target = new Vector3(); + 1, 0, 0, 0, + 0, c, - s, 0, + 0, s, c, 0, + 0, 0, 0, 1 - } + ); - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ), - ( point.z - this.min.z ) / ( this.max.z - this.min.z ) - ); + return this; - }, + } - intersectsBox: function ( box ) { + makeRotationY( theta ) { - // using 6 splitting planes to rule out intersections. - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y || - box.max.z < this.min.z || box.min.z > this.max.z ? false : true; + const c = Math.cos( theta ), s = Math.sin( theta ); - }, + this.set( - intersectsSphere: function ( sphere ) { + c, 0, s, 0, + 0, 1, 0, 0, + - s, 0, c, 0, + 0, 0, 0, 1 - // Find the point on the AABB closest to the sphere center. - this.clampPoint( sphere.center, _vector$2 ); + ); - // If that point is inside the sphere, the AABB and sphere intersect. - return _vector$2.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); + return this; - }, + } - intersectsPlane: function ( plane ) { + makeRotationZ( theta ) { - // We compute the minimum and maximum dot product values. If those values - // are on the same side (back or front) of the plane, then there is no intersection. + const c = Math.cos( theta ), s = Math.sin( theta ); - var min, max; + this.set( - if ( plane.normal.x > 0 ) { + c, - s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - min = plane.normal.x * this.min.x; - max = plane.normal.x * this.max.x; + ); - } else { + return this; - min = plane.normal.x * this.max.x; - max = plane.normal.x * this.min.x; + } - } + makeRotationAxis( axis, angle ) { - if ( plane.normal.y > 0 ) { + // Based on http://www.gamedev.net/reference/articles/article1199.asp - min += plane.normal.y * this.min.y; - max += plane.normal.y * this.max.y; + const c = Math.cos( angle ); + const s = Math.sin( angle ); + const t = 1 - c; + const x = axis.x, y = axis.y, z = axis.z; + const tx = t * x, ty = t * y; - } else { + this.set( - min += plane.normal.y * this.max.y; - max += plane.normal.y * this.min.y; + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 - } + ); - if ( plane.normal.z > 0 ) { + return this; - min += plane.normal.z * this.min.z; - max += plane.normal.z * this.max.z; + } - } else { + makeScale( x, y, z ) { - min += plane.normal.z * this.max.z; - max += plane.normal.z * this.min.z; + this.set( - } + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 - return ( min <= - plane.constant && max >= - plane.constant ); + ); - }, + return this; - intersectsTriangle: function ( triangle ) { + } - if ( this.isEmpty() ) { + makeShear( xy, xz, yx, yz, zx, zy ) { - return false; + this.set( - } + 1, yx, zx, 0, + xy, 1, zy, 0, + xz, yz, 1, 0, + 0, 0, 0, 1 - // compute box center and extents - this.getCenter( _center ); - _extents.subVectors( this.max, _center ); + ); - // translate triangle to aabb origin - _v0.subVectors( triangle.a, _center ); - _v1$2.subVectors( triangle.b, _center ); - _v2.subVectors( triangle.c, _center ); + return this; - // compute edge vectors for triangle - _f0.subVectors( _v1$2, _v0 ); - _f1.subVectors( _v2, _v1$2 ); - _f2.subVectors( _v0, _v2 ); + } - // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb - // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation - // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned) - var axes = [ - 0, - _f0.z, _f0.y, 0, - _f1.z, _f1.y, 0, - _f2.z, _f2.y, - _f0.z, 0, - _f0.x, _f1.z, 0, - _f1.x, _f2.z, 0, - _f2.x, - - _f0.y, _f0.x, 0, - _f1.y, _f1.x, 0, - _f2.y, _f2.x, 0 - ]; - if ( ! satForAxes( axes, _v0, _v1$2, _v2, _extents ) ) { + compose( position, quaternion, scale ) { - return false; + const te = this.elements; - } + const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w; + const x2 = x + x, y2 = y + y, z2 = z + z; + const xx = x * x2, xy = x * y2, xz = x * z2; + const yy = y * y2, yz = y * z2, zz = z * z2; + const wx = w * x2, wy = w * y2, wz = w * z2; - // test 3 face normals from the aabb - axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ]; - if ( ! satForAxes( axes, _v0, _v1$2, _v2, _extents ) ) { + const sx = scale.x, sy = scale.y, sz = scale.z; - return false; + te[ 0 ] = ( 1 - ( yy + zz ) ) * sx; + te[ 1 ] = ( xy + wz ) * sx; + te[ 2 ] = ( xz - wy ) * sx; + te[ 3 ] = 0; - } + te[ 4 ] = ( xy - wz ) * sy; + te[ 5 ] = ( 1 - ( xx + zz ) ) * sy; + te[ 6 ] = ( yz + wx ) * sy; + te[ 7 ] = 0; - // finally testing the face normal of the triangle - // use already existing triangle edge vectors here - _triangleNormal.crossVectors( _f0, _f1 ); - axes = [ _triangleNormal.x, _triangleNormal.y, _triangleNormal.z ]; + te[ 8 ] = ( xz + wy ) * sz; + te[ 9 ] = ( yz - wx ) * sz; + te[ 10 ] = ( 1 - ( xx + yy ) ) * sz; + te[ 11 ] = 0; - return satForAxes( axes, _v0, _v1$2, _v2, _extents ); + te[ 12 ] = position.x; + te[ 13 ] = position.y; + te[ 14 ] = position.z; + te[ 15 ] = 1; - }, + return this; - clampPoint: function ( point, target ) { + } - if ( target === undefined ) { + decompose( position, quaternion, scale ) { - console.warn( 'THREE.Box3: .clampPoint() target is now required' ); - target = new Vector3(); + const te = this.elements; - } + let sx = _v1$5.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); + const sy = _v1$5.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); + const sz = _v1$5.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); - return target.copy( point ).clamp( this.min, this.max ); + // if determine is negative, we need to invert one scale + const det = this.determinant(); + if ( det < 0 ) sx = - sx; - }, + position.x = te[ 12 ]; + position.y = te[ 13 ]; + position.z = te[ 14 ]; - distanceToPoint: function ( point ) { + // scale the rotation part + _m1$2.copy( this ); - var clampedPoint = _vector$2.copy( point ).clamp( this.min, this.max ); + const invSX = 1 / sx; + const invSY = 1 / sy; + const invSZ = 1 / sz; - return clampedPoint.sub( point ).length(); + _m1$2.elements[ 0 ] *= invSX; + _m1$2.elements[ 1 ] *= invSX; + _m1$2.elements[ 2 ] *= invSX; - }, + _m1$2.elements[ 4 ] *= invSY; + _m1$2.elements[ 5 ] *= invSY; + _m1$2.elements[ 6 ] *= invSY; - getBoundingSphere: function ( target ) { + _m1$2.elements[ 8 ] *= invSZ; + _m1$2.elements[ 9 ] *= invSZ; + _m1$2.elements[ 10 ] *= invSZ; - if ( target === undefined ) { + quaternion.setFromRotationMatrix( _m1$2 ); - console.error( 'THREE.Box3: .getBoundingSphere() target is now required' ); - //target = new Sphere(); // removed to avoid cyclic dependency + scale.x = sx; + scale.y = sy; + scale.z = sz; - } + return this; - this.getCenter( target.center ); + } - target.radius = this.getSize( _vector$2 ).length() * 0.5; + makePerspective( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { - return target; + const te = this.elements; + const x = 2 * near / ( right - left ); + const y = 2 * near / ( top - bottom ); - }, + const a = ( right + left ) / ( right - left ); + const b = ( top + bottom ) / ( top - bottom ); - intersect: function ( box ) { + let c, d; - this.min.max( box.min ); - this.max.min( box.max ); + if ( coordinateSystem === WebGLCoordinateSystem ) { - // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values. - if ( this.isEmpty() ) this.makeEmpty(); + c = - ( far + near ) / ( far - near ); + d = ( - 2 * far * near ) / ( far - near ); - return this; + } else if ( coordinateSystem === WebGPUCoordinateSystem ) { - }, + c = - far / ( far - near ); + d = ( - far * near ) / ( far - near ); - union: function ( box ) { + } else { - this.min.min( box.min ); - this.max.max( box.max ); + throw new Error( 'THREE.Matrix4.makePerspective(): Invalid coordinate system: ' + coordinateSystem ); - return this; + } - }, + te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; + te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; - applyMatrix4: function ( matrix ) { + return this; - // transform of empty box is an empty box. - if ( this.isEmpty() ) return this; + } - // NOTE: I am using a binary pattern to specify all 2^3 combinations below - _points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 - _points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 - _points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 - _points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 - _points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 - _points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 - _points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 - _points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 + makeOrthographic( left, right, top, bottom, near, far, coordinateSystem = WebGLCoordinateSystem ) { - this.setFromPoints( _points ); + const te = this.elements; + const w = 1.0 / ( right - left ); + const h = 1.0 / ( top - bottom ); + const p = 1.0 / ( far - near ); - return this; + const x = ( right + left ) * w; + const y = ( top + bottom ) * h; - }, + let z, zInv; - translate: function ( offset ) { + if ( coordinateSystem === WebGLCoordinateSystem ) { - this.min.add( offset ); - this.max.add( offset ); + z = ( far + near ) * p; + zInv = - 2 * p; - return this; + } else if ( coordinateSystem === WebGPUCoordinateSystem ) { - }, + z = near * p; + zInv = - 1 * p; - equals: function ( box ) { + } else { - return box.min.equals( this.min ) && box.max.equals( this.max ); + throw new Error( 'THREE.Matrix4.makeOrthographic(): Invalid coordinate system: ' + coordinateSystem ); } - } ); + te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; + te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y; + te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = zInv; te[ 14 ] = - z; + te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; - function satForAxes( axes, v0, v1, v2, extents ) { + return this; - var i, j; + } - for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) { + equals( matrix ) { - _testAxis.fromArray( axes, i ); - // project the aabb onto the seperating axis - var r = extents.x * Math.abs( _testAxis.x ) + extents.y * Math.abs( _testAxis.y ) + extents.z * Math.abs( _testAxis.z ); - // project all 3 vertices of the triangle onto the seperating axis - var p0 = v0.dot( _testAxis ); - var p1 = v1.dot( _testAxis ); - var p2 = v2.dot( _testAxis ); - // actual test, basically see if either of the most extreme of the triangle points intersects r - if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) { + const te = this.elements; + const me = matrix.elements; - // points of the projected triangle are outside the projected half-length of the aabb - // the axis is seperating and we can exit - return false; + for ( let i = 0; i < 16; i ++ ) { - } + if ( te[ i ] !== me[ i ] ) return false; } @@ -6641,1683 +6727,1724 @@ } - var _box = new Box3(); - - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ - - function Sphere( center, radius ) { - - this.center = ( center !== undefined ) ? center : new Vector3(); - this.radius = ( radius !== undefined ) ? radius : 0; - - } - - Object.assign( Sphere.prototype, { + fromArray( array, offset = 0 ) { - set: function ( center, radius ) { + for ( let i = 0; i < 16; i ++ ) { - this.center.copy( center ); - this.radius = radius; + this.elements[ i ] = array[ i + offset ]; - return this; + } - }, + return this; - setFromPoints: function ( points, optionalCenter ) { + } - var center = this.center; + toArray( array = [], offset = 0 ) { - if ( optionalCenter !== undefined ) { + const te = this.elements; - center.copy( optionalCenter ); + array[ offset ] = te[ 0 ]; + array[ offset + 1 ] = te[ 1 ]; + array[ offset + 2 ] = te[ 2 ]; + array[ offset + 3 ] = te[ 3 ]; - } else { + array[ offset + 4 ] = te[ 4 ]; + array[ offset + 5 ] = te[ 5 ]; + array[ offset + 6 ] = te[ 6 ]; + array[ offset + 7 ] = te[ 7 ]; - _box.setFromPoints( points ).getCenter( center ); + array[ offset + 8 ] = te[ 8 ]; + array[ offset + 9 ] = te[ 9 ]; + array[ offset + 10 ] = te[ 10 ]; + array[ offset + 11 ] = te[ 11 ]; - } + array[ offset + 12 ] = te[ 12 ]; + array[ offset + 13 ] = te[ 13 ]; + array[ offset + 14 ] = te[ 14 ]; + array[ offset + 15 ] = te[ 15 ]; - var maxRadiusSq = 0; + return array; - for ( var i = 0, il = points.length; i < il; i ++ ) { + } - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); +} - } +const _v1$5 = /*@__PURE__*/ new Vector3(); +const _m1$2 = /*@__PURE__*/ new Matrix4(); +const _zero = /*@__PURE__*/ new Vector3( 0, 0, 0 ); +const _one = /*@__PURE__*/ new Vector3( 1, 1, 1 ); +const _x = /*@__PURE__*/ new Vector3(); +const _y = /*@__PURE__*/ new Vector3(); +const _z = /*@__PURE__*/ new Vector3(); - this.radius = Math.sqrt( maxRadiusSq ); +const _matrix = /*@__PURE__*/ new Matrix4(); +const _quaternion$3 = /*@__PURE__*/ new Quaternion(); - return this; +class Euler { - }, + constructor( x = 0, y = 0, z = 0, order = Euler.DEFAULT_ORDER ) { - clone: function () { + this.isEuler = true; - return new this.constructor().copy( this ); + this._x = x; + this._y = y; + this._z = z; + this._order = order; - }, + } - copy: function ( sphere ) { + get x() { - this.center.copy( sphere.center ); - this.radius = sphere.radius; + return this._x; - return this; + } - }, + set x( value ) { - empty: function () { + this._x = value; + this._onChangeCallback(); - return ( this.radius <= 0 ); + } - }, + get y() { - containsPoint: function ( point ) { + return this._y; - return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); + } - }, + set y( value ) { - distanceToPoint: function ( point ) { + this._y = value; + this._onChangeCallback(); - return ( point.distanceTo( this.center ) - this.radius ); + } - }, + get z() { - intersectsSphere: function ( sphere ) { + return this._z; - var radiusSum = this.radius + sphere.radius; + } - return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); + set z( value ) { - }, + this._z = value; + this._onChangeCallback(); - intersectsBox: function ( box ) { + } - return box.intersectsSphere( this ); + get order() { - }, + return this._order; - intersectsPlane: function ( plane ) { + } - return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius; + set order( value ) { - }, + this._order = value; + this._onChangeCallback(); - clampPoint: function ( point, target ) { + } - var deltaLengthSq = this.center.distanceToSquared( point ); + set( x, y, z, order = this._order ) { - if ( target === undefined ) { + this._x = x; + this._y = y; + this._z = z; + this._order = order; - console.warn( 'THREE.Sphere: .clampPoint() target is now required' ); - target = new Vector3(); + this._onChangeCallback(); - } + return this; - target.copy( point ); + } - if ( deltaLengthSq > ( this.radius * this.radius ) ) { + clone() { - target.sub( this.center ).normalize(); - target.multiplyScalar( this.radius ).add( this.center ); + return new this.constructor( this._x, this._y, this._z, this._order ); - } + } - return target; + copy( euler ) { - }, + this._x = euler._x; + this._y = euler._y; + this._z = euler._z; + this._order = euler._order; - getBoundingBox: function ( target ) { + this._onChangeCallback(); - if ( target === undefined ) { + return this; - console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' ); - target = new Box3(); + } - } + setFromRotationMatrix( m, order = this._order, update = true ) { - target.set( this.center, this.center ); - target.expandByScalar( this.radius ); + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - return target; + const te = m.elements; + const m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; + const m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; + const m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; - }, + switch ( order ) { - applyMatrix4: function ( matrix ) { + case 'XYZ': - this.center.applyMatrix4( matrix ); - this.radius = this.radius * matrix.getMaxScaleOnAxis(); + this._y = Math.asin( clamp( m13, - 1, 1 ) ); - return this; + if ( Math.abs( m13 ) < 0.9999999 ) { - }, + this._x = Math.atan2( - m23, m33 ); + this._z = Math.atan2( - m12, m11 ); - translate: function ( offset ) { + } else { - this.center.add( offset ); + this._x = Math.atan2( m32, m22 ); + this._z = 0; - return this; + } - }, + break; - equals: function ( sphere ) { + case 'YXZ': - return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); + this._x = Math.asin( - clamp( m23, - 1, 1 ) ); - } + if ( Math.abs( m23 ) < 0.9999999 ) { - } ); + this._y = Math.atan2( m13, m33 ); + this._z = Math.atan2( m21, m22 ); - var _vector$3 = new Vector3(); - var _segCenter = new Vector3(); - var _segDir = new Vector3(); - var _diff = new Vector3(); + } else { - var _edge1 = new Vector3(); - var _edge2 = new Vector3(); - var _normal = new Vector3(); + this._y = Math.atan2( - m31, m11 ); + this._z = 0; - /** - * @author bhouston / http://clara.io - */ + } - function Ray( origin, direction ) { + break; - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3(); + case 'ZXY': - } + this._x = Math.asin( clamp( m32, - 1, 1 ) ); - Object.assign( Ray.prototype, { + if ( Math.abs( m32 ) < 0.9999999 ) { - set: function ( origin, direction ) { + this._y = Math.atan2( - m31, m33 ); + this._z = Math.atan2( - m12, m22 ); - this.origin.copy( origin ); - this.direction.copy( direction ); + } else { - return this; + this._y = 0; + this._z = Math.atan2( m21, m11 ); - }, + } - clone: function () { + break; - return new this.constructor().copy( this ); + case 'ZYX': - }, + this._y = Math.asin( - clamp( m31, - 1, 1 ) ); - copy: function ( ray ) { + if ( Math.abs( m31 ) < 0.9999999 ) { - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); + this._x = Math.atan2( m32, m33 ); + this._z = Math.atan2( m21, m11 ); - return this; + } else { - }, + this._x = 0; + this._z = Math.atan2( - m12, m22 ); - at: function ( t, target ) { + } - if ( target === undefined ) { + break; - console.warn( 'THREE.Ray: .at() target is now required' ); - target = new Vector3(); + case 'YZX': - } + this._z = Math.asin( clamp( m21, - 1, 1 ) ); - return target.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + if ( Math.abs( m21 ) < 0.9999999 ) { - }, + this._x = Math.atan2( - m23, m22 ); + this._y = Math.atan2( - m31, m11 ); - lookAt: function ( v ) { + } else { - this.direction.copy( v ).sub( this.origin ).normalize(); + this._x = 0; + this._y = Math.atan2( m13, m33 ); - return this; + } - }, + break; - recast: function ( t ) { + case 'XZY': - this.origin.copy( this.at( t, _vector$3 ) ); + this._z = Math.asin( - clamp( m12, - 1, 1 ) ); - return this; + if ( Math.abs( m12 ) < 0.9999999 ) { - }, + this._x = Math.atan2( m32, m22 ); + this._y = Math.atan2( m13, m11 ); - closestPointToPoint: function ( point, target ) { + } else { - if ( target === undefined ) { + this._x = Math.atan2( - m23, m33 ); + this._y = 0; - console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' ); - target = new Vector3(); + } - } + break; - target.subVectors( point, this.origin ); + default: - var directionDistance = target.dot( this.direction ); + console.warn( 'THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order ); - if ( directionDistance < 0 ) { + } - return target.copy( this.origin ); + this._order = order; - } + if ( update === true ) this._onChangeCallback(); - return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + return this; - }, + } - distanceToPoint: function ( point ) { + setFromQuaternion( q, order, update ) { - return Math.sqrt( this.distanceSqToPoint( point ) ); + _matrix.makeRotationFromQuaternion( q ); - }, + return this.setFromRotationMatrix( _matrix, order, update ); - distanceSqToPoint: function ( point ) { + } - var directionDistance = _vector$3.subVectors( point, this.origin ).dot( this.direction ); + setFromVector3( v, order = this._order ) { - // point behind the ray + return this.set( v.x, v.y, v.z, order ); - if ( directionDistance < 0 ) { + } - return this.origin.distanceToSquared( point ); + reorder( newOrder ) { - } + // WARNING: this discards revolution information -bhouston - _vector$3.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + _quaternion$3.setFromEuler( this ); - return _vector$3.distanceToSquared( point ); + return this.setFromQuaternion( _quaternion$3, newOrder ); - }, + } - distanceSqToSegment: function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + equals( euler ) { - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment + return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); - _segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - _segDir.copy( v1 ).sub( v0 ).normalize(); - _diff.copy( this.origin ).sub( _segCenter ); + } - var segExtent = v0.distanceTo( v1 ) * 0.5; - var a01 = - this.direction.dot( _segDir ); - var b0 = _diff.dot( this.direction ); - var b1 = - _diff.dot( _segDir ); - var c = _diff.lengthSq(); - var det = Math.abs( 1 - a01 * a01 ); - var s0, s1, sqrDist, extDet; + fromArray( array ) { - if ( det > 0 ) { + this._x = array[ 0 ]; + this._y = array[ 1 ]; + this._z = array[ 2 ]; + if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; - // The ray and segment are not parallel. + this._onChangeCallback(); - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; + return this; - if ( s0 >= 0 ) { + } - if ( s1 >= - extDet ) { + toArray( array = [], offset = 0 ) { - if ( s1 <= extDet ) { + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._order; - // region 0 - // Minimum at interior points of ray and segment. + return array; - var invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + } - } else { + _onChange( callback ) { - // region 1 + this._onChangeCallback = callback; - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + return this; - } + } - } else { + _onChangeCallback() {} - // region 5 + *[ Symbol.iterator ]() { - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + yield this._x; + yield this._y; + yield this._z; + yield this._order; - } + } - } else { +} - if ( s1 <= - extDet ) { +Euler.DEFAULT_ORDER = 'XYZ'; - // region 4 +class Layers { - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + constructor() { - } else if ( s1 <= extDet ) { + this.mask = 1 | 0; - // region 3 + } - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; + set( channel ) { - } else { + this.mask = ( 1 << channel | 0 ) >>> 0; - // region 2 + } - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + enable( channel ) { - } + this.mask |= 1 << channel | 0; - } + } - } else { + enableAll() { - // Ray and segment are parallel. + this.mask = 0xffffffff | 0; - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + } - } + toggle( channel ) { - if ( optionalPointOnRay ) { + this.mask ^= 1 << channel | 0; - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); + } - } + disable( channel ) { - if ( optionalPointOnSegment ) { + this.mask &= ~ ( 1 << channel | 0 ); - optionalPointOnSegment.copy( _segDir ).multiplyScalar( s1 ).add( _segCenter ); + } - } + disableAll() { - return sqrDist; + this.mask = 0; - }, + } - intersectSphere: function ( sphere, target ) { + test( layers ) { - _vector$3.subVectors( sphere.center, this.origin ); - var tca = _vector$3.dot( this.direction ); - var d2 = _vector$3.dot( _vector$3 ) - tca * tca; - var radius2 = sphere.radius * sphere.radius; + return ( this.mask & layers.mask ) !== 0; - if ( d2 > radius2 ) return null; + } - var thc = Math.sqrt( radius2 - d2 ); + isEnabled( channel ) { - // t0 = first intersect point - entrance on front of sphere - var t0 = tca - thc; + return ( this.mask & ( 1 << channel | 0 ) ) !== 0; - // t1 = second intersect point - exit point on back of sphere - var t1 = tca + thc; + } - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; +} - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, target ); +let _object3DId = 0; - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, target ); +const _v1$4 = /*@__PURE__*/ new Vector3(); +const _q1 = /*@__PURE__*/ new Quaternion(); +const _m1$1 = /*@__PURE__*/ new Matrix4(); +const _target = /*@__PURE__*/ new Vector3(); - }, +const _position$3 = /*@__PURE__*/ new Vector3(); +const _scale$2 = /*@__PURE__*/ new Vector3(); +const _quaternion$2 = /*@__PURE__*/ new Quaternion(); - intersectsSphere: function ( sphere ) { +const _xAxis = /*@__PURE__*/ new Vector3( 1, 0, 0 ); +const _yAxis = /*@__PURE__*/ new Vector3( 0, 1, 0 ); +const _zAxis = /*@__PURE__*/ new Vector3( 0, 0, 1 ); - return this.distanceSqToPoint( sphere.center ) <= ( sphere.radius * sphere.radius ); +const _addedEvent = { type: 'added' }; +const _removedEvent = { type: 'removed' }; - }, +class Object3D extends EventDispatcher { - distanceToPlane: function ( plane ) { + constructor() { - var denominator = plane.normal.dot( this.direction ); + super(); - if ( denominator === 0 ) { + this.isObject3D = true; - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { + Object.defineProperty( this, 'id', { value: _object3DId ++ } ); - return 0; + this.uuid = generateUUID(); - } + this.name = ''; + this.type = 'Object3D'; - // Null is preferable to undefined since undefined means.... it is undefined + this.parent = null; + this.children = []; - return null; + this.up = Object3D.DEFAULT_UP.clone(); - } + const position = new Vector3(); + const rotation = new Euler(); + const quaternion = new Quaternion(); + const scale = new Vector3( 1, 1, 1 ); - var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + function onRotationChange() { - // Return if the ray never intersects the plane + quaternion.setFromEuler( rotation, false ); - return t >= 0 ? t : null; + } - }, + function onQuaternionChange() { - intersectPlane: function ( plane, target ) { + rotation.setFromQuaternion( quaternion, undefined, false ); - var t = this.distanceToPlane( plane ); + } - if ( t === null ) { - - return null; + rotation._onChange( onRotationChange ); + quaternion._onChange( onQuaternionChange ); + Object.defineProperties( this, { + position: { + configurable: true, + enumerable: true, + value: position + }, + rotation: { + configurable: true, + enumerable: true, + value: rotation + }, + quaternion: { + configurable: true, + enumerable: true, + value: quaternion + }, + scale: { + configurable: true, + enumerable: true, + value: scale + }, + modelViewMatrix: { + value: new Matrix4() + }, + normalMatrix: { + value: new Matrix3() } + } ); - return this.at( t, target ); + this.matrix = new Matrix4(); + this.matrixWorld = new Matrix4(); - }, + this.matrixAutoUpdate = Object3D.DEFAULT_MATRIX_AUTO_UPDATE; + this.matrixWorldNeedsUpdate = false; - intersectsPlane: function ( plane ) { + this.matrixWorldAutoUpdate = Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE; // checked by the renderer - // check if the ray lies on the plane first + this.layers = new Layers(); + this.visible = true; - var distToPoint = plane.distanceToPoint( this.origin ); + this.castShadow = false; + this.receiveShadow = false; - if ( distToPoint === 0 ) { + this.frustumCulled = true; + this.renderOrder = 0; - return true; + this.animations = []; - } + this.userData = {}; - var denominator = plane.normal.dot( this.direction ); + } - if ( denominator * distToPoint < 0 ) { + onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {} - return true; + onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {} - } + applyMatrix4( matrix ) { - // ray origin is behind the plane (and is pointing behind it) + if ( this.matrixAutoUpdate ) this.updateMatrix(); - return false; + this.matrix.premultiply( matrix ); - }, + this.matrix.decompose( this.position, this.quaternion, this.scale ); - intersectBox: function ( box, target ) { + } - var tmin, tmax, tymin, tymax, tzmin, tzmax; + applyQuaternion( q ) { - var invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; + this.quaternion.premultiply( q ); - var origin = this.origin; + return this; - if ( invdirx >= 0 ) { + } - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; + setRotationFromAxisAngle( axis, angle ) { - } else { + // assumes axis is normalized - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; + this.quaternion.setFromAxisAngle( axis, angle ); - } + } - if ( invdiry >= 0 ) { + setRotationFromEuler( euler ) { - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; + this.quaternion.setFromEuler( euler, true ); - } else { + } - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; + setRotationFromMatrix( m ) { - } + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + this.quaternion.setFromRotationMatrix( m ); - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN + } - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + setRotationFromQuaternion( q ) { - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + // assumes q is normalized - if ( invdirz >= 0 ) { + this.quaternion.copy( q ); - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; + } - } else { + rotateOnAxis( axis, angle ) { - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; + // rotate object on axis in object space + // axis is assumed to be normalized - } + _q1.setFromAxisAngle( axis, angle ); - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + this.quaternion.multiply( _q1 ); - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + return this; - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + } - //return point closest to the ray (positive side) + rotateOnWorldAxis( axis, angle ) { - if ( tmax < 0 ) return null; + // rotate object on axis in world space + // axis is assumed to be normalized + // method assumes no rotated parent - return this.at( tmin >= 0 ? tmin : tmax, target ); + _q1.setFromAxisAngle( axis, angle ); - }, + this.quaternion.premultiply( _q1 ); - intersectsBox: function ( box ) { + return this; + + } - return this.intersectBox( box, _vector$3 ) !== null; + rotateX( angle ) { - }, + return this.rotateOnAxis( _xAxis, angle ); - intersectTriangle: function ( a, b, c, backfaceCulling, target ) { + } - // Compute the offset origin, edges, and normal. + rotateY( angle ) { - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + return this.rotateOnAxis( _yAxis, angle ); - _edge1.subVectors( b, a ); - _edge2.subVectors( c, a ); - _normal.crossVectors( _edge1, _edge2 ); + } - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - var DdN = this.direction.dot( _normal ); - var sign; + rotateZ( angle ) { - if ( DdN > 0 ) { + return this.rotateOnAxis( _zAxis, angle ); - if ( backfaceCulling ) return null; - sign = 1; + } - } else if ( DdN < 0 ) { + translateOnAxis( axis, distance ) { - sign = - 1; - DdN = - DdN; + // translate object by distance along axis in object space + // axis is assumed to be normalized - } else { + _v1$4.copy( axis ).applyQuaternion( this.quaternion ); - return null; + this.position.add( _v1$4.multiplyScalar( distance ) ); - } + return this; - _diff.subVectors( this.origin, a ); - var DdQxE2 = sign * this.direction.dot( _edge2.crossVectors( _diff, _edge2 ) ); + } - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + translateX( distance ) { - return null; + return this.translateOnAxis( _xAxis, distance ); - } + } - var DdE1xQ = sign * this.direction.dot( _edge1.cross( _diff ) ); + translateY( distance ) { - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + return this.translateOnAxis( _yAxis, distance ); - return null; + } - } + translateZ( distance ) { - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + return this.translateOnAxis( _zAxis, distance ); - return null; + } - } + localToWorld( vector ) { - // Line intersects triangle, check if ray does. - var QdN = - sign * _diff.dot( _normal ); + this.updateWorldMatrix( true, false ); - // t < 0, no intersection - if ( QdN < 0 ) { + return vector.applyMatrix4( this.matrixWorld ); - return null; + } - } + worldToLocal( vector ) { - // Ray intersects triangle. - return this.at( QdN / DdN, target ); + this.updateWorldMatrix( true, false ); - }, + return vector.applyMatrix4( _m1$1.copy( this.matrixWorld ).invert() ); - applyMatrix4: function ( matrix4 ) { + } - this.origin.applyMatrix4( matrix4 ); - this.direction.transformDirection( matrix4 ); + lookAt( x, y, z ) { - return this; + // This method does not support objects having non-uniformly-scaled parent(s) - }, + if ( x.isVector3 ) { - equals: function ( ray ) { + _target.copy( x ); - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + } else { + + _target.set( x, y, z ); } - } ); + const parent = this.parent; - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ + this.updateWorldMatrix( true, false ); + + _position$3.setFromMatrixPosition( this.matrixWorld ); - var _v0$1 = new Vector3(); - var _v1$3 = new Vector3(); - var _v2$1 = new Vector3(); - var _v3 = new Vector3(); + if ( this.isCamera || this.isLight ) { - var _vab = new Vector3(); - var _vac = new Vector3(); - var _vbc = new Vector3(); - var _vap = new Vector3(); - var _vbp = new Vector3(); - var _vcp = new Vector3(); + _m1$1.lookAt( _position$3, _target, this.up ); - function Triangle( a, b, c ) { + } else { - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); + _m1$1.lookAt( _target, _position$3, this.up ); - } + } - Object.assign( Triangle, { + this.quaternion.setFromRotationMatrix( _m1$1 ); - getNormal: function ( a, b, c, target ) { + if ( parent ) { - if ( target === undefined ) { + _m1$1.extractRotation( parent.matrixWorld ); + _q1.setFromRotationMatrix( _m1$1 ); + this.quaternion.premultiply( _q1.invert() ); - console.warn( 'THREE.Triangle: .getNormal() target is now required' ); - target = new Vector3(); + } - } + } - target.subVectors( c, b ); - _v0$1.subVectors( a, b ); - target.cross( _v0$1 ); + add( object ) { - var targetLengthSq = target.lengthSq(); - if ( targetLengthSq > 0 ) { + if ( arguments.length > 1 ) { - return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); + for ( let i = 0; i < arguments.length; i ++ ) { + + this.add( arguments[ i ] ); } - return target.set( 0, 0, 0 ); + return this; - }, + } - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - getBarycoord: function ( point, a, b, c, target ) { + if ( object === this ) { - _v0$1.subVectors( c, a ); - _v1$3.subVectors( b, a ); - _v2$1.subVectors( point, a ); + console.error( 'THREE.Object3D.add: object can\'t be added as a child of itself.', object ); + return this; - var dot00 = _v0$1.dot( _v0$1 ); - var dot01 = _v0$1.dot( _v1$3 ); - var dot02 = _v0$1.dot( _v2$1 ); - var dot11 = _v1$3.dot( _v1$3 ); - var dot12 = _v1$3.dot( _v2$1 ); + } - var denom = ( dot00 * dot11 - dot01 * dot01 ); + if ( object && object.isObject3D ) { - if ( target === undefined ) { + if ( object.parent !== null ) { - console.warn( 'THREE.Triangle: .getBarycoord() target is now required' ); - target = new Vector3(); + object.parent.remove( object ); } - // collinear or singular triangle - if ( denom === 0 ) { + object.parent = this; + this.children.push( object ); - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return target.set( - 2, - 1, - 1 ); + object.dispatchEvent( _addedEvent ); - } + } else { - var invDenom = 1 / denom; - var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + console.error( 'THREE.Object3D.add: object not an instance of THREE.Object3D.', object ); - // barycentric coordinates must always sum to 1 - return target.set( 1 - u - v, v, u ); + } - }, + return this; - containsPoint: function ( point, a, b, c ) { + } - Triangle.getBarycoord( point, a, b, c, _v3 ); + remove( object ) { - return ( _v3.x >= 0 ) && ( _v3.y >= 0 ) && ( ( _v3.x + _v3.y ) <= 1 ); + if ( arguments.length > 1 ) { - }, + for ( let i = 0; i < arguments.length; i ++ ) { - getUV: function ( point, p1, p2, p3, uv1, uv2, uv3, target ) { + this.remove( arguments[ i ] ); - this.getBarycoord( point, p1, p2, p3, _v3 ); + } - target.set( 0, 0 ); - target.addScaledVector( uv1, _v3.x ); - target.addScaledVector( uv2, _v3.y ); - target.addScaledVector( uv3, _v3.z ); + return this; - return target; + } - }, + const index = this.children.indexOf( object ); - isFrontFacing: function ( a, b, c, direction ) { + if ( index !== - 1 ) { - _v0$1.subVectors( c, b ); - _v1$3.subVectors( a, b ); + object.parent = null; + this.children.splice( index, 1 ); - // strictly front facing - return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; + object.dispatchEvent( _removedEvent ); } - } ); + return this; - Object.assign( Triangle.prototype, { + } - set: function ( a, b, c ) { + removeFromParent() { - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); + const parent = this.parent; - return this; + if ( parent !== null ) { - }, + parent.remove( this ); - setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + } - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); + return this; - return this; + } - }, + clear() { - clone: function () { + return this.remove( ... this.children ); - return new this.constructor().copy( this ); + } - }, + attach( object ) { - copy: function ( triangle ) { + // adds object as a child of this, while maintaining the object's world transform - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); + // Note: This method does not support scene graphs having non-uniformly-scaled nodes(s) - return this; + this.updateWorldMatrix( true, false ); - }, + _m1$1.copy( this.matrixWorld ).invert(); - getArea: function () { + if ( object.parent !== null ) { - _v0$1.subVectors( this.c, this.b ); - _v1$3.subVectors( this.a, this.b ); + object.parent.updateWorldMatrix( true, false ); - return _v0$1.cross( _v1$3 ).length() * 0.5; + _m1$1.multiply( object.parent.matrixWorld ); - }, + } - getMidpoint: function ( target ) { + object.applyMatrix4( _m1$1 ); - if ( target === undefined ) { + this.add( object ); - console.warn( 'THREE.Triangle: .getMidpoint() target is now required' ); - target = new Vector3(); + object.updateWorldMatrix( false, true ); - } + return this; - return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + } - }, + getObjectById( id ) { - getNormal: function ( target ) { + return this.getObjectByProperty( 'id', id ); - return Triangle.getNormal( this.a, this.b, this.c, target ); + } - }, + getObjectByName( name ) { - getPlane: function ( target ) { + return this.getObjectByProperty( 'name', name ); - if ( target === undefined ) { + } - console.warn( 'THREE.Triangle: .getPlane() target is now required' ); - target = new Vector3(); + getObjectByProperty( name, value ) { - } + if ( this[ name ] === value ) return this; - return target.setFromCoplanarPoints( this.a, this.b, this.c ); + for ( let i = 0, l = this.children.length; i < l; i ++ ) { - }, + const child = this.children[ i ]; + const object = child.getObjectByProperty( name, value ); - getBarycoord: function ( point, target ) { + if ( object !== undefined ) { - return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); + return object; - }, + } - getUV: function ( point, uv1, uv2, uv3, target ) { + } - return Triangle.getUV( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); + return undefined; - }, + } - containsPoint: function ( point ) { + getObjectsByProperty( name, value ) { - return Triangle.containsPoint( point, this.a, this.b, this.c ); + let result = []; - }, + if ( this[ name ] === value ) result.push( this ); - isFrontFacing: function ( direction ) { + for ( let i = 0, l = this.children.length; i < l; i ++ ) { - return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); + const childResult = this.children[ i ].getObjectsByProperty( name, value ); - }, + if ( childResult.length > 0 ) { - intersectsBox: function ( box ) { + result = result.concat( childResult ); - return box.intersectsTriangle( this ); + } - }, + } - closestPointToPoint: function ( p, target ) { + return result; - if ( target === undefined ) { + } - console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' ); - target = new Vector3(); + getWorldPosition( target ) { - } + this.updateWorldMatrix( true, false ); - var a = this.a, b = this.b, c = this.c; - var v, w; + return target.setFromMatrixPosition( this.matrixWorld ); - // algorithm thanks to Real-Time Collision Detection by Christer Ericson, - // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., - // under the accompanying license; see chapter 5.1.5 for detailed explanation. - // basically, we're distinguishing which of the voronoi regions of the triangle - // the point lies in with the minimum amount of redundant computation. + } - _vab.subVectors( b, a ); - _vac.subVectors( c, a ); - _vap.subVectors( p, a ); - var d1 = _vab.dot( _vap ); - var d2 = _vac.dot( _vap ); - if ( d1 <= 0 && d2 <= 0 ) { + getWorldQuaternion( target ) { - // vertex region of A; barycentric coords (1, 0, 0) - return target.copy( a ); + this.updateWorldMatrix( true, false ); - } + this.matrixWorld.decompose( _position$3, target, _scale$2 ); - _vbp.subVectors( p, b ); - var d3 = _vab.dot( _vbp ); - var d4 = _vac.dot( _vbp ); - if ( d3 >= 0 && d4 <= d3 ) { + return target; - // vertex region of B; barycentric coords (0, 1, 0) - return target.copy( b ); + } - } + getWorldScale( target ) { - var vc = d1 * d4 - d3 * d2; - if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { + this.updateWorldMatrix( true, false ); - v = d1 / ( d1 - d3 ); - // edge region of AB; barycentric coords (1-v, v, 0) - return target.copy( a ).addScaledVector( _vab, v ); + this.matrixWorld.decompose( _position$3, _quaternion$2, target ); - } + return target; - _vcp.subVectors( p, c ); - var d5 = _vab.dot( _vcp ); - var d6 = _vac.dot( _vcp ); - if ( d6 >= 0 && d5 <= d6 ) { + } - // vertex region of C; barycentric coords (0, 0, 1) - return target.copy( c ); + getWorldDirection( target ) { - } + this.updateWorldMatrix( true, false ); - var vb = d5 * d2 - d1 * d6; - if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { + const e = this.matrixWorld.elements; - w = d2 / ( d2 - d6 ); - // edge region of AC; barycentric coords (1-w, 0, w) - return target.copy( a ).addScaledVector( _vac, w ); + return target.set( e[ 8 ], e[ 9 ], e[ 10 ] ).normalize(); - } + } - var va = d3 * d6 - d5 * d4; - if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { + raycast( /* raycaster, intersects */ ) {} - _vbc.subVectors( c, b ); - w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); - // edge region of BC; barycentric coords (0, 1-w, w) - return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC + traverse( callback ) { - } + callback( this ); - // face region - var denom = 1 / ( va + vb + vc ); - // u = va * denom - v = vb * denom; - w = vc * denom; + const children = this.children; - return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - }, + children[ i ].traverse( callback ); - equals: function ( triangle ) { + } - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + } - } + traverseVisible( callback ) { - } ); + if ( this.visible === false ) return; - /** - * @author mrdoob / http://mrdoob.com/ - */ + callback( this ); - var _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, - 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, - 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, - 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, - 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, - 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, - 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, - 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, - 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, - 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, - 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, - 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, - 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, - 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, - 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, - 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, - 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, - 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, - 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, - 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, - 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, - 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, - 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, - 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - - var _hslA = { h: 0, s: 0, l: 0 }; - var _hslB = { h: 0, s: 0, l: 0 }; - - function Color( r, g, b ) { + const children = this.children; - if ( g === undefined && b === undefined ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - // r is THREE.Color, hex or string - return this.set( r ); + children[ i ].traverseVisible( callback ); } - return this.setRGB( r, g, b ); - } - function hue2rgb( p, q, t ) { + traverseAncestors( callback ) { - if ( t < 0 ) t += 1; - if ( t > 1 ) t -= 1; - if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; - if ( t < 1 / 2 ) return q; - if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); - return p; + const parent = this.parent; - } - - function SRGBToLinear( c ) { + if ( parent !== null ) { - return ( c < 0.04045 ) ? c * 0.0773993808 : Math.pow( c * 0.9478672986 + 0.0521327014, 2.4 ); - - } + callback( parent ); - function LinearToSRGB( c ) { + parent.traverseAncestors( callback ); - return ( c < 0.0031308 ) ? c * 12.92 : 1.055 * ( Math.pow( c, 0.41666 ) ) - 0.055; + } } - Object.assign( Color.prototype, { + updateMatrix() { - isColor: true, + this.matrix.compose( this.position, this.quaternion, this.scale ); - r: 1, g: 1, b: 1, + this.matrixWorldNeedsUpdate = true; - set: function ( value ) { + } - if ( value && value.isColor ) { + updateMatrixWorld( force ) { - this.copy( value ); + if ( this.matrixAutoUpdate ) this.updateMatrix(); - } else if ( typeof value === 'number' ) { + if ( this.matrixWorldNeedsUpdate || force ) { - this.setHex( value ); + if ( this.parent === null ) { - } else if ( typeof value === 'string' ) { + this.matrixWorld.copy( this.matrix ); - this.setStyle( value ); + } else { + + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); } - return this; + this.matrixWorldNeedsUpdate = false; - }, + force = true; - setScalar: function ( scalar ) { + } - this.r = scalar; - this.g = scalar; - this.b = scalar; + // update children - return this; + const children = this.children; - }, + for ( let i = 0, l = children.length; i < l; i ++ ) { - setHex: function ( hex ) { + const child = children[ i ]; - hex = Math.floor( hex ); + if ( child.matrixWorldAutoUpdate === true || force === true ) { - this.r = ( hex >> 16 & 255 ) / 255; - this.g = ( hex >> 8 & 255 ) / 255; - this.b = ( hex & 255 ) / 255; + child.updateMatrixWorld( force ); - return this; + } - }, + } - setRGB: function ( r, g, b ) { + } - this.r = r; - this.g = g; - this.b = b; + updateWorldMatrix( updateParents, updateChildren ) { - return this; + const parent = this.parent; - }, + if ( updateParents === true && parent !== null && parent.matrixWorldAutoUpdate === true ) { - setHSL: function ( h, s, l ) { + parent.updateWorldMatrix( true, false ); - // h,s,l ranges are in 0.0 - 1.0 - h = _Math.euclideanModulo( h, 1 ); - s = _Math.clamp( s, 0, 1 ); - l = _Math.clamp( l, 0, 1 ); + } - if ( s === 0 ) { + if ( this.matrixAutoUpdate ) this.updateMatrix(); - this.r = this.g = this.b = l; + if ( this.parent === null ) { - } else { + this.matrixWorld.copy( this.matrix ); - var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); - var q = ( 2 * l ) - p; + } else { - this.r = hue2rgb( q, p, h + 1 / 3 ); - this.g = hue2rgb( q, p, h ); - this.b = hue2rgb( q, p, h - 1 / 3 ); + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - } + } - return this; + // update children - }, + if ( updateChildren === true ) { - setStyle: function ( style ) { + const children = this.children; - function handleAlpha( string ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( string === undefined ) return; + const child = children[ i ]; - if ( parseFloat( string ) < 1 ) { + if ( child.matrixWorldAutoUpdate === true ) { - console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); + child.updateWorldMatrix( false, true ); } } + } - var m; + } - if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { + toJSON( meta ) { - // rgb / hsl + // meta is a string when called from JSON.stringify + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - var color; - var name = m[ 1 ]; - var components = m[ 2 ]; + const output = {}; - switch ( name ) { + // meta is a hash used to collect geometries, materials. + // not providing it implies that this is the root object + // being serialized. + if ( isRootObject ) { - case 'rgb': - case 'rgba': + // initialize meta obj + meta = { + geometries: {}, + materials: {}, + textures: {}, + images: {}, + shapes: {}, + skeletons: {}, + animations: {}, + nodes: {} + }; - if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + output.metadata = { + version: 4.6, + type: 'Object', + generator: 'Object3D.toJSON' + }; - // rgb(255,0,0) rgba(255,0,0,0.5) - this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; - this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; - this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; + } - handleAlpha( color[ 5 ] ); + // standard Object3D serialization - return this; + const object = {}; - } + object.uuid = this.uuid; + object.type = this.type; - if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + if ( this.name !== '' ) object.name = this.name; + if ( this.castShadow === true ) object.castShadow = true; + if ( this.receiveShadow === true ) object.receiveShadow = true; + if ( this.visible === false ) object.visible = false; + if ( this.frustumCulled === false ) object.frustumCulled = false; + if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder; + if ( Object.keys( this.userData ).length > 0 ) object.userData = this.userData; - // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; - this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; - this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; + object.layers = this.layers.mask; + object.matrix = this.matrix.toArray(); + object.up = this.up.toArray(); - handleAlpha( color[ 5 ] ); + if ( this.matrixAutoUpdate === false ) object.matrixAutoUpdate = false; - return this; + // object specific properties - } + if ( this.isInstancedMesh ) { - break; + object.type = 'InstancedMesh'; + object.count = this.count; + object.instanceMatrix = this.instanceMatrix.toJSON(); + if ( this.instanceColor !== null ) object.instanceColor = this.instanceColor.toJSON(); - case 'hsl': - case 'hsla': + } - if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { + // - // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - var h = parseFloat( color[ 1 ] ) / 360; - var s = parseInt( color[ 2 ], 10 ) / 100; - var l = parseInt( color[ 3 ], 10 ) / 100; + function serialize( library, element ) { - handleAlpha( color[ 5 ] ); + if ( library[ element.uuid ] === undefined ) { - return this.setHSL( h, s, l ); + library[ element.uuid ] = element.toJSON( meta ); - } + } - break; + return element.uuid; - } + } - } else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { + if ( this.isScene ) { - // hex color + if ( this.background ) { - var hex = m[ 1 ]; - var size = hex.length; + if ( this.background.isColor ) { - if ( size === 3 ) { + object.background = this.background.toJSON(); - // #ff0 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; + } else if ( this.background.isTexture ) { - return this; + object.background = this.background.toJSON( meta ).uuid; - } else if ( size === 6 ) { + } - // #ff0000 - this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; - this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; - this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; + } - return this; + if ( this.environment && this.environment.isTexture && this.environment.isRenderTargetTexture !== true ) { - } + object.environment = this.environment.toJSON( meta ).uuid; } - if ( style && style.length > 0 ) { + } else if ( this.isMesh || this.isLine || this.isPoints ) { + + object.geometry = serialize( meta.geometries, this.geometry ); + + const parameters = this.geometry.parameters; - // color keywords - var hex = _colorKeywords[ style ]; + if ( parameters !== undefined && parameters.shapes !== undefined ) { - if ( hex !== undefined ) { + const shapes = parameters.shapes; - // red - this.setHex( hex ); + if ( Array.isArray( shapes ) ) { + + for ( let i = 0, l = shapes.length; i < l; i ++ ) { + + const shape = shapes[ i ]; + + serialize( meta.shapes, shape ); + + } } else { - // unknown color - console.warn( 'THREE.Color: Unknown color ' + style ); + serialize( meta.shapes, shapes ); } } - return this; + } - }, + if ( this.isSkinnedMesh ) { - clone: function () { + object.bindMode = this.bindMode; + object.bindMatrix = this.bindMatrix.toArray(); - return new this.constructor( this.r, this.g, this.b ); + if ( this.skeleton !== undefined ) { - }, + serialize( meta.skeletons, this.skeleton ); - copy: function ( color ) { + object.skeleton = this.skeleton.uuid; - this.r = color.r; - this.g = color.g; - this.b = color.b; + } - return this; + } - }, + if ( this.material !== undefined ) { - copyGammaToLinear: function ( color, gammaFactor ) { + if ( Array.isArray( this.material ) ) { - if ( gammaFactor === undefined ) gammaFactor = 2.0; + const uuids = []; - this.r = Math.pow( color.r, gammaFactor ); - this.g = Math.pow( color.g, gammaFactor ); - this.b = Math.pow( color.b, gammaFactor ); + for ( let i = 0, l = this.material.length; i < l; i ++ ) { - return this; + uuids.push( serialize( meta.materials, this.material[ i ] ) ); - }, + } - copyLinearToGamma: function ( color, gammaFactor ) { + object.material = uuids; - if ( gammaFactor === undefined ) gammaFactor = 2.0; + } else { - var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; + object.material = serialize( meta.materials, this.material ); - this.r = Math.pow( color.r, safeInverse ); - this.g = Math.pow( color.g, safeInverse ); - this.b = Math.pow( color.b, safeInverse ); + } - return this; + } - }, + // - convertGammaToLinear: function ( gammaFactor ) { + if ( this.children.length > 0 ) { - this.copyGammaToLinear( this, gammaFactor ); + object.children = []; - return this; + for ( let i = 0; i < this.children.length; i ++ ) { - }, + object.children.push( this.children[ i ].toJSON( meta ).object ); - convertLinearToGamma: function ( gammaFactor ) { + } - this.copyLinearToGamma( this, gammaFactor ); + } - return this; + // - }, + if ( this.animations.length > 0 ) { - copySRGBToLinear: function ( color ) { + object.animations = []; - this.r = SRGBToLinear( color.r ); - this.g = SRGBToLinear( color.g ); - this.b = SRGBToLinear( color.b ); + for ( let i = 0; i < this.animations.length; i ++ ) { - return this; + const animation = this.animations[ i ]; - }, + object.animations.push( serialize( meta.animations, animation ) ); - copyLinearToSRGB: function ( color ) { + } - this.r = LinearToSRGB( color.r ); - this.g = LinearToSRGB( color.g ); - this.b = LinearToSRGB( color.b ); + } - return this; + if ( isRootObject ) { - }, + const geometries = extractFromCache( meta.geometries ); + const materials = extractFromCache( meta.materials ); + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); + const shapes = extractFromCache( meta.shapes ); + const skeletons = extractFromCache( meta.skeletons ); + const animations = extractFromCache( meta.animations ); + const nodes = extractFromCache( meta.nodes ); - convertSRGBToLinear: function () { + if ( geometries.length > 0 ) output.geometries = geometries; + if ( materials.length > 0 ) output.materials = materials; + if ( textures.length > 0 ) output.textures = textures; + if ( images.length > 0 ) output.images = images; + if ( shapes.length > 0 ) output.shapes = shapes; + if ( skeletons.length > 0 ) output.skeletons = skeletons; + if ( animations.length > 0 ) output.animations = animations; + if ( nodes.length > 0 ) output.nodes = nodes; - this.copySRGBToLinear( this ); + } - return this; + output.object = object; - }, + return output; - convertLinearToSRGB: function () { + // extract data from the cache hash + // remove metadata on each item + // and return as array + function extractFromCache( cache ) { - this.copyLinearToSRGB( this ); + const values = []; + for ( const key in cache ) { - return this; + const data = cache[ key ]; + delete data.metadata; + values.push( data ); - }, + } - getHex: function () { + return values; - return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + } - }, + } - getHexString: function () { + clone( recursive ) { - return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); + return new this.constructor().copy( this, recursive ); - }, + } + + copy( source, recursive = true ) { + + this.name = source.name; + + this.up.copy( source.up ); + + this.position.copy( source.position ); + this.rotation.order = source.rotation.order; + this.quaternion.copy( source.quaternion ); + this.scale.copy( source.scale ); + + this.matrix.copy( source.matrix ); + this.matrixWorld.copy( source.matrixWorld ); + + this.matrixAutoUpdate = source.matrixAutoUpdate; + this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; + + this.matrixWorldAutoUpdate = source.matrixWorldAutoUpdate; + + this.layers.mask = source.layers.mask; + this.visible = source.visible; + + this.castShadow = source.castShadow; + this.receiveShadow = source.receiveShadow; + + this.frustumCulled = source.frustumCulled; + this.renderOrder = source.renderOrder; - getHSL: function ( target ) { + this.animations = source.animations.slice(); - // h,s,l ranges are in 0.0 - 1.0 + this.userData = JSON.parse( JSON.stringify( source.userData ) ); - if ( target === undefined ) { + if ( recursive === true ) { + + for ( let i = 0; i < source.children.length; i ++ ) { - console.warn( 'THREE.Color: .getHSL() target is now required' ); - target = { h: 0, s: 0, l: 0 }; + const child = source.children[ i ]; + this.add( child.clone() ); } - var r = this.r, g = this.g, b = this.b; + } - var max = Math.max( r, g, b ); - var min = Math.min( r, g, b ); + return this; - var hue, saturation; - var lightness = ( min + max ) / 2.0; + } - if ( min === max ) { +} - hue = 0; - saturation = 0; +Object3D.DEFAULT_UP = /*@__PURE__*/ new Vector3( 0, 1, 0 ); +Object3D.DEFAULT_MATRIX_AUTO_UPDATE = true; +Object3D.DEFAULT_MATRIX_WORLD_AUTO_UPDATE = true; - } else { +const _v0$1 = /*@__PURE__*/ new Vector3(); +const _v1$3 = /*@__PURE__*/ new Vector3(); +const _v2$2 = /*@__PURE__*/ new Vector3(); +const _v3$1 = /*@__PURE__*/ new Vector3(); - var delta = max - min; +const _vab = /*@__PURE__*/ new Vector3(); +const _vac = /*@__PURE__*/ new Vector3(); +const _vbc = /*@__PURE__*/ new Vector3(); +const _vap = /*@__PURE__*/ new Vector3(); +const _vbp = /*@__PURE__*/ new Vector3(); +const _vcp = /*@__PURE__*/ new Vector3(); - saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); +let warnedGetUV = false; - switch ( max ) { +class Triangle { - case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; - case g: hue = ( b - r ) / delta + 2; break; - case b: hue = ( r - g ) / delta + 4; break; + constructor( a = new Vector3(), b = new Vector3(), c = new Vector3() ) { - } + this.a = a; + this.b = b; + this.c = c; - hue /= 6; + } - } + static getNormal( a, b, c, target ) { - target.h = hue; - target.s = saturation; - target.l = lightness; + target.subVectors( c, b ); + _v0$1.subVectors( a, b ); + target.cross( _v0$1 ); - return target; + const targetLengthSq = target.lengthSq(); + if ( targetLengthSq > 0 ) { - }, + return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) ); - getStyle: function () { + } - return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + return target.set( 0, 0, 0 ); - }, + } - offsetHSL: function ( h, s, l ) { + // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + static getBarycoord( point, a, b, c, target ) { - this.getHSL( _hslA ); + _v0$1.subVectors( c, a ); + _v1$3.subVectors( b, a ); + _v2$2.subVectors( point, a ); - _hslA.h += h; _hslA.s += s; _hslA.l += l; + const dot00 = _v0$1.dot( _v0$1 ); + const dot01 = _v0$1.dot( _v1$3 ); + const dot02 = _v0$1.dot( _v2$2 ); + const dot11 = _v1$3.dot( _v1$3 ); + const dot12 = _v1$3.dot( _v2$2 ); - this.setHSL( _hslA.h, _hslA.s, _hslA.l ); + const denom = ( dot00 * dot11 - dot01 * dot01 ); - return this; + // collinear or singular triangle + if ( denom === 0 ) { - }, + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return target.set( - 2, - 1, - 1 ); - add: function ( color ) { + } - this.r += color.r; - this.g += color.g; - this.b += color.b; + const invDenom = 1 / denom; + const u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + const v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - return this; + // barycentric coordinates must always sum to 1 + return target.set( 1 - u - v, v, u ); - }, + } - addColors: function ( color1, color2 ) { + static containsPoint( point, a, b, c ) { - this.r = color1.r + color2.r; - this.g = color1.g + color2.g; - this.b = color1.b + color2.b; + this.getBarycoord( point, a, b, c, _v3$1 ); - return this; + return ( _v3$1.x >= 0 ) && ( _v3$1.y >= 0 ) && ( ( _v3$1.x + _v3$1.y ) <= 1 ); - }, + } - addScalar: function ( s ) { + static getUV( point, p1, p2, p3, uv1, uv2, uv3, target ) { // @deprecated, r151 - this.r += s; - this.g += s; - this.b += s; + if ( warnedGetUV === false ) { - return this; + console.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' ); - }, + warnedGetUV = true; - sub: function ( color ) { + } - this.r = Math.max( 0, this.r - color.r ); - this.g = Math.max( 0, this.g - color.g ); - this.b = Math.max( 0, this.b - color.b ); + return this.getInterpolation( point, p1, p2, p3, uv1, uv2, uv3, target ); - return this; + } - }, + static getInterpolation( point, p1, p2, p3, v1, v2, v3, target ) { - multiply: function ( color ) { + this.getBarycoord( point, p1, p2, p3, _v3$1 ); - this.r *= color.r; - this.g *= color.g; - this.b *= color.b; + target.setScalar( 0 ); + target.addScaledVector( v1, _v3$1.x ); + target.addScaledVector( v2, _v3$1.y ); + target.addScaledVector( v3, _v3$1.z ); - return this; + return target; - }, + } - multiplyScalar: function ( s ) { + static isFrontFacing( a, b, c, direction ) { - this.r *= s; - this.g *= s; - this.b *= s; + _v0$1.subVectors( c, b ); + _v1$3.subVectors( a, b ); - return this; + // strictly front facing + return ( _v0$1.cross( _v1$3 ).dot( direction ) < 0 ) ? true : false; - }, + } - lerp: function ( color, alpha ) { + set( a, b, c ) { - this.r += ( color.r - this.r ) * alpha; - this.g += ( color.g - this.g ) * alpha; - this.b += ( color.b - this.b ) * alpha; + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); - return this; + return this; - }, + } - lerpHSL: function ( color, alpha ) { + setFromPointsAndIndices( points, i0, i1, i2 ) { - this.getHSL( _hslA ); - color.getHSL( _hslB ); + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); + + return this; - var h = _Math.lerp( _hslA.h, _hslB.h, alpha ); - var s = _Math.lerp( _hslA.s, _hslB.s, alpha ); - var l = _Math.lerp( _hslA.l, _hslB.l, alpha ); + } - this.setHSL( h, s, l ); + setFromAttributeAndIndices( attribute, i0, i1, i2 ) { - return this; + this.a.fromBufferAttribute( attribute, i0 ); + this.b.fromBufferAttribute( attribute, i1 ); + this.c.fromBufferAttribute( attribute, i2 ); - }, + return this; - equals: function ( c ) { + } - return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); + clone() { - }, + return new this.constructor().copy( this ); - fromArray: function ( array, offset ) { + } - if ( offset === undefined ) offset = 0; + copy( triangle ) { - this.r = array[ offset ]; - this.g = array[ offset + 1 ]; - this.b = array[ offset + 2 ]; + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); - return this; + return this; - }, + } - toArray: function ( array, offset ) { + getArea() { - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; + _v0$1.subVectors( this.c, this.b ); + _v1$3.subVectors( this.a, this.b ); - array[ offset ] = this.r; - array[ offset + 1 ] = this.g; - array[ offset + 2 ] = this.b; + return _v0$1.cross( _v1$3 ).length() * 0.5; - return array; + } - }, + getMidpoint( target ) { + + return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + + } + + getNormal( target ) { + + return Triangle.getNormal( this.a, this.b, this.c, target ); + + } + + getPlane( target ) { + + return target.setFromCoplanarPoints( this.a, this.b, this.c ); + + } + + getBarycoord( point, target ) { + + return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); + + } + + getUV( point, uv1, uv2, uv3, target ) { // @deprecated, r151 - toJSON: function () { + if ( warnedGetUV === false ) { - return this.getHex(); + console.warn( 'THREE.Triangle.getUV() has been renamed to THREE.Triangle.getInterpolation().' ); + + warnedGetUV = true; } - } ); + return Triangle.getInterpolation( point, this.a, this.b, this.c, uv1, uv2, uv3, target ); - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } - function Face3( a, b, c, normal, color, materialIndex ) { + getInterpolation( point, v1, v2, v3, target ) { - this.a = a; - this.b = b; - this.c = c; + return Triangle.getInterpolation( point, this.a, this.b, this.c, v1, v2, v3, target ); - this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; + } - this.color = ( color && color.isColor ) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; + containsPoint( point ) { - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + return Triangle.containsPoint( point, this.a, this.b, this.c ); } - Object.assign( Face3.prototype, { + isFrontFacing( direction ) { + + return Triangle.isFrontFacing( this.a, this.b, this.c, direction ); - clone: function () { + } - return new this.constructor().copy( this ); + intersectsBox( box ) { - }, + return box.intersectsTriangle( this ); - copy: function ( source ) { + } - this.a = source.a; - this.b = source.b; - this.c = source.c; + closestPointToPoint( p, target ) { - this.normal.copy( source.normal ); - this.color.copy( source.color ); + const a = this.a, b = this.b, c = this.c; + let v, w; - this.materialIndex = source.materialIndex; + // algorithm thanks to Real-Time Collision Detection by Christer Ericson, + // published by Morgan Kaufmann Publishers, (c) 2005 Elsevier Inc., + // under the accompanying license; see chapter 5.1.5 for detailed explanation. + // basically, we're distinguishing which of the voronoi regions of the triangle + // the point lies in with the minimum amount of redundant computation. - for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + _vab.subVectors( b, a ); + _vac.subVectors( c, a ); + _vap.subVectors( p, a ); + const d1 = _vab.dot( _vap ); + const d2 = _vac.dot( _vap ); + if ( d1 <= 0 && d2 <= 0 ) { - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + // vertex region of A; barycentric coords (1, 0, 0) + return target.copy( a ); - } + } - for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { + _vbp.subVectors( p, b ); + const d3 = _vab.dot( _vbp ); + const d4 = _vac.dot( _vbp ); + if ( d3 >= 0 && d4 <= d3 ) { - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + // vertex region of B; barycentric coords (0, 1, 0) + return target.copy( b ); - } + } - return this; + const vc = d1 * d4 - d3 * d2; + if ( vc <= 0 && d1 >= 0 && d3 <= 0 ) { + + v = d1 / ( d1 - d3 ); + // edge region of AB; barycentric coords (1-v, v, 0) + return target.copy( a ).addScaledVector( _vab, v ); } - } ); + _vcp.subVectors( p, c ); + const d5 = _vab.dot( _vcp ); + const d6 = _vac.dot( _vcp ); + if ( d6 >= 0 && d5 <= d6 ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + // vertex region of C; barycentric coords (0, 0, 1) + return target.copy( c ); - var materialId = 0; + } + + const vb = d5 * d2 - d1 * d6; + if ( vb <= 0 && d2 >= 0 && d6 <= 0 ) { + + w = d2 / ( d2 - d6 ); + // edge region of AC; barycentric coords (1-w, 0, w) + return target.copy( a ).addScaledVector( _vac, w ); + + } + + const va = d3 * d6 - d5 * d4; + if ( va <= 0 && ( d4 - d3 ) >= 0 && ( d5 - d6 ) >= 0 ) { + + _vbc.subVectors( c, b ); + w = ( d4 - d3 ) / ( ( d4 - d3 ) + ( d5 - d6 ) ); + // edge region of BC; barycentric coords (0, 1-w, w) + return target.copy( b ).addScaledVector( _vbc, w ); // edge region of BC + + } + + // face region + const denom = 1 / ( va + vb + vc ); + // u = va * denom + v = vb * denom; + w = vc * denom; + + return target.copy( a ).addScaledVector( _vab, v ).addScaledVector( _vac, w ); + + } + + equals( triangle ) { + + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + + } + +} + +let _materialId = 0; + +class Material extends EventDispatcher { - function Material() { + constructor() { - Object.defineProperty( this, 'id', { value: materialId ++ } ); + super(); - this.uuid = _Math.generateUUID(); + this.isMaterial = true; + + Object.defineProperty( this, 'id', { value: _materialId ++ } ); + + this.uuid = generateUUID(); this.name = ''; this.type = 'Material'; - this.fog = true; - this.lights = true; - this.blending = NormalBlending; this.side = FrontSide; - this.flatShading = false; - this.vertexTangents = false; - this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors + this.vertexColors = false; this.opacity = 1; this.transparent = false; + this.alphaHash = false; this.blendSrc = SrcAlphaFactor; this.blendDst = OneMinusSrcAlphaFactor; @@ -8330,9 +8457,10 @@ this.depthTest = true; this.depthWrite = true; + this.stencilWriteMask = 0xff; this.stencilFunc = AlwaysStencilFunc; this.stencilRef = 0; - this.stencilMask = 0xff; + this.stencilFuncMask = 0xff; this.stencilFail = KeepStencilOp; this.stencilZFail = KeepStencilOp; this.stencilZPass = KeepStencilOp; @@ -8354,8 +8482,9 @@ this.dithering = false; - this.alphaTest = 0; + this.alphaToCoverage = false; this.premultipliedAlpha = false; + this.forceSinglePass = false; this.visible = true; @@ -8363,15825 +8492,16226 @@ this.userData = {}; - this.needsUpdate = true; + this.version = 0; + + this._alphaTest = 0; } - Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + get alphaTest() { - constructor: Material, + return this._alphaTest; - isMaterial: true, + } - onBeforeCompile: function () {}, + set alphaTest( value ) { - setValues: function ( values ) { + if ( this._alphaTest > 0 !== value > 0 ) { - if ( values === undefined ) return; + this.version ++; - for ( var key in values ) { + } - var newValue = values[ key ]; + this._alphaTest = value; - if ( newValue === undefined ) { + } - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; + onBuild( /* shaderobject, renderer */ ) {} - } + onBeforeRender( /* renderer, scene, camera, geometry, object, group */ ) {} - // for backward compatability if shading is set in the constructor - if ( key === 'shading' ) { + onBeforeCompile( /* shaderobject, renderer */ ) {} - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( newValue === FlatShading ) ? true : false; - continue; + customProgramCacheKey() { - } + return this.onBeforeCompile.toString(); - var currentValue = this[ key ]; + } - if ( currentValue === undefined ) { + setValues( values ) { - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; + if ( values === undefined ) return; - } + for ( const key in values ) { - if ( currentValue && currentValue.isColor ) { + const newValue = values[ key ]; - currentValue.set( newValue ); + if ( newValue === undefined ) { - } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { + console.warn( `THREE.Material: parameter '${ key }' has value of undefined.` ); + continue; - currentValue.copy( newValue ); + } - } else { + const currentValue = this[ key ]; - this[ key ] = newValue; + if ( currentValue === undefined ) { - } + console.warn( `THREE.Material: '${ key }' is not a property of THREE.${ this.type }.` ); + continue; } - }, - - toJSON: function ( meta ) { + if ( currentValue && currentValue.isColor ) { - var isRoot = ( meta === undefined || typeof meta === 'string' ); + currentValue.set( newValue ); - if ( isRoot ) { + } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) { - meta = { - textures: {}, - images: {} - }; + currentValue.copy( newValue ); - } + } else { - var data = { - metadata: { - version: 4.5, - type: 'Material', - generator: 'Material.toJSON' - } - }; + this[ key ] = newValue; - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; + } - if ( this.name !== '' ) data.name = this.name; + } - if ( this.color && this.color.isColor ) data.color = this.color.getHex(); + } - if ( this.roughness !== undefined ) data.roughness = this.roughness; - if ( this.metalness !== undefined ) data.metalness = this.metalness; + toJSON( meta ) { - if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); - if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; + const isRootObject = ( meta === undefined || typeof meta === 'string' ); - if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; - if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; - if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; + if ( isRootObject ) { - if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { + meta = { + textures: {}, + images: {} + }; - data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; - data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); + } + const data = { + metadata: { + version: 4.6, + type: 'Material', + generator: 'Material.toJSON' } + }; - if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; - if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - - if ( this.aoMap && this.aoMap.isTexture ) { + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; - data.aoMap = this.aoMap.toJSON( meta ).uuid; - data.aoMapIntensity = this.aoMapIntensity; + if ( this.name !== '' ) data.name = this.name; - } + if ( this.color && this.color.isColor ) data.color = this.color.getHex(); - if ( this.bumpMap && this.bumpMap.isTexture ) { + if ( this.roughness !== undefined ) data.roughness = this.roughness; + if ( this.metalness !== undefined ) data.metalness = this.metalness; - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; + if ( this.sheen !== undefined ) data.sheen = this.sheen; + if ( this.sheenColor && this.sheenColor.isColor ) data.sheenColor = this.sheenColor.getHex(); + if ( this.sheenRoughness !== undefined ) data.sheenRoughness = this.sheenRoughness; + if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex(); + if ( this.emissiveIntensity && this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity; - } + if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex(); + if ( this.specularIntensity !== undefined ) data.specularIntensity = this.specularIntensity; + if ( this.specularColor && this.specularColor.isColor ) data.specularColor = this.specularColor.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; + if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat; + if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness; - if ( this.normalMap && this.normalMap.isTexture ) { + if ( this.clearcoatMap && this.clearcoatMap.isTexture ) { - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalMapType = this.normalMapType; - data.normalScale = this.normalScale.toArray(); + data.clearcoatMap = this.clearcoatMap.toJSON( meta ).uuid; - } + } - if ( this.displacementMap && this.displacementMap.isTexture ) { + if ( this.clearcoatRoughnessMap && this.clearcoatRoughnessMap.isTexture ) { - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; + data.clearcoatRoughnessMap = this.clearcoatRoughnessMap.toJSON( meta ).uuid; - } + } - if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; - if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; + if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) { - if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; - if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid; + data.clearcoatNormalScale = this.clearcoatNormalScale.toArray(); - if ( this.envMap && this.envMap.isTexture ) { + } - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap - data.refractionRatio = this.refractionRatio; + if ( this.iridescence !== undefined ) data.iridescence = this.iridescence; + if ( this.iridescenceIOR !== undefined ) data.iridescenceIOR = this.iridescenceIOR; + if ( this.iridescenceThicknessRange !== undefined ) data.iridescenceThicknessRange = this.iridescenceThicknessRange; - if ( this.combine !== undefined ) data.combine = this.combine; - if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + if ( this.iridescenceMap && this.iridescenceMap.isTexture ) { - } + data.iridescenceMap = this.iridescenceMap.toJSON( meta ).uuid; - if ( this.gradientMap && this.gradientMap.isTexture ) { + } - data.gradientMap = this.gradientMap.toJSON( meta ).uuid; + if ( this.iridescenceThicknessMap && this.iridescenceThicknessMap.isTexture ) { - } + data.iridescenceThicknessMap = this.iridescenceThicknessMap.toJSON( meta ).uuid; - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + } - if ( this.blending !== NormalBlending ) data.blending = this.blending; - if ( this.flatShading === true ) data.flatShading = this.flatShading; - if ( this.side !== FrontSide ) data.side = this.side; - if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors; + if ( this.anisotropy !== undefined ) data.anisotropy = this.anisotropy; + if ( this.anisotropyRotation !== undefined ) data.anisotropyRotation = this.anisotropyRotation; - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; + if ( this.anisotropyMap && this.anisotropyMap.isTexture ) { - data.depthFunc = this.depthFunc; - data.depthTest = this.depthTest; - data.depthWrite = this.depthWrite; + data.anisotropyMap = this.anisotropyMap.toJSON( meta ).uuid; - data.stencilWrite = this.stencilWrite; - data.stencilFunc = this.stencilFunc; - data.stencilRef = this.stencilRef; - data.stencilMask = this.stencilMask; - data.stencilFail = this.stencilFail; - data.stencilZFail = this.stencilZFail; - data.stencilZPass = this.stencilZPass; + } - // rotation (SpriteMaterial) - if ( this.rotation && this.rotation !== 0 ) data.rotation = this.rotation; + if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.matcap && this.matcap.isTexture ) data.matcap = this.matcap.toJSON( meta ).uuid; + if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.polygonOffset === true ) data.polygonOffset = true; - if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; - if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; + if ( this.lightMap && this.lightMap.isTexture ) { - if ( this.linewidth && this.linewidth !== 1 ) data.linewidth = this.linewidth; - if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; - if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; - if ( this.scale !== undefined ) data.scale = this.scale; + data.lightMap = this.lightMap.toJSON( meta ).uuid; + data.lightMapIntensity = this.lightMapIntensity; - if ( this.dithering === true ) data.dithering = true; + } - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; + if ( this.aoMap && this.aoMap.isTexture ) { - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; - if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; + data.aoMap = this.aoMap.toJSON( meta ).uuid; + data.aoMapIntensity = this.aoMapIntensity; - if ( this.morphTargets === true ) data.morphTargets = true; - if ( this.morphNormals === true ) data.morphNormals = true; - if ( this.skinning === true ) data.skinning = true; + } - if ( this.visible === false ) data.visible = false; + if ( this.bumpMap && this.bumpMap.isTexture ) { - if ( this.toneMapped === false ) data.toneMapped = false; + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; - if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData; + } - // TODO: Copied from Object3D.toJSON + if ( this.normalMap && this.normalMap.isTexture ) { - function extractFromCache( cache ) { + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalMapType = this.normalMapType; + data.normalScale = this.normalScale.toArray(); - var values = []; + } - for ( var key in cache ) { + if ( this.displacementMap && this.displacementMap.isTexture ) { - var data = cache[ key ]; - delete data.metadata; - values.push( data ); + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; - } + } - return values; + if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; + if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - } + if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; + if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + if ( this.specularIntensityMap && this.specularIntensityMap.isTexture ) data.specularIntensityMap = this.specularIntensityMap.toJSON( meta ).uuid; + if ( this.specularColorMap && this.specularColorMap.isTexture ) data.specularColorMap = this.specularColorMap.toJSON( meta ).uuid; - if ( isRoot ) { + if ( this.envMap && this.envMap.isTexture ) { - var textures = extractFromCache( meta.textures ); - var images = extractFromCache( meta.images ); + data.envMap = this.envMap.toJSON( meta ).uuid; - if ( textures.length > 0 ) data.textures = textures; - if ( images.length > 0 ) data.images = images; + if ( this.combine !== undefined ) data.combine = this.combine; - } + } - return data; + if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity; + if ( this.reflectivity !== undefined ) data.reflectivity = this.reflectivity; + if ( this.refractionRatio !== undefined ) data.refractionRatio = this.refractionRatio; - }, + if ( this.gradientMap && this.gradientMap.isTexture ) { - clone: function () { + data.gradientMap = this.gradientMap.toJSON( meta ).uuid; - return new this.constructor().copy( this ); + } - }, + if ( this.transmission !== undefined ) data.transmission = this.transmission; + if ( this.transmissionMap && this.transmissionMap.isTexture ) data.transmissionMap = this.transmissionMap.toJSON( meta ).uuid; + if ( this.thickness !== undefined ) data.thickness = this.thickness; + if ( this.thicknessMap && this.thicknessMap.isTexture ) data.thicknessMap = this.thicknessMap.toJSON( meta ).uuid; + if ( this.attenuationDistance !== undefined && this.attenuationDistance !== Infinity ) data.attenuationDistance = this.attenuationDistance; + if ( this.attenuationColor !== undefined ) data.attenuationColor = this.attenuationColor.getHex(); - copy: function ( source ) { + if ( this.size !== undefined ) data.size = this.size; + if ( this.shadowSide !== null ) data.shadowSide = this.shadowSide; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - this.name = source.name; + if ( this.blending !== NormalBlending ) data.blending = this.blending; + if ( this.side !== FrontSide ) data.side = this.side; + if ( this.vertexColors === true ) data.vertexColors = true; - this.fog = source.fog; - this.lights = source.lights; + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = true; - this.blending = source.blending; - this.side = source.side; - this.flatShading = source.flatShading; - this.vertexColors = source.vertexColors; + data.depthFunc = this.depthFunc; + data.depthTest = this.depthTest; + data.depthWrite = this.depthWrite; + data.colorWrite = this.colorWrite; - this.opacity = source.opacity; - this.transparent = source.transparent; + data.stencilWrite = this.stencilWrite; + data.stencilWriteMask = this.stencilWriteMask; + data.stencilFunc = this.stencilFunc; + data.stencilRef = this.stencilRef; + data.stencilFuncMask = this.stencilFuncMask; + data.stencilFail = this.stencilFail; + data.stencilZFail = this.stencilZFail; + data.stencilZPass = this.stencilZPass; - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; + // rotation (SpriteMaterial) + if ( this.rotation !== undefined && this.rotation !== 0 ) data.rotation = this.rotation; - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; + if ( this.polygonOffset === true ) data.polygonOffset = true; + if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor; + if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits; - this.stencilWrite = source.stencilWrite; - this.stencilFunc = source.stencilFunc; - this.stencilRef = source.stencilRef; - this.stencilMask = source.stencilMask; - this.stencilFail = source.stencilFail; - this.stencilZFail = source.stencilZFail; - this.stencilZPass = source.stencilZPass; + if ( this.linewidth !== undefined && this.linewidth !== 1 ) data.linewidth = this.linewidth; + if ( this.dashSize !== undefined ) data.dashSize = this.dashSize; + if ( this.gapSize !== undefined ) data.gapSize = this.gapSize; + if ( this.scale !== undefined ) data.scale = this.scale; - this.colorWrite = source.colorWrite; + if ( this.dithering === true ) data.dithering = true; - this.precision = source.precision; + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.alphaHash === true ) data.alphaHash = true; + if ( this.alphaToCoverage === true ) data.alphaToCoverage = true; + if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = true; + if ( this.forceSinglePass === true ) data.forceSinglePass = true; - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; + if ( this.wireframe === true ) data.wireframe = true; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap; + if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin; - this.dithering = source.dithering; + if ( this.flatShading === true ) data.flatShading = true; - this.alphaTest = source.alphaTest; - this.premultipliedAlpha = source.premultipliedAlpha; + if ( this.visible === false ) data.visible = false; - this.visible = source.visible; + if ( this.toneMapped === false ) data.toneMapped = false; - this.toneMapped = source.toneMapped; + if ( this.fog === false ) data.fog = false; - this.userData = JSON.parse( JSON.stringify( source.userData ) ); + if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; - this.clipShadows = source.clipShadows; - this.clipIntersection = source.clipIntersection; + // TODO: Copied from Object3D.toJSON - var srcPlanes = source.clippingPlanes, - dstPlanes = null; + function extractFromCache( cache ) { - if ( srcPlanes !== null ) { + const values = []; - var n = srcPlanes.length; - dstPlanes = new Array( n ); + for ( const key in cache ) { - for ( var i = 0; i !== n; ++ i ) - dstPlanes[ i ] = srcPlanes[ i ].clone(); + const data = cache[ key ]; + delete data.metadata; + values.push( data ); } - this.clippingPlanes = dstPlanes; + return values; - this.shadowSide = source.shadowSide; - - return this; + } - }, + if ( isRootObject ) { - dispose: function () { + const textures = extractFromCache( meta.textures ); + const images = extractFromCache( meta.images ); - this.dispatchEvent( { type: 'dispose' } ); + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; } - } ); + return data; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: - * } - */ + } - function MeshBasicMaterial( parameters ) { + clone() { - Material.call( this ); + return new this.constructor().copy( this ); - this.type = 'MeshBasicMaterial'; + } - this.color = new Color( 0xffffff ); // emissive + copy( source ) { - this.map = null; + this.name = source.name; - this.lightMap = null; - this.lightMapIntensity = 1.0; + this.blending = source.blending; + this.side = source.side; + this.vertexColors = source.vertexColors; - this.aoMap = null; - this.aoMapIntensity = 1.0; + this.opacity = source.opacity; + this.transparent = source.transparent; - this.specularMap = null; + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; - this.alphaMap = null; + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + this.stencilWriteMask = source.stencilWriteMask; + this.stencilFunc = source.stencilFunc; + this.stencilRef = source.stencilRef; + this.stencilFuncMask = source.stencilFuncMask; + this.stencilFail = source.stencilFail; + this.stencilZFail = source.stencilZFail; + this.stencilZPass = source.stencilZPass; + this.stencilWrite = source.stencilWrite; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + const srcPlanes = source.clippingPlanes; + let dstPlanes = null; - this.skinning = false; - this.morphTargets = false; + if ( srcPlanes !== null ) { - this.lights = false; + const n = srcPlanes.length; + dstPlanes = new Array( n ); - this.setValues( parameters ); + for ( let i = 0; i !== n; ++ i ) { - } + dstPlanes[ i ] = srcPlanes[ i ].clone(); - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; + } - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; + } - MeshBasicMaterial.prototype.copy = function ( source ) { + this.clippingPlanes = dstPlanes; + this.clipIntersection = source.clipIntersection; + this.clipShadows = source.clipShadows; - Material.prototype.copy.call( this, source ); + this.shadowSide = source.shadowSide; - this.color.copy( source.color ); + this.colorWrite = source.colorWrite; - this.map = source.map; + this.precision = source.precision; - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; + this.dithering = source.dithering; - this.alphaMap = source.alphaMap; + this.alphaTest = source.alphaTest; + this.alphaHash = source.alphaHash; + this.alphaToCoverage = source.alphaToCoverage; + this.premultipliedAlpha = source.premultipliedAlpha; + this.forceSinglePass = source.forceSinglePass; - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + this.visible = source.visible; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + this.toneMapped = source.toneMapped; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + this.userData = JSON.parse( JSON.stringify( source.userData ) ); return this; - }; + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + dispose() { - function BufferAttribute( array, itemSize, normalized ) { + this.dispatchEvent( { type: 'dispose' } ); - if ( Array.isArray( array ) ) { + } - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + set needsUpdate( value ) { - } + if ( value === true ) this.version ++; - this.name = ''; + } - this.array = array; - this.itemSize = itemSize; - this.count = array !== undefined ? array.length / itemSize : 0; - this.normalized = normalized === true; +} - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; +const _colorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, + 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, + 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, + 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, + 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, + 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, + 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, + 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, + 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, + 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, + 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, + 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, + 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, + 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, + 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, + 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, + 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, + 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, + 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, + 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, + 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, + 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, + 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, + 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; - this.version = 0; +const _hslA = { h: 0, s: 0, l: 0 }; +const _hslB = { h: 0, s: 0, l: 0 }; - } +function hue2rgb( p, q, t ) { - Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', { + if ( t < 0 ) t += 1; + if ( t > 1 ) t -= 1; + if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; + if ( t < 1 / 2 ) return q; + if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); + return p; - set: function ( value ) { +} - if ( value === true ) this.version ++; +class Color { - } + constructor( r, g, b ) { - } ); + this.isColor = true; - Object.assign( BufferAttribute.prototype, { + this.r = 1; + this.g = 1; + this.b = 1; - isBufferAttribute: true, + return this.set( r, g, b ); - onUploadCallback: function () {}, + } - setArray: function ( array ) { + set( r, g, b ) { - if ( Array.isArray( array ) ) { + if ( g === undefined && b === undefined ) { - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + // r is THREE.Color, hex or string - } + const value = r; - this.count = array !== undefined ? array.length / this.itemSize : 0; - this.array = array; + if ( value && value.isColor ) { - return this; + this.copy( value ); - }, + } else if ( typeof value === 'number' ) { - setDynamic: function ( value ) { + this.setHex( value ); - this.dynamic = value; + } else if ( typeof value === 'string' ) { - return this; + this.setStyle( value ); - }, + } - copy: function ( source ) { + } else { - this.name = source.name; - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; + this.setRGB( r, g, b ); - this.dynamic = source.dynamic; + } - return this; + return this; - }, + } - copyAt: function ( index1, attribute, index2 ) { + setScalar( scalar ) { - index1 *= this.itemSize; - index2 *= attribute.itemSize; + this.r = scalar; + this.g = scalar; + this.b = scalar; - for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + return this; - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + } - } + setHex( hex, colorSpace = SRGBColorSpace ) { - return this; + hex = Math.floor( hex ); - }, + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; - copyArray: function ( array ) { + ColorManagement.toWorkingColorSpace( this, colorSpace ); - this.array.set( array ); + return this; - return this; + } - }, + setRGB( r, g, b, colorSpace = ColorManagement.workingColorSpace ) { - copyColorsArray: function ( colors ) { + this.r = r; + this.g = g; + this.b = b; - var array = this.array, offset = 0; + ColorManagement.toWorkingColorSpace( this, colorSpace ); - for ( var i = 0, l = colors.length; i < l; i ++ ) { + return this; - var color = colors[ i ]; + } - if ( color === undefined ) { + setHSL( h, s, l, colorSpace = ColorManagement.workingColorSpace ) { - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); + // h,s,l ranges are in 0.0 - 1.0 + h = euclideanModulo( h, 1 ); + s = clamp( s, 0, 1 ); + l = clamp( l, 0, 1 ); - } + if ( s === 0 ) { - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; + this.r = this.g = this.b = l; - } + } else { - return this; + const p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); + const q = ( 2 * l ) - p; - }, + this.r = hue2rgb( q, p, h + 1 / 3 ); + this.g = hue2rgb( q, p, h ); + this.b = hue2rgb( q, p, h - 1 / 3 ); - copyVector2sArray: function ( vectors ) { + } - var array = this.array, offset = 0; + ColorManagement.toWorkingColorSpace( this, colorSpace ); - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + return this; - var vector = vectors[ i ]; + } - if ( vector === undefined ) { + setStyle( style, colorSpace = SRGBColorSpace ) { - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); + function handleAlpha( string ) { - } + if ( string === undefined ) return; - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; + if ( parseFloat( string ) < 1 ) { + + console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); } - return this; + } - }, - copyVector3sArray: function ( vectors ) { + let m; - var array = this.array, offset = 0; + if ( m = /^(\w+)\(([^\)]*)\)/.exec( style ) ) { - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + // rgb / hsl - var vector = vectors[ i ]; + let color; + const name = m[ 1 ]; + const components = m[ 2 ]; - if ( vector === undefined ) { + switch ( name ) { - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); + case 'rgb': + case 'rgba': - } + if ( color = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; + // rgb(255,0,0) rgba(255,0,0,0.5) - } + handleAlpha( color[ 4 ] ); - return this; + return this.setRGB( + Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255, + Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255, + Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255, + colorSpace + ); - }, + } - copyVector4sArray: function ( vectors ) { + if ( color = /^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - var array = this.array, offset = 0; + // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + handleAlpha( color[ 4 ] ); - var vector = vectors[ i ]; + return this.setRGB( + Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100, + Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100, + Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100, + colorSpace + ); - if ( vector === undefined ) { + } - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); + break; - } + case 'hsl': + case 'hsla': - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; + if ( color = /^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec( components ) ) { - } + // hsl(120,50%,50%) hsla(120,50%,50%,0.5) - return this; + handleAlpha( color[ 4 ] ); - }, + return this.setHSL( + parseFloat( color[ 1 ] ) / 360, + parseFloat( color[ 2 ] ) / 100, + parseFloat( color[ 3 ] ) / 100, + colorSpace + ); - set: function ( value, offset ) { + } - if ( offset === undefined ) offset = 0; + break; - this.array.set( value, offset ); + default: - return this; + console.warn( 'THREE.Color: Unknown color model ' + style ); - }, + } - getX: function ( index ) { + } else if ( m = /^\#([A-Fa-f\d]+)$/.exec( style ) ) { - return this.array[ index * this.itemSize ]; + // hex color - }, + const hex = m[ 1 ]; + const size = hex.length; - setX: function ( index, x ) { + if ( size === 3 ) { - this.array[ index * this.itemSize ] = x; + // #ff0 + return this.setRGB( + parseInt( hex.charAt( 0 ), 16 ) / 15, + parseInt( hex.charAt( 1 ), 16 ) / 15, + parseInt( hex.charAt( 2 ), 16 ) / 15, + colorSpace + ); - return this; + } else if ( size === 6 ) { - }, + // #ff0000 + return this.setHex( parseInt( hex, 16 ), colorSpace ); - getY: function ( index ) { + } else { - return this.array[ index * this.itemSize + 1 ]; + console.warn( 'THREE.Color: Invalid hex color ' + style ); - }, + } - setY: function ( index, y ) { + } else if ( style && style.length > 0 ) { - this.array[ index * this.itemSize + 1 ] = y; + return this.setColorName( style, colorSpace ); - return this; + } - }, + return this; - getZ: function ( index ) { + } - return this.array[ index * this.itemSize + 2 ]; + setColorName( style, colorSpace = SRGBColorSpace ) { - }, + // color keywords + const hex = _colorKeywords[ style.toLowerCase() ]; - setZ: function ( index, z ) { + if ( hex !== undefined ) { - this.array[ index * this.itemSize + 2 ] = z; + // red + this.setHex( hex, colorSpace ); - return this; + } else { - }, + // unknown color + console.warn( 'THREE.Color: Unknown color ' + style ); - getW: function ( index ) { + } - return this.array[ index * this.itemSize + 3 ]; + return this; - }, + } - setW: function ( index, w ) { + clone() { - this.array[ index * this.itemSize + 3 ] = w; + return new this.constructor( this.r, this.g, this.b ); - return this; + } - }, + copy( color ) { - setXY: function ( index, x, y ) { + this.r = color.r; + this.g = color.g; + this.b = color.b; - index *= this.itemSize; + return this; - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; + } - return this; + copySRGBToLinear( color ) { - }, + this.r = SRGBToLinear( color.r ); + this.g = SRGBToLinear( color.g ); + this.b = SRGBToLinear( color.b ); - setXYZ: function ( index, x, y, z ) { + return this; - index *= this.itemSize; + } - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; + copyLinearToSRGB( color ) { - return this; + this.r = LinearToSRGB( color.r ); + this.g = LinearToSRGB( color.g ); + this.b = LinearToSRGB( color.b ); - }, + return this; - setXYZW: function ( index, x, y, z, w ) { + } - index *= this.itemSize; + convertSRGBToLinear() { - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; + this.copySRGBToLinear( this ); - return this; + return this; - }, + } - onUpload: function ( callback ) { + convertLinearToSRGB() { - this.onUploadCallback = callback; + this.copyLinearToSRGB( this ); - return this; + return this; - }, + } - clone: function () { + getHex( colorSpace = SRGBColorSpace ) { - return new this.constructor( this.array, this.itemSize ).copy( this ); + ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); - }, + return Math.round( clamp( _color.r * 255, 0, 255 ) ) * 65536 + Math.round( clamp( _color.g * 255, 0, 255 ) ) * 256 + Math.round( clamp( _color.b * 255, 0, 255 ) ); - toJSON: function () { + } - return { - itemSize: this.itemSize, - type: this.array.constructor.name, - array: Array.prototype.slice.call( this.array ), - normalized: this.normalized - }; + getHexString( colorSpace = SRGBColorSpace ) { - } + return ( '000000' + this.getHex( colorSpace ).toString( 16 ) ).slice( - 6 ); - } ); + } - // + getHSL( target, colorSpace = ColorManagement.workingColorSpace ) { - function Int8BufferAttribute( array, itemSize, normalized ) { + // h,s,l ranges are in 0.0 - 1.0 - BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized ); + ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); - } + const r = _color.r, g = _color.g, b = _color.b; - Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int8BufferAttribute.prototype.constructor = Int8BufferAttribute; + const max = Math.max( r, g, b ); + const min = Math.min( r, g, b ); + let hue, saturation; + const lightness = ( min + max ) / 2.0; - function Uint8BufferAttribute( array, itemSize, normalized ) { + if ( min === max ) { - BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized ); + hue = 0; + saturation = 0; - } + } else { - Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute; + const delta = max - min; + saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); - function Uint8ClampedBufferAttribute( array, itemSize, normalized ) { + switch ( max ) { - BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized ); + case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; + case g: hue = ( b - r ) / delta + 2; break; + case b: hue = ( r - g ) / delta + 4; break; - } + } - Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute; + hue /= 6; + } - function Int16BufferAttribute( array, itemSize, normalized ) { + target.h = hue; + target.s = saturation; + target.l = lightness; - BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized ); + return target; } - Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int16BufferAttribute.prototype.constructor = Int16BufferAttribute; + getRGB( target, colorSpace = ColorManagement.workingColorSpace ) { + ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); - function Uint16BufferAttribute( array, itemSize, normalized ) { + target.r = _color.r; + target.g = _color.g; + target.b = _color.b; - BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized ); + return target; } - Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute; + getStyle( colorSpace = SRGBColorSpace ) { + ColorManagement.fromWorkingColorSpace( _color.copy( this ), colorSpace ); - function Int32BufferAttribute( array, itemSize, normalized ) { + const r = _color.r, g = _color.g, b = _color.b; - BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized ); + if ( colorSpace !== SRGBColorSpace ) { - } + // Requires CSS Color Module Level 4 (https://www.w3.org/TR/css-color-4/). + return `color(${ colorSpace } ${ r.toFixed( 3 ) } ${ g.toFixed( 3 ) } ${ b.toFixed( 3 ) })`; - Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Int32BufferAttribute.prototype.constructor = Int32BufferAttribute; + } + + return `rgb(${ Math.round( r * 255 ) },${ Math.round( g * 255 ) },${ Math.round( b * 255 ) })`; + } - function Uint32BufferAttribute( array, itemSize, normalized ) { + offsetHSL( h, s, l ) { - BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized ); + this.getHSL( _hslA ); - } + return this.setHSL( _hslA.h + h, _hslA.s + s, _hslA.l + l ); - Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute; + } + add( color ) { - function Float32BufferAttribute( array, itemSize, normalized ) { + this.r += color.r; + this.g += color.g; + this.b += color.b; - BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized ); + return this; } - Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float32BufferAttribute.prototype.constructor = Float32BufferAttribute; + addColors( color1, color2 ) { + this.r = color1.r + color2.r; + this.g = color1.g + color2.g; + this.b = color1.b + color2.b; - function Float64BufferAttribute( array, itemSize, normalized ) { - - BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized ); + return this; } - Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype ); - Float64BufferAttribute.prototype.constructor = Float64BufferAttribute; + addScalar( s ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + this.r += s; + this.g += s; + this.b += s; - function DirectGeometry() { + return this; - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; + } - this.groups = []; + sub( color ) { - this.morphTargets = {}; + this.r = Math.max( 0, this.r - color.r ); + this.g = Math.max( 0, this.g - color.g ); + this.b = Math.max( 0, this.b - color.b ); - this.skinWeights = []; - this.skinIndices = []; + return this; - // this.lineDistances = []; + } - this.boundingBox = null; - this.boundingSphere = null; + multiply( color ) { - // update flags + this.r *= color.r; + this.g *= color.g; + this.b *= color.b; - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; + return this; } - Object.assign( DirectGeometry.prototype, { + multiplyScalar( s ) { - computeGroups: function ( geometry ) { + this.r *= s; + this.g *= s; + this.b *= s; - var group; - var groups = []; - var materialIndex = undefined; + return this; - var faces = geometry.faces; + } - for ( var i = 0; i < faces.length; i ++ ) { + lerp( color, alpha ) { - var face = faces[ i ]; + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; - // materials + return this; - if ( face.materialIndex !== materialIndex ) { + } - materialIndex = face.materialIndex; + lerpColors( color1, color2, alpha ) { - if ( group !== undefined ) { + this.r = color1.r + ( color2.r - color1.r ) * alpha; + this.g = color1.g + ( color2.g - color1.g ) * alpha; + this.b = color1.b + ( color2.b - color1.b ) * alpha; - group.count = ( i * 3 ) - group.start; - groups.push( group ); + return this; - } + } - group = { - start: i * 3, - materialIndex: materialIndex - }; + lerpHSL( color, alpha ) { - } + this.getHSL( _hslA ); + color.getHSL( _hslB ); - } + const h = lerp( _hslA.h, _hslB.h, alpha ); + const s = lerp( _hslA.s, _hslB.s, alpha ); + const l = lerp( _hslA.l, _hslB.l, alpha ); - if ( group !== undefined ) { + this.setHSL( h, s, l ); - group.count = ( i * 3 ) - group.start; - groups.push( group ); + return this; - } + } - this.groups = groups; + setFromVector3( v ) { - }, + this.r = v.x; + this.g = v.y; + this.b = v.z; - fromGeometry: function ( geometry ) { + return this; - var faces = geometry.faces; - var vertices = geometry.vertices; - var faceVertexUvs = geometry.faceVertexUvs; + } - var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; + applyMatrix3( m ) { - // morphs + const r = this.r, g = this.g, b = this.b; + const e = m.elements; - var morphTargets = geometry.morphTargets; - var morphTargetsLength = morphTargets.length; + this.r = e[ 0 ] * r + e[ 3 ] * g + e[ 6 ] * b; + this.g = e[ 1 ] * r + e[ 4 ] * g + e[ 7 ] * b; + this.b = e[ 2 ] * r + e[ 5 ] * g + e[ 8 ] * b; - var morphTargetsPosition; + return this; - if ( morphTargetsLength > 0 ) { + } - morphTargetsPosition = []; + equals( c ) { - for ( var i = 0; i < morphTargetsLength; i ++ ) { + return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); - morphTargetsPosition[ i ] = { - name: morphTargets[ i ].name, - data: [] - }; + } - } + fromArray( array, offset = 0 ) { - this.morphTargets.position = morphTargetsPosition; + this.r = array[ offset ]; + this.g = array[ offset + 1 ]; + this.b = array[ offset + 2 ]; - } + return this; - var morphNormals = geometry.morphNormals; - var morphNormalsLength = morphNormals.length; + } - var morphTargetsNormal; + toArray( array = [], offset = 0 ) { - if ( morphNormalsLength > 0 ) { + array[ offset ] = this.r; + array[ offset + 1 ] = this.g; + array[ offset + 2 ] = this.b; - morphTargetsNormal = []; + return array; - for ( var i = 0; i < morphNormalsLength; i ++ ) { + } - morphTargetsNormal[ i ] = { - name: morphNormals[ i ].name, - data: [] - }; + fromBufferAttribute( attribute, index ) { - } + this.r = attribute.getX( index ); + this.g = attribute.getY( index ); + this.b = attribute.getZ( index ); - this.morphTargets.normal = morphTargetsNormal; + return this; - } + } - // skins + toJSON() { - var skinIndices = geometry.skinIndices; - var skinWeights = geometry.skinWeights; + return this.getHex(); - var hasSkinIndices = skinIndices.length === vertices.length; - var hasSkinWeights = skinWeights.length === vertices.length; + } - // + *[ Symbol.iterator ]() { - if ( vertices.length > 0 && faces.length === 0 ) { + yield this.r; + yield this.g; + yield this.b; - console.error( 'THREE.DirectGeometry: Faceless geometries are not supported.' ); + } - } +} - for ( var i = 0; i < faces.length; i ++ ) { +const _color = /*@__PURE__*/ new Color(); - var face = faces[ i ]; +Color.NAMES = _colorKeywords; - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); +class MeshBasicMaterial extends Material { - var vertexNormals = face.vertexNormals; + constructor( parameters ) { - if ( vertexNormals.length === 3 ) { + super(); - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); + this.isMeshBasicMaterial = true; - } else { + this.type = 'MeshBasicMaterial'; - var normal = face.normal; + this.color = new Color( 0xffffff ); // emissive - this.normals.push( normal, normal, normal ); + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - var vertexColors = face.vertexColors; + this.aoMap = null; + this.aoMapIntensity = 1.0; - if ( vertexColors.length === 3 ) { + this.specularMap = null; - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); + this.alphaMap = null; - } else { + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - var color = face.color; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - this.colors.push( color, color, color ); + this.fog = true; - } + this.setValues( parameters ); - if ( hasFaceVertexUv === true ) { + } - var vertexUvs = faceVertexUvs[ 0 ][ i ]; + copy( source ) { - if ( vertexUvs !== undefined ) { + super.copy( source ); - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + this.color.copy( source.color ); - } else { + this.map = source.map; - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - } + this.specularMap = source.specularMap; - } + this.alphaMap = source.alphaMap; - if ( hasFaceVertexUv2 === true ) { + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - var vertexUvs = faceVertexUvs[ 1 ][ i ]; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - if ( vertexUvs !== undefined ) { + this.fog = source.fog; - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + return this; - } else { + } - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); +} - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); +// Fast Half Float Conversions, http://www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf - } +const _tables = /*@__PURE__*/ _generateTables(); - } +function _generateTables() { - // morphs + // float32 to float16 helpers - for ( var j = 0; j < morphTargetsLength; j ++ ) { + const buffer = new ArrayBuffer( 4 ); + const floatView = new Float32Array( buffer ); + const uint32View = new Uint32Array( buffer ); - var morphTarget = morphTargets[ j ].vertices; + const baseTable = new Uint32Array( 512 ); + const shiftTable = new Uint32Array( 512 ); - morphTargetsPosition[ j ].data.push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + for ( let i = 0; i < 256; ++ i ) { - } + const e = i - 127; - for ( var j = 0; j < morphNormalsLength; j ++ ) { + // very small number (0, -0) - var morphNormal = morphNormals[ j ].vertexNormals[ i ]; + if ( e < - 27 ) { - morphTargetsNormal[ j ].data.push( morphNormal.a, morphNormal.b, morphNormal.c ); + baseTable[ i ] = 0x0000; + baseTable[ i | 0x100 ] = 0x8000; + shiftTable[ i ] = 24; + shiftTable[ i | 0x100 ] = 24; - } + // small number (denorm) - // skins + } else if ( e < - 14 ) { - if ( hasSkinIndices ) { + baseTable[ i ] = 0x0400 >> ( - e - 14 ); + baseTable[ i | 0x100 ] = ( 0x0400 >> ( - e - 14 ) ) | 0x8000; + shiftTable[ i ] = - e - 1; + shiftTable[ i | 0x100 ] = - e - 1; - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); + // normal number - } + } else if ( e <= 15 ) { - if ( hasSkinWeights ) { + baseTable[ i ] = ( e + 15 ) << 10; + baseTable[ i | 0x100 ] = ( ( e + 15 ) << 10 ) | 0x8000; + shiftTable[ i ] = 13; + shiftTable[ i | 0x100 ] = 13; - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); + // large number (Infinity, -Infinity) - } + } else if ( e < 128 ) { - } - - this.computeGroups( geometry ); + baseTable[ i ] = 0x7c00; + baseTable[ i | 0x100 ] = 0xfc00; + shiftTable[ i ] = 24; + shiftTable[ i | 0x100 ] = 24; - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; + // stay (NaN, Infinity, -Infinity) - if ( geometry.boundingSphere !== null ) { - - this.boundingSphere = geometry.boundingSphere.clone(); + } else { - } + baseTable[ i ] = 0x7c00; + baseTable[ i | 0x100 ] = 0xfc00; + shiftTable[ i ] = 13; + shiftTable[ i | 0x100 ] = 13; - if ( geometry.boundingBox !== null ) { + } - this.boundingBox = geometry.boundingBox.clone(); + } - } + // float16 to float32 helpers - return this; + const mantissaTable = new Uint32Array( 2048 ); + const exponentTable = new Uint32Array( 64 ); + const offsetTable = new Uint32Array( 64 ); - } + for ( let i = 1; i < 1024; ++ i ) { - } ); + let m = i << 13; // zero pad mantissa bits + let e = 0; // zero exponent - /** - * @author mrdoob / http://mrdoob.com/ - */ + // normalized + while ( ( m & 0x00800000 ) === 0 ) { - function arrayMax( array ) { + m <<= 1; + e -= 0x00800000; // decrement exponent - if ( array.length === 0 ) return - Infinity; + } - var max = array[ 0 ]; + m &= ~ 0x00800000; // clear leading 1 bit + e += 0x38800000; // adjust bias - for ( var i = 1, l = array.length; i < l; ++ i ) { + mantissaTable[ i ] = m | e; - if ( array[ i ] > max ) max = array[ i ]; + } - } + for ( let i = 1024; i < 2048; ++ i ) { - return max; + mantissaTable[ i ] = 0x38000000 + ( ( i - 1024 ) << 13 ); } - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + for ( let i = 1; i < 31; ++ i ) { - var _bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id + exponentTable[ i ] = i << 23; - var _m1$2 = new Matrix4(); - var _obj = new Object3D(); - var _offset = new Vector3(); - var _box$1 = new Box3(); - var _boxMorphTargets = new Box3(); - var _vector$4 = new Vector3(); - - function BufferGeometry() { + } - Object.defineProperty( this, 'id', { value: _bufferGeometryId += 2 } ); + exponentTable[ 31 ] = 0x47800000; + exponentTable[ 32 ] = 0x80000000; - this.uuid = _Math.generateUUID(); + for ( let i = 33; i < 63; ++ i ) { - this.name = ''; - this.type = 'BufferGeometry'; + exponentTable[ i ] = 0x80000000 + ( ( i - 32 ) << 23 ); - this.index = null; - this.attributes = {}; + } - this.morphAttributes = {}; + exponentTable[ 63 ] = 0xc7800000; - this.groups = []; + for ( let i = 1; i < 64; ++ i ) { - this.boundingBox = null; - this.boundingSphere = null; + if ( i !== 32 ) { - this.drawRange = { start: 0, count: Infinity }; + offsetTable[ i ] = 1024; - this.userData = {}; + } } - BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + return { + floatView: floatView, + uint32View: uint32View, + baseTable: baseTable, + shiftTable: shiftTable, + mantissaTable: mantissaTable, + exponentTable: exponentTable, + offsetTable: offsetTable + }; - constructor: BufferGeometry, +} - isBufferGeometry: true, +// float32 to float16 - getIndex: function () { +function toHalfFloat( val ) { - return this.index; + if ( Math.abs( val ) > 65504 ) console.warn( 'THREE.DataUtils.toHalfFloat(): Value out of range.' ); - }, + val = clamp( val, - 65504, 65504 ); - setIndex: function ( index ) { + _tables.floatView[ 0 ] = val; + const f = _tables.uint32View[ 0 ]; + const e = ( f >> 23 ) & 0x1ff; + return _tables.baseTable[ e ] + ( ( f & 0x007fffff ) >> _tables.shiftTable[ e ] ); - if ( Array.isArray( index ) ) { +} - this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); +// float16 to float32 - } else { +function fromHalfFloat( val ) { - this.index = index; + const m = val >> 10; + _tables.uint32View[ 0 ] = _tables.mantissaTable[ _tables.offsetTable[ m ] + ( val & 0x3ff ) ] + _tables.exponentTable[ m ]; + return _tables.floatView[ 0 ]; - } +} - }, +const DataUtils = { + toHalfFloat: toHalfFloat, + fromHalfFloat: fromHalfFloat, +}; - addAttribute: function ( name, attribute ) { +const _vector$8 = /*@__PURE__*/ new Vector3(); +const _vector2$1 = /*@__PURE__*/ new Vector2(); - if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) { +class BufferAttribute { - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + constructor( array, itemSize, normalized = false ) { - return this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + if ( Array.isArray( array ) ) { - } + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - if ( name === 'index' ) { + } - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); + this.isBufferAttribute = true; - return this; + this.name = ''; - } + this.array = array; + this.itemSize = itemSize; + this.count = array !== undefined ? array.length / itemSize : 0; + this.normalized = normalized; - this.attributes[ name ] = attribute; + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; + this.gpuType = FloatType; - return this; + this.version = 0; - }, + } - getAttribute: function ( name ) { + onUploadCallback() {} - return this.attributes[ name ]; + set needsUpdate( value ) { - }, + if ( value === true ) this.version ++; - removeAttribute: function ( name ) { + } - delete this.attributes[ name ]; + setUsage( value ) { - return this; + this.usage = value; - }, + return this; - addGroup: function ( start, count, materialIndex ) { + } - this.groups.push( { + copy( source ) { - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 + this.name = source.name; + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; - } ); + this.usage = source.usage; + this.gpuType = source.gpuType; - }, + return this; - clearGroups: function () { + } - this.groups = []; + copyAt( index1, attribute, index2 ) { - }, + index1 *= this.itemSize; + index2 *= attribute.itemSize; - setDrawRange: function ( start, count ) { + for ( let i = 0, l = this.itemSize; i < l; i ++ ) { - this.drawRange.start = start; - this.drawRange.count = count; + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - }, + } - applyMatrix: function ( matrix ) { + return this; - var position = this.attributes.position; + } - if ( position !== undefined ) { + copyArray( array ) { - matrix.applyToBufferAttribute( position ); - position.needsUpdate = true; + this.array.set( array ); - } + return this; - var normal = this.attributes.normal; + } + + applyMatrix3( m ) { - if ( normal !== undefined ) { + if ( this.itemSize === 2 ) { - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + for ( let i = 0, l = this.count; i < l; i ++ ) { - normalMatrix.applyToBufferAttribute( normal ); - normal.needsUpdate = true; + _vector2$1.fromBufferAttribute( this, i ); + _vector2$1.applyMatrix3( m ); + + this.setXY( i, _vector2$1.x, _vector2$1.y ); } - var tangent = this.attributes.tangent; + } else if ( this.itemSize === 3 ) { - if ( tangent !== undefined ) { + for ( let i = 0, l = this.count; i < l; i ++ ) { - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + _vector$8.fromBufferAttribute( this, i ); + _vector$8.applyMatrix3( m ); - // Tangent is vec4, but the '.w' component is a sign value (+1/-1). - normalMatrix.applyToBufferAttribute( tangent ); - tangent.needsUpdate = true; + this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); } - if ( this.boundingBox !== null ) { + } - this.computeBoundingBox(); + return this; - } + } - if ( this.boundingSphere !== null ) { + applyMatrix4( m ) { - this.computeBoundingSphere(); + for ( let i = 0, l = this.count; i < l; i ++ ) { - } + _vector$8.fromBufferAttribute( this, i ); - return this; + _vector$8.applyMatrix4( m ); - }, + this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); - rotateX: function ( angle ) { + } - // rotate geometry around world x-axis + return this; - _m1$2.makeRotationX( angle ); + } - this.applyMatrix( _m1$2 ); + applyNormalMatrix( m ) { - return this; + for ( let i = 0, l = this.count; i < l; i ++ ) { - }, + _vector$8.fromBufferAttribute( this, i ); - rotateY: function ( angle ) { + _vector$8.applyNormalMatrix( m ); - // rotate geometry around world y-axis + this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); - _m1$2.makeRotationY( angle ); + } - this.applyMatrix( _m1$2 ); + return this; - return this; + } - }, + transformDirection( m ) { - rotateZ: function ( angle ) { + for ( let i = 0, l = this.count; i < l; i ++ ) { - // rotate geometry around world z-axis + _vector$8.fromBufferAttribute( this, i ); - _m1$2.makeRotationZ( angle ); + _vector$8.transformDirection( m ); - this.applyMatrix( _m1$2 ); + this.setXYZ( i, _vector$8.x, _vector$8.y, _vector$8.z ); - return this; + } - }, + return this; - translate: function ( x, y, z ) { + } - // translate geometry + set( value, offset = 0 ) { - _m1$2.makeTranslation( x, y, z ); + // Matching BufferAttribute constructor, do not normalize the array. + this.array.set( value, offset ); - this.applyMatrix( _m1$2 ); + return this; - return this; + } - }, + getComponent( index, component ) { - scale: function ( x, y, z ) { + let value = this.array[ index * this.itemSize + component ]; - // scale geometry + if ( this.normalized ) value = denormalize( value, this.array ); - _m1$2.makeScale( x, y, z ); + return value; - this.applyMatrix( _m1$2 ); + } - return this; + setComponent( index, component, value ) { - }, + if ( this.normalized ) value = normalize( value, this.array ); - lookAt: function ( vector ) { + this.array[ index * this.itemSize + component ] = value; - _obj.lookAt( vector ); + return this; - _obj.updateMatrix(); + } - this.applyMatrix( _obj.matrix ); + getX( index ) { - return this; + let x = this.array[ index * this.itemSize ]; - }, + if ( this.normalized ) x = denormalize( x, this.array ); - center: function () { + return x; - this.computeBoundingBox(); + } - this.boundingBox.getCenter( _offset ).negate(); + setX( index, x ) { - this.translate( _offset.x, _offset.y, _offset.z ); + if ( this.normalized ) x = normalize( x, this.array ); - return this; + this.array[ index * this.itemSize ] = x; - }, + return this; - setFromObject: function ( object ) { + } - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); + getY( index ) { - var geometry = object.geometry; + let y = this.array[ index * this.itemSize + 1 ]; - if ( object.isPoints || object.isLine ) { + if ( this.normalized ) y = denormalize( y, this.array ); - var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 ); - var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 ); + return y; - this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); + } - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { + setY( index, y ) { - var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 ); + if ( this.normalized ) y = normalize( y, this.array ); - this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); + this.array[ index * this.itemSize + 1 ] = y; - } + return this; - if ( geometry.boundingSphere !== null ) { + } - this.boundingSphere = geometry.boundingSphere.clone(); + getZ( index ) { - } + let z = this.array[ index * this.itemSize + 2 ]; - if ( geometry.boundingBox !== null ) { + if ( this.normalized ) z = denormalize( z, this.array ); - this.boundingBox = geometry.boundingBox.clone(); + return z; - } + } - } else if ( object.isMesh ) { + setZ( index, z ) { - if ( geometry && geometry.isGeometry ) { + if ( this.normalized ) z = normalize( z, this.array ); - this.fromGeometry( geometry ); + this.array[ index * this.itemSize + 2 ] = z; - } + return this; - } + } - return this; + getW( index ) { - }, + let w = this.array[ index * this.itemSize + 3 ]; - setFromPoints: function ( points ) { + if ( this.normalized ) w = denormalize( w, this.array ); - var position = []; + return w; - for ( var i = 0, l = points.length; i < l; i ++ ) { + } - var point = points[ i ]; - position.push( point.x, point.y, point.z || 0 ); + setW( index, w ) { - } + if ( this.normalized ) w = normalize( w, this.array ); - this.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); + this.array[ index * this.itemSize + 3 ] = w; - return this; + return this; - }, + } - updateFromObject: function ( object ) { + setXY( index, x, y ) { - var geometry = object.geometry; + index *= this.itemSize; - if ( object.isMesh ) { + if ( this.normalized ) { - var direct = geometry.__directGeometry; + x = normalize( x, this.array ); + y = normalize( y, this.array ); - if ( geometry.elementsNeedUpdate === true ) { + } - direct = undefined; - geometry.elementsNeedUpdate = false; + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; - } + return this; - if ( direct === undefined ) { + } - return this.fromGeometry( geometry ); + setXYZ( index, x, y, z ) { - } + index *= this.itemSize; - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; + if ( this.normalized ) { - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); - geometry = direct; + } - } + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; - var attribute; + return this; - if ( geometry.verticesNeedUpdate === true ) { + } - attribute = this.attributes.position; + setXYZW( index, x, y, z, w ) { - if ( attribute !== undefined ) { + index *= this.itemSize; - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; + if ( this.normalized ) { - } + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); + w = normalize( w, this.array ); - geometry.verticesNeedUpdate = false; + } - } + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; - if ( geometry.normalsNeedUpdate === true ) { + return this; - attribute = this.attributes.normal; + } - if ( attribute !== undefined ) { + onUpload( callback ) { - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; + this.onUploadCallback = callback; - } + return this; - geometry.normalsNeedUpdate = false; + } - } + clone() { - if ( geometry.colorsNeedUpdate === true ) { + return new this.constructor( this.array, this.itemSize ).copy( this ); - attribute = this.attributes.color; + } - if ( attribute !== undefined ) { + toJSON() { - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; + const data = { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: Array.from( this.array ), + normalized: this.normalized + }; - } + if ( this.name !== '' ) data.name = this.name; + if ( this.usage !== StaticDrawUsage ) data.usage = this.usage; + if ( this.updateRange.offset !== 0 || this.updateRange.count !== - 1 ) data.updateRange = this.updateRange; - geometry.colorsNeedUpdate = false; + return data; - } + } - if ( geometry.uvsNeedUpdate ) { +} - attribute = this.attributes.uv; +// - if ( attribute !== undefined ) { +class Int8BufferAttribute extends BufferAttribute { - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; + constructor( array, itemSize, normalized ) { - } + super( new Int8Array( array ), itemSize, normalized ); - geometry.uvsNeedUpdate = false; + } - } +} - if ( geometry.lineDistancesNeedUpdate ) { +class Uint8BufferAttribute extends BufferAttribute { - attribute = this.attributes.lineDistance; + constructor( array, itemSize, normalized ) { - if ( attribute !== undefined ) { + super( new Uint8Array( array ), itemSize, normalized ); - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; + } - } +} - geometry.lineDistancesNeedUpdate = false; +class Uint8ClampedBufferAttribute extends BufferAttribute { - } + constructor( array, itemSize, normalized ) { - if ( geometry.groupsNeedUpdate ) { + super( new Uint8ClampedArray( array ), itemSize, normalized ); - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; + } - geometry.groupsNeedUpdate = false; +} - } +class Int16BufferAttribute extends BufferAttribute { - return this; + constructor( array, itemSize, normalized ) { - }, + super( new Int16Array( array ), itemSize, normalized ); - fromGeometry: function ( geometry ) { + } - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); +} - return this.fromDirectGeometry( geometry.__directGeometry ); +class Uint16BufferAttribute extends BufferAttribute { - }, + constructor( array, itemSize, normalized ) { - fromDirectGeometry: function ( geometry ) { + super( new Uint16Array( array ), itemSize, normalized ); - var positions = new Float32Array( geometry.vertices.length * 3 ); - this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); + } - if ( geometry.normals.length > 0 ) { +} - var normals = new Float32Array( geometry.normals.length * 3 ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); +class Int32BufferAttribute extends BufferAttribute { - } + constructor( array, itemSize, normalized ) { - if ( geometry.colors.length > 0 ) { + super( new Int32Array( array ), itemSize, normalized ); - var colors = new Float32Array( geometry.colors.length * 3 ); - this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); + } - } +} - if ( geometry.uvs.length > 0 ) { +class Uint32BufferAttribute extends BufferAttribute { - var uvs = new Float32Array( geometry.uvs.length * 2 ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); + constructor( array, itemSize, normalized ) { - } + super( new Uint32Array( array ), itemSize, normalized ); - if ( geometry.uvs2.length > 0 ) { + } - var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); +} - } +class Float16BufferAttribute extends BufferAttribute { - // groups + constructor( array, itemSize, normalized ) { - this.groups = geometry.groups; + super( new Uint16Array( array ), itemSize, normalized ); - // morphs + this.isFloat16BufferAttribute = true; - for ( var name in geometry.morphTargets ) { + } - var array = []; - var morphTargets = geometry.morphTargets[ name ]; + getX( index ) { - for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { + let x = fromHalfFloat( this.array[ index * this.itemSize ] ); - var morphTarget = morphTargets[ i ]; + if ( this.normalized ) x = denormalize( x, this.array ); - var attribute = new Float32BufferAttribute( morphTarget.data.length * 3, 3 ); - attribute.name = morphTarget.name; + return x; - array.push( attribute.copyVector3sArray( morphTarget.data ) ); + } - } + setX( index, x ) { - this.morphAttributes[ name ] = array; + if ( this.normalized ) x = normalize( x, this.array ); - } + this.array[ index * this.itemSize ] = toHalfFloat( x ); - // skinning + return this; - if ( geometry.skinIndices.length > 0 ) { + } - var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 ); - this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + getY( index ) { - } + let y = fromHalfFloat( this.array[ index * this.itemSize + 1 ] ); - if ( geometry.skinWeights.length > 0 ) { + if ( this.normalized ) y = denormalize( y, this.array ); - var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 ); - this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); + return y; - } + } - // + setY( index, y ) { - if ( geometry.boundingSphere !== null ) { + if ( this.normalized ) y = normalize( y, this.array ); - this.boundingSphere = geometry.boundingSphere.clone(); + this.array[ index * this.itemSize + 1 ] = toHalfFloat( y ); - } + return this; - if ( geometry.boundingBox !== null ) { + } - this.boundingBox = geometry.boundingBox.clone(); + getZ( index ) { - } + let z = fromHalfFloat( this.array[ index * this.itemSize + 2 ] ); - return this; + if ( this.normalized ) z = denormalize( z, this.array ); - }, + return z; - computeBoundingBox: function () { + } - if ( this.boundingBox === null ) { + setZ( index, z ) { - this.boundingBox = new Box3(); + if ( this.normalized ) z = normalize( z, this.array ); - } + this.array[ index * this.itemSize + 2 ] = toHalfFloat( z ); - var position = this.attributes.position; - var morphAttributesPosition = this.morphAttributes.position; + return this; - if ( position !== undefined ) { + } - this.boundingBox.setFromBufferAttribute( position ); + getW( index ) { - // process morph attributes if present + let w = fromHalfFloat( this.array[ index * this.itemSize + 3 ] ); - if ( morphAttributesPosition ) { + if ( this.normalized ) w = denormalize( w, this.array ); - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { + return w; - var morphAttribute = morphAttributesPosition[ i ]; - _box$1.setFromBufferAttribute( morphAttribute ); + } - this.boundingBox.expandByPoint( _box$1.min ); - this.boundingBox.expandByPoint( _box$1.max ); + setW( index, w ) { - } + if ( this.normalized ) w = normalize( w, this.array ); - } + this.array[ index * this.itemSize + 3 ] = toHalfFloat( w ); - } else { + return this; - this.boundingBox.makeEmpty(); + } - } + setXY( index, x, y ) { - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + index *= this.itemSize; - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + if ( this.normalized ) { - } + x = normalize( x, this.array ); + y = normalize( y, this.array ); - }, + } - computeBoundingSphere: function () { + this.array[ index + 0 ] = toHalfFloat( x ); + this.array[ index + 1 ] = toHalfFloat( y ); - if ( this.boundingSphere === null ) { + return this; - this.boundingSphere = new Sphere(); + } - } + setXYZ( index, x, y, z ) { - var position = this.attributes.position; - var morphAttributesPosition = this.morphAttributes.position; + index *= this.itemSize; - if ( position ) { + if ( this.normalized ) { - // first, find the center of the bounding sphere + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); - var center = this.boundingSphere.center; + } - _box$1.setFromBufferAttribute( position ); + this.array[ index + 0 ] = toHalfFloat( x ); + this.array[ index + 1 ] = toHalfFloat( y ); + this.array[ index + 2 ] = toHalfFloat( z ); - // process morph attributes if present + return this; - if ( morphAttributesPosition ) { + } - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { + setXYZW( index, x, y, z, w ) { - var morphAttribute = morphAttributesPosition[ i ]; - _boxMorphTargets.setFromBufferAttribute( morphAttribute ); + index *= this.itemSize; - _box$1.expandByPoint( _boxMorphTargets.min ); - _box$1.expandByPoint( _boxMorphTargets.max ); + if ( this.normalized ) { - } + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); + w = normalize( w, this.array ); - } + } - _box$1.getCenter( center ); + this.array[ index + 0 ] = toHalfFloat( x ); + this.array[ index + 1 ] = toHalfFloat( y ); + this.array[ index + 2 ] = toHalfFloat( z ); + this.array[ index + 3 ] = toHalfFloat( w ); - // second, try to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + return this; - var maxRadiusSq = 0; + } - for ( var i = 0, il = position.count; i < il; i ++ ) { +} - _vector$4.fromBufferAttribute( position, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); +class Float32BufferAttribute extends BufferAttribute { - } + constructor( array, itemSize, normalized ) { - // process morph attributes if present + super( new Float32Array( array ), itemSize, normalized ); - if ( morphAttributesPosition ) { + } - for ( var i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { +} - var morphAttribute = morphAttributesPosition[ i ]; +class Float64BufferAttribute extends BufferAttribute { - for ( var j = 0, jl = morphAttribute.count; j < jl; j ++ ) { + constructor( array, itemSize, normalized ) { - _vector$4.fromBufferAttribute( morphAttribute, j ); + super( new Float64Array( array ), itemSize, normalized ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$4 ) ); + } - } +} - } +let _id$2 = 0; - } +const _m1 = /*@__PURE__*/ new Matrix4(); +const _obj = /*@__PURE__*/ new Object3D(); +const _offset = /*@__PURE__*/ new Vector3(); +const _box$1 = /*@__PURE__*/ new Box3(); +const _boxMorphTargets = /*@__PURE__*/ new Box3(); +const _vector$7 = /*@__PURE__*/ new Vector3(); - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); +class BufferGeometry extends EventDispatcher { - if ( isNaN( this.boundingSphere.radius ) ) { + constructor() { - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + super(); - } + this.isBufferGeometry = true; - } + Object.defineProperty( this, 'id', { value: _id$2 ++ } ); - }, + this.uuid = generateUUID(); - computeFaceNormals: function () { + this.name = ''; + this.type = 'BufferGeometry'; - // backwards compatibility + this.index = null; + this.attributes = {}; - }, + this.morphAttributes = {}; + this.morphTargetsRelative = false; - computeVertexNormals: function () { + this.groups = []; - var index = this.index; - var attributes = this.attributes; + this.boundingBox = null; + this.boundingSphere = null; - if ( attributes.position ) { + this.drawRange = { start: 0, count: Infinity }; - var positions = attributes.position.array; + this.userData = {}; - if ( attributes.normal === undefined ) { + } - this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); + getIndex() { - } else { + return this.index; - // reset existing normals to zero + } - var array = attributes.normal.array; + setIndex( index ) { - for ( var i = 0, il = array.length; i < il; i ++ ) { + if ( Array.isArray( index ) ) { - array[ i ] = 0; + this.index = new ( arrayNeedsUint32( index ) ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 ); - } + } else { - } + this.index = index; - var normals = attributes.normal.array; + } - var vA, vB, vC; - var pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); - var cb = new Vector3(), ab = new Vector3(); + return this; - // indexed elements + } - if ( index ) { + getAttribute( name ) { - var indices = index.array; + return this.attributes[ name ]; - for ( var i = 0, il = index.count; i < il; i += 3 ) { + } - vA = indices[ i + 0 ] * 3; - vB = indices[ i + 1 ] * 3; - vC = indices[ i + 2 ] * 3; + setAttribute( name, attribute ) { - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); + this.attributes[ name ] = attribute; - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + return this; - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; + } - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; + deleteAttribute( name ) { - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; + delete this.attributes[ name ]; - } + return this; - } else { + } - // non-indexed elements (unconnected triangle soup) + hasAttribute( name ) { - for ( var i = 0, il = positions.length; i < il; i += 9 ) { + return this.attributes[ name ] !== undefined; - pA.fromArray( positions, i ); - pB.fromArray( positions, i + 3 ); - pC.fromArray( positions, i + 6 ); + } - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + addGroup( start, count, materialIndex = 0 ) { - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; + this.groups.push( { - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; + start: start, + count: count, + materialIndex: materialIndex - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; + } ); - } + } - } + clearGroups() { - this.normalizeNormals(); + this.groups = []; - attributes.normal.needsUpdate = true; + } - } + setDrawRange( start, count ) { - }, + this.drawRange.start = start; + this.drawRange.count = count; - merge: function ( geometry, offset ) { + } - if ( ! ( geometry && geometry.isBufferGeometry ) ) { + applyMatrix4( matrix ) { - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; + const position = this.attributes.position; - } + if ( position !== undefined ) { - if ( offset === undefined ) { + position.applyMatrix4( matrix ); - offset = 0; + position.needsUpdate = true; - console.warn( - 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. ' - + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.' - ); + } - } + const normal = this.attributes.normal; - var attributes = this.attributes; + if ( normal !== undefined ) { - for ( var key in attributes ) { + const normalMatrix = new Matrix3().getNormalMatrix( matrix ); - if ( geometry.attributes[ key ] === undefined ) continue; + normal.applyNormalMatrix( normalMatrix ); - var attribute1 = attributes[ key ]; - var attributeArray1 = attribute1.array; + normal.needsUpdate = true; - var attribute2 = geometry.attributes[ key ]; - var attributeArray2 = attribute2.array; + } - var attributeOffset = attribute2.itemSize * offset; - var length = Math.min( attributeArray2.length, attributeArray1.length - attributeOffset ); + const tangent = this.attributes.tangent; - for ( var i = 0, j = attributeOffset; i < length; i ++, j ++ ) { + if ( tangent !== undefined ) { - attributeArray1[ j ] = attributeArray2[ i ]; + tangent.transformDirection( matrix ); - } + tangent.needsUpdate = true; - } + } - return this; + if ( this.boundingBox !== null ) { - }, + this.computeBoundingBox(); - normalizeNormals: function () { + } - var normals = this.attributes.normal; + if ( this.boundingSphere !== null ) { - for ( var i = 0, il = normals.count; i < il; i ++ ) { + this.computeBoundingSphere(); - _vector$4.x = normals.getX( i ); - _vector$4.y = normals.getY( i ); - _vector$4.z = normals.getZ( i ); + } - _vector$4.normalize(); + return this; - normals.setXYZ( i, _vector$4.x, _vector$4.y, _vector$4.z ); + } - } + applyQuaternion( q ) { - }, + _m1.makeRotationFromQuaternion( q ); - toNonIndexed: function () { + this.applyMatrix4( _m1 ); - function convertBufferAttribute( attribute, indices ) { + return this; - var array = attribute.array; - var itemSize = attribute.itemSize; + } - var array2 = new array.constructor( indices.length * itemSize ); + rotateX( angle ) { - var index = 0, index2 = 0; + // rotate geometry around world x-axis - for ( var i = 0, l = indices.length; i < l; i ++ ) { + _m1.makeRotationX( angle ); - index = indices[ i ] * itemSize; + this.applyMatrix4( _m1 ); - for ( var j = 0; j < itemSize; j ++ ) { + return this; - array2[ index2 ++ ] = array[ index ++ ]; + } - } + rotateY( angle ) { - } + // rotate geometry around world y-axis - return new BufferAttribute( array2, itemSize ); + _m1.makeRotationY( angle ); - } + this.applyMatrix4( _m1 ); - // + return this; - if ( this.index === null ) { + } - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; + rotateZ( angle ) { - } + // rotate geometry around world z-axis - var geometry2 = new BufferGeometry(); + _m1.makeRotationZ( angle ); - var indices = this.index.array; - var attributes = this.attributes; + this.applyMatrix4( _m1 ); - // attributes + return this; - for ( var name in attributes ) { + } - var attribute = attributes[ name ]; + translate( x, y, z ) { - var newAttribute = convertBufferAttribute( attribute, indices ); + // translate geometry - geometry2.addAttribute( name, newAttribute ); + _m1.makeTranslation( x, y, z ); - } + this.applyMatrix4( _m1 ); - // morph attributes + return this; - var morphAttributes = this.morphAttributes; + } - for ( name in morphAttributes ) { + scale( x, y, z ) { - var morphArray = []; - var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes + // scale geometry - for ( var i = 0, il = morphAttribute.length; i < il; i ++ ) { + _m1.makeScale( x, y, z ); - var attribute = morphAttribute[ i ]; + this.applyMatrix4( _m1 ); - var newAttribute = convertBufferAttribute( attribute, indices ); + return this; - morphArray.push( newAttribute ); + } - } + lookAt( vector ) { - geometry2.morphAttributes[ name ] = morphArray; + _obj.lookAt( vector ); - } + _obj.updateMatrix(); - // groups + this.applyMatrix4( _obj.matrix ); - var groups = this.groups; + return this; - for ( var i = 0, l = groups.length; i < l; i ++ ) { + } - var group = groups[ i ]; - geometry2.addGroup( group.start, group.count, group.materialIndex ); + center() { - } + this.computeBoundingBox(); - return geometry2; + this.boundingBox.getCenter( _offset ).negate(); - }, + this.translate( _offset.x, _offset.y, _offset.z ); - toJSON: function () { + return this; - var data = { - metadata: { - version: 4.5, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' - } - }; + } - // standard BufferGeometry serialization + setFromPoints( points ) { - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; - if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; + const position = []; - if ( this.parameters !== undefined ) { + for ( let i = 0, l = points.length; i < l; i ++ ) { - var parameters = this.parameters; + const point = points[ i ]; + position.push( point.x, point.y, point.z || 0 ); - for ( var key in parameters ) { + } - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + this.setAttribute( 'position', new Float32BufferAttribute( position, 3 ) ); - } + return this; - return data; + } - } + computeBoundingBox() { - data.data = { attributes: {} }; + if ( this.boundingBox === null ) { - var index = this.index; + this.boundingBox = new Box3(); - if ( index !== null ) { + } - data.data.index = { - type: index.array.constructor.name, - array: Array.prototype.slice.call( index.array ) - }; + const position = this.attributes.position; + const morphAttributesPosition = this.morphAttributes.position; - } + if ( position && position.isGLBufferAttribute ) { - var attributes = this.attributes; + console.error( 'THREE.BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box. Alternatively set "mesh.frustumCulled" to "false".', this ); - for ( var key in attributes ) { + this.boundingBox.set( + new Vector3( - Infinity, - Infinity, - Infinity ), + new Vector3( + Infinity, + Infinity, + Infinity ) + ); - var attribute = attributes[ key ]; + return; - var attributeData = attribute.toJSON(); + } - if ( attribute.name !== '' ) attributeData.name = attribute.name; + if ( position !== undefined ) { - data.data.attributes[ key ] = attributeData; + this.boundingBox.setFromBufferAttribute( position ); - } + // process morph attributes if present - var morphAttributes = {}; - var hasMorphAttributes = false; + if ( morphAttributesPosition ) { - for ( var key in this.morphAttributes ) { + for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - var attributeArray = this.morphAttributes[ key ]; + const morphAttribute = morphAttributesPosition[ i ]; + _box$1.setFromBufferAttribute( morphAttribute ); - var array = []; + if ( this.morphTargetsRelative ) { - for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { + _vector$7.addVectors( this.boundingBox.min, _box$1.min ); + this.boundingBox.expandByPoint( _vector$7 ); - var attribute = attributeArray[ i ]; + _vector$7.addVectors( this.boundingBox.max, _box$1.max ); + this.boundingBox.expandByPoint( _vector$7 ); - var attributeData = attribute.toJSON(); + } else { - if ( attribute.name !== '' ) attributeData.name = attribute.name; + this.boundingBox.expandByPoint( _box$1.min ); + this.boundingBox.expandByPoint( _box$1.max ); - array.push( attributeData ); + } } - if ( array.length > 0 ) { + } - morphAttributes[ key ] = array; + } else { - hasMorphAttributes = true; + this.boundingBox.makeEmpty(); - } + } - } + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - if ( hasMorphAttributes ) data.data.morphAttributes = morphAttributes; + console.error( 'THREE.BufferGeometry.computeBoundingBox(): Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + + } - var groups = this.groups; + } - if ( groups.length > 0 ) { + computeBoundingSphere() { - data.data.groups = JSON.parse( JSON.stringify( groups ) ); + if ( this.boundingSphere === null ) { - } + this.boundingSphere = new Sphere(); - var boundingSphere = this.boundingSphere; + } - if ( boundingSphere !== null ) { + const position = this.attributes.position; + const morphAttributesPosition = this.morphAttributes.position; - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; + if ( position && position.isGLBufferAttribute ) { - } + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set "mesh.frustumCulled" to "false".', this ); - return data; + this.boundingSphere.set( new Vector3(), Infinity ); - }, + return; - clone: function () { + } - /* - // Handle primitives + if ( position ) { - var parameters = this.parameters; + // first, find the center of the bounding sphere - if ( parameters !== undefined ) { + const center = this.boundingSphere.center; - var values = []; + _box$1.setFromBufferAttribute( position ); - for ( var key in parameters ) { + // process morph attributes if present - values.push( parameters[ key ] ); + if ( morphAttributesPosition ) { - } + for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; + const morphAttribute = morphAttributesPosition[ i ]; + _boxMorphTargets.setFromBufferAttribute( morphAttribute ); - } + if ( this.morphTargetsRelative ) { - return new this.constructor().copy( this ); - */ + _vector$7.addVectors( _box$1.min, _boxMorphTargets.min ); + _box$1.expandByPoint( _vector$7 ); - return new BufferGeometry().copy( this ); + _vector$7.addVectors( _box$1.max, _boxMorphTargets.max ); + _box$1.expandByPoint( _vector$7 ); - }, + } else { - copy: function ( source ) { + _box$1.expandByPoint( _boxMorphTargets.min ); + _box$1.expandByPoint( _boxMorphTargets.max ); - var name, i, l; + } - // reset + } - this.index = null; - this.attributes = {}; - this.morphAttributes = {}; - this.groups = []; - this.boundingBox = null; - this.boundingSphere = null; + } - // name + _box$1.getCenter( center ); - this.name = source.name; + // second, try to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - // index + let maxRadiusSq = 0; - var index = source.index; + for ( let i = 0, il = position.count; i < il; i ++ ) { - if ( index !== null ) { + _vector$7.fromBufferAttribute( position, i ); - this.setIndex( index.clone() ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); } - // attributes + // process morph attributes if present - var attributes = source.attributes; + if ( morphAttributesPosition ) { - for ( name in attributes ) { + for ( let i = 0, il = morphAttributesPosition.length; i < il; i ++ ) { - var attribute = attributes[ name ]; - this.addAttribute( name, attribute.clone() ); + const morphAttribute = morphAttributesPosition[ i ]; + const morphTargetsRelative = this.morphTargetsRelative; - } + for ( let j = 0, jl = morphAttribute.count; j < jl; j ++ ) { - // morph attributes + _vector$7.fromBufferAttribute( morphAttribute, j ); - var morphAttributes = source.morphAttributes; + if ( morphTargetsRelative ) { - for ( name in morphAttributes ) { + _offset.fromBufferAttribute( position, j ); + _vector$7.add( _offset ); - var array = []; - var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes + } - for ( i = 0, l = morphAttribute.length; i < l; i ++ ) { + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector$7 ) ); - array.push( morphAttribute[ i ].clone() ); + } } - this.morphAttributes[ name ] = array; - } - // groups + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - var groups = source.groups; + if ( isNaN( this.boundingSphere.radius ) ) { - for ( i = 0, l = groups.length; i < l; i ++ ) { - - var group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); } - // bounding box - - var boundingBox = source.boundingBox; + } - if ( boundingBox !== null ) { + } - this.boundingBox = boundingBox.clone(); + computeTangents() { - } + const index = this.index; + const attributes = this.attributes; - // bounding sphere + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) - var boundingSphere = source.boundingSphere; + if ( index === null || + attributes.position === undefined || + attributes.normal === undefined || + attributes.uv === undefined ) { - if ( boundingSphere !== null ) { + console.error( 'THREE.BufferGeometry: .computeTangents() failed. Missing required attributes (index, position, normal or uv)' ); + return; - this.boundingSphere = boundingSphere.clone(); + } - } + const indices = index.array; + const positions = attributes.position.array; + const normals = attributes.normal.array; + const uvs = attributes.uv.array; - // draw range + const nVertices = positions.length / 3; - this.drawRange.start = source.drawRange.start; - this.drawRange.count = source.drawRange.count; + if ( this.hasAttribute( 'tangent' ) === false ) { - // user data + this.setAttribute( 'tangent', new BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - this.userData = source.userData; + } - return this; + const tangents = this.getAttribute( 'tangent' ).array; - }, + const tan1 = [], tan2 = []; - dispose: function () { + for ( let i = 0; i < nVertices; i ++ ) { - this.dispatchEvent( { type: 'dispose' } ); + tan1[ i ] = new Vector3(); + tan2[ i ] = new Vector3(); } - } ); + const vA = new Vector3(), + vB = new Vector3(), + vC = new Vector3(), - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ - */ + uvA = new Vector2(), + uvB = new Vector2(), + uvC = new Vector2(), - var _inverseMatrix = new Matrix4(); - var _ray = new Ray(); - var _sphere = new Sphere(); + sdir = new Vector3(), + tdir = new Vector3(); - var _vA = new Vector3(); - var _vB = new Vector3(); - var _vC = new Vector3(); + function handleTriangle( a, b, c ) { - var _tempA = new Vector3(); - var _tempB = new Vector3(); - var _tempC = new Vector3(); + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); - var _morphA = new Vector3(); - var _morphB = new Vector3(); - var _morphC = new Vector3(); + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); - var _uvA = new Vector2(); - var _uvB = new Vector2(); - var _uvC = new Vector2(); + vB.sub( vA ); + vC.sub( vA ); - var _intersectionPoint = new Vector3(); - var _intersectionPointWorld = new Vector3(); + uvB.sub( uvA ); + uvC.sub( uvA ); - function Mesh( geometry, material ) { + const r = 1.0 / ( uvB.x * uvC.y - uvC.x * uvB.y ); - Object3D.call( this ); + // silently ignore degenerate uv triangles having coincident or colinear vertices - this.type = 'Mesh'; + if ( ! isFinite( r ) ) return; - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + sdir.copy( vB ).multiplyScalar( uvC.y ).addScaledVector( vC, - uvB.y ).multiplyScalar( r ); + tdir.copy( vC ).multiplyScalar( uvB.x ).addScaledVector( vB, - uvC.x ).multiplyScalar( r ); - this.drawMode = TrianglesDrawMode; + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); - this.updateMorphTargets(); + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); - } + } - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { + let groups = this.groups; - constructor: Mesh, + if ( groups.length === 0 ) { - isMesh: true, + groups = [ { + start: 0, + count: indices.length + } ]; - setDrawMode: function ( value ) { + } - this.drawMode = value; + for ( let i = 0, il = groups.length; i < il; ++ i ) { - }, + const group = groups[ i ]; - copy: function ( source ) { + const start = group.start; + const count = group.count; - Object3D.prototype.copy.call( this, source ); + for ( let j = start, jl = start + count; j < jl; j += 3 ) { - this.drawMode = source.drawMode; + handleTriangle( + indices[ j + 0 ], + indices[ j + 1 ], + indices[ j + 2 ] + ); - if ( source.morphTargetInfluences !== undefined ) { + } - this.morphTargetInfluences = source.morphTargetInfluences.slice(); + } - } + const tmp = new Vector3(), tmp2 = new Vector3(); + const n = new Vector3(), n2 = new Vector3(); - if ( source.morphTargetDictionary !== undefined ) { + function handleVertex( v ) { - this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); + n.fromArray( normals, v * 3 ); + n2.copy( n ); - } + const t = tan1[ v ]; - return this; + // Gram-Schmidt orthogonalize - }, + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - updateMorphTargets: function () { + // Calculate handedness - var geometry = this.geometry; - var m, ml, name; + tmp2.crossVectors( n2, t ); + const test = tmp2.dot( tan2[ v ] ); + const w = ( test < 0.0 ) ? - 1.0 : 1.0; - if ( geometry.isBufferGeometry ) { + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; - var morphAttributes = geometry.morphAttributes; - var keys = Object.keys( morphAttributes ); + } - if ( keys.length > 0 ) { + for ( let i = 0, il = groups.length; i < il; ++ i ) { - var morphAttribute = morphAttributes[ keys[ 0 ] ]; + const group = groups[ i ]; - if ( morphAttribute !== undefined ) { + const start = group.start; + const count = group.count; - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + for ( let j = start, jl = start + count; j < jl; j += 3 ) { - for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + handleVertex( indices[ j + 0 ] ); + handleVertex( indices[ j + 1 ] ); + handleVertex( indices[ j + 2 ] ); - name = morphAttribute[ m ].name || String( m ); + } - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + } - } + } - } + computeVertexNormals() { - } + const index = this.index; + const positionAttribute = this.getAttribute( 'position' ); + + if ( positionAttribute !== undefined ) { + + let normalAttribute = this.getAttribute( 'normal' ); + + if ( normalAttribute === undefined ) { + + normalAttribute = new BufferAttribute( new Float32Array( positionAttribute.count * 3 ), 3 ); + this.setAttribute( 'normal', normalAttribute ); } else { - var morphTargets = geometry.morphTargets; + // reset existing normals to zero - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + for ( let i = 0, il = normalAttribute.count; i < il; i ++ ) { - console.error( 'THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + normalAttribute.setXYZ( i, 0, 0, 0 ); } } - }, + const pA = new Vector3(), pB = new Vector3(), pC = new Vector3(); + const nA = new Vector3(), nB = new Vector3(), nC = new Vector3(); + const cb = new Vector3(), ab = new Vector3(); - raycast: function ( raycaster, intersects ) { + // indexed elements - var geometry = this.geometry; - var material = this.material; - var matrixWorld = this.matrixWorld; + if ( index ) { - if ( material === undefined ) return; + for ( let i = 0, il = index.count; i < il; i += 3 ) { - // Checking boundingSphere distance to ray + const vA = index.getX( i + 0 ); + const vB = index.getX( i + 1 ); + const vC = index.getX( i + 2 ); - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + pA.fromBufferAttribute( positionAttribute, vA ); + pB.fromBufferAttribute( positionAttribute, vB ); + pC.fromBufferAttribute( positionAttribute, vC ); - _sphere.copy( geometry.boundingSphere ); - _sphere.applyMatrix4( matrixWorld ); + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; + nA.fromBufferAttribute( normalAttribute, vA ); + nB.fromBufferAttribute( normalAttribute, vB ); + nC.fromBufferAttribute( normalAttribute, vC ); - // + nA.add( cb ); + nB.add( cb ); + nC.add( cb ); - _inverseMatrix.getInverse( matrixWorld ); - _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); + normalAttribute.setXYZ( vA, nA.x, nA.y, nA.z ); + normalAttribute.setXYZ( vB, nB.x, nB.y, nB.z ); + normalAttribute.setXYZ( vC, nC.x, nC.y, nC.z ); - // Check boundingBox before continuing + } - if ( geometry.boundingBox !== null ) { + } else { - if ( _ray.intersectsBox( geometry.boundingBox ) === false ) return; + // non-indexed elements (unconnected triangle soup) - } + for ( let i = 0, il = positionAttribute.count; i < il; i += 3 ) { - var intersection; + pA.fromBufferAttribute( positionAttribute, i + 0 ); + pB.fromBufferAttribute( positionAttribute, i + 1 ); + pC.fromBufferAttribute( positionAttribute, i + 2 ); - if ( geometry.isBufferGeometry ) { + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - var a, b, c; - var index = geometry.index; - var position = geometry.attributes.position; - var morphPosition = geometry.morphAttributes.position; - var uv = geometry.attributes.uv; - var uv2 = geometry.attributes.uv2; - var groups = geometry.groups; - var drawRange = geometry.drawRange; - var i, j, il, jl; - var group, groupMaterial; - var start, end; + normalAttribute.setXYZ( i + 0, cb.x, cb.y, cb.z ); + normalAttribute.setXYZ( i + 1, cb.x, cb.y, cb.z ); + normalAttribute.setXYZ( i + 2, cb.x, cb.y, cb.z ); - if ( index !== null ) { + } - // indexed buffer geometry + } - if ( Array.isArray( material ) ) { + this.normalizeNormals(); - for ( i = 0, il = groups.length; i < il; i ++ ) { + normalAttribute.needsUpdate = true; - group = groups[ i ]; - groupMaterial = material[ group.materialIndex ]; + } - start = Math.max( group.start, drawRange.start ); - end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + } - for ( j = start, jl = end; j < jl; j += 3 ) { + normalizeNormals() { - a = index.getX( j ); - b = index.getX( j + 1 ); - c = index.getX( j + 2 ); + const normals = this.attributes.normal; - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, uv, uv2, a, b, c ); + for ( let i = 0, il = normals.count; i < il; i ++ ) { - if ( intersection ) { + _vector$7.fromBufferAttribute( normals, i ); - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); + _vector$7.normalize(); - } + normals.setXYZ( i, _vector$7.x, _vector$7.y, _vector$7.z ); - } + } - } + } - } else { + toNonIndexed() { - start = Math.max( 0, drawRange.start ); - end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); + function convertBufferAttribute( attribute, indices ) { - for ( i = start, il = end; i < il; i += 3 ) { + const array = attribute.array; + const itemSize = attribute.itemSize; + const normalized = attribute.normalized; - a = index.getX( i ); - b = index.getX( i + 1 ); - c = index.getX( i + 2 ); + const array2 = new array.constructor( indices.length * itemSize ); - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, uv, uv2, a, b, c ); + let index = 0, index2 = 0; - if ( intersection ) { + for ( let i = 0, l = indices.length; i < l; i ++ ) { - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics - intersects.push( intersection ); + if ( attribute.isInterleavedBufferAttribute ) { - } + index = indices[ i ] * attribute.data.stride + attribute.offset; - } + } else { - } + index = indices[ i ] * itemSize; - } else if ( position !== undefined ) { + } - // non-indexed buffer geometry + for ( let j = 0; j < itemSize; j ++ ) { - if ( Array.isArray( material ) ) { + array2[ index2 ++ ] = array[ index ++ ]; - for ( i = 0, il = groups.length; i < il; i ++ ) { + } - group = groups[ i ]; - groupMaterial = material[ group.materialIndex ]; + } - start = Math.max( group.start, drawRange.start ); - end = Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ); + return new BufferAttribute( array2, itemSize, normalized ); - for ( j = start, jl = end; j < jl; j += 3 ) { + } - a = j; - b = j + 1; - c = j + 2; + // - intersection = checkBufferGeometryIntersection( this, groupMaterial, raycaster, _ray, position, morphPosition, uv, uv2, a, b, c ); + if ( this.index === null ) { - if ( intersection ) { + console.warn( 'THREE.BufferGeometry.toNonIndexed(): BufferGeometry is already non-indexed.' ); + return this; - intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics - intersection.face.materialIndex = group.materialIndex; - intersects.push( intersection ); + } - } + const geometry2 = new BufferGeometry(); - } + const indices = this.index.array; + const attributes = this.attributes; - } + // attributes - } else { + for ( const name in attributes ) { - start = Math.max( 0, drawRange.start ); - end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); + const attribute = attributes[ name ]; - for ( i = start, il = end; i < il; i += 3 ) { + const newAttribute = convertBufferAttribute( attribute, indices ); - a = i; - b = i + 1; - c = i + 2; + geometry2.setAttribute( name, newAttribute ); - intersection = checkBufferGeometryIntersection( this, material, raycaster, _ray, position, morphPosition, uv, uv2, a, b, c ); + } - if ( intersection ) { + // morph attributes - intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics - intersects.push( intersection ); + const morphAttributes = this.morphAttributes; - } + for ( const name in morphAttributes ) { - } + const morphArray = []; + const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - } + for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) { - } + const attribute = morphAttribute[ i ]; - } else if ( geometry.isGeometry ) { + const newAttribute = convertBufferAttribute( attribute, indices ); - var fvA, fvB, fvC; - var isMultiMaterial = Array.isArray( material ); + morphArray.push( newAttribute ); - var vertices = geometry.vertices; - var faces = geometry.faces; - var uvs; + } - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; - if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; + geometry2.morphAttributes[ name ] = morphArray; - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + } - var face = faces[ f ]; - var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material; + geometry2.morphTargetsRelative = this.morphTargetsRelative; - if ( faceMaterial === undefined ) continue; + // groups - fvA = vertices[ face.a ]; - fvB = vertices[ face.b ]; - fvC = vertices[ face.c ]; + const groups = this.groups; - intersection = checkIntersection( this, faceMaterial, raycaster, _ray, fvA, fvB, fvC, _intersectionPoint ); + for ( let i = 0, l = groups.length; i < l; i ++ ) { - if ( intersection ) { + const group = groups[ i ]; + geometry2.addGroup( group.start, group.count, group.materialIndex ); - if ( uvs && uvs[ f ] ) { + } - var uvs_f = uvs[ f ]; - _uvA.copy( uvs_f[ 0 ] ); - _uvB.copy( uvs_f[ 1 ] ); - _uvC.copy( uvs_f[ 2 ] ); + return geometry2; - intersection.uv = Triangle.getUV( _intersectionPoint, fvA, fvB, fvC, _uvA, _uvB, _uvC, new Vector2() ); + } - } + toJSON() { - intersection.face = face; - intersection.faceIndex = f; - intersects.push( intersection ); + const data = { + metadata: { + version: 4.6, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; - } + // standard BufferGeometry serialization - } + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; + if ( Object.keys( this.userData ).length > 0 ) data.userData = this.userData; - } + if ( this.parameters !== undefined ) { - }, + const parameters = this.parameters; - clone: function () { + for ( const key in parameters ) { - return new this.constructor( this.geometry, this.material ).copy( this ); + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - } + } - } ); + return data; - function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { + } - var intersect; + // for simplicity the code assumes attributes are not shared across geometries, see #15811 - if ( material.side === BackSide ) { + data.data = { attributes: {} }; - intersect = ray.intersectTriangle( pC, pB, pA, true, point ); + const index = this.index; - } else { + if ( index !== null ) { - intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point ); + data.data.index = { + type: index.array.constructor.name, + array: Array.prototype.slice.call( index.array ) + }; } - if ( intersect === null ) return null; + const attributes = this.attributes; - _intersectionPointWorld.copy( point ); - _intersectionPointWorld.applyMatrix4( object.matrixWorld ); + for ( const key in attributes ) { - var distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); + const attribute = attributes[ key ]; - if ( distance < raycaster.near || distance > raycaster.far ) return null; + data.data.attributes[ key ] = attribute.toJSON( data.data ); - return { - distance: distance, - point: _intersectionPointWorld.clone(), - object: object - }; + } - } + const morphAttributes = {}; + let hasMorphAttributes = false; - function checkBufferGeometryIntersection( object, material, raycaster, ray, position, morphPosition, uv, uv2, a, b, c ) { + for ( const key in this.morphAttributes ) { - _vA.fromBufferAttribute( position, a ); - _vB.fromBufferAttribute( position, b ); - _vC.fromBufferAttribute( position, c ); + const attributeArray = this.morphAttributes[ key ]; - var morphInfluences = object.morphTargetInfluences; + const array = []; - if ( material.morphTargets && morphPosition && morphInfluences ) { + for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - _morphA.set( 0, 0, 0 ); - _morphB.set( 0, 0, 0 ); - _morphC.set( 0, 0, 0 ); + const attribute = attributeArray[ i ]; - for ( var i = 0, il = morphPosition.length; i < il; i ++ ) { + array.push( attribute.toJSON( data.data ) ); - var influence = morphInfluences[ i ]; - var morphAttribute = morphPosition[ i ]; + } - if ( influence === 0 ) continue; + if ( array.length > 0 ) { - _tempA.fromBufferAttribute( morphAttribute, a ); - _tempB.fromBufferAttribute( morphAttribute, b ); - _tempC.fromBufferAttribute( morphAttribute, c ); + morphAttributes[ key ] = array; - _morphA.addScaledVector( _tempA.sub( _vA ), influence ); - _morphB.addScaledVector( _tempB.sub( _vB ), influence ); - _morphC.addScaledVector( _tempC.sub( _vC ), influence ); + hasMorphAttributes = true; } - _vA.add( _morphA ); - _vB.add( _morphB ); - _vC.add( _morphC ); - } - var intersection = checkIntersection( object, material, raycaster, ray, _vA, _vB, _vC, _intersectionPoint ); - - if ( intersection ) { + if ( hasMorphAttributes ) { - if ( uv ) { + data.data.morphAttributes = morphAttributes; + data.data.morphTargetsRelative = this.morphTargetsRelative; - _uvA.fromBufferAttribute( uv, a ); - _uvB.fromBufferAttribute( uv, b ); - _uvC.fromBufferAttribute( uv, c ); - - intersection.uv = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); + } - } + const groups = this.groups; - if ( uv2 ) { + if ( groups.length > 0 ) { - _uvA.fromBufferAttribute( uv2, a ); - _uvB.fromBufferAttribute( uv2, b ); - _uvC.fromBufferAttribute( uv2, c ); + data.data.groups = JSON.parse( JSON.stringify( groups ) ); - intersection.uv2 = Triangle.getUV( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ); + } - } + const boundingSphere = this.boundingSphere; - var face = new Face3( a, b, c ); - Triangle.getNormal( _vA, _vB, _vC, face.normal ); + if ( boundingSphere !== null ) { - intersection.face = face; + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; } - return intersection; + return data; } - /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://clara.io - */ - - var _geometryId = 0; // Geometry uses even numbers as Id - var _m1$3 = new Matrix4(); - var _obj$1 = new Object3D(); - var _offset$1 = new Vector3(); + clone() { - function Geometry() { + return new this.constructor().copy( this ); - Object.defineProperty( this, 'id', { value: _geometryId += 2 } ); + } - this.uuid = _Math.generateUUID(); + copy( source ) { - this.name = ''; - this.type = 'Geometry'; + // reset - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; + this.index = null; + this.attributes = {}; + this.morphAttributes = {}; + this.groups = []; + this.boundingBox = null; + this.boundingSphere = null; - this.morphTargets = []; - this.morphNormals = []; + // used for storing cloned, shared data - this.skinWeights = []; - this.skinIndices = []; + const data = {}; - this.lineDistances = []; + // name - this.boundingBox = null; - this.boundingSphere = null; + this.name = source.name; - // update flags + // index - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; + const index = source.index; - } + if ( index !== null ) { - Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + this.setIndex( index.clone( data ) ); - constructor: Geometry, + } - isGeometry: true, + // attributes - applyMatrix: function ( matrix ) { + const attributes = source.attributes; - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + for ( const name in attributes ) { - for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + const attribute = attributes[ name ]; + this.setAttribute( name, attribute.clone( data ) ); - var vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); + } - } + // morph attributes - for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + const morphAttributes = source.morphAttributes; - var face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); + for ( const name in morphAttributes ) { - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + const array = []; + const morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + for ( let i = 0, l = morphAttribute.length; i < l; i ++ ) { - } + array.push( morphAttribute[ i ].clone( data ) ); } - if ( this.boundingBox !== null ) { + this.morphAttributes[ name ] = array; - this.computeBoundingBox(); + } - } + this.morphTargetsRelative = source.morphTargetsRelative; - if ( this.boundingSphere !== null ) { + // groups - this.computeBoundingSphere(); + const groups = source.groups; - } + for ( let i = 0, l = groups.length; i < l; i ++ ) { - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; + const group = groups[ i ]; + this.addGroup( group.start, group.count, group.materialIndex ); - return this; + } - }, + // bounding box - rotateX: function ( angle ) { + const boundingBox = source.boundingBox; - // rotate geometry around world x-axis + if ( boundingBox !== null ) { - _m1$3.makeRotationX( angle ); + this.boundingBox = boundingBox.clone(); - this.applyMatrix( _m1$3 ); + } - return this; + // bounding sphere - }, + const boundingSphere = source.boundingSphere; - rotateY: function ( angle ) { + if ( boundingSphere !== null ) { - // rotate geometry around world y-axis + this.boundingSphere = boundingSphere.clone(); - _m1$3.makeRotationY( angle ); + } - this.applyMatrix( _m1$3 ); + // draw range - return this; + this.drawRange.start = source.drawRange.start; + this.drawRange.count = source.drawRange.count; - }, + // user data - rotateZ: function ( angle ) { + this.userData = source.userData; - // rotate geometry around world z-axis + return this; - _m1$3.makeRotationZ( angle ); + } - this.applyMatrix( _m1$3 ); + dispose() { - return this; + this.dispatchEvent( { type: 'dispose' } ); - }, + } - translate: function ( x, y, z ) { +} - // translate geometry +const _inverseMatrix$3 = /*@__PURE__*/ new Matrix4(); +const _ray$3 = /*@__PURE__*/ new Ray(); +const _sphere$5 = /*@__PURE__*/ new Sphere(); +const _sphereHitAt = /*@__PURE__*/ new Vector3(); - _m1$3.makeTranslation( x, y, z ); +const _vA$1 = /*@__PURE__*/ new Vector3(); +const _vB$1 = /*@__PURE__*/ new Vector3(); +const _vC$1 = /*@__PURE__*/ new Vector3(); - this.applyMatrix( _m1$3 ); +const _tempA = /*@__PURE__*/ new Vector3(); +const _morphA = /*@__PURE__*/ new Vector3(); - return this; +const _uvA$1 = /*@__PURE__*/ new Vector2(); +const _uvB$1 = /*@__PURE__*/ new Vector2(); +const _uvC$1 = /*@__PURE__*/ new Vector2(); - }, +const _normalA = /*@__PURE__*/ new Vector3(); +const _normalB = /*@__PURE__*/ new Vector3(); +const _normalC = /*@__PURE__*/ new Vector3(); - scale: function ( x, y, z ) { +const _intersectionPoint = /*@__PURE__*/ new Vector3(); +const _intersectionPointWorld = /*@__PURE__*/ new Vector3(); - // scale geometry +class Mesh extends Object3D { - _m1$3.makeScale( x, y, z ); + constructor( geometry = new BufferGeometry(), material = new MeshBasicMaterial() ) { - this.applyMatrix( _m1$3 ); + super(); - return this; + this.isMesh = true; - }, + this.type = 'Mesh'; - lookAt: function ( vector ) { + this.geometry = geometry; + this.material = material; - _obj$1.lookAt( vector ); + this.updateMorphTargets(); - _obj$1.updateMatrix(); + } - this.applyMatrix( _obj$1.matrix ); + copy( source, recursive ) { - return this; + super.copy( source, recursive ); - }, + if ( source.morphTargetInfluences !== undefined ) { - fromBufferGeometry: function ( geometry ) { + this.morphTargetInfluences = source.morphTargetInfluences.slice(); - var scope = this; + } - var indices = geometry.index !== null ? geometry.index.array : undefined; - var attributes = geometry.attributes; + if ( source.morphTargetDictionary !== undefined ) { - var positions = attributes.position.array; - var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; - var colors = attributes.color !== undefined ? attributes.color.array : undefined; - var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; - var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; + this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary ); - if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; + } - for ( var i = 0; i < positions.length; i += 3 ) { + this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; + this.geometry = source.geometry; - scope.vertices.push( new Vector3().fromArray( positions, i ) ); + return this; - if ( colors !== undefined ) { + } - scope.colors.push( new Color().fromArray( colors, i ) ); + updateMorphTargets() { - } + const geometry = this.geometry; - } + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - function addFace( a, b, c, materialIndex ) { + if ( keys.length > 0 ) { - var vertexColors = ( colors === undefined ) ? [] : [ - scope.colors[ a ].clone(), - scope.colors[ b ].clone(), - scope.colors[ c ].clone() ]; + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - var vertexNormals = ( normals === undefined ) ? [] : [ - new Vector3().fromArray( normals, a * 3 ), - new Vector3().fromArray( normals, b * 3 ), - new Vector3().fromArray( normals, c * 3 ) - ]; + if ( morphAttribute !== undefined ) { - var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - scope.faces.push( face ); + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - if ( uvs !== undefined ) { + const name = morphAttribute[ m ].name || String( m ); - scope.faceVertexUvs[ 0 ].push( [ - new Vector2().fromArray( uvs, a * 2 ), - new Vector2().fromArray( uvs, b * 2 ), - new Vector2().fromArray( uvs, c * 2 ) - ] ); + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; } - if ( uvs2 !== undefined ) { + } + + } - scope.faceVertexUvs[ 1 ].push( [ - new Vector2().fromArray( uvs2, a * 2 ), - new Vector2().fromArray( uvs2, b * 2 ), - new Vector2().fromArray( uvs2, c * 2 ) - ] ); + } - } + getVertexPosition( index, target ) { - } + const geometry = this.geometry; + const position = geometry.attributes.position; + const morphPosition = geometry.morphAttributes.position; + const morphTargetsRelative = geometry.morphTargetsRelative; - var groups = geometry.groups; + target.fromBufferAttribute( position, index ); - if ( groups.length > 0 ) { + const morphInfluences = this.morphTargetInfluences; - for ( var i = 0; i < groups.length; i ++ ) { + if ( morphPosition && morphInfluences ) { - var group = groups[ i ]; + _morphA.set( 0, 0, 0 ); - var start = group.start; - var count = group.count; + for ( let i = 0, il = morphPosition.length; i < il; i ++ ) { - for ( var j = start, jl = start + count; j < jl; j += 3 ) { + const influence = morphInfluences[ i ]; + const morphAttribute = morphPosition[ i ]; - if ( indices !== undefined ) { + if ( influence === 0 ) continue; - addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); + _tempA.fromBufferAttribute( morphAttribute, index ); - } else { + if ( morphTargetsRelative ) { - addFace( j, j + 1, j + 2, group.materialIndex ); + _morphA.addScaledVector( _tempA, influence ); - } + } else { - } + _morphA.addScaledVector( _tempA.sub( target ), influence ); } - } else { - - if ( indices !== undefined ) { + } - for ( var i = 0; i < indices.length; i += 3 ) { + target.add( _morphA ); - addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + } - } + return target; - } else { + } - for ( var i = 0; i < positions.length / 3; i += 3 ) { + raycast( raycaster, intersects ) { - addFace( i, i + 1, i + 2 ); + const geometry = this.geometry; + const material = this.material; + const matrixWorld = this.matrixWorld; - } + if ( material === undefined ) return; - } + // test with bounding sphere in world space - } + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - this.computeFaceNormals(); + _sphere$5.copy( geometry.boundingSphere ); + _sphere$5.applyMatrix4( matrixWorld ); - if ( geometry.boundingBox !== null ) { + // check distance from ray origin to bounding sphere - this.boundingBox = geometry.boundingBox.clone(); + _ray$3.copy( raycaster.ray ).recast( raycaster.near ); - } + if ( _sphere$5.containsPoint( _ray$3.origin ) === false ) { - if ( geometry.boundingSphere !== null ) { + if ( _ray$3.intersectSphere( _sphere$5, _sphereHitAt ) === null ) return; - this.boundingSphere = geometry.boundingSphere.clone(); + if ( _ray$3.origin.distanceToSquared( _sphereHitAt ) > ( raycaster.far - raycaster.near ) ** 2 ) return; - } + } - return this; + // convert ray to local space of mesh - }, + _inverseMatrix$3.copy( matrixWorld ).invert(); + _ray$3.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$3 ); - center: function () { + // test with bounding box in local space - this.computeBoundingBox(); + if ( geometry.boundingBox !== null ) { - this.boundingBox.getCenter( _offset$1 ).negate(); + if ( _ray$3.intersectsBox( geometry.boundingBox ) === false ) return; - this.translate( _offset$1.x, _offset$1.y, _offset$1.z ); + } - return this; + // test for intersections with geometry - }, + this._computeIntersections( raycaster, intersects, _ray$3 ); - normalize: function () { + } - this.computeBoundingSphere(); + _computeIntersections( raycaster, intersects, rayLocalSpace ) { - var center = this.boundingSphere.center; - var radius = this.boundingSphere.radius; + let intersection; - var s = radius === 0 ? 1 : 1.0 / radius; + const geometry = this.geometry; + const material = this.material; - var matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); + const index = geometry.index; + const position = geometry.attributes.position; + const uv = geometry.attributes.uv; + const uv1 = geometry.attributes.uv1; + const normal = geometry.attributes.normal; + const groups = geometry.groups; + const drawRange = geometry.drawRange; - this.applyMatrix( matrix ); + if ( index !== null ) { - return this; + // indexed buffer geometry - }, + if ( Array.isArray( material ) ) { - computeFaceNormals: function () { + for ( let i = 0, il = groups.length; i < il; i ++ ) { - var cb = new Vector3(), ab = new Vector3(); + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + const start = Math.max( group.start, drawRange.start ); + const end = Math.min( index.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); - var face = this.faces[ f ]; + for ( let j = start, jl = end; j < jl; j += 3 ) { - var vA = this.vertices[ face.a ]; - var vB = this.vertices[ face.b ]; - var vC = this.vertices[ face.c ]; + const a = index.getX( j ); + const b = index.getX( j + 1 ); + const c = index.getX( j + 2 ); - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); - cb.normalize(); + if ( intersection ) { - face.normal.copy( cb ); + intersection.faceIndex = Math.floor( j / 3 ); // triangle number in indexed buffer semantics + intersection.face.materialIndex = group.materialIndex; + intersects.push( intersection ); - } + } - }, + } - computeVertexNormals: function ( areaWeighted ) { + } - if ( areaWeighted === undefined ) areaWeighted = true; + } else { - var v, vl, f, fl, face, vertices; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - vertices = new Array( this.vertices.length ); + for ( let i = start, il = end; i < il; i += 3 ) { - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + const a = index.getX( i ); + const b = index.getX( i + 1 ); + const c = index.getX( i + 2 ); - vertices[ v ] = new Vector3(); + intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); - } + if ( intersection ) { - if ( areaWeighted ) { + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indexed buffer semantics + intersects.push( intersection ); - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm + } - var vA, vB, vC; - var cb = new Vector3(), ab = new Vector3(); + } - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + } - face = this.faces[ f ]; + } else if ( position !== undefined ) { - vA = this.vertices[ face.a ]; - vB = this.vertices[ face.b ]; - vC = this.vertices[ face.c ]; + // non-indexed buffer geometry - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + if ( Array.isArray( material ) ) { - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); + for ( let i = 0, il = groups.length; i < il; i ++ ) { - } + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - } else { + const start = Math.max( group.start, drawRange.start ); + const end = Math.min( position.count, Math.min( ( group.start + group.count ), ( drawRange.start + drawRange.count ) ) ); - this.computeFaceNormals(); + for ( let j = start, jl = end; j < jl; j += 3 ) { - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + const a = j; + const b = j + 1; + const c = j + 2; - face = this.faces[ f ]; + intersection = checkGeometryIntersection( this, groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); + if ( intersection ) { - } + intersection.faceIndex = Math.floor( j / 3 ); // triangle number in non-indexed buffer semantics + intersection.face.materialIndex = group.materialIndex; + intersects.push( intersection ); - } + } - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + } - vertices[ v ].normalize(); + } - } + } else { - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + const start = Math.max( 0, drawRange.start ); + const end = Math.min( position.count, ( drawRange.start + drawRange.count ) ); - face = this.faces[ f ]; + for ( let i = start, il = end; i < il; i += 3 ) { - var vertexNormals = face.vertexNormals; + const a = i; + const b = i + 1; + const c = i + 2; - if ( vertexNormals.length === 3 ) { + intersection = checkGeometryIntersection( this, material, raycaster, rayLocalSpace, uv, uv1, normal, a, b, c ); - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); + if ( intersection ) { - } else { + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in non-indexed buffer semantics + intersects.push( intersection ); - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); + } } } - if ( this.faces.length > 0 ) { + } - this.normalsNeedUpdate = true; + } - } +} - }, +function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) { - computeFlatVertexNormals: function () { + let intersect; - var f, fl, face; + if ( material.side === BackSide ) { - this.computeFaceNormals(); + intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + } else { - face = this.faces[ f ]; + intersect = ray.intersectTriangle( pA, pB, pC, ( material.side === FrontSide ), point ); - var vertexNormals = face.vertexNormals; + } - if ( vertexNormals.length === 3 ) { + if ( intersect === null ) return null; - vertexNormals[ 0 ].copy( face.normal ); - vertexNormals[ 1 ].copy( face.normal ); - vertexNormals[ 2 ].copy( face.normal ); + _intersectionPointWorld.copy( point ); + _intersectionPointWorld.applyMatrix4( object.matrixWorld ); - } else { + const distance = raycaster.ray.origin.distanceTo( _intersectionPointWorld ); - vertexNormals[ 0 ] = face.normal.clone(); - vertexNormals[ 1 ] = face.normal.clone(); - vertexNormals[ 2 ] = face.normal.clone(); + if ( distance < raycaster.near || distance > raycaster.far ) return null; - } + return { + distance: distance, + point: _intersectionPointWorld.clone(), + object: object + }; - } +} - if ( this.faces.length > 0 ) { +function checkGeometryIntersection( object, material, raycaster, ray, uv, uv1, normal, a, b, c ) { - this.normalsNeedUpdate = true; + object.getVertexPosition( a, _vA$1 ); + object.getVertexPosition( b, _vB$1 ); + object.getVertexPosition( c, _vC$1 ); - } + const intersection = checkIntersection( object, material, raycaster, ray, _vA$1, _vB$1, _vC$1, _intersectionPoint ); - }, + if ( intersection ) { - computeMorphNormals: function () { + if ( uv ) { - var i, il, f, fl, face; + _uvA$1.fromBufferAttribute( uv, a ); + _uvB$1.fromBufferAttribute( uv, b ); + _uvC$1.fromBufferAttribute( uv, c ); - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) + intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + } - face = this.faces[ f ]; + if ( uv1 ) { - if ( ! face.__originalFaceNormal ) { + _uvA$1.fromBufferAttribute( uv1, a ); + _uvB$1.fromBufferAttribute( uv1, b ); + _uvC$1.fromBufferAttribute( uv1, c ); - face.__originalFaceNormal = face.normal.clone(); + intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ); + intersection.uv2 = intersection.uv1; // @deprecated, r152 - } else { + } - face.__originalFaceNormal.copy( face.normal ); + if ( normal ) { - } + _normalA.fromBufferAttribute( normal, a ); + _normalB.fromBufferAttribute( normal, b ); + _normalC.fromBufferAttribute( normal, c ); - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA$1, _vB$1, _vC$1, _normalA, _normalB, _normalC, new Vector3() ); - for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + if ( intersection.normal.dot( ray.direction ) > 0 ) { - if ( ! face.__originalVertexNormals[ i ] ) { + intersection.normal.multiplyScalar( - 1 ); - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + } - } else { + } - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + const face = { + a: a, + b: b, + c: c, + normal: new Vector3(), + materialIndex: 0 + }; - } + Triangle.getNormal( _vA$1, _vB$1, _vC$1, face.normal ); - } + intersection.face = face; - } + } - // use temp geometry to compute face and vertex normals for each morph + return intersection; - var tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; +} - for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { +class BoxGeometry extends BufferGeometry { - // create on first access + constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) { - if ( ! this.morphNormals[ i ] ) { + super(); - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; + this.type = 'BoxGeometry'; - var dstNormalsFace = this.morphNormals[ i ].faceNormals; - var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; - var faceNormal, vertexNormals; + const scope = this; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + // segments - faceNormal = new Vector3(); - vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; + widthSegments = Math.floor( widthSegments ); + heightSegments = Math.floor( heightSegments ); + depthSegments = Math.floor( depthSegments ); - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); + // buffers - } + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - } + // helper variables - var morphNormals = this.morphNormals[ i ]; + let numberOfVertices = 0; + let groupStart = 0; - // set vertices to morph target + // build each side of the box geometry - tmpGeo.vertices = this.morphTargets[ i ].vertices; + buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px + buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx + buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py + buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny + buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz + buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz - // compute morph normals + // build geometry - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - // store morph normals + function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - var faceNormal, vertexNormals; + const segmentWidth = width / gridX; + const segmentHeight = height / gridY; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + const widthHalf = width / 2; + const heightHalf = height / 2; + const depthHalf = depth / 2; - face = this.faces[ f ]; + const gridX1 = gridX + 1; + const gridY1 = gridY + 1; - faceNormal = morphNormals.faceNormals[ f ]; - vertexNormals = morphNormals.vertexNormals[ f ]; + let vertexCounter = 0; + let groupCount = 0; - faceNormal.copy( face.normal ); + const vector = new Vector3(); - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + // generate vertices, normals and uvs - } + for ( let iy = 0; iy < gridY1; iy ++ ) { - } + const y = iy * segmentHeight - heightHalf; - // restore original normals + for ( let ix = 0; ix < gridX1; ix ++ ) { - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + const x = ix * segmentWidth - widthHalf; - face = this.faces[ f ]; + // set values to correct vector component - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; + vector[ u ] = x * udir; + vector[ v ] = y * vdir; + vector[ w ] = depthHalf; - } + // now apply vector to vertex buffer - }, + vertices.push( vector.x, vector.y, vector.z ); - computeBoundingBox: function () { + // set values to correct vector component - if ( this.boundingBox === null ) { + vector[ u ] = 0; + vector[ v ] = 0; + vector[ w ] = depth > 0 ? 1 : - 1; - this.boundingBox = new Box3(); + // now apply vector to normal buffer - } + normals.push( vector.x, vector.y, vector.z ); - this.boundingBox.setFromPoints( this.vertices ); + // uvs - }, + uvs.push( ix / gridX ); + uvs.push( 1 - ( iy / gridY ) ); - computeBoundingSphere: function () { + // counters - if ( this.boundingSphere === null ) { + vertexCounter += 1; - this.boundingSphere = new Sphere(); + } } - this.boundingSphere.setFromPoints( this.vertices ); + // indices - }, + // 1. you need three indices to draw a single face + // 2. a single segment consists of two faces + // 3. so we need to generate six (2*3) indices per segment - merge: function ( geometry, matrix, materialIndexOffset ) { + for ( let iy = 0; iy < gridY; iy ++ ) { - if ( ! ( geometry && geometry.isGeometry ) ) { + for ( let ix = 0; ix < gridX; ix ++ ) { - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; + const a = numberOfVertices + ix + gridX1 * iy; + const b = numberOfVertices + ix + gridX1 * ( iy + 1 ); + const c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); + const d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - } + // faces - var normalMatrix, - vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - colors1 = this.colors, - colors2 = geometry.colors; + indices.push( a, b, d ); + indices.push( b, c, d ); - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + // increase counter - if ( matrix !== undefined ) { + groupCount += 6; - normalMatrix = new Matrix3().getNormalMatrix( matrix ); + } } - // vertices + // add a group to the geometry. this will ensure multi material support + + scope.addGroup( groupStart, groupCount, materialIndex ); - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + // calculate new start value for groups - var vertex = vertices2[ i ]; + groupStart += groupCount; - var vertexCopy = vertex.clone(); + // update total number of vertices - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + numberOfVertices += vertexCounter; - vertices1.push( vertexCopy ); + } - } + } - // colors + copy( source ) { - for ( var i = 0, il = colors2.length; i < il; i ++ ) { + super.copy( source ); - colors1.push( colors2[ i ].clone() ); + this.parameters = Object.assign( {}, source.parameters ); - } + return this; - // faces + } - for ( i = 0, il = faces2.length; i < il; i ++ ) { + static fromJSON( data ) { - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; + return new BoxGeometry( data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments ); - faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); + } - if ( normalMatrix !== undefined ) { +} - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); +/** + * Uniform Utilities + */ - } +function cloneUniforms( src ) { - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + const dst = {}; - normal = faceVertexNormals[ j ].clone(); + for ( const u in src ) { - if ( normalMatrix !== undefined ) { + dst[ u ] = {}; - normal.applyMatrix3( normalMatrix ).normalize(); + for ( const p in src[ u ] ) { - } + const property = src[ u ][ p ]; - faceCopy.vertexNormals.push( normal ); + if ( property && ( property.isColor || + property.isMatrix3 || property.isMatrix4 || + property.isVector2 || property.isVector3 || property.isVector4 || + property.isTexture || property.isQuaternion ) ) { - } + if ( property.isRenderTargetTexture ) { - faceCopy.color.copy( face.color ); + console.warn( 'UniformsUtils: Textures of render targets cannot be cloned via cloneUniforms() or mergeUniforms().' ); + dst[ u ][ p ] = null; - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + } else { - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); + dst[ u ][ p ] = property.clone(); } - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + } else if ( Array.isArray( property ) ) { - faces1.push( faceCopy ); + dst[ u ][ p ] = property.slice(); - } + } else { - // uvs + dst[ u ][ p ] = property; - for ( var i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { + } - var faceVertexUvs2 = geometry.faceVertexUvs[ i ]; + } - if ( this.faceVertexUvs[ i ] === undefined ) this.faceVertexUvs[ i ] = []; + } - for ( var j = 0, jl = faceVertexUvs2.length; j < jl; j ++ ) { + return dst; - var uvs2 = faceVertexUvs2[ j ], uvsCopy = []; +} - for ( var k = 0, kl = uvs2.length; k < kl; k ++ ) { +function mergeUniforms( uniforms ) { - uvsCopy.push( uvs2[ k ].clone() ); + const merged = {}; - } + for ( let u = 0; u < uniforms.length; u ++ ) { - this.faceVertexUvs[ i ].push( uvsCopy ); + const tmp = cloneUniforms( uniforms[ u ] ); - } + for ( const p in tmp ) { - } + merged[ p ] = tmp[ p ]; - }, + } - mergeMesh: function ( mesh ) { + } - if ( ! ( mesh && mesh.isMesh ) ) { + return merged; - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; +} - } +function cloneUniformsGroups( src ) { - if ( mesh.matrixAutoUpdate ) mesh.updateMatrix(); + const dst = []; - this.merge( mesh.geometry, mesh.matrix ); + for ( let u = 0; u < src.length; u ++ ) { - }, + dst.push( src[ u ].clone() ); - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ + } - mergeVertices: function () { + return dst; - var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - var unique = [], changes = []; +} - var v, key; - var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - var precision = Math.pow( 10, precisionPoints ); - var i, il, face; - var indices, j, jl; +function getUnlitUniformColorSpace( renderer ) { - for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + if ( renderer.getRenderTarget() === null ) { - v = this.vertices[ i ]; - key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + // https://github.com/mrdoob/three.js/pull/23937#issuecomment-1111067398 + return renderer.outputColorSpace; - if ( verticesMap[ key ] === undefined ) { + } - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; + return ColorManagement.workingColorSpace; - } else { +} - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; +// Legacy - } +const UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; - } +var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; +var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - var faceIndicesToRemove = []; +class ShaderMaterial extends Material { - for ( i = 0, il = this.faces.length; i < il; i ++ ) { + constructor( parameters ) { - face = this.faces[ i ]; + super(); - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; + this.isShaderMaterial = true; - indices = [ face.a, face.b, face.c ]; + this.type = 'ShaderMaterial'; - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( var n = 0; n < 3; n ++ ) { + this.defines = {}; + this.uniforms = {}; + this.uniformsGroups = []; - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { + this.vertexShader = default_vertex; + this.fragmentShader = default_fragment; - faceIndicesToRemove.push( i ); - break; + this.linewidth = 1; - } + this.wireframe = false; + this.wireframeLinewidth = 1; - } + this.fog = false; // set to use scene fog + this.lights = false; // set to use scene lights + this.clipping = false; // set to use user-defined clipping planes - } + this.forceSinglePass = true; - for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + this.extensions = { + derivatives: false, // set to use derivatives + fragDepth: false, // set to use fragment depth values + drawBuffers: false, // set to use draw buffers + shaderTextureLOD: false // set to use shader texture LOD + }; - var idx = faceIndicesToRemove[ i ]; + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv1': [ 0, 0 ] + }; - this.faces.splice( idx, 1 ); + this.index0AttributeName = undefined; + this.uniformsNeedUpdate = false; - for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + this.glslVersion = null; - this.faceVertexUvs[ j ].splice( idx, 1 ); + if ( parameters !== undefined ) { - } + this.setValues( parameters ); - } + } - // Use unique set of vertices + } - var diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; + copy( source ) { - }, + super.copy( source ); - setFromPoints: function ( points ) { + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; - this.vertices = []; + this.uniforms = cloneUniforms( source.uniforms ); + this.uniformsGroups = cloneUniformsGroups( source.uniformsGroups ); - for ( var i = 0, l = points.length; i < l; i ++ ) { + this.defines = Object.assign( {}, source.defines ); - var point = points[ i ]; - this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - } + this.fog = source.fog; + this.lights = source.lights; + this.clipping = source.clipping; - return this; + this.extensions = Object.assign( {}, source.extensions ); - }, + this.glslVersion = source.glslVersion; - sortFacesByMaterialIndex: function () { + return this; - var faces = this.faces; - var length = faces.length; + } - // tag faces + toJSON( meta ) { - for ( var i = 0; i < length; i ++ ) { + const data = super.toJSON( meta ); - faces[ i ]._id = i; + data.glslVersion = this.glslVersion; + data.uniforms = {}; - } + for ( const name in this.uniforms ) { - // sort faces + const uniform = this.uniforms[ name ]; + const value = uniform.value; - function materialIndexSort( a, b ) { + if ( value && value.isTexture ) { - return a.materialIndex - b.materialIndex; + data.uniforms[ name ] = { + type: 't', + value: value.toJSON( meta ).uuid + }; - } + } else if ( value && value.isColor ) { - faces.sort( materialIndexSort ); + data.uniforms[ name ] = { + type: 'c', + value: value.getHex() + }; - // sort uvs + } else if ( value && value.isVector2 ) { - var uvs1 = this.faceVertexUvs[ 0 ]; - var uvs2 = this.faceVertexUvs[ 1 ]; + data.uniforms[ name ] = { + type: 'v2', + value: value.toArray() + }; - var newUvs1, newUvs2; + } else if ( value && value.isVector3 ) { - if ( uvs1 && uvs1.length === length ) newUvs1 = []; - if ( uvs2 && uvs2.length === length ) newUvs2 = []; + data.uniforms[ name ] = { + type: 'v3', + value: value.toArray() + }; - for ( var i = 0; i < length; i ++ ) { + } else if ( value && value.isVector4 ) { - var id = faces[ i ]._id; + data.uniforms[ name ] = { + type: 'v4', + value: value.toArray() + }; - if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); - if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); + } else if ( value && value.isMatrix3 ) { - } + data.uniforms[ name ] = { + type: 'm3', + value: value.toArray() + }; - if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; - if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; + } else if ( value && value.isMatrix4 ) { - }, + data.uniforms[ name ] = { + type: 'm4', + value: value.toArray() + }; - toJSON: function () { + } else { - var data = { - metadata: { - version: 4.5, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; + data.uniforms[ name ] = { + value: value + }; - // standard Geometry serialization + // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; + } - if ( this.parameters !== undefined ) { + } - var parameters = this.parameters; + if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; - for ( var key in parameters ) { + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + data.lights = this.lights; + data.clipping = this.clipping; - } + const extensions = {}; - return data; + for ( const key in this.extensions ) { - } + if ( this.extensions[ key ] === true ) extensions[ key ] = true; - var vertices = []; + } - for ( var i = 0; i < this.vertices.length; i ++ ) { + if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; - var vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + return data; - } + } - var faces = []; - var normals = []; - var normalsHash = {}; - var colors = []; - var colorsHash = {}; - var uvs = []; - var uvsHash = {}; +} - for ( var i = 0; i < this.faces.length; i ++ ) { +class Camera extends Object3D { - var face = this.faces[ i ]; + constructor() { - var hasMaterial = true; - var hasFaceUv = false; // deprecated - var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - var hasFaceNormal = face.normal.length() > 0; - var hasFaceVertexNormal = face.vertexNormals.length > 0; - var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - var hasFaceVertexColor = face.vertexColors.length > 0; + super(); - var faceType = 0; + this.isCamera = true; - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); + this.type = 'Camera'; - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); + this.matrixWorldInverse = new Matrix4(); - if ( hasFaceVertexUv ) { + this.projectionMatrix = new Matrix4(); + this.projectionMatrixInverse = new Matrix4(); - var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + this.coordinateSystem = WebGLCoordinateSystem; - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); + } - } + copy( source, recursive ) { - if ( hasFaceNormal ) { + super.copy( source, recursive ); - faces.push( getNormalIndex( face.normal ) ); + this.matrixWorldInverse.copy( source.matrixWorldInverse ); - } + this.projectionMatrix.copy( source.projectionMatrix ); + this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); - if ( hasFaceVertexNormal ) { + this.coordinateSystem = source.coordinateSystem; - var vertexNormals = face.vertexNormals; + return this; - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); + } - } + getWorldDirection( target ) { - if ( hasFaceColor ) { + return super.getWorldDirection( target ).negate(); - faces.push( getColorIndex( face.color ) ); + } - } + updateMatrixWorld( force ) { - if ( hasFaceVertexColor ) { + super.updateMatrixWorld( force ); - var vertexColors = face.vertexColors; + this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); + } - } + updateWorldMatrix( updateParents, updateChildren ) { - } + super.updateWorldMatrix( updateParents, updateChildren ); - function setBit( value, position, enabled ) { + this.matrixWorldInverse.copy( this.matrixWorld ).invert(); - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); + } - } + clone() { - function getNormalIndex( normal ) { + return new this.constructor().copy( this ); - var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + } - if ( normalsHash[ hash ] !== undefined ) { +} - return normalsHash[ hash ]; +class PerspectiveCamera extends Camera { - } + constructor( fov = 50, aspect = 1, near = 0.1, far = 2000 ) { - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); + super(); - return normalsHash[ hash ]; + this.isPerspectiveCamera = true; - } + this.type = 'PerspectiveCamera'; - function getColorIndex( color ) { + this.fov = fov; + this.zoom = 1; - var hash = color.r.toString() + color.g.toString() + color.b.toString(); + this.near = near; + this.far = far; + this.focus = 10; - if ( colorsHash[ hash ] !== undefined ) { + this.aspect = aspect; + this.view = null; - return colorsHash[ hash ]; + this.filmGauge = 35; // width of the film (default in millimeters) + this.filmOffset = 0; // horizontal film offset (same unit as gauge) - } + this.updateProjectionMatrix(); - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); + } - return colorsHash[ hash ]; + copy( source, recursive ) { - } + super.copy( source, recursive ); - function getUvIndex( uv ) { + this.fov = source.fov; + this.zoom = source.zoom; - var hash = uv.x.toString() + uv.y.toString(); + this.near = source.near; + this.far = source.far; + this.focus = source.focus; - if ( uvsHash[ hash ] !== undefined ) { + this.aspect = source.aspect; + this.view = source.view === null ? null : Object.assign( {}, source.view ); - return uvsHash[ hash ]; + this.filmGauge = source.filmGauge; + this.filmOffset = source.filmOffset; - } + return this; - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); + } - return uvsHash[ hash ]; + /** + * Sets the FOV by focal length in respect to the current .filmGauge. + * + * The default film gauge is 35, so that the focal length can be specified for + * a 35mm (full frame) camera. + * + * Values for focal length and film gauge must have the same unit. + */ + setFocalLength( focalLength ) { - } + /** see {@link http://www.bobatkins.com/photography/technical/field_of_view.html} */ + const vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - data.data = {}; + this.fov = RAD2DEG * 2 * Math.atan( vExtentSlope ); + this.updateProjectionMatrix(); - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) data.data.colors = colors; - if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility - data.data.faces = faces; + } - return data; + /** + * Calculates the focal length from the current .fov and .filmGauge. + */ + getFocalLength() { - }, + const vExtentSlope = Math.tan( DEG2RAD * 0.5 * this.fov ); - clone: function () { + return 0.5 * this.getFilmHeight() / vExtentSlope; - /* - // Handle primitives + } - var parameters = this.parameters; + getEffectiveFOV() { - if ( parameters !== undefined ) { + return RAD2DEG * 2 * Math.atan( + Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom ); - var values = []; + } - for ( var key in parameters ) { + getFilmWidth() { - values.push( parameters[ key ] ); + // film not completely covered in portrait format (aspect < 1) + return this.filmGauge * Math.min( this.aspect, 1 ); - } + } - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; + getFilmHeight() { - } + // film not completely covered in landscape format (aspect > 1) + return this.filmGauge / Math.max( this.aspect, 1 ); - return new this.constructor().copy( this ); - */ + } - return new Geometry().copy( this ); + /** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * const w = 1920; + * const h = 1080; + * const fullWidth = w * 3; + * const fullHeight = h * 2; + * + * --A-- + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - }, + this.aspect = fullWidth / fullHeight; - copy: function ( source ) { + if ( this.view === null ) { - var i, il, j, jl, k, kl; + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; - // reset + } - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [[]]; - this.morphTargets = []; - this.morphNormals = []; - this.skinWeights = []; - this.skinIndices = []; - this.lineDistances = []; - this.boundingBox = null; - this.boundingSphere = null; + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; - // name + this.updateProjectionMatrix(); - this.name = source.name; + } - // vertices + clearViewOffset() { - var vertices = source.vertices; + if ( this.view !== null ) { - for ( i = 0, il = vertices.length; i < il; i ++ ) { + this.view.enabled = false; - this.vertices.push( vertices[ i ].clone() ); + } - } + this.updateProjectionMatrix(); - // colors + } - var colors = source.colors; + updateProjectionMatrix() { - for ( i = 0, il = colors.length; i < il; i ++ ) { + const near = this.near; + let top = near * Math.tan( DEG2RAD * 0.5 * this.fov ) / this.zoom; + let height = 2 * top; + let width = this.aspect * height; + let left = - 0.5 * width; + const view = this.view; - this.colors.push( colors[ i ].clone() ); + if ( this.view !== null && this.view.enabled ) { - } + const fullWidth = view.fullWidth, + fullHeight = view.fullHeight; - // faces + left += view.offsetX * width / fullWidth; + top -= view.offsetY * height / fullHeight; + width *= view.width / fullWidth; + height *= view.height / fullHeight; - var faces = source.faces; + } - for ( i = 0, il = faces.length; i < il; i ++ ) { + const skew = this.filmOffset; + if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - this.faces.push( faces[ i ].clone() ); + this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far, this.coordinateSystem ); - } + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - // face vertex uvs + } - for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { + toJSON( meta ) { - var faceVertexUvs = source.faceVertexUvs[ i ]; + const data = super.toJSON( meta ); - if ( this.faceVertexUvs[ i ] === undefined ) { + data.object.fov = this.fov; + data.object.zoom = this.zoom; - this.faceVertexUvs[ i ] = []; + data.object.near = this.near; + data.object.far = this.far; + data.object.focus = this.focus; - } + data.object.aspect = this.aspect; - for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - var uvs = faceVertexUvs[ j ], uvsCopy = []; + data.object.filmGauge = this.filmGauge; + data.object.filmOffset = this.filmOffset; - for ( k = 0, kl = uvs.length; k < kl; k ++ ) { + return data; - var uv = uvs[ k ]; + } - uvsCopy.push( uv.clone() ); +} - } +const fov = - 90; // negative fov is not an error +const aspect = 1; - this.faceVertexUvs[ i ].push( uvsCopy ); +class CubeCamera extends Object3D { - } + constructor( near, far, renderTarget ) { - } + super(); - // morph targets + this.type = 'CubeCamera'; - var morphTargets = source.morphTargets; + this.renderTarget = renderTarget; + this.coordinateSystem = null; + this.activeMipmapLevel = 0; - for ( i = 0, il = morphTargets.length; i < il; i ++ ) { + const cameraPX = new PerspectiveCamera( fov, aspect, near, far ); + cameraPX.layers = this.layers; + this.add( cameraPX ); - var morphTarget = {}; - morphTarget.name = morphTargets[ i ].name; + const cameraNX = new PerspectiveCamera( fov, aspect, near, far ); + cameraNX.layers = this.layers; + this.add( cameraNX ); - // vertices + const cameraPY = new PerspectiveCamera( fov, aspect, near, far ); + cameraPY.layers = this.layers; + this.add( cameraPY ); - if ( morphTargets[ i ].vertices !== undefined ) { + const cameraNY = new PerspectiveCamera( fov, aspect, near, far ); + cameraNY.layers = this.layers; + this.add( cameraNY ); - morphTarget.vertices = []; + const cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.layers = this.layers; + this.add( cameraPZ ); - for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) { + const cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.layers = this.layers; + this.add( cameraNZ ); - morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() ); + } - } + updateCoordinateSystem() { - } + const coordinateSystem = this.coordinateSystem; - // normals + const cameras = this.children.concat(); - if ( morphTargets[ i ].normals !== undefined ) { + const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras; - morphTarget.normals = []; + for ( const camera of cameras ) this.remove( camera ); - for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) { + if ( coordinateSystem === WebGLCoordinateSystem ) { - morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() ); + cameraPX.up.set( 0, 1, 0 ); + cameraPX.lookAt( 1, 0, 0 ); - } + cameraNX.up.set( 0, 1, 0 ); + cameraNX.lookAt( - 1, 0, 0 ); - } + cameraPY.up.set( 0, 0, - 1 ); + cameraPY.lookAt( 0, 1, 0 ); - this.morphTargets.push( morphTarget ); + cameraNY.up.set( 0, 0, 1 ); + cameraNY.lookAt( 0, - 1, 0 ); - } + cameraPZ.up.set( 0, 1, 0 ); + cameraPZ.lookAt( 0, 0, 1 ); - // morph normals + cameraNZ.up.set( 0, 1, 0 ); + cameraNZ.lookAt( 0, 0, - 1 ); - var morphNormals = source.morphNormals; + } else if ( coordinateSystem === WebGPUCoordinateSystem ) { - for ( i = 0, il = morphNormals.length; i < il; i ++ ) { + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( - 1, 0, 0 ); - var morphNormal = {}; + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( 1, 0, 0 ); - // vertex normals + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( 0, 1, 0 ); - if ( morphNormals[ i ].vertexNormals !== undefined ) { + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( 0, - 1, 0 ); - morphNormal.vertexNormals = []; + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( 0, 0, 1 ); - for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) { + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( 0, 0, - 1 ); - var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ]; - var destVertexNormal = {}; + } else { - destVertexNormal.a = srcVertexNormal.a.clone(); - destVertexNormal.b = srcVertexNormal.b.clone(); - destVertexNormal.c = srcVertexNormal.c.clone(); + throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem ); - morphNormal.vertexNormals.push( destVertexNormal ); + } - } + for ( const camera of cameras ) { - } + this.add( camera ); - // face normals + camera.updateMatrixWorld(); - if ( morphNormals[ i ].faceNormals !== undefined ) { + } - morphNormal.faceNormals = []; + } - for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) { + update( renderer, scene ) { - morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() ); + if ( this.parent === null ) this.updateMatrixWorld(); - } + const { renderTarget, activeMipmapLevel } = this; - } + if ( this.coordinateSystem !== renderer.coordinateSystem ) { - this.morphNormals.push( morphNormal ); + this.coordinateSystem = renderer.coordinateSystem; - } + this.updateCoordinateSystem(); - // skin weights + } - var skinWeights = source.skinWeights; + const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children; - for ( i = 0, il = skinWeights.length; i < il; i ++ ) { + const currentRenderTarget = renderer.getRenderTarget(); + const currentActiveCubeFace = renderer.getActiveCubeFace(); + const currentActiveMipmapLevel = renderer.getActiveMipmapLevel(); - this.skinWeights.push( skinWeights[ i ].clone() ); + const currentXrEnabled = renderer.xr.enabled; - } + renderer.xr.enabled = false; - // skin indices + const generateMipmaps = renderTarget.texture.generateMipmaps; - var skinIndices = source.skinIndices; + renderTarget.texture.generateMipmaps = false; - for ( i = 0, il = skinIndices.length; i < il; i ++ ) { + renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel ); + renderer.render( scene, cameraPX ); - this.skinIndices.push( skinIndices[ i ].clone() ); + renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel ); + renderer.render( scene, cameraNX ); - } + renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel ); + renderer.render( scene, cameraPY ); - // line distances + renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel ); + renderer.render( scene, cameraNY ); - var lineDistances = source.lineDistances; + renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel ); + renderer.render( scene, cameraPZ ); - for ( i = 0, il = lineDistances.length; i < il; i ++ ) { + // mipmaps are generated during the last call of render() + // at this point, all sides of the cube render target are defined - this.lineDistances.push( lineDistances[ i ] ); + renderTarget.texture.generateMipmaps = generateMipmaps; - } + renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel ); + renderer.render( scene, cameraNZ ); - // bounding box + renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel ); - var boundingBox = source.boundingBox; + renderer.xr.enabled = currentXrEnabled; - if ( boundingBox !== null ) { + renderTarget.texture.needsPMREMUpdate = true; - this.boundingBox = boundingBox.clone(); + } - } +} - // bounding sphere +class CubeTexture extends Texture { - var boundingSphere = source.boundingSphere; + constructor( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ) { - if ( boundingSphere !== null ) { + images = images !== undefined ? images : []; + mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - this.boundingSphere = boundingSphere.clone(); + super( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); - } + this.isCubeTexture = true; - // update flags + this.flipY = false; - this.elementsNeedUpdate = source.elementsNeedUpdate; - this.verticesNeedUpdate = source.verticesNeedUpdate; - this.uvsNeedUpdate = source.uvsNeedUpdate; - this.normalsNeedUpdate = source.normalsNeedUpdate; - this.colorsNeedUpdate = source.colorsNeedUpdate; - this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate; - this.groupsNeedUpdate = source.groupsNeedUpdate; + } - return this; + get images() { - }, + return this.image; - dispose: function () { + } - this.dispatchEvent( { type: 'dispose' } ); + set images( value ) { - } + this.image = value; - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ +} - // BoxGeometry +class WebGLCubeRenderTarget extends WebGLRenderTarget { - function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { + constructor( size = 1, options = {} ) { - Geometry.call( this ); + super( size, size, options ); - this.type = 'BoxGeometry'; + this.isWebGLCubeRenderTarget = true; - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; + const image = { width: size, height: size, depth: 1 }; + const images = [ image, image, image, image, image, image ]; - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); + if ( options.encoding !== undefined ) { - } + // @deprecated, r152 + warnOnce( 'THREE.WebGLCubeRenderTarget: option.encoding has been replaced by option.colorSpace.' ); + options.colorSpace = options.encoding === sRGBEncoding ? SRGBColorSpace : NoColorSpace; - BoxGeometry.prototype = Object.create( Geometry.prototype ); - BoxGeometry.prototype.constructor = BoxGeometry; + } - // BoxBufferGeometry + this.texture = new CubeTexture( images, options.mapping, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.colorSpace ); - function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { + // By convention -- likely based on the RenderMan spec from the 1990's -- cube maps are specified by WebGL (and three.js) + // in a coordinate system in which positive-x is to the right when looking up the positive-z axis -- in other words, + // in a left-handed coordinate system. By continuing this convention, preexisting cube maps continued to render correctly. - BufferGeometry.call( this ); + // three.js uses a right-handed coordinate system. So environment maps used in three.js appear to have px and nx swapped + // and the flag isRenderTargetTexture controls this conversion. The flip is not required when using WebGLCubeRenderTarget.texture + // as a cube texture (this is detected when isRenderTargetTexture is set to true for cube textures). - this.type = 'BoxBufferGeometry'; + this.texture.isRenderTargetTexture = true; - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; + this.texture.generateMipmaps = options.generateMipmaps !== undefined ? options.generateMipmaps : false; + this.texture.minFilter = options.minFilter !== undefined ? options.minFilter : LinearFilter; - var scope = this; + } - width = width || 1; - height = height || 1; - depth = depth || 1; + fromEquirectangularTexture( renderer, texture ) { - // segments + this.texture.type = texture.type; + this.texture.colorSpace = texture.colorSpace; - widthSegments = Math.floor( widthSegments ) || 1; - heightSegments = Math.floor( heightSegments ) || 1; - depthSegments = Math.floor( depthSegments ) || 1; + this.texture.generateMipmaps = texture.generateMipmaps; + this.texture.minFilter = texture.minFilter; + this.texture.magFilter = texture.magFilter; - // buffers + const shader = { - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + uniforms: { + tEquirect: { value: null }, + }, - // helper variables + vertexShader: /* glsl */` - var numberOfVertices = 0; - var groupStart = 0; + varying vec3 vWorldDirection; - // build each side of the box geometry + vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px - buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx - buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py - buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny - buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz - buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz + return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - // build geometry + } - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + void main() { - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { + vWorldDirection = transformDirection( position, modelMatrix ); - var segmentWidth = width / gridX; - var segmentHeight = height / gridY; + #include + #include - var widthHalf = width / 2; - var heightHalf = height / 2; - var depthHalf = depth / 2; + } + `, - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; + fragmentShader: /* glsl */` - var vertexCounter = 0; - var groupCount = 0; + uniform sampler2D tEquirect; - var ix, iy; + varying vec3 vWorldDirection; - var vector = new Vector3(); + #include - // generate vertices, normals and uvs + void main() { - for ( iy = 0; iy < gridY1; iy ++ ) { + vec3 direction = normalize( vWorldDirection ); - var y = iy * segmentHeight - heightHalf; + vec2 sampleUV = equirectUv( direction ); - for ( ix = 0; ix < gridX1; ix ++ ) { + gl_FragColor = texture2D( tEquirect, sampleUV ); - var x = ix * segmentWidth - widthHalf; + } + ` + }; - // set values to correct vector component + const geometry = new BoxGeometry( 5, 5, 5 ); - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; + const material = new ShaderMaterial( { - // now apply vector to vertex buffer + name: 'CubemapFromEquirect', - vertices.push( vector.x, vector.y, vector.z ); + uniforms: cloneUniforms( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader, + side: BackSide, + blending: NoBlending - // set values to correct vector component + } ); - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; + material.uniforms.tEquirect.value = texture; - // now apply vector to normal buffer + const mesh = new Mesh( geometry, material ); - normals.push( vector.x, vector.y, vector.z ); + const currentMinFilter = texture.minFilter; - // uvs + // Avoid blurred poles + if ( texture.minFilter === LinearMipmapLinearFilter ) texture.minFilter = LinearFilter; - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); + const camera = new CubeCamera( 1, 10, this ); + camera.update( renderer, mesh ); - // counters + texture.minFilter = currentMinFilter; - vertexCounter += 1; + mesh.geometry.dispose(); + mesh.material.dispose(); - } + return this; - } + } - // indices + clear( renderer, color, depth, stencil ) { - // 1. you need three indices to draw a single face - // 2. a single segment consists of two faces - // 3. so we need to generate six (2*3) indices per segment + const currentRenderTarget = renderer.getRenderTarget(); - for ( iy = 0; iy < gridY; iy ++ ) { + for ( let i = 0; i < 6; i ++ ) { - for ( ix = 0; ix < gridX; ix ++ ) { + renderer.setRenderTarget( this, i ); - var a = numberOfVertices + ix + gridX1 * iy; - var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); - var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; + renderer.clear( color, depth, stencil ); - // faces + } - indices.push( a, b, d ); - indices.push( b, c, d ); + renderer.setRenderTarget( currentRenderTarget ); - // increase counter + } - groupCount += 6; +} - } +const _vector1 = /*@__PURE__*/ new Vector3(); +const _vector2 = /*@__PURE__*/ new Vector3(); +const _normalMatrix = /*@__PURE__*/ new Matrix3(); - } +class Plane { - // add a group to the geometry. this will ensure multi material support + constructor( normal = new Vector3( 1, 0, 0 ), constant = 0 ) { - scope.addGroup( groupStart, groupCount, materialIndex ); + this.isPlane = true; - // calculate new start value for groups + // normal is assumed to be normalized - groupStart += groupCount; + this.normal = normal; + this.constant = constant; - // update total number of vertices + } - numberOfVertices += vertexCounter; + set( normal, constant ) { - } + this.normal.copy( normal ); + this.constant = constant; - } + return this; - BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; + } - /** - * Uniform Utilities - */ + setComponents( x, y, z, w ) { - function cloneUniforms( src ) { + this.normal.set( x, y, z ); + this.constant = w; - var dst = {}; + return this; - for ( var u in src ) { + } - dst[ u ] = {}; + setFromNormalAndCoplanarPoint( normal, point ) { - for ( var p in src[ u ] ) { + this.normal.copy( normal ); + this.constant = - point.dot( this.normal ); - var property = src[ u ][ p ]; + return this; - if ( property && ( property.isColor || - property.isMatrix3 || property.isMatrix4 || - property.isVector2 || property.isVector3 || property.isVector4 || - property.isTexture ) ) { + } - dst[ u ][ p ] = property.clone(); + setFromCoplanarPoints( a, b, c ) { - } else if ( Array.isArray( property ) ) { + const normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); - dst[ u ][ p ] = property.slice(); + // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? - } else { + this.setFromNormalAndCoplanarPoint( normal, a ); - dst[ u ][ p ] = property; + return this; - } + } - } + copy( plane ) { - } + this.normal.copy( plane.normal ); + this.constant = plane.constant; - return dst; + return this; } - function mergeUniforms( uniforms ) { + normalize() { - var merged = {}; + // Note: will lead to a divide by zero if the plane is invalid. - for ( var u = 0; u < uniforms.length; u ++ ) { + const inverseNormalLength = 1.0 / this.normal.length(); + this.normal.multiplyScalar( inverseNormalLength ); + this.constant *= inverseNormalLength; - var tmp = cloneUniforms( uniforms[ u ] ); + return this; - for ( var p in tmp ) { + } - merged[ p ] = tmp[ p ]; + negate() { - } + this.constant *= - 1; + this.normal.negate(); - } + return this; + + } + + distanceToPoint( point ) { - return merged; + return this.normal.dot( point ) + this.constant; } - // Legacy + distanceToSphere( sphere ) { - var UniformsUtils = { clone: cloneUniforms, merge: mergeUniforms }; + return this.distanceToPoint( sphere.center ) - sphere.radius; - var default_vertex = "void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; + } - var default_fragment = "void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}"; + projectPoint( point, target ) { - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + return target.copy( point ).addScaledVector( this.normal, - this.distanceToPoint( point ) ); - function ShaderMaterial( parameters ) { + } - Material.call( this ); + intersectLine( line, target ) { - this.type = 'ShaderMaterial'; + const direction = line.delta( _vector1 ); - this.defines = {}; - this.uniforms = {}; + const denominator = this.normal.dot( direction ); - this.vertexShader = default_vertex; - this.fragmentShader = default_fragment; + if ( denominator === 0 ) { - this.linewidth = 1; + // line is coplanar, return origin + if ( this.distanceToPoint( line.start ) === 0 ) { - this.wireframe = false; - this.wireframeLinewidth = 1; + return target.copy( line.start ); - this.fog = false; // set to use scene fog - this.lights = false; // set to use scene lights - this.clipping = false; // set to use user-defined clipping planes + } - this.skinning = false; // set to use skinning attribute streams - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals + // Unsure if this is the correct method to handle this case. + return null; - this.extensions = { - derivatives: false, // set to use derivatives - fragDepth: false, // set to use fragment depth values - drawBuffers: false, // set to use draw buffers - shaderTextureLOD: false // set to use shader texture LOD - }; + } - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; + const t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; - this.index0AttributeName = undefined; - this.uniformsNeedUpdate = false; + if ( t < 0 || t > 1 ) { - if ( parameters !== undefined ) { + return null; + + } - if ( parameters.attributes !== undefined ) { + return target.copy( line.start ).addScaledVector( direction, t ); - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + } - } + intersectsLine( line ) { - this.setValues( parameters ); + // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. - } + const startSign = this.distanceToPoint( line.start ); + const endSign = this.distanceToPoint( line.end ); + + return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); } - ShaderMaterial.prototype = Object.create( Material.prototype ); - ShaderMaterial.prototype.constructor = ShaderMaterial; + intersectsBox( box ) { - ShaderMaterial.prototype.isShaderMaterial = true; + return box.intersectsPlane( this ); - ShaderMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + intersectsSphere( sphere ) { - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; + return sphere.intersectsPlane( this ); - this.uniforms = cloneUniforms( source.uniforms ); + } - this.defines = Object.assign( {}, source.defines ); + coplanarPoint( target ) { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + return target.copy( this.normal ).multiplyScalar( - this.constant ); - this.lights = source.lights; - this.clipping = source.clipping; + } + + applyMatrix4( matrix, optionalNormalMatrix ) { - this.skinning = source.skinning; + const normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + const referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); - this.extensions = source.extensions; + const normal = this.normal.applyMatrix3( normalMatrix ).normalize(); + + this.constant = - referencePoint.dot( normal ); return this; - }; + } - ShaderMaterial.prototype.toJSON = function ( meta ) { + translate( offset ) { - var data = Material.prototype.toJSON.call( this, meta ); + this.constant -= offset.dot( this.normal ); - data.uniforms = {}; + return this; - for ( var name in this.uniforms ) { + } - var uniform = this.uniforms[ name ]; - var value = uniform.value; + equals( plane ) { - if ( value && value.isTexture ) { + return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); - data.uniforms[ name ] = { - type: 't', - value: value.toJSON( meta ).uuid - }; + } - } else if ( value && value.isColor ) { + clone() { - data.uniforms[ name ] = { - type: 'c', - value: value.getHex() - }; + return new this.constructor().copy( this ); - } else if ( value && value.isVector2 ) { + } - data.uniforms[ name ] = { - type: 'v2', - value: value.toArray() - }; +} - } else if ( value && value.isVector3 ) { +const _sphere$4 = /*@__PURE__*/ new Sphere(); +const _vector$6 = /*@__PURE__*/ new Vector3(); - data.uniforms[ name ] = { - type: 'v3', - value: value.toArray() - }; +class Frustum { - } else if ( value && value.isVector4 ) { + constructor( p0 = new Plane(), p1 = new Plane(), p2 = new Plane(), p3 = new Plane(), p4 = new Plane(), p5 = new Plane() ) { - data.uniforms[ name ] = { - type: 'v4', - value: value.toArray() - }; + this.planes = [ p0, p1, p2, p3, p4, p5 ]; - } else if ( value && value.isMatrix3 ) { + } - data.uniforms[ name ] = { - type: 'm3', - value: value.toArray() - }; + set( p0, p1, p2, p3, p4, p5 ) { - } else if ( value && value.isMatrix4 ) { + const planes = this.planes; - data.uniforms[ name ] = { - type: 'm4', - value: value.toArray() - }; + planes[ 0 ].copy( p0 ); + planes[ 1 ].copy( p1 ); + planes[ 2 ].copy( p2 ); + planes[ 3 ].copy( p3 ); + planes[ 4 ].copy( p4 ); + planes[ 5 ].copy( p5 ); - } else { + return this; - data.uniforms[ name ] = { - value: value - }; + } - // note: the array variants v2v, v3v, v4v, m4v and tv are not supported so far + copy( frustum ) { - } + const planes = this.planes; + + for ( let i = 0; i < 6; i ++ ) { + + planes[ i ].copy( frustum.planes[ i ] ); } - if ( Object.keys( this.defines ).length > 0 ) data.defines = this.defines; + return this; - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; + } - var extensions = {}; + setFromProjectionMatrix( m, coordinateSystem = WebGLCoordinateSystem ) { - for ( var key in this.extensions ) { + const planes = this.planes; + const me = m.elements; + const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; + const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; + const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; + const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; - if ( this.extensions[ key ] === true ) extensions[ key ] = true; + planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); + planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); + planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); + planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); + planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); + + if ( coordinateSystem === WebGLCoordinateSystem ) { + + planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); + + } else if ( coordinateSystem === WebGPUCoordinateSystem ) { + + planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize(); + + } else { + + throw new Error( 'THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: ' + coordinateSystem ); } - if ( Object.keys( extensions ).length > 0 ) data.extensions = extensions; + return this; - return data; + } - }; + intersectsObject( object ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley - */ + if ( object.boundingSphere !== undefined ) { - function Camera() { + if ( object.boundingSphere === null ) object.computeBoundingSphere(); - Object3D.call( this ); + _sphere$4.copy( object.boundingSphere ).applyMatrix4( object.matrixWorld ); - this.type = 'Camera'; + } else { - this.matrixWorldInverse = new Matrix4(); + const geometry = object.geometry; - this.projectionMatrix = new Matrix4(); - this.projectionMatrixInverse = new Matrix4(); + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - } + _sphere$4.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); + + } - Camera.prototype = Object.assign( Object.create( Object3D.prototype ), { + return this.intersectsSphere( _sphere$4 ); - constructor: Camera, + } - isCamera: true, + intersectsSprite( sprite ) { - copy: function ( source, recursive ) { + _sphere$4.center.set( 0, 0, 0 ); + _sphere$4.radius = 0.7071067811865476; + _sphere$4.applyMatrix4( sprite.matrixWorld ); - Object3D.prototype.copy.call( this, source, recursive ); + return this.intersectsSphere( _sphere$4 ); - this.matrixWorldInverse.copy( source.matrixWorldInverse ); + } - this.projectionMatrix.copy( source.projectionMatrix ); - this.projectionMatrixInverse.copy( source.projectionMatrixInverse ); + intersectsSphere( sphere ) { - return this; + const planes = this.planes; + const center = sphere.center; + const negRadius = - sphere.radius; - }, + for ( let i = 0; i < 6; i ++ ) { - getWorldDirection: function ( target ) { + const distance = planes[ i ].distanceToPoint( center ); - if ( target === undefined ) { + if ( distance < negRadius ) { - console.warn( 'THREE.Camera: .getWorldDirection() target is now required' ); - target = new Vector3(); + return false; } - this.updateMatrixWorld( true ); + } + + return true; - var e = this.matrixWorld.elements; + } - return target.set( - e[ 8 ], - e[ 9 ], - e[ 10 ] ).normalize(); + intersectsBox( box ) { - }, + const planes = this.planes; - updateMatrixWorld: function ( force ) { + for ( let i = 0; i < 6; i ++ ) { - Object3D.prototype.updateMatrixWorld.call( this, force ); + const plane = planes[ i ]; - this.matrixWorldInverse.getInverse( this.matrixWorld ); + // corner at max distance - }, + _vector$6.x = plane.normal.x > 0 ? box.max.x : box.min.x; + _vector$6.y = plane.normal.y > 0 ? box.max.y : box.min.y; + _vector$6.z = plane.normal.z > 0 ? box.max.z : box.min.z; + + if ( plane.distanceToPoint( _vector$6 ) < 0 ) { - clone: function () { + return false; - return new this.constructor().copy( this ); + } } - } ); + return true; - /** - * @author mrdoob / http://mrdoob.com/ - * @author greggman / http://games.greggman.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author tschw - */ + } - function PerspectiveCamera( fov, aspect, near, far ) { + containsPoint( point ) { - Camera.call( this ); + const planes = this.planes; - this.type = 'PerspectiveCamera'; + for ( let i = 0; i < 6; i ++ ) { - this.fov = fov !== undefined ? fov : 50; - this.zoom = 1; + if ( planes[ i ].distanceToPoint( point ) < 0 ) { - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; - this.focus = 10; + return false; - this.aspect = aspect !== undefined ? aspect : 1; - this.view = null; + } - this.filmGauge = 35; // width of the film (default in millimeters) - this.filmOffset = 0; // horizontal film offset (same unit as gauge) + } - this.updateProjectionMatrix(); + return true; } - PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), { + clone() { + + return new this.constructor().copy( this ); - constructor: PerspectiveCamera, + } - isPerspectiveCamera: true, +} - copy: function ( source, recursive ) { +function WebGLAnimation() { - Camera.prototype.copy.call( this, source, recursive ); + let context = null; + let isAnimating = false; + let animationLoop = null; + let requestId = null; - this.fov = source.fov; - this.zoom = source.zoom; + function onAnimationFrame( time, frame ) { - this.near = source.near; - this.far = source.far; - this.focus = source.focus; + animationLoop( time, frame ); - this.aspect = source.aspect; - this.view = source.view === null ? null : Object.assign( {}, source.view ); + requestId = context.requestAnimationFrame( onAnimationFrame ); - this.filmGauge = source.filmGauge; - this.filmOffset = source.filmOffset; + } - return this; + return { - }, + start: function () { - /** - * Sets the FOV by focal length in respect to the current .filmGauge. - * - * The default film gauge is 35, so that the focal length can be specified for - * a 35mm (full frame) camera. - * - * Values for focal length and film gauge must have the same unit. - */ - setFocalLength: function ( focalLength ) { + if ( isAnimating === true ) return; + if ( animationLoop === null ) return; - // see http://www.bobatkins.com/photography/technical/field_of_view.html - var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; + requestId = context.requestAnimationFrame( onAnimationFrame ); - this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); - this.updateProjectionMatrix(); + isAnimating = true; }, - /** - * Calculates the focal length from the current .fov and .filmGauge. - */ - getFocalLength: function () { + stop: function () { - var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov ); + context.cancelAnimationFrame( requestId ); - return 0.5 * this.getFilmHeight() / vExtentSlope; + isAnimating = false; }, - getEffectiveFOV: function () { + setAnimationLoop: function ( callback ) { - return _Math.RAD2DEG * 2 * Math.atan( - Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); + animationLoop = callback; }, - getFilmWidth: function () { + setContext: function ( value ) { - // film not completely covered in portrait format (aspect < 1) - return this.filmGauge * Math.min( this.aspect, 1 ); + context = value; - }, + } - getFilmHeight: function () { + }; - // film not completely covered in landscape format (aspect > 1) - return this.filmGauge / Math.max( this.aspect, 1 ); +} - }, +function WebGLAttributes( gl, capabilities ) { - /** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * var w = 1920; - * var h = 1080; - * var fullWidth = w * 3; - * var fullHeight = h * 2; - * - * --A-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setViewOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setViewOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setViewOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { + const isWebGL2 = capabilities.isWebGL2; - this.aspect = fullWidth / fullHeight; + const buffers = new WeakMap(); - if ( this.view === null ) { + function createBuffer( attribute, bufferType ) { - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; + const array = attribute.array; + const usage = attribute.usage; - } + const buffer = gl.createBuffer(); - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; + gl.bindBuffer( bufferType, buffer ); + gl.bufferData( bufferType, array, usage ); - this.updateProjectionMatrix(); + attribute.onUploadCallback(); - }, + let type; - clearViewOffset: function () { + if ( array instanceof Float32Array ) { - if ( this.view !== null ) { + type = gl.FLOAT; - this.view.enabled = false; + } else if ( array instanceof Uint16Array ) { - } + if ( attribute.isFloat16BufferAttribute ) { - this.updateProjectionMatrix(); + if ( isWebGL2 ) { - }, + type = gl.HALF_FLOAT; - updateProjectionMatrix: function () { + } else { - var near = this.near, - top = near * Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, - height = 2 * top, - width = this.aspect * height, - left = - 0.5 * width, - view = this.view; + throw new Error( 'THREE.WebGLAttributes: Usage of Float16BufferAttribute requires WebGL2.' ); - if ( this.view !== null && this.view.enabled ) { + } - var fullWidth = view.fullWidth, - fullHeight = view.fullHeight; + } else { - left += view.offsetX * width / fullWidth; - top -= view.offsetY * height / fullHeight; - width *= view.width / fullWidth; - height *= view.height / fullHeight; + type = gl.UNSIGNED_SHORT; } - var skew = this.filmOffset; - if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); + } else if ( array instanceof Int16Array ) { - this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far ); + type = gl.SHORT; - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); + } else if ( array instanceof Uint32Array ) { - }, + type = gl.UNSIGNED_INT; - toJSON: function ( meta ) { + } else if ( array instanceof Int32Array ) { - var data = Object3D.prototype.toJSON.call( this, meta ); + type = gl.INT; - data.object.fov = this.fov; - data.object.zoom = this.zoom; + } else if ( array instanceof Int8Array ) { - data.object.near = this.near; - data.object.far = this.far; - data.object.focus = this.focus; + type = gl.BYTE; - data.object.aspect = this.aspect; + } else if ( array instanceof Uint8Array ) { - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + type = gl.UNSIGNED_BYTE; - data.object.filmGauge = this.filmGauge; - data.object.filmOffset = this.filmOffset; + } else if ( array instanceof Uint8ClampedArray ) { - return data; + type = gl.UNSIGNED_BYTE; - } + } else { - } ); + throw new Error( 'THREE.WebGLAttributes: Unsupported buffer data format: ' + array ); - /** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ - */ + } - var fov = 90, aspect = 1; + return { + buffer: buffer, + type: type, + bytesPerElement: array.BYTES_PER_ELEMENT, + version: attribute.version + }; - function CubeCamera( near, far, cubeResolution, options ) { + } - Object3D.call( this ); + function updateBuffer( buffer, attribute, bufferType ) { - this.type = 'CubeCamera'; + const array = attribute.array; + const updateRange = attribute.updateRange; - var cameraPX = new PerspectiveCamera( fov, aspect, near, far ); - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); + gl.bindBuffer( bufferType, buffer ); - var cameraNX = new PerspectiveCamera( fov, aspect, near, far ); - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); + if ( updateRange.count === - 1 ) { - var cameraPY = new PerspectiveCamera( fov, aspect, near, far ); - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); + // Not using update ranges - var cameraNY = new PerspectiveCamera( fov, aspect, near, far ); - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); + gl.bufferSubData( bufferType, 0, array ); - var cameraPZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); + } else { - var cameraNZ = new PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); + if ( isWebGL2 ) { - options = options || { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter }; + gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, + array, updateRange.offset, updateRange.count ); - this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); - this.renderTarget.texture.name = "CubeCamera"; + } else { - this.update = function ( renderer, scene ) { + gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, + array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); - if ( this.parent === null ) this.updateMatrixWorld(); + } - var currentRenderTarget = renderer.getRenderTarget(); + updateRange.count = - 1; // reset range - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.texture.generateMipmaps; + } - renderTarget.texture.generateMipmaps = false; + attribute.onUploadCallback(); - renderer.setRenderTarget( renderTarget, 0 ); - renderer.render( scene, cameraPX ); + } - renderer.setRenderTarget( renderTarget, 1 ); - renderer.render( scene, cameraNX ); + // - renderer.setRenderTarget( renderTarget, 2 ); - renderer.render( scene, cameraPY ); + function get( attribute ) { - renderer.setRenderTarget( renderTarget, 3 ); - renderer.render( scene, cameraNY ); + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - renderer.setRenderTarget( renderTarget, 4 ); - renderer.render( scene, cameraPZ ); + return buffers.get( attribute ); - renderTarget.texture.generateMipmaps = generateMipmaps; + } - renderer.setRenderTarget( renderTarget, 5 ); - renderer.render( scene, cameraNZ ); + function remove( attribute ) { - renderer.setRenderTarget( currentRenderTarget ); + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - }; + const data = buffers.get( attribute ); - this.clear = function ( renderer, color, depth, stencil ) { + if ( data ) { - var currentRenderTarget = renderer.getRenderTarget(); + gl.deleteBuffer( data.buffer ); - var renderTarget = this.renderTarget; + buffers.delete( attribute ); - for ( var i = 0; i < 6; i ++ ) { + } - renderer.setRenderTarget( renderTarget, i ); + } - renderer.clear( color, depth, stencil ); + function update( attribute, bufferType ) { - } + if ( attribute.isGLBufferAttribute ) { - renderer.setRenderTarget( currentRenderTarget ); + const cached = buffers.get( attribute ); - }; + if ( ! cached || cached.version < attribute.version ) { - } + buffers.set( attribute, { + buffer: attribute.buffer, + type: attribute.type, + bytesPerElement: attribute.elementSize, + version: attribute.version + } ); - CubeCamera.prototype = Object.create( Object3D.prototype ); - CubeCamera.prototype.constructor = CubeCamera; + } - /** - * @author alteredq / http://alteredqualia.com - * @author WestLangley / http://github.com/WestLangley - */ + return; - function WebGLRenderTargetCube( width, height, options ) { + } - WebGLRenderTarget.call( this, width, height, options ); + if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; - } + const data = buffers.get( attribute ); - WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype ); - WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube; + if ( data === undefined ) { - WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true; + buffers.set( attribute, createBuffer( attribute, bufferType ) ); - WebGLRenderTargetCube.prototype.fromEquirectangularTexture = function ( renderer, texture ) { + } else if ( data.version < attribute.version ) { - this.texture.type = texture.type; - this.texture.format = texture.format; - this.texture.encoding = texture.encoding; + updateBuffer( data.buffer, attribute, bufferType ); - var scene = new Scene(); + data.version = attribute.version; - var shader = { + } - uniforms: { - tEquirect: { value: null }, - }, + } - vertexShader: [ + return { - "varying vec3 vWorldDirection;", + get: get, + remove: remove, + update: update - "vec3 transformDirection( in vec3 dir, in mat4 matrix ) {", + }; - " return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );", +} - "}", +class PlaneGeometry extends BufferGeometry { - "void main() {", + constructor( width = 1, height = 1, widthSegments = 1, heightSegments = 1 ) { - " vWorldDirection = transformDirection( position, modelMatrix );", + super(); - " #include ", - " #include ", + this.type = 'PlaneGeometry'; - "}" + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; - ].join( '\n' ), + const width_half = width / 2; + const height_half = height / 2; - fragmentShader: [ + const gridX = Math.floor( widthSegments ); + const gridY = Math.floor( heightSegments ); - "uniform sampler2D tEquirect;", + const gridX1 = gridX + 1; + const gridY1 = gridY + 1; - "varying vec3 vWorldDirection;", + const segment_width = width / gridX; + const segment_height = height / gridY; - "#define RECIPROCAL_PI 0.31830988618", - "#define RECIPROCAL_PI2 0.15915494", + // - "void main() {", + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - " vec3 direction = normalize( vWorldDirection );", + for ( let iy = 0; iy < gridY1; iy ++ ) { - " vec2 sampleUV;", + const y = iy * segment_height - height_half; - " sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;", + for ( let ix = 0; ix < gridX1; ix ++ ) { - " sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", + const x = ix * segment_width - width_half; - " gl_FragColor = texture2D( tEquirect, sampleUV );", + vertices.push( x, - y, 0 ); - "}" + normals.push( 0, 0, 1 ); - ].join( '\n' ), - }; + uvs.push( ix / gridX ); + uvs.push( 1 - ( iy / gridY ) ); - var material = new ShaderMaterial( { + } - type: 'CubemapFromEquirect', + } - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader, - side: BackSide, - blending: NoBlending + for ( let iy = 0; iy < gridY; iy ++ ) { - } ); + for ( let ix = 0; ix < gridX; ix ++ ) { - material.uniforms.tEquirect.value = texture; + const a = ix + gridX1 * iy; + const b = ix + gridX1 * ( iy + 1 ); + const c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + const d = ( ix + 1 ) + gridX1 * iy; - var mesh = new Mesh( new BoxBufferGeometry( 5, 5, 5 ), material ); + indices.push( a, b, d ); + indices.push( b, c, d ); - scene.add( mesh ); + } - var camera = new CubeCamera( 1, 10, 1 ); + } - camera.renderTarget = this; - camera.renderTarget.texture.name = 'CubeCameraTexture'; + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - camera.update( renderer, scene ); + } - mesh.geometry.dispose(); - mesh.material.dispose(); + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); return this; - }; + } - /** - * @author alteredq / http://alteredqualia.com/ - */ + static fromJSON( data ) { - function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + return new PlaneGeometry( data.width, data.height, data.widthSegments, data.heightSegments ); - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + } - this.image = { data: data, width: width, height: height }; +} - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; +var alphahash_fragment = "#ifdef USE_ALPHAHASH\n\tif ( diffuseColor.a < getAlphaHashThreshold( vPosition ) ) discard;\n#endif"; - this.generateMipmaps = false; - this.flipY = false; - this.unpackAlignment = 1; +var alphahash_pars_fragment = "#ifdef USE_ALPHAHASH\n\tconst float ALPHA_HASH_SCALE = 0.05;\n\tfloat hash2D( vec2 value ) {\n\t\treturn fract( 1.0e4 * sin( 17.0 * value.x + 0.1 * value.y ) * ( 0.1 + abs( sin( 13.0 * value.y + value.x ) ) ) );\n\t}\n\tfloat hash3D( vec3 value ) {\n\t\treturn hash2D( vec2( hash2D( value.xy ), value.z ) );\n\t}\n\tfloat getAlphaHashThreshold( vec3 position ) {\n\t\tfloat maxDeriv = max(\n\t\t\tlength( dFdx( position.xyz ) ),\n\t\t\tlength( dFdy( position.xyz ) )\n\t\t);\n\t\tfloat pixScale = 1.0 / ( ALPHA_HASH_SCALE * maxDeriv );\n\t\tvec2 pixScales = vec2(\n\t\t\texp2( floor( log2( pixScale ) ) ),\n\t\t\texp2( ceil( log2( pixScale ) ) )\n\t\t);\n\t\tvec2 alpha = vec2(\n\t\t\thash3D( floor( pixScales.x * position.xyz ) ),\n\t\t\thash3D( floor( pixScales.y * position.xyz ) )\n\t\t);\n\t\tfloat lerpFactor = fract( log2( pixScale ) );\n\t\tfloat x = ( 1.0 - lerpFactor ) * alpha.x + lerpFactor * alpha.y;\n\t\tfloat a = min( lerpFactor, 1.0 - lerpFactor );\n\t\tvec3 cases = vec3(\n\t\t\tx * x / ( 2.0 * a * ( 1.0 - a ) ),\n\t\t\t( x - 0.5 * a ) / ( 1.0 - a ),\n\t\t\t1.0 - ( ( 1.0 - x ) * ( 1.0 - x ) / ( 2.0 * a * ( 1.0 - a ) ) )\n\t\t);\n\t\tfloat threshold = ( x < ( 1.0 - a ) )\n\t\t\t? ( ( x < a ) ? cases.x : cases.y )\n\t\t\t: cases.z;\n\t\treturn clamp( threshold , 1.0e-6, 1.0 );\n\t}\n#endif"; - } +var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g;\n#endif"; - DataTexture.prototype = Object.create( Texture.prototype ); - DataTexture.prototype.constructor = DataTexture; +var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - DataTexture.prototype.isDataTexture = true; +var alphatest_fragment = "#ifdef USE_ALPHATEST\n\tif ( diffuseColor.a < alphaTest ) discard;\n#endif"; - /** - * @author bhouston / http://clara.io - */ +var alphatest_pars_fragment = "#ifdef USE_ALPHATEST\n\tuniform float alphaTest;\n#endif"; - var _vector1 = new Vector3(); - var _vector2 = new Vector3(); - var _normalMatrix = new Matrix3(); +var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometryNormal, geometryViewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness );\n\t#endif\n#endif"; - function Plane( normal, constant ) { +var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; - // normal is assumed to be normalized +var begin_vertex = "vec3 transformed = vec3( position );\n#ifdef USE_ALPHAHASH\n\tvPosition = vec3( position );\n#endif"; - this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 ); - this.constant = ( constant !== undefined ) ? constant : 0; +var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; - } +var bsdfs = "float G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, 1.0, dotVH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n} // validated"; - Object.assign( Plane.prototype, { +var iridescence_fragment = "#ifdef USE_IRIDESCENCE\n\tconst mat3 XYZ_TO_REC709 = mat3(\n\t\t 3.2404542, -0.9692660, 0.0556434,\n\t\t-1.5371385, 1.8760108, -0.2040259,\n\t\t-0.4985314, 0.0415560, 1.0572252\n\t);\n\tvec3 Fresnel0ToIor( vec3 fresnel0 ) {\n\t\tvec3 sqrtF0 = sqrt( fresnel0 );\n\t\treturn ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );\n\t}\n\tvec3 IorToFresnel0( vec3 transmittedIor, float incidentIor ) {\n\t\treturn pow2( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );\n\t}\n\tfloat IorToFresnel0( float transmittedIor, float incidentIor ) {\n\t\treturn pow2( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ));\n\t}\n\tvec3 evalSensitivity( float OPD, vec3 shift ) {\n\t\tfloat phase = 2.0 * PI * OPD * 1.0e-9;\n\t\tvec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );\n\t\tvec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );\n\t\tvec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );\n\t\tvec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - pow2( phase ) * var );\n\t\txyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * pow2( phase ) );\n\t\txyz /= 1.0685e-7;\n\t\tvec3 rgb = XYZ_TO_REC709 * xyz;\n\t\treturn rgb;\n\t}\n\tvec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {\n\t\tvec3 I;\n\t\tfloat iridescenceIOR = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );\n\t\tfloat sinTheta2Sq = pow2( outsideIOR / iridescenceIOR ) * ( 1.0 - pow2( cosTheta1 ) );\n\t\tfloat cosTheta2Sq = 1.0 - sinTheta2Sq;\n\t\tif ( cosTheta2Sq < 0.0 ) {\n\t\t\treturn vec3( 1.0 );\n\t\t}\n\t\tfloat cosTheta2 = sqrt( cosTheta2Sq );\n\t\tfloat R0 = IorToFresnel0( iridescenceIOR, outsideIOR );\n\t\tfloat R12 = F_Schlick( R0, 1.0, cosTheta1 );\n\t\tfloat T121 = 1.0 - R12;\n\t\tfloat phi12 = 0.0;\n\t\tif ( iridescenceIOR < outsideIOR ) phi12 = PI;\n\t\tfloat phi21 = PI - phi12;\n\t\tvec3 baseIOR = Fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) );\t\tvec3 R1 = IorToFresnel0( baseIOR, iridescenceIOR );\n\t\tvec3 R23 = F_Schlick( R1, 1.0, cosTheta2 );\n\t\tvec3 phi23 = vec3( 0.0 );\n\t\tif ( baseIOR[ 0 ] < iridescenceIOR ) phi23[ 0 ] = PI;\n\t\tif ( baseIOR[ 1 ] < iridescenceIOR ) phi23[ 1 ] = PI;\n\t\tif ( baseIOR[ 2 ] < iridescenceIOR ) phi23[ 2 ] = PI;\n\t\tfloat OPD = 2.0 * iridescenceIOR * thinFilmThickness * cosTheta2;\n\t\tvec3 phi = vec3( phi21 ) + phi23;\n\t\tvec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );\n\t\tvec3 r123 = sqrt( R123 );\n\t\tvec3 Rs = pow2( T121 ) * R23 / ( vec3( 1.0 ) - R123 );\n\t\tvec3 C0 = R12 + Rs;\n\t\tI = C0;\n\t\tvec3 Cm = Rs - T121;\n\t\tfor ( int m = 1; m <= 2; ++ m ) {\n\t\t\tCm *= r123;\n\t\t\tvec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );\n\t\t\tI += Cm * Sm;\n\t\t}\n\t\treturn max( I, vec3( 0.0 ) );\n\t}\n#endif"; - isPlane: true, +var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vBumpMapUv );\n\t\tvec2 dSTdy = dFdy( vBumpMapUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vBumpMapUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vBumpMapUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy, float faceDirection ) {\n\t\tvec3 vSigmaX = dFdx( surf_pos.xyz );\n\t\tvec3 vSigmaY = dFdy( surf_pos.xyz );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 ) * faceDirection;\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; - set: function ( normal, constant ) { +var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vClipPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#pragma unroll_loop_end\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; - this.normal.copy( normal ); - this.constant = constant; +var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; - return this; +var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvarying vec3 vClipPosition;\n#endif"; - }, +var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0\n\tvClipPosition = - mvPosition.xyz;\n#endif"; - setComponents: function ( x, y, z, w ) { +var color_fragment = "#if defined( USE_COLOR_ALPHA )\n\tdiffuseColor *= vColor;\n#elif defined( USE_COLOR )\n\tdiffuseColor.rgb *= vColor;\n#endif"; - this.normal.set( x, y, z ); - this.constant = w; +var color_pars_fragment = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR )\n\tvarying vec3 vColor;\n#endif"; - return this; +var color_pars_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvarying vec4 vColor;\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvarying vec3 vColor;\n#endif"; - }, +var color_vertex = "#if defined( USE_COLOR_ALPHA )\n\tvColor = vec4( 1.0 );\n#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )\n\tvColor = vec3( 1.0 );\n#endif\n#ifdef USE_COLOR\n\tvColor *= color;\n#endif\n#ifdef USE_INSTANCING_COLOR\n\tvColor.xyz *= instanceColor.xyz;\n#endif"; - setFromNormalAndCoplanarPoint: function ( normal, point ) { +var common = "#define PI 3.141592653589793\n#define PI2 6.283185307179586\n#define PI_HALF 1.5707963267948966\n#define RECIPROCAL_PI 0.3183098861837907\n#define RECIPROCAL_PI2 0.15915494309189535\n#define EPSILON 1e-6\n#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\n#define whiteComplement( a ) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nvec3 pow2( const in vec3 x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); }\nfloat average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract( sin( sn ) * c );\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\n#ifdef USE_ALPHAHASH\n\tvarying vec3 vPosition;\n#endif\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat luminance( const in vec3 rgb ) {\n\tconst vec3 weights = vec3( 0.2126729, 0.7151522, 0.0721750 );\n\treturn dot( weights, rgb );\n}\nbool isPerspectiveMatrix( mat4 m ) {\n\treturn m[ 2 ][ 3 ] == - 1.0;\n}\nvec2 equirectUv( in vec3 dir ) {\n\tfloat u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5;\n\tfloat v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\treturn vec2( u, v );\n}\nvec3 BRDF_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n}\nfloat F_Schlick( const in float f0, const in float f90, const in float dotVH ) {\n\tfloat fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH );\n\treturn f0 * ( 1.0 - fresnel ) + ( f90 * fresnel );\n} // validated"; - this.normal.copy( normal ); - this.constant = - point.dot( this.normal ); +var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n\t#define cubeUV_minMipLevel 4.0\n\t#define cubeUV_minTileSize 16.0\n\tfloat getFace( vec3 direction ) {\n\t\tvec3 absDirection = abs( direction );\n\t\tfloat face = - 1.0;\n\t\tif ( absDirection.x > absDirection.z ) {\n\t\t\tif ( absDirection.x > absDirection.y )\n\t\t\t\tface = direction.x > 0.0 ? 0.0 : 3.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t} else {\n\t\t\tif ( absDirection.z > absDirection.y )\n\t\t\t\tface = direction.z > 0.0 ? 2.0 : 5.0;\n\t\t\telse\n\t\t\t\tface = direction.y > 0.0 ? 1.0 : 4.0;\n\t\t}\n\t\treturn face;\n\t}\n\tvec2 getUV( vec3 direction, float face ) {\n\t\tvec2 uv;\n\t\tif ( face == 0.0 ) {\n\t\t\tuv = vec2( direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 1.0 ) {\n\t\t\tuv = vec2( - direction.x, - direction.z ) / abs( direction.y );\n\t\t} else if ( face == 2.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.y ) / abs( direction.z );\n\t\t} else if ( face == 3.0 ) {\n\t\t\tuv = vec2( - direction.z, direction.y ) / abs( direction.x );\n\t\t} else if ( face == 4.0 ) {\n\t\t\tuv = vec2( - direction.x, direction.z ) / abs( direction.y );\n\t\t} else {\n\t\t\tuv = vec2( direction.x, direction.y ) / abs( direction.z );\n\t\t}\n\t\treturn 0.5 * ( uv + 1.0 );\n\t}\n\tvec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) {\n\t\tfloat face = getFace( direction );\n\t\tfloat filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 );\n\t\tmipInt = max( mipInt, cubeUV_minMipLevel );\n\t\tfloat faceSize = exp2( mipInt );\n\t\thighp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0;\n\t\tif ( face > 2.0 ) {\n\t\t\tuv.y += faceSize;\n\t\t\tface -= 3.0;\n\t\t}\n\t\tuv.x += face * faceSize;\n\t\tuv.x += filterInt * 3.0 * cubeUV_minTileSize;\n\t\tuv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize );\n\t\tuv.x *= CUBEUV_TEXEL_WIDTH;\n\t\tuv.y *= CUBEUV_TEXEL_HEIGHT;\n\t\t#ifdef texture2DGradEXT\n\t\t\treturn texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb;\n\t\t#else\n\t\t\treturn texture2D( envMap, uv ).rgb;\n\t\t#endif\n\t}\n\t#define cubeUV_r0 1.0\n\t#define cubeUV_v0 0.339\n\t#define cubeUV_m0 - 2.0\n\t#define cubeUV_r1 0.8\n\t#define cubeUV_v1 0.276\n\t#define cubeUV_m1 - 1.0\n\t#define cubeUV_r4 0.4\n\t#define cubeUV_v4 0.046\n\t#define cubeUV_m4 2.0\n\t#define cubeUV_r5 0.305\n\t#define cubeUV_v5 0.016\n\t#define cubeUV_m5 3.0\n\t#define cubeUV_r6 0.21\n\t#define cubeUV_v6 0.0038\n\t#define cubeUV_m6 4.0\n\tfloat roughnessToMip( float roughness ) {\n\t\tfloat mip = 0.0;\n\t\tif ( roughness >= cubeUV_r1 ) {\n\t\t\tmip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0;\n\t\t} else if ( roughness >= cubeUV_r4 ) {\n\t\t\tmip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1;\n\t\t} else if ( roughness >= cubeUV_r5 ) {\n\t\t\tmip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4;\n\t\t} else if ( roughness >= cubeUV_r6 ) {\n\t\t\tmip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5;\n\t\t} else {\n\t\t\tmip = - 2.0 * log2( 1.16 * roughness );\t\t}\n\t\treturn mip;\n\t}\n\tvec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) {\n\t\tfloat mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP );\n\t\tfloat mipF = fract( mip );\n\t\tfloat mipInt = floor( mip );\n\t\tvec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt );\n\t\tif ( mipF == 0.0 ) {\n\t\t\treturn vec4( color0, 1.0 );\n\t\t} else {\n\t\t\tvec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 );\n\t\t\treturn vec4( mix( color0, color1, mipF ), 1.0 );\n\t\t}\n\t}\n#endif"; - return this; +var defaultnormal_vertex = "vec3 transformedNormal = objectNormal;\n#ifdef USE_INSTANCING\n\tmat3 m = mat3( instanceMatrix );\n\ttransformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );\n\ttransformedNormal = m * transformedNormal;\n#endif\ntransformedNormal = normalMatrix * transformedNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; - }, +var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; - setFromCoplanarPoints: function ( a, b, c ) { +var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias );\n#endif"; - var normal = _vector1.subVectors( c, b ).cross( _vector2.subVectors( a, b ) ).normalize(); +var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv );\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; - // Q: should an error be thrown if normal is zero (e.g. degenerate plane)? +var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; - this.setFromNormalAndCoplanarPoint( normal, a ); +var colorspace_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; - return this; +var colorspace_pars_fragment = "\nconst mat3 LINEAR_SRGB_TO_LINEAR_DISPLAY_P3 = mat3(\n\tvec3( 0.8224621, 0.177538, 0.0 ),\n\tvec3( 0.0331941, 0.9668058, 0.0 ),\n\tvec3( 0.0170827, 0.0723974, 0.9105199 )\n);\nconst mat3 LINEAR_DISPLAY_P3_TO_LINEAR_SRGB = mat3(\n\tvec3( 1.2249401, - 0.2249404, 0.0 ),\n\tvec3( - 0.0420569, 1.0420571, 0.0 ),\n\tvec3( - 0.0196376, - 0.0786361, 1.0982735 )\n);\nvec4 LinearSRGBToLinearDisplayP3( in vec4 value ) {\n\treturn vec4( value.rgb * LINEAR_SRGB_TO_LINEAR_DISPLAY_P3, value.a );\n}\nvec4 LinearDisplayP3ToLinearSRGB( in vec4 value ) {\n\treturn vec4( value.rgb * LINEAR_DISPLAY_P3_TO_LINEAR_SRGB, value.a );\n}\nvec4 LinearTransferOETF( in vec4 value ) {\n\treturn value;\n}\nvec4 sRGBTransferOETF( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn sRGBTransferOETF( value );\n}"; - }, +var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToFrag;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToFrag = normalize( vWorldPosition - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToFrag, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; - clone: function () { +var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; - return new this.constructor().copy( this ); +var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; - }, +var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; - copy: function ( plane ) { +var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex;\n\t\tif ( isOrthographic ) {\n\t\t\tcameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) );\n\t\t} else {\n\t\t\tcameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\t}\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; - this.normal.copy( plane.normal ); - this.constant = plane.constant; +var fog_vertex = "#ifdef USE_FOG\n\tvFogDepth = - mvPosition.z;\n#endif"; - return this; +var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float vFogDepth;\n#endif"; - }, +var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, vFogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; - normalize: function () { +var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float vFogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; - // Note: will lead to a divide by zero if the plane is invalid. +var gradientmap_pars_fragment = "#ifdef USE_GRADIENTMAP\n\tuniform sampler2D gradientMap;\n#endif\nvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\tfloat dotNL = dot( normal, lightDirection );\n\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t#ifdef USE_GRADIENTMAP\n\t\treturn vec3( texture2D( gradientMap, coord ).r );\n\t#else\n\t\tvec2 fw = fwidth( coord ) * 0.5;\n\t\treturn mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) );\n\t#endif\n}"; - var inverseNormalLength = 1.0 / this.normal.length(); - this.normal.multiplyScalar( inverseNormalLength ); - this.constant *= inverseNormalLength; +var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\treflectedLight.indirectDiffuse += lightMapIrradiance;\n#endif"; - return this; +var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; - }, +var lights_lambert_fragment = "LambertMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularStrength = specularStrength;"; - negate: function () { +var lights_lambert_pars_fragment = "varying vec3 vViewPosition;\nstruct LambertMaterial {\n\tvec3 diffuseColor;\n\tfloat specularStrength;\n};\nvoid RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Lambert\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Lambert"; - this.constant *= - 1; - this.normal.negate(); +var lights_pars_begin = "uniform bool receiveShadow;\nuniform vec3 ambientLightColor;\n#if defined( USE_LIGHT_PROBES )\n\tuniform vec3 lightProbe[ 9 ];\n#endif\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) {\n\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\treturn irradiance;\n}\nfloat getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n\t#if defined ( LEGACY_LIGHTS )\n\t\tif ( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\t\treturn pow( saturate( - lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t\t}\n\t\treturn 1.0;\n\t#else\n\t\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\t\tif ( cutoffDistance > 0.0 ) {\n\t\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t\t}\n\t\treturn distanceFalloff;\n\t#endif\n}\nfloat getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {\n\treturn smoothstep( coneCosine, penumbraCosine, angleCosine );\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) {\n\t\tlight.color = directionalLight.color;\n\t\tlight.direction = directionalLight.direction;\n\t\tlight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\t\tvec3 lVector = pointLight.position - geometryPosition;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tlight.color = pointLight.color;\n\t\tlight.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) {\n\t\tvec3 lVector = spotLight.position - geometryPosition;\n\t\tlight.direction = normalize( lVector );\n\t\tfloat angleCos = dot( light.direction, spotLight.direction );\n\t\tfloat spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\tif ( spotAttenuation > 0.0 ) {\n\t\t\tfloat lightDistance = length( lVector );\n\t\t\tlight.color = spotLight.color * spotAttenuation;\n\t\t\tlight.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tlight.visible = ( light.color != vec3( 0.0 ) );\n\t\t} else {\n\t\t\tlight.color = vec3( 0.0 );\n\t\t\tlight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) {\n\t\tfloat dotNL = dot( normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\treturn irradiance;\n\t}\n#endif"; - return this; +var envmap_physical_pars_fragment = "#ifdef USE_ENVMAP\n\tvec3 getIBLIrradiance( const in vec3 normal ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, worldNormal, 1.0 );\n\t\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\tvec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) {\n\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\tvec3 reflectVec = reflect( - viewDir, normal );\n\t\t\treflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, reflectVec, roughness );\n\t\t\treturn envMapColor.rgb * envMapIntensity;\n\t\t#else\n\t\t\treturn vec3( 0.0 );\n\t\t#endif\n\t}\n\t#ifdef USE_ANISOTROPY\n\t\tvec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) {\n\t\t\t#ifdef ENVMAP_TYPE_CUBE_UV\n\t\t\t\tvec3 bentNormal = cross( bitangent, viewDir );\n\t\t\t\tbentNormal = normalize( cross( bentNormal, bitangent ) );\n\t\t\t\tbentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) );\n\t\t\t\treturn getIBLRadiance( viewDir, bentNormal, roughness );\n\t\t\t#else\n\t\t\t\treturn vec3( 0.0 );\n\t\t\t#endif\n\t\t}\n\t#endif\n#endif"; - }, +var lights_toon_fragment = "ToonMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;"; - distanceToPoint: function ( point ) { +var lights_toon_pars_fragment = "varying vec3 vViewPosition;\nstruct ToonMaterial {\n\tvec3 diffuseColor;\n};\nvoid RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\tvec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_Toon\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Toon"; - return this.normal.dot( point ) + this.constant; +var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; - }, +var lights_phong_pars_fragment = "varying vec3 vViewPosition;\nstruct BlinnPhongMaterial {\n\tvec3 diffuseColor;\n\tvec3 specularColor;\n\tfloat specularShininess;\n\tfloat specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong"; - distanceToSphere: function ( sphere ) { +var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nvec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) );\nfloat geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );\nmaterial.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness;\nmaterial.roughness = min( material.roughness, 1.0 );\n#ifdef IOR\n\tmaterial.ior = ior;\n\t#ifdef USE_SPECULAR\n\t\tfloat specularIntensityFactor = specularIntensity;\n\t\tvec3 specularColorFactor = specularColor;\n\t\t#ifdef USE_SPECULAR_COLORMAP\n\t\t\tspecularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb;\n\t\t#endif\n\t\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\t\tspecularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a;\n\t\t#endif\n\t\tmaterial.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor );\n\t#else\n\t\tfloat specularIntensityFactor = 1.0;\n\t\tvec3 specularColorFactor = vec3( 1.0 );\n\t\tmaterial.specularF90 = 1.0;\n\t#endif\n\tmaterial.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n\tmaterial.specularF90 = 1.0;\n#endif\n#ifdef USE_CLEARCOAT\n\tmaterial.clearcoat = clearcoat;\n\tmaterial.clearcoatRoughness = clearcoatRoughness;\n\tmaterial.clearcoatF0 = vec3( 0.04 );\n\tmaterial.clearcoatF90 = 1.0;\n\t#ifdef USE_CLEARCOATMAP\n\t\tmaterial.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x;\n\t#endif\n\t#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\t\tmaterial.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y;\n\t#endif\n\tmaterial.clearcoat = saturate( material.clearcoat );\tmaterial.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 );\n\tmaterial.clearcoatRoughness += geometryRoughness;\n\tmaterial.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 );\n#endif\n#ifdef USE_IRIDESCENCE\n\tmaterial.iridescence = iridescence;\n\tmaterial.iridescenceIOR = iridescenceIOR;\n\t#ifdef USE_IRIDESCENCEMAP\n\t\tmaterial.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r;\n\t#endif\n\t#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\t\tmaterial.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum;\n\t#else\n\t\tmaterial.iridescenceThickness = iridescenceThicknessMaximum;\n\t#endif\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheenColor;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tmaterial.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb;\n\t#endif\n\tmaterial.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 );\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tmaterial.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\t#ifdef USE_ANISOTROPYMAP\n\t\tmat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x );\n\t\tvec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb;\n\t\tvec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b;\n\t#else\n\t\tvec2 anisotropyV = anisotropyVector;\n\t#endif\n\tmaterial.anisotropy = length( anisotropyV );\n\tanisotropyV /= material.anisotropy;\n\tmaterial.anisotropy = saturate( material.anisotropy );\n\tmaterial.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) );\n\tmaterial.anisotropyT = tbn[ 0 ] * anisotropyV.x - tbn[ 1 ] * anisotropyV.y;\n\tmaterial.anisotropyB = tbn[ 1 ] * anisotropyV.x + tbn[ 0 ] * anisotropyV.y;\n#endif"; - return this.distanceToPoint( sphere.center ) - sphere.radius; +var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3 diffuseColor;\n\tfloat roughness;\n\tvec3 specularColor;\n\tfloat specularF90;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat clearcoat;\n\t\tfloat clearcoatRoughness;\n\t\tvec3 clearcoatF0;\n\t\tfloat clearcoatF90;\n\t#endif\n\t#ifdef USE_IRIDESCENCE\n\t\tfloat iridescence;\n\t\tfloat iridescenceIOR;\n\t\tfloat iridescenceThickness;\n\t\tvec3 iridescenceFresnel;\n\t\tvec3 iridescenceF0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tvec3 sheenColor;\n\t\tfloat sheenRoughness;\n\t#endif\n\t#ifdef IOR\n\t\tfloat ior;\n\t#endif\n\t#ifdef USE_TRANSMISSION\n\t\tfloat transmission;\n\t\tfloat transmissionAlpha;\n\t\tfloat thickness;\n\t\tfloat attenuationDistance;\n\t\tvec3 attenuationColor;\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat anisotropy;\n\t\tfloat alphaT;\n\t\tvec3 anisotropyT;\n\t\tvec3 anisotropyB;\n\t#endif\n};\nvec3 clearcoatSpecular = vec3( 0.0 );\nvec3 sheenSpecular = vec3( 0.0 );\nvec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) {\n float x = clamp( 1.0 - dotVH, 0.0, 1.0 );\n float x2 = x * x;\n float x5 = clamp( x * x2 * x2, 0.0, 0.9999 );\n return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 );\n}\nfloat V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\n#ifdef USE_ANISOTROPY\n\tfloat V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) {\n\t\tfloat gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) );\n\t\tfloat gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) );\n\t\tfloat v = 0.5 / ( gv + gl );\n\t\treturn saturate(v);\n\t}\n\tfloat D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) {\n\t\tfloat a2 = alphaT * alphaB;\n\t\thighp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH );\n\t\thighp float v2 = dot( v, v );\n\t\tfloat w2 = a2 / v2;\n\t\treturn RECIPROCAL_PI * a2 * pow2 ( w2 );\n\t}\n#endif\n#ifdef USE_CLEARCOAT\n\tvec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) {\n\t\tvec3 f0 = material.clearcoatF0;\n\t\tfloat f90 = material.clearcoatF90;\n\t\tfloat roughness = material.clearcoatRoughness;\n\t\tfloat alpha = pow2( roughness );\n\t\tvec3 halfDir = normalize( lightDir + viewDir );\n\t\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\t\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\t\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\t\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\t\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t\treturn F * ( V * D );\n\t}\n#endif\nvec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {\n\tvec3 f0 = material.specularColor;\n\tfloat f90 = material.specularF90;\n\tfloat roughness = material.roughness;\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotVH = saturate( dot( viewDir, halfDir ) );\n\tvec3 F = F_Schlick( f0, f90, dotVH );\n\t#ifdef USE_IRIDESCENCE\n\t\tF = mix( F, material.iridescenceFresnel, material.iridescence );\n\t#endif\n\t#ifdef USE_ANISOTROPY\n\t\tfloat dotTL = dot( material.anisotropyT, lightDir );\n\t\tfloat dotTV = dot( material.anisotropyT, viewDir );\n\t\tfloat dotTH = dot( material.anisotropyT, halfDir );\n\t\tfloat dotBL = dot( material.anisotropyB, lightDir );\n\t\tfloat dotBV = dot( material.anisotropyB, viewDir );\n\t\tfloat dotBH = dot( material.anisotropyB, halfDir );\n\t\tfloat V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL );\n\t\tfloat D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH );\n\t#else\n\t\tfloat V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\t\tfloat D = D_GGX( alpha, dotNH );\n\t#endif\n\treturn F * ( V * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie( float roughness, float dotNH ) {\n\tfloat alpha = pow2( roughness );\n\tfloat invAlpha = 1.0 / alpha;\n\tfloat cos2h = dotNH * dotNH;\n\tfloat sin2h = max( 1.0 - cos2h, 0.0078125 );\n\treturn ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI );\n}\nfloat V_Neubelt( float dotNV, float dotNL ) {\n\treturn saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) );\n}\nvec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) {\n\tvec3 halfDir = normalize( lightDir + viewDir );\n\tfloat dotNL = saturate( dot( normal, lightDir ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat D = D_Charlie( sheenRoughness, dotNH );\n\tfloat V = V_Neubelt( dotNV, dotNL );\n\treturn sheenColor * ( D * V );\n}\n#endif\nfloat IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat r2 = roughness * roughness;\n\tfloat a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95;\n\tfloat b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72;\n\tfloat DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) );\n\treturn saturate( DG * RECIPROCAL_PI );\n}\nvec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\tvec2 fab = vec2( - 1.04, 1.04 ) * a004 + r.zw;\n\treturn fab;\n}\nvec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) {\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\treturn specularColor * fab.x + specularF90 * fab.y;\n}\n#ifdef USE_IRIDESCENCE\nvoid computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#else\nvoid computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n#endif\n\tvec2 fab = DFGApprox( normal, viewDir, roughness );\n\t#ifdef USE_IRIDESCENCE\n\t\tvec3 Fr = mix( specularColor, iridescenceF0, iridescence );\n\t#else\n\t\tvec3 Fr = specularColor;\n\t#endif\n\tvec3 FssEss = Fr * fab.x + specularF90 * fab.y;\n\tfloat Ess = fab.x + fab.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometryNormal;\n\t\tvec3 viewDir = geometryViewDir;\n\t\tvec3 position = geometryPosition;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.roughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometryNormal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = dotNLcc * directLight.color;\n\t\tclearcoatSpecular += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness );\n\t#endif\n\treflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material );\n\treflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatSpecular += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness );\n\t#endif\n\t#ifdef USE_SHEEN\n\t\tsheenSpecular += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness );\n\t#endif\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\t#ifdef USE_IRIDESCENCE\n\t\tcomputeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering );\n\t#else\n\t\tcomputeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering );\n\t#endif\n\tvec3 totalScattering = singleScattering + multiScattering;\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) );\n\treflectedLight.indirectSpecular += radiance * singleScattering;\n\treflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; - }, +var lights_fragment_begin = "\nvec3 geometryPosition = - vViewPosition;\nvec3 geometryNormal = normal;\nvec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );\nvec3 geometryClearcoatNormal;\n#ifdef USE_CLEARCOAT\n\tgeometryClearcoatNormal = clearcoatNormal;\n#endif\n#ifdef USE_IRIDESCENCE\n\tfloat dotNVi = saturate( dot( normal, geometryViewDir ) );\n\tif ( material.iridescenceThickness == 0.0 ) {\n\t\tmaterial.iridescence = 0.0;\n\t} else {\n\t\tmaterial.iridescence = saturate( material.iridescence );\n\t}\n\tif ( material.iridescence > 0.0 ) {\n\t\tmaterial.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );\n\t\tmaterial.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );\n\t}\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointLightInfo( pointLight, geometryPosition, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tpointLightShadow = pointLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\tvec4 spotColor;\n\tvec3 spotLightCoord;\n\tbool inSpotLightMap;\n\t#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotLightInfo( spotLight, geometryPosition, directLight );\n\t\t#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX\n\t\t#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS\n\t\t#else\n\t\t#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )\n\t\t#endif\n\t\t#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )\n\t\t\tspotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;\n\t\t\tinSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );\n\t\t\tspotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );\n\t\t\tdirectLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;\n\t\t#endif\n\t\t#undef SPOT_LIGHT_MAP_INDEX\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tspotLightShadow = spotLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLightShadow;\n\t#endif\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalLightInfo( directionalLight, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectionalLightShadow = directionalLightShadows[ i ];\n\t\tdirectLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n\t}\n\t#pragma unroll_loop_end\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\t#if defined( USE_LIGHT_PROBES )\n\t\tirradiance += getLightProbeIrradiance( lightProbe, geometryNormal );\n\t#endif\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; - projectPoint: function ( point, target ) { +var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\tvec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity;\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getIBLIrradiance( geometryNormal );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\t#ifdef USE_ANISOTROPY\n\t\tradiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy );\n\t#else\n\t\tradiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness );\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tclearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness );\n\t#endif\n#endif"; - if ( target === undefined ) { +var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n#endif"; - console.warn( 'THREE.Plane: .projectPoint() target is now required' ); - target = new Vector3(); +var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; - } +var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n\tvarying float vIsPerspective;\n#endif"; - return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point ); +var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t\tvarying float vIsPerspective;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; - }, +var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t\tvIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) );\n\t#else\n\t\tif ( isPerspectiveMatrix( projectionMatrix ) ) {\n\t\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\t\tgl_Position.z *= gl_Position.w;\n\t\t}\n\t#endif\n#endif"; - intersectLine: function ( line, target ) { +var map_fragment = "#ifdef USE_MAP\n\tvec4 sampledDiffuseColor = texture2D( map, vMapUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\tsampledDiffuseColor = vec4( mix( pow( sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), sampledDiffuseColor.rgb * 0.0773993808, vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) ), sampledDiffuseColor.w );\n\t\n\t#endif\n\tdiffuseColor *= sampledDiffuseColor;\n#endif"; - if ( target === undefined ) { +var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; - console.warn( 'THREE.Plane: .intersectLine() target is now required' ); - target = new Vector3(); +var map_particle_fragment = "#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\t#if defined( USE_POINTS_UV )\n\t\tvec2 uv = vUv;\n\t#else\n\t\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\t#endif\n#endif\n#ifdef USE_MAP\n\tdiffuseColor *= texture2D( map, uv );\n#endif\n#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, uv ).g;\n#endif"; - } +var map_particle_pars_fragment = "#if defined( USE_POINTS_UV )\n\tvarying vec2 vUv;\n#else\n\t#if defined( USE_MAP ) || defined( USE_ALPHAMAP )\n\t\tuniform mat3 uvTransform;\n\t#endif\n#endif\n#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; - var direction = line.delta( _vector1 ); +var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; - var denominator = this.normal.dot( direction ); +var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; - if ( denominator === 0 ) { +var morphcolor_vertex = "#if defined( USE_MORPHCOLORS ) && defined( MORPHTARGETS_TEXTURE )\n\tvColor *= morphTargetBaseInfluence;\n\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t#if defined( USE_COLOR_ALPHA )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ];\n\t\t#elif defined( USE_COLOR )\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ];\n\t\t#endif\n\t}\n#endif"; - // line is coplanar, return origin - if ( this.distanceToPoint( line.start ) === 0 ) { +var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\tobjectNormal += morphNormal0 * morphTargetInfluences[ 0 ];\n\t\tobjectNormal += morphNormal1 * morphTargetInfluences[ 1 ];\n\t\tobjectNormal += morphNormal2 * morphTargetInfluences[ 2 ];\n\t\tobjectNormal += morphNormal3 * morphTargetInfluences[ 3 ];\n\t#endif\n#endif"; - return target.copy( line.start ); +var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\tuniform float morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tuniform float morphTargetInfluences[ MORPHTARGETS_COUNT ];\n\t\tuniform sampler2DArray morphTargetsTexture;\n\t\tuniform ivec2 morphTargetsTextureSize;\n\t\tvec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) {\n\t\t\tint texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset;\n\t\t\tint y = texelIndex / morphTargetsTextureSize.x;\n\t\t\tint x = texelIndex - y * morphTargetsTextureSize.x;\n\t\t\tivec3 morphUV = ivec3( x, y, morphTargetIndex );\n\t\t\treturn texelFetch( morphTargetsTexture, morphUV, 0 );\n\t\t}\n\t#else\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\tuniform float morphTargetInfluences[ 8 ];\n\t\t#else\n\t\t\tuniform float morphTargetInfluences[ 4 ];\n\t\t#endif\n\t#endif\n#endif"; - } +var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed *= morphTargetBaseInfluence;\n\t#ifdef MORPHTARGETS_TEXTURE\n\t\tfor ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) {\n\t\t\tif ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ];\n\t\t}\n\t#else\n\t\ttransformed += morphTarget0 * morphTargetInfluences[ 0 ];\n\t\ttransformed += morphTarget1 * morphTargetInfluences[ 1 ];\n\t\ttransformed += morphTarget2 * morphTargetInfluences[ 2 ];\n\t\ttransformed += morphTarget3 * morphTargetInfluences[ 3 ];\n\t\t#ifndef USE_MORPHNORMALS\n\t\t\ttransformed += morphTarget4 * morphTargetInfluences[ 4 ];\n\t\t\ttransformed += morphTarget5 * morphTargetInfluences[ 5 ];\n\t\t\ttransformed += morphTarget6 * morphTargetInfluences[ 6 ];\n\t\t\ttransformed += morphTarget7 * morphTargetInfluences[ 7 ];\n\t\t#endif\n\t#endif\n#endif"; - // Unsure if this is the correct method to handle this case. - return undefined; +var normal_fragment_begin = "float faceDirection = gl_FrontFacing ? 1.0 : - 1.0;\n#ifdef FLAT_SHADED\n\tvec3 fdx = dFdx( vViewPosition );\n\tvec3 fdy = dFdy( vViewPosition );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal *= faceDirection;\n\t#endif\n#endif\n#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY )\n\t#ifdef USE_TANGENT\n\t\tmat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\t#else\n\t\tmat3 tbn = getTangentFrame( - vViewPosition, normal,\n\t\t#if defined( USE_NORMALMAP )\n\t\t\tvNormalMapUv\n\t\t#elif defined( USE_CLEARCOAT_NORMALMAP )\n\t\t\tvClearcoatNormalMapUv\n\t\t#else\n\t\t\tvUv\n\t\t#endif\n\t\t);\n\t#endif\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\t\ttbn[0] *= faceDirection;\n\t\ttbn[1] *= faceDirection;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\t#ifdef USE_TANGENT\n\t\tmat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal );\n\t#else\n\t\tmat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv );\n\t#endif\n\t#if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED )\n\t\ttbn2[0] *= faceDirection;\n\t\ttbn2[1] *= faceDirection;\n\t#endif\n#endif\nvec3 nonPerturbedNormal = normal;"; - } +var normal_fragment_maps = "#ifdef USE_NORMALMAP_OBJECTSPACE\n\tnormal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * faceDirection;\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( USE_NORMALMAP_TANGENTSPACE )\n\tvec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0;\n\tmapN.xy *= normalScale;\n\tnormal = normalize( tbn * mapN );\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );\n#endif"; - var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; +var normal_pars_fragment = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - if ( t < 0 || t > 1 ) { +var normal_pars_vertex = "#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif"; - return undefined; +var normal_vertex = "#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif"; - } +var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef USE_NORMALMAP_OBJECTSPACE\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) )\n\tmat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) {\n\t\tvec3 q0 = dFdx( eye_pos.xyz );\n\t\tvec3 q1 = dFdy( eye_pos.xyz );\n\t\tvec2 st0 = dFdx( uv.st );\n\t\tvec2 st1 = dFdy( uv.st );\n\t\tvec3 N = surf_norm;\n\t\tvec3 q1perp = cross( q1, N );\n\t\tvec3 q0perp = cross( N, q0 );\n\t\tvec3 T = q1perp * st0.x + q0perp * st1.x;\n\t\tvec3 B = q1perp * st0.y + q0perp * st1.y;\n\t\tfloat det = max( dot( T, T ), dot( B, B ) );\n\t\tfloat scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det );\n\t\treturn mat3( T * scale, B * scale, N );\n\t}\n#endif"; - return target.copy( direction ).multiplyScalar( t ).add( line.start ); +var clearcoat_normal_fragment_begin = "#ifdef USE_CLEARCOAT\n\tvec3 clearcoatNormal = nonPerturbedNormal;\n#endif"; - }, +var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tvec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0;\n\tclearcoatMapN.xy *= clearcoatNormalScale;\n\tclearcoatNormal = normalize( tbn2 * clearcoatMapN );\n#endif"; - intersectsLine: function ( line ) { +var clearcoat_pars_fragment = "#ifdef USE_CLEARCOATMAP\n\tuniform sampler2D clearcoatMap;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform sampler2D clearcoatRoughnessMap;\n#endif"; - // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. +var iridescence_pars_fragment = "#ifdef USE_IRIDESCENCEMAP\n\tuniform sampler2D iridescenceMap;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tuniform sampler2D iridescenceThicknessMap;\n#endif"; - var startSign = this.distanceToPoint( line.start ); - var endSign = this.distanceToPoint( line.end ); +var opaque_fragment = "#ifdef OPAQUE\ndiffuseColor.a = 1.0;\n#endif\n#ifdef USE_TRANSMISSION\ndiffuseColor.a *= material.transmissionAlpha;\n#endif\ngl_FragColor = vec4( outgoingLight, diffuseColor.a );"; - return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); +var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec2 packDepthToRG( in highp float v ) {\n\treturn packDepthToRGBA( v ).yx;\n}\nfloat unpackRGToDepth( const in highp vec2 v ) {\n\treturn unpackRGBAToDepth( vec4( v.xy, 0.0, 0.0 ) );\n}\nvec4 pack2HalfToRGBA( vec2 v ) {\n\tvec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) );\n\treturn vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w );\n}\nvec2 unpackRGBATo2Half( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn depth * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * depth - far );\n}"; - }, +var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; - intersectsBox: function ( box ) { +var project_vertex = "vec4 mvPosition = vec4( transformed, 1.0 );\n#ifdef USE_INSTANCING\n\tmvPosition = instanceMatrix * mvPosition;\n#endif\nmvPosition = modelViewMatrix * mvPosition;\ngl_Position = projectionMatrix * mvPosition;"; - return box.intersectsPlane( this ); +var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; - }, +var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; - intersectsSphere: function ( sphere ) { +var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; - return sphere.intersectsPlane( this ); +var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; - }, +var shadowmap_pars_fragment = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#if NUM_SPOT_LIGHT_MAPS > 0\n\tuniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn unpackRGBATo2Half( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0;\n\t\tbool frustumTest = inFrustum && shadowCoord.z <= 1.0;\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx = texelSize.x;\n\t\t\tfloat dy = texelSize.y;\n\t\t\tvec2 uv = shadowCoord.xy;\n\t\t\tvec2 f = fract( uv * shadowMapSize + 0.5 );\n\t\t\tuv -= f * texelSize;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, uv, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ),\n\t\t\t\t\t f.x ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t f.y ) +\n\t\t\t\tmix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ),\n\t\t\t\t\t\t f.x ),\n\t\t\t\t\t f.y )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; - coplanarPoint: function ( target ) { +var shadowmap_pars_vertex = "#if NUM_SPOT_LIGHT_COORDS > 0\n\tuniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ];\n\tvarying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ];\n#endif\n#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tstruct DirectionalLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tstruct SpotLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t};\n\t\tuniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tstruct PointLightShadow {\n\t\t\tfloat shadowBias;\n\t\t\tfloat shadowNormalBias;\n\t\t\tfloat shadowRadius;\n\t\t\tvec2 shadowMapSize;\n\t\t\tfloat shadowCameraNear;\n\t\t\tfloat shadowCameraFar;\n\t\t};\n\t\tuniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; - if ( target === undefined ) { +var shadowmap_vertex = "#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 )\n\tvec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\tvec4 shadowWorldPosition;\n#endif\n#if defined( USE_SHADOWMAP )\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\t#pragma unroll_loop_start\n\t\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\t\tshadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 );\n\t\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition;\n\t\t}\n\t\t#pragma unroll_loop_end\n\t#endif\n#endif\n#if NUM_SPOT_LIGHT_COORDS > 0\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) {\n\t\tshadowWorldPosition = worldPosition;\n\t\t#if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\t\tshadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias;\n\t\t#endif\n\t\tvSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition;\n\t}\n\t#pragma unroll_loop_end\n#endif"; - console.warn( 'THREE.Plane: .coplanarPoint() target is now required' ); - target = new Vector3(); +var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLightShadow directionalLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLightShadow spotLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLightShadow pointLight;\n\t#pragma unroll_loop_start\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLightShadows[ i ];\n\t\tshadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#pragma unroll_loop_end\n\t#endif\n\t#endif\n\treturn shadow;\n}"; - } +var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; - return target.copy( this.normal ).multiplyScalar( - this.constant ); +var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\tuniform highp sampler2D boneTexture;\n\tuniform int boneTextureSize;\n\tmat4 getBoneMatrix( const in float i ) {\n\t\tfloat j = i * 4.0;\n\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\ty = dy * ( y + 0.5 );\n\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\treturn bone;\n\t}\n#endif"; - }, +var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; - applyMatrix4: function ( matrix, optionalNormalMatrix ) { +var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; - var normalMatrix = optionalNormalMatrix || _normalMatrix.getNormalMatrix( matrix ); +var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vSpecularMapUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; - var referencePoint = this.coplanarPoint( _vector1 ).applyMatrix4( matrix ); +var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; - var normal = this.normal.applyMatrix3( normalMatrix ).normalize(); +var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; - this.constant = - referencePoint.dot( normal ); +var tonemapping_pars_fragment = "#ifndef saturate\n#define saturate( a ) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn saturate( toneMappingExposure * color );\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 RRTAndODTFit( vec3 v ) {\n\tvec3 a = v * ( v + 0.0245786 ) - 0.000090537;\n\tvec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081;\n\treturn a / b;\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tconst mat3 ACESInputMat = mat3(\n\t\tvec3( 0.59719, 0.07600, 0.02840 ),\t\tvec3( 0.35458, 0.90834, 0.13383 ),\n\t\tvec3( 0.04823, 0.01566, 0.83777 )\n\t);\n\tconst mat3 ACESOutputMat = mat3(\n\t\tvec3( 1.60475, -0.10208, -0.00327 ),\t\tvec3( -0.53108, 1.10813, -0.07276 ),\n\t\tvec3( -0.07367, -0.00605, 1.07602 )\n\t);\n\tcolor *= toneMappingExposure / 0.6;\n\tcolor = ACESInputMat * color;\n\tcolor = RRTAndODTFit( color );\n\tcolor = ACESOutputMat * color;\n\treturn saturate( color );\n}\nvec3 CustomToneMapping( vec3 color ) { return color; }"; - return this; +var transmission_fragment = "#ifdef USE_TRANSMISSION\n\tmaterial.transmission = transmission;\n\tmaterial.transmissionAlpha = 1.0;\n\tmaterial.thickness = thickness;\n\tmaterial.attenuationDistance = attenuationDistance;\n\tmaterial.attenuationColor = attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tmaterial.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tmaterial.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g;\n\t#endif\n\tvec3 pos = vWorldPosition;\n\tvec3 v = normalize( cameraPosition - pos );\n\tvec3 n = inverseTransformDirection( normal, viewMatrix );\n\tvec4 transmitted = getIBLVolumeRefraction(\n\t\tn, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90,\n\t\tpos, modelMatrix, viewMatrix, projectionMatrix, material.ior, material.thickness,\n\t\tmaterial.attenuationColor, material.attenuationDistance );\n\tmaterial.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission );\n\ttotalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission );\n#endif"; - }, +var transmission_pars_fragment = "#ifdef USE_TRANSMISSION\n\tuniform float transmission;\n\tuniform float thickness;\n\tuniform float attenuationDistance;\n\tuniform vec3 attenuationColor;\n\t#ifdef USE_TRANSMISSIONMAP\n\t\tuniform sampler2D transmissionMap;\n\t#endif\n\t#ifdef USE_THICKNESSMAP\n\t\tuniform sampler2D thicknessMap;\n\t#endif\n\tuniform vec2 transmissionSamplerSize;\n\tuniform sampler2D transmissionSamplerMap;\n\tuniform mat4 modelMatrix;\n\tuniform mat4 projectionMatrix;\n\tvarying vec3 vWorldPosition;\n\tfloat w0( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 );\n\t}\n\tfloat w1( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 );\n\t}\n\tfloat w2( float a ){\n\t\treturn ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 );\n\t}\n\tfloat w3( float a ) {\n\t\treturn ( 1.0 / 6.0 ) * ( a * a * a );\n\t}\n\tfloat g0( float a ) {\n\t\treturn w0( a ) + w1( a );\n\t}\n\tfloat g1( float a ) {\n\t\treturn w2( a ) + w3( a );\n\t}\n\tfloat h0( float a ) {\n\t\treturn - 1.0 + w1( a ) / ( w0( a ) + w1( a ) );\n\t}\n\tfloat h1( float a ) {\n\t\treturn 1.0 + w3( a ) / ( w2( a ) + w3( a ) );\n\t}\n\tvec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) {\n\t\tuv = uv * texelSize.zw + 0.5;\n\t\tvec2 iuv = floor( uv );\n\t\tvec2 fuv = fract( uv );\n\t\tfloat g0x = g0( fuv.x );\n\t\tfloat g1x = g1( fuv.x );\n\t\tfloat h0x = h0( fuv.x );\n\t\tfloat h1x = h1( fuv.x );\n\t\tfloat h0y = h0( fuv.y );\n\t\tfloat h1y = h1( fuv.y );\n\t\tvec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\tvec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy;\n\t\treturn g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) +\n\t\t\tg1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) );\n\t}\n\tvec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) {\n\t\tvec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) );\n\t\tvec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) );\n\t\tvec2 fLodSizeInv = 1.0 / fLodSize;\n\t\tvec2 cLodSizeInv = 1.0 / cLodSize;\n\t\tvec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) );\n\t\tvec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) );\n\t\treturn mix( fSample, cSample, fract( lod ) );\n\t}\n\tvec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) {\n\t\tvec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior );\n\t\tvec3 modelScale;\n\t\tmodelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) );\n\t\tmodelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) );\n\t\tmodelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) );\n\t\treturn normalize( refractionVector ) * thickness * modelScale;\n\t}\n\tfloat applyIorToRoughness( const in float roughness, const in float ior ) {\n\t\treturn roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 );\n\t}\n\tvec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) {\n\t\tfloat lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior );\n\t\treturn textureBicubic( transmissionSamplerMap, fragCoord.xy, lod );\n\t}\n\tvec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tif ( isinf( attenuationDistance ) ) {\n\t\t\treturn vec3( 1.0 );\n\t\t} else {\n\t\t\tvec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance;\n\t\t\tvec3 transmittance = exp( - attenuationCoefficient * transmissionDistance );\t\t\treturn transmittance;\n\t\t}\n\t}\n\tvec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor,\n\t\tconst in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix,\n\t\tconst in mat4 viewMatrix, const in mat4 projMatrix, const in float ior, const in float thickness,\n\t\tconst in vec3 attenuationColor, const in float attenuationDistance ) {\n\t\tvec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix );\n\t\tvec3 refractedRayExit = position + transmissionRay;\n\t\tvec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );\n\t\tvec2 refractionCoords = ndcPos.xy / ndcPos.w;\n\t\trefractionCoords += 1.0;\n\t\trefractionCoords /= 2.0;\n\t\tvec4 transmittedLight = getTransmissionSample( refractionCoords, roughness, ior );\n\t\tvec3 transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance );\n\t\tvec3 attenuatedColor = transmittance * transmittedLight.rgb;\n\t\tvec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness );\n\t\tfloat transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0;\n\t\treturn vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor );\n\t}\n#endif"; - translate: function ( offset ) { +var uv_pars_fragment = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\n\tvarying vec2 vMapUv;\n#endif\n#ifdef USE_ALPHAMAP\n\tvarying vec2 vAlphaMapUv;\n#endif\n#ifdef USE_LIGHTMAP\n\tvarying vec2 vLightMapUv;\n#endif\n#ifdef USE_AOMAP\n\tvarying vec2 vAoMapUv;\n#endif\n#ifdef USE_BUMPMAP\n\tvarying vec2 vBumpMapUv;\n#endif\n#ifdef USE_NORMALMAP\n\tvarying vec2 vNormalMapUv;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tvarying vec2 vEmissiveMapUv;\n#endif\n#ifdef USE_METALNESSMAP\n\tvarying vec2 vMetalnessMapUv;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tvarying vec2 vRoughnessMapUv;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tvarying vec2 vAnisotropyMapUv;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tvarying vec2 vClearcoatMapUv;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tvarying vec2 vClearcoatNormalMapUv;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tvarying vec2 vClearcoatRoughnessMapUv;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tvarying vec2 vIridescenceMapUv;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tvarying vec2 vIridescenceThicknessMapUv;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tvarying vec2 vSheenColorMapUv;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tvarying vec2 vSheenRoughnessMapUv;\n#endif\n#ifdef USE_SPECULARMAP\n\tvarying vec2 vSpecularMapUv;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tvarying vec2 vSpecularColorMapUv;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tvarying vec2 vSpecularIntensityMapUv;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n#endif\n#ifdef USE_THICKNESSMAP\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n#endif"; - this.constant -= offset.dot( this.normal ); +var uv_pars_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvarying vec2 vUv;\n#endif\n#ifdef USE_MAP\n\tuniform mat3 mapTransform;\n\tvarying vec2 vMapUv;\n#endif\n#ifdef USE_ALPHAMAP\n\tuniform mat3 alphaMapTransform;\n\tvarying vec2 vAlphaMapUv;\n#endif\n#ifdef USE_LIGHTMAP\n\tuniform mat3 lightMapTransform;\n\tvarying vec2 vLightMapUv;\n#endif\n#ifdef USE_AOMAP\n\tuniform mat3 aoMapTransform;\n\tvarying vec2 vAoMapUv;\n#endif\n#ifdef USE_BUMPMAP\n\tuniform mat3 bumpMapTransform;\n\tvarying vec2 vBumpMapUv;\n#endif\n#ifdef USE_NORMALMAP\n\tuniform mat3 normalMapTransform;\n\tvarying vec2 vNormalMapUv;\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\tuniform mat3 displacementMapTransform;\n\tvarying vec2 vDisplacementMapUv;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tuniform mat3 emissiveMapTransform;\n\tvarying vec2 vEmissiveMapUv;\n#endif\n#ifdef USE_METALNESSMAP\n\tuniform mat3 metalnessMapTransform;\n\tvarying vec2 vMetalnessMapUv;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tuniform mat3 roughnessMapTransform;\n\tvarying vec2 vRoughnessMapUv;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tuniform mat3 anisotropyMapTransform;\n\tvarying vec2 vAnisotropyMapUv;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tuniform mat3 clearcoatMapTransform;\n\tvarying vec2 vClearcoatMapUv;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform mat3 clearcoatNormalMapTransform;\n\tvarying vec2 vClearcoatNormalMapUv;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tuniform mat3 clearcoatRoughnessMapTransform;\n\tvarying vec2 vClearcoatRoughnessMapUv;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tuniform mat3 sheenColorMapTransform;\n\tvarying vec2 vSheenColorMapUv;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tuniform mat3 sheenRoughnessMapTransform;\n\tvarying vec2 vSheenRoughnessMapUv;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tuniform mat3 iridescenceMapTransform;\n\tvarying vec2 vIridescenceMapUv;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tuniform mat3 iridescenceThicknessMapTransform;\n\tvarying vec2 vIridescenceThicknessMapUv;\n#endif\n#ifdef USE_SPECULARMAP\n\tuniform mat3 specularMapTransform;\n\tvarying vec2 vSpecularMapUv;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tuniform mat3 specularColorMapTransform;\n\tvarying vec2 vSpecularColorMapUv;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tuniform mat3 specularIntensityMapTransform;\n\tvarying vec2 vSpecularIntensityMapUv;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tuniform mat3 transmissionMapTransform;\n\tvarying vec2 vTransmissionMapUv;\n#endif\n#ifdef USE_THICKNESSMAP\n\tuniform mat3 thicknessMapTransform;\n\tvarying vec2 vThicknessMapUv;\n#endif"; - return this; +var uv_vertex = "#if defined( USE_UV ) || defined( USE_ANISOTROPY )\n\tvUv = vec3( uv, 1 ).xy;\n#endif\n#ifdef USE_MAP\n\tvMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ALPHAMAP\n\tvAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_LIGHTMAP\n\tvLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_AOMAP\n\tvAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_BUMPMAP\n\tvBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_NORMALMAP\n\tvNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_DISPLACEMENTMAP\n\tvDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_EMISSIVEMAP\n\tvEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_METALNESSMAP\n\tvMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ROUGHNESSMAP\n\tvRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_ANISOTROPYMAP\n\tvAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOATMAP\n\tvClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOAT_NORMALMAP\n\tvClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_CLEARCOAT_ROUGHNESSMAP\n\tvClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_IRIDESCENCEMAP\n\tvIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_IRIDESCENCE_THICKNESSMAP\n\tvIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SHEEN_COLORMAP\n\tvSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SHEEN_ROUGHNESSMAP\n\tvSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULARMAP\n\tvSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULAR_COLORMAP\n\tvSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_SPECULAR_INTENSITYMAP\n\tvSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_TRANSMISSIONMAP\n\tvTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy;\n#endif\n#ifdef USE_THICKNESSMAP\n\tvThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy;\n#endif"; - }, +var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0\n\tvec4 worldPosition = vec4( transformed, 1.0 );\n\t#ifdef USE_INSTANCING\n\t\tworldPosition = instanceMatrix * worldPosition;\n\t#endif\n\tworldPosition = modelMatrix * worldPosition;\n#endif"; - equals: function ( plane ) { +const vertex$h = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; - return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); +const fragment$h = "uniform sampler2D t2D;\nuniform float backgroundIntensity;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\t#ifdef DECODE_VIDEO_TEXTURE\n\t\ttexColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w );\n\t#endif\n\ttexColor.rgb *= backgroundIntensity;\n\tgl_FragColor = texColor;\n\t#include \n\t#include \n}"; - } +const vertex$g = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - } ); +const fragment$g = "#ifdef ENVMAP_TYPE_CUBE\n\tuniform samplerCube envMap;\n#elif defined( ENVMAP_TYPE_CUBE_UV )\n\tuniform sampler2D envMap;\n#endif\nuniform float flipEnvMap;\nuniform float backgroundBlurriness;\nuniform float backgroundIntensity;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 texColor = textureCube( envMap, vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) );\n\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\tvec4 texColor = textureCubeUV( envMap, vWorldDirection, backgroundBlurriness );\n\t#else\n\t\tvec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t#endif\n\ttexColor.rgb *= backgroundIntensity;\n\tgl_FragColor = texColor;\n\t#include \n\t#include \n}"; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://clara.io - */ +const vertex$f = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; - var _sphere$1 = new Sphere(); - var _vector$5 = new Vector3(); +const fragment$f = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldDirection;\nvoid main() {\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\tgl_FragColor = texColor;\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; - function Frustum( p0, p1, p2, p3, p4, p5 ) { +const vertex$e = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvHighPrecisionZW = gl_Position.zw;\n}"; - this.planes = [ +const fragment$e = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvarying vec2 vHighPrecisionZW;\nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat fragCoordZ = 0.5 * vHighPrecisionZW[0] / vHighPrecisionZW[1] + 0.5;\n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( fragCoordZ );\n\t#endif\n}"; - ( p0 !== undefined ) ? p0 : new Plane(), - ( p1 !== undefined ) ? p1 : new Plane(), - ( p2 !== undefined ) ? p2 : new Plane(), - ( p3 !== undefined ) ? p3 : new Plane(), - ( p4 !== undefined ) ? p4 : new Plane(), - ( p5 !== undefined ) ? p5 : new Plane() +const vertex$d = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; - ]; +const fragment$d = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; - } +const vertex$c = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; - Object.assign( Frustum.prototype, { +const fragment$c = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV = equirectUv( direction );\n\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\t#include \n\t#include \n}"; - set: function ( p0, p1, p2, p3, p4, p5 ) { +const vertex$b = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tvLineDistance = scale * lineDistance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var planes = this.planes; +const fragment$b = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - planes[ 0 ].copy( p0 ); - planes[ 1 ].copy( p1 ); - planes[ 2 ].copy( p2 ); - planes[ 3 ].copy( p3 ); - planes[ 4 ].copy( p4 ); - planes[ 5 ].copy( p5 ); +const vertex$a = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#if defined ( USE_ENVMAP ) || defined ( USE_SKINNING )\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - return this; +const fragment$a = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\tvec4 lightMapTexel = texture2D( lightMap, vLightMapUv );\n\t\treflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - }, +const vertex$9 = "#define LAMBERT\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - clone: function () { +const fragment$9 = "#define LAMBERT\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - return new this.constructor().copy( this ); +const vertex$8 = "#define MATCAP\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; - }, +const fragment$8 = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t#else\n\t\tvec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - copy: function ( frustum ) { +const vertex$7 = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; - var planes = this.planes; +const fragment$7 = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE )\n\tvarying vec3 vViewPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n\t#ifdef OPAQUE\n\t\tgl_FragColor.a = 1.0;\n\t#endif\n}"; - for ( var i = 0; i < 6; i ++ ) { +const vertex$6 = "#define PHONG\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - planes[ i ].copy( frustum.planes[ i ] ); +const fragment$6 = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - } +const vertex$5 = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifdef USE_TRANSMISSION\n\tvarying vec3 vWorldPosition;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n#ifdef USE_TRANSMISSION\n\tvWorldPosition = worldPosition.xyz;\n#endif\n}"; - return this; +const fragment$5 = "#define STANDARD\n#ifdef PHYSICAL\n\t#define IOR\n\t#define USE_SPECULAR\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef IOR\n\tuniform float ior;\n#endif\n#ifdef USE_SPECULAR\n\tuniform float specularIntensity;\n\tuniform vec3 specularColor;\n\t#ifdef USE_SPECULAR_COLORMAP\n\t\tuniform sampler2D specularColorMap;\n\t#endif\n\t#ifdef USE_SPECULAR_INTENSITYMAP\n\t\tuniform sampler2D specularIntensityMap;\n\t#endif\n#endif\n#ifdef USE_CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_IRIDESCENCE\n\tuniform float iridescence;\n\tuniform float iridescenceIOR;\n\tuniform float iridescenceThicknessMinimum;\n\tuniform float iridescenceThicknessMaximum;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheenColor;\n\tuniform float sheenRoughness;\n\t#ifdef USE_SHEEN_COLORMAP\n\t\tuniform sampler2D sheenColorMap;\n\t#endif\n\t#ifdef USE_SHEEN_ROUGHNESSMAP\n\t\tuniform sampler2D sheenRoughnessMap;\n\t#endif\n#endif\n#ifdef USE_ANISOTROPY\n\tuniform vec2 anisotropyVector;\n\t#ifdef USE_ANISOTROPYMAP\n\t\tuniform sampler2D anisotropyMap;\n\t#endif\n#endif\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;\n\tvec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;\n\t#include \n\tvec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;\n\t#ifdef USE_SHEEN\n\t\tfloat sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );\n\t\toutgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;\n\t#endif\n\t#ifdef USE_CLEARCOAT\n\t\tfloat dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) );\n\t\tvec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );\n\t\toutgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - }, +const vertex$4 = "#define TOON\nvarying vec3 vViewPosition;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; - setFromMatrix: function ( m ) { +const fragment$4 = "#define TOON\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - var planes = this.planes; - var me = m.elements; - var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; - var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; - var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; - var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; +const vertex$3 = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \n#ifdef USE_POINTS_UV\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif\nvoid main() {\n\t#ifdef USE_POINTS_UV\n\t\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); - planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); - planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); - planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); - planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); - planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); +const fragment$3 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - return this; +const vertex$2 = "#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; - }, +const fragment$2 = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n\t#include \n\t#include \n}"; - intersectsObject: function ( object ) { +const vertex$1 = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = isPerspectiveMatrix( projectionMatrix );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; - var geometry = object.geometry; +const fragment$1 = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); +const ShaderChunk = { + alphahash_fragment: alphahash_fragment, + alphahash_pars_fragment: alphahash_pars_fragment, + alphamap_fragment: alphamap_fragment, + alphamap_pars_fragment: alphamap_pars_fragment, + alphatest_fragment: alphatest_fragment, + alphatest_pars_fragment: alphatest_pars_fragment, + aomap_fragment: aomap_fragment, + aomap_pars_fragment: aomap_pars_fragment, + begin_vertex: begin_vertex, + beginnormal_vertex: beginnormal_vertex, + bsdfs: bsdfs, + iridescence_fragment: iridescence_fragment, + bumpmap_pars_fragment: bumpmap_pars_fragment, + clipping_planes_fragment: clipping_planes_fragment, + clipping_planes_pars_fragment: clipping_planes_pars_fragment, + clipping_planes_pars_vertex: clipping_planes_pars_vertex, + clipping_planes_vertex: clipping_planes_vertex, + color_fragment: color_fragment, + color_pars_fragment: color_pars_fragment, + color_pars_vertex: color_pars_vertex, + color_vertex: color_vertex, + common: common, + cube_uv_reflection_fragment: cube_uv_reflection_fragment, + defaultnormal_vertex: defaultnormal_vertex, + displacementmap_pars_vertex: displacementmap_pars_vertex, + displacementmap_vertex: displacementmap_vertex, + emissivemap_fragment: emissivemap_fragment, + emissivemap_pars_fragment: emissivemap_pars_fragment, + colorspace_fragment: colorspace_fragment, + colorspace_pars_fragment: colorspace_pars_fragment, + envmap_fragment: envmap_fragment, + envmap_common_pars_fragment: envmap_common_pars_fragment, + envmap_pars_fragment: envmap_pars_fragment, + envmap_pars_vertex: envmap_pars_vertex, + envmap_physical_pars_fragment: envmap_physical_pars_fragment, + envmap_vertex: envmap_vertex, + fog_vertex: fog_vertex, + fog_pars_vertex: fog_pars_vertex, + fog_fragment: fog_fragment, + fog_pars_fragment: fog_pars_fragment, + gradientmap_pars_fragment: gradientmap_pars_fragment, + lightmap_fragment: lightmap_fragment, + lightmap_pars_fragment: lightmap_pars_fragment, + lights_lambert_fragment: lights_lambert_fragment, + lights_lambert_pars_fragment: lights_lambert_pars_fragment, + lights_pars_begin: lights_pars_begin, + lights_toon_fragment: lights_toon_fragment, + lights_toon_pars_fragment: lights_toon_pars_fragment, + lights_phong_fragment: lights_phong_fragment, + lights_phong_pars_fragment: lights_phong_pars_fragment, + lights_physical_fragment: lights_physical_fragment, + lights_physical_pars_fragment: lights_physical_pars_fragment, + lights_fragment_begin: lights_fragment_begin, + lights_fragment_maps: lights_fragment_maps, + lights_fragment_end: lights_fragment_end, + logdepthbuf_fragment: logdepthbuf_fragment, + logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, + logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, + logdepthbuf_vertex: logdepthbuf_vertex, + map_fragment: map_fragment, + map_pars_fragment: map_pars_fragment, + map_particle_fragment: map_particle_fragment, + map_particle_pars_fragment: map_particle_pars_fragment, + metalnessmap_fragment: metalnessmap_fragment, + metalnessmap_pars_fragment: metalnessmap_pars_fragment, + morphcolor_vertex: morphcolor_vertex, + morphnormal_vertex: morphnormal_vertex, + morphtarget_pars_vertex: morphtarget_pars_vertex, + morphtarget_vertex: morphtarget_vertex, + normal_fragment_begin: normal_fragment_begin, + normal_fragment_maps: normal_fragment_maps, + normal_pars_fragment: normal_pars_fragment, + normal_pars_vertex: normal_pars_vertex, + normal_vertex: normal_vertex, + normalmap_pars_fragment: normalmap_pars_fragment, + clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, + clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, + clearcoat_pars_fragment: clearcoat_pars_fragment, + iridescence_pars_fragment: iridescence_pars_fragment, + opaque_fragment: opaque_fragment, + packing: packing, + premultiplied_alpha_fragment: premultiplied_alpha_fragment, + project_vertex: project_vertex, + dithering_fragment: dithering_fragment, + dithering_pars_fragment: dithering_pars_fragment, + roughnessmap_fragment: roughnessmap_fragment, + roughnessmap_pars_fragment: roughnessmap_pars_fragment, + shadowmap_pars_fragment: shadowmap_pars_fragment, + shadowmap_pars_vertex: shadowmap_pars_vertex, + shadowmap_vertex: shadowmap_vertex, + shadowmask_pars_fragment: shadowmask_pars_fragment, + skinbase_vertex: skinbase_vertex, + skinning_pars_vertex: skinning_pars_vertex, + skinning_vertex: skinning_vertex, + skinnormal_vertex: skinnormal_vertex, + specularmap_fragment: specularmap_fragment, + specularmap_pars_fragment: specularmap_pars_fragment, + tonemapping_fragment: tonemapping_fragment, + tonemapping_pars_fragment: tonemapping_pars_fragment, + transmission_fragment: transmission_fragment, + transmission_pars_fragment: transmission_pars_fragment, + uv_pars_fragment: uv_pars_fragment, + uv_pars_vertex: uv_pars_vertex, + uv_vertex: uv_vertex, + worldpos_vertex: worldpos_vertex, + + background_vert: vertex$h, + background_frag: fragment$h, + backgroundCube_vert: vertex$g, + backgroundCube_frag: fragment$g, + cube_vert: vertex$f, + cube_frag: fragment$f, + depth_vert: vertex$e, + depth_frag: fragment$e, + distanceRGBA_vert: vertex$d, + distanceRGBA_frag: fragment$d, + equirect_vert: vertex$c, + equirect_frag: fragment$c, + linedashed_vert: vertex$b, + linedashed_frag: fragment$b, + meshbasic_vert: vertex$a, + meshbasic_frag: fragment$a, + meshlambert_vert: vertex$9, + meshlambert_frag: fragment$9, + meshmatcap_vert: vertex$8, + meshmatcap_frag: fragment$8, + meshnormal_vert: vertex$7, + meshnormal_frag: fragment$7, + meshphong_vert: vertex$6, + meshphong_frag: fragment$6, + meshphysical_vert: vertex$5, + meshphysical_frag: fragment$5, + meshtoon_vert: vertex$4, + meshtoon_frag: fragment$4, + points_vert: vertex$3, + points_frag: fragment$3, + shadow_vert: vertex$2, + shadow_frag: fragment$2, + sprite_vert: vertex$1, + sprite_frag: fragment$1 +}; + +/** + * Uniforms library for shared webgl shaders + */ + +const UniformsLib = { + + common: { + + diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, + opacity: { value: 1.0 }, + + map: { value: null }, + mapTransform: { value: /*@__PURE__*/ new Matrix3() }, + + alphaMap: { value: null }, + alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + + alphaTest: { value: 0 } + + }, + + specularmap: { + + specularMap: { value: null }, + specularMapTransform: { value: /*@__PURE__*/ new Matrix3() } - _sphere$1.copy( geometry.boundingSphere ).applyMatrix4( object.matrixWorld ); + }, - return this.intersectsSphere( _sphere$1 ); + envmap: { - }, + envMap: { value: null }, + flipEnvMap: { value: - 1 }, + reflectivity: { value: 1.0 }, // basic, lambert, phong + ior: { value: 1.5 }, // physical + refractionRatio: { value: 0.98 }, // basic, lambert, phong - intersectsSprite: function ( sprite ) { + }, - _sphere$1.center.set( 0, 0, 0 ); - _sphere$1.radius = 0.7071067811865476; - _sphere$1.applyMatrix4( sprite.matrixWorld ); + aomap: { - return this.intersectsSphere( _sphere$1 ); + aoMap: { value: null }, + aoMapIntensity: { value: 1 }, + aoMapTransform: { value: /*@__PURE__*/ new Matrix3() } - }, + }, - intersectsSphere: function ( sphere ) { + lightmap: { - var planes = this.planes; - var center = sphere.center; - var negRadius = - sphere.radius; + lightMap: { value: null }, + lightMapIntensity: { value: 1 }, + lightMapTransform: { value: /*@__PURE__*/ new Matrix3() } - for ( var i = 0; i < 6; i ++ ) { + }, - var distance = planes[ i ].distanceToPoint( center ); + bumpmap: { - if ( distance < negRadius ) { + bumpMap: { value: null }, + bumpMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + bumpScale: { value: 1 } - return false; + }, - } + normalmap: { - } + normalMap: { value: null }, + normalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + normalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) } - return true; + }, - }, + displacementmap: { - intersectsBox: function ( box ) { + displacementMap: { value: null }, + displacementMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + displacementScale: { value: 1 }, + displacementBias: { value: 0 } - var planes = this.planes; + }, - for ( var i = 0; i < 6; i ++ ) { + emissivemap: { - var plane = planes[ i ]; + emissiveMap: { value: null }, + emissiveMapTransform: { value: /*@__PURE__*/ new Matrix3() } - // corner at max distance + }, - _vector$5.x = plane.normal.x > 0 ? box.max.x : box.min.x; - _vector$5.y = plane.normal.y > 0 ? box.max.y : box.min.y; - _vector$5.z = plane.normal.z > 0 ? box.max.z : box.min.z; + metalnessmap: { - if ( plane.distanceToPoint( _vector$5 ) < 0 ) { + metalnessMap: { value: null }, + metalnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } - return false; + }, - } + roughnessmap: { - } + roughnessMap: { value: null }, + roughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() } - return true; + }, - }, + gradientmap: { - containsPoint: function ( point ) { + gradientMap: { value: null } - var planes = this.planes; + }, - for ( var i = 0; i < 6; i ++ ) { + fog: { - if ( planes[ i ].distanceToPoint( point ) < 0 ) { + fogDensity: { value: 0.00025 }, + fogNear: { value: 1 }, + fogFar: { value: 2000 }, + fogColor: { value: /*@__PURE__*/ new Color( 0xffffff ) } - return false; + }, - } + lights: { - } + ambientLightColor: { value: [] }, - return true; + lightProbe: { value: [] }, - } + directionalLights: { value: [], properties: { + direction: {}, + color: {} + } }, - } ); + directionalLightShadows: { value: [], properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {} + } }, - var alphamap_fragment = "#ifdef USE_ALPHAMAP\n\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif"; + directionalShadowMap: { value: [] }, + directionalShadowMatrix: { value: [] }, - var alphamap_pars_fragment = "#ifdef USE_ALPHAMAP\n\tuniform sampler2D alphaMap;\n#endif"; + spotLights: { value: [], properties: { + color: {}, + position: {}, + direction: {}, + distance: {}, + coneCos: {}, + penumbraCos: {}, + decay: {} + } }, - var alphatest_fragment = "#ifdef ALPHATEST\n\tif ( diffuseColor.a < ALPHATEST ) discard;\n#endif"; + spotLightShadows: { value: [], properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {} + } }, - var aomap_fragment = "#ifdef USE_AOMAP\n\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\treflectedLight.indirectDiffuse *= ambientOcclusion;\n\t#if defined( USE_ENVMAP ) && defined( STANDARD )\n\t\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n\t#endif\n#endif"; + spotLightMap: { value: [] }, + spotShadowMap: { value: [] }, + spotLightMatrix: { value: [] }, - var aomap_pars_fragment = "#ifdef USE_AOMAP\n\tuniform sampler2D aoMap;\n\tuniform float aoMapIntensity;\n#endif"; + pointLights: { value: [], properties: { + color: {}, + position: {}, + decay: {}, + distance: {} + } }, - var begin_vertex = "vec3 transformed = vec3( position );"; + pointLightShadows: { value: [], properties: { + shadowBias: {}, + shadowNormalBias: {}, + shadowRadius: {}, + shadowMapSize: {}, + shadowCameraNear: {}, + shadowCameraFar: {} + } }, - var beginnormal_vertex = "vec3 objectNormal = vec3( normal );\n#ifdef USE_TANGENT\n\tvec3 objectTangent = vec3( tangent.xyz );\n#endif"; + pointShadowMap: { value: [] }, + pointShadowMatrix: { value: [] }, - var bsdfs = "vec2 integrateSpecularBRDF( const in float dotNV, const in float roughness ) {\n\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n\tvec4 r = roughness * c0 + c1;\n\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n\treturn vec2( -1.04, 1.04 ) * a004 + r.zw;\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n\tif( cutoffDistance > 0.0 ) {\n\t\tdistanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n\t}\n\treturn distanceFalloff;\n#else\n\tif( cutoffDistance > 0.0 && decayExponent > 0.0 ) {\n\t\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\t}\n\treturn 1.0;\n#endif\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n\treturn RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nvec3 F_Schlick_RoughnessDependent( const in vec3 F0, const in float dotNV, const in float roughness ) {\n\tfloat fresnel = exp2( ( -5.55473 * dotNV - 6.98316 ) * dotNV );\n\tvec3 Fr = max( vec3( 1.0 - roughness ), F0 ) - F0;\n\treturn Fr * fresnel + F0;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\treturn 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n\tfloat a2 = pow2( alpha );\n\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n\treturn 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n\tfloat a2 = pow2( alpha );\n\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n\treturn RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat alpha = pow2( roughness );\n\tvec3 halfDir = normalize( incidentLight.direction + viewDir );\n\tfloat dotNL = saturate( dot( normal, incidentLight.direction ) );\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tfloat dotNH = saturate( dot( normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n\tfloat D = D_GGX( alpha, dotNH );\n\treturn F * ( G * D );\n}\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\n\tconst float LUT_SIZE = 64.0;\n\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\n\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\n\tfloat dotNV = saturate( dot( N, V ) );\n\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\n\tuv = uv * LUT_SCALE + LUT_BIAS;\n\treturn uv;\n}\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\n\tfloat l = length( f );\n\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\n}\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\n\tfloat x = dot( v1, v2 );\n\tfloat y = abs( x );\n\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\n\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\n\tfloat v = a / b;\n\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\n\treturn cross( v1, v2 ) * theta_sintheta;\n}\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\n\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\n\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\n\tvec3 lightNormal = cross( v1, v2 );\n\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\n\tvec3 T1, T2;\n\tT1 = normalize( V - N * dot( V, N ) );\n\tT2 = - cross( N, T1 );\n\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\n\tvec3 coords[ 4 ];\n\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\n\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\n\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\n\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\n\tcoords[ 0 ] = normalize( coords[ 0 ] );\n\tcoords[ 1 ] = normalize( coords[ 1 ] );\n\tcoords[ 2 ] = normalize( coords[ 2 ] );\n\tcoords[ 3 ] = normalize( coords[ 3 ] );\n\tvec3 vectorFormFactor = vec3( 0.0 );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\n\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\n\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\n\treturn vec3( result );\n}\nvec3 BRDF_Specular_GGX_Environment( const in vec3 viewDir, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {\n\tfloat dotNV = saturate( dot( normal, viewDir ) );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\treturn specularColor * brdf.x + brdf.y;\n}\nvoid BRDF_Specular_Multiscattering_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) {\n\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n\tvec3 F = F_Schlick_RoughnessDependent( specularColor, dotNV, roughness );\n\tvec2 brdf = integrateSpecularBRDF( dotNV, roughness );\n\tvec3 FssEss = F * brdf.x + brdf.y;\n\tfloat Ess = brdf.x + brdf.y;\n\tfloat Ems = 1.0 - Ess;\n\tvec3 Favg = specularColor + ( 1.0 - specularColor ) * 0.047619;\tvec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg );\n\tsingleScatter += FssEss;\n\tmultiScatter += Fms * Ems;\n}\nfloat G_BlinnPhong_Implicit( ) {\n\treturn 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\n\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n\tvec3 F = F_Schlick( specularColor, dotLH );\n\tfloat G = G_BlinnPhong_Implicit( );\n\tfloat D = D_BlinnPhong( shininess, dotNH );\n\treturn F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n#if defined( USE_SHEEN )\nfloat D_Charlie(float roughness, float NoH) {\n\tfloat invAlpha = 1.0 / roughness;\n\tfloat cos2h = NoH * NoH;\n\tfloat sin2h = max(1.0 - cos2h, 0.0078125);\treturn (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);\n}\nfloat V_Neubelt(float NoV, float NoL) {\n\treturn saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));\n}\nvec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {\n\tvec3 N = geometry.normal;\n\tvec3 V = geometry.viewDir;\n\tvec3 H = normalize( V + L );\n\tfloat dotNH = saturate( dot( N, H ) );\n\treturn specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );\n}\n#endif"; + hemisphereLights: { value: [], properties: { + direction: {}, + skyColor: {}, + groundColor: {} + } }, - var bumpmap_pars_fragment = "#ifdef USE_BUMPMAP\n\tuniform sampler2D bumpMap;\n\tuniform float bumpScale;\n\tvec2 dHdxy_fwd() {\n\t\tvec2 dSTdx = dFdx( vUv );\n\t\tvec2 dSTdy = dFdy( vUv );\n\t\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n\t\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n\t\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\t\treturn vec2( dBx, dBy );\n\t}\n\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\t\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\n\t\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\n\t\tvec3 vN = surf_norm;\n\t\tvec3 R1 = cross( vSigmaY, vN );\n\t\tvec3 R2 = cross( vN, vSigmaX );\n\t\tfloat fDet = dot( vSigmaX, R1 );\n\t\tfDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n\t\treturn normalize( abs( fDet ) * surf_norm - vGrad );\n\t}\n#endif"; + // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src + rectAreaLights: { value: [], properties: { + color: {}, + position: {}, + width: {}, + height: {} + } }, - var clipping_planes_fragment = "#if NUM_CLIPPING_PLANES > 0\n\tvec4 plane;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\n\t\tplane = clippingPlanes[ i ];\n\t\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n\t}\n\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\n\t\tbool clipped = true;\n\t\t#pragma unroll_loop\n\t\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\n\t\t\tplane = clippingPlanes[ i ];\n\t\t\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\n\t\t}\n\t\tif ( clipped ) discard;\n\t#endif\n#endif"; + ltc_1: { value: null }, + ltc_2: { value: null } - var clipping_planes_pars_fragment = "#if NUM_CLIPPING_PLANES > 0\n\t#if ! defined( STANDARD ) && ! defined( PHONG ) && ! defined( MATCAP )\n\t\tvarying vec3 vViewPosition;\n\t#endif\n\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif"; + }, - var clipping_planes_pars_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvarying vec3 vViewPosition;\n#endif"; + points: { + + diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, + opacity: { value: 1.0 }, + size: { value: 1.0 }, + scale: { value: 1.0 }, + map: { value: null }, + alphaMap: { value: null }, + alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + alphaTest: { value: 0 }, + uvTransform: { value: /*@__PURE__*/ new Matrix3() } + + }, + + sprite: { + + diffuse: { value: /*@__PURE__*/ new Color( 0xffffff ) }, + opacity: { value: 1.0 }, + center: { value: /*@__PURE__*/ new Vector2( 0.5, 0.5 ) }, + rotation: { value: 0.0 }, + map: { value: null }, + mapTransform: { value: /*@__PURE__*/ new Matrix3() }, + alphaMap: { value: null }, + alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + alphaTest: { value: 0 } + + } + +}; + +const ShaderLib = { + + basic: { + + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.specularmap, + UniformsLib.envmap, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.fog + ] ), - var clipping_planes_vertex = "#if NUM_CLIPPING_PLANES > 0 && ! defined( STANDARD ) && ! defined( PHONG ) && ! defined( MATCAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif"; + vertexShader: ShaderChunk.meshbasic_vert, + fragmentShader: ShaderChunk.meshbasic_frag + + }, + + lambert: { + + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.specularmap, + UniformsLib.envmap, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.emissivemap, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.fog, + UniformsLib.lights, + { + emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } + } + ] ), - var color_fragment = "#ifdef USE_COLOR\n\tdiffuseColor.rgb *= vColor;\n#endif"; + vertexShader: ShaderChunk.meshlambert_vert, + fragmentShader: ShaderChunk.meshlambert_frag + + }, + + phong: { + + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.specularmap, + UniformsLib.envmap, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.emissivemap, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.fog, + UniformsLib.lights, + { + emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, + specular: { value: /*@__PURE__*/ new Color( 0x111111 ) }, + shininess: { value: 30 } + } + ] ), - var color_pars_fragment = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; + vertexShader: ShaderChunk.meshphong_vert, + fragmentShader: ShaderChunk.meshphong_frag + + }, + + standard: { + + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.envmap, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.emissivemap, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.roughnessmap, + UniformsLib.metalnessmap, + UniformsLib.fog, + UniformsLib.lights, + { + emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) }, + roughness: { value: 1.0 }, + metalness: { value: 0.0 }, + envMapIntensity: { value: 1 } // temporary + } + ] ), - var color_pars_vertex = "#ifdef USE_COLOR\n\tvarying vec3 vColor;\n#endif"; + vertexShader: ShaderChunk.meshphysical_vert, + fragmentShader: ShaderChunk.meshphysical_frag - var color_vertex = "#ifdef USE_COLOR\n\tvColor.xyz = color.xyz;\n#endif"; + }, + + toon: { + + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.aomap, + UniformsLib.lightmap, + UniformsLib.emissivemap, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.gradientmap, + UniformsLib.fog, + UniformsLib.lights, + { + emissive: { value: /*@__PURE__*/ new Color( 0x000000 ) } + } + ] ), - var common = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define PI_HALF 1.5707963267949\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteComplement(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\n\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n\treturn fract(sin(sn) * c);\n}\n#ifdef HIGH_PRECISION\n\tfloat precisionSafeLength( vec3 v ) { return length( v ); }\n#else\n\tfloat max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }\n\tfloat precisionSafeLength( vec3 v ) {\n\t\tfloat maxComponent = max3( abs( v ) );\n\t\treturn length( v / maxComponent ) * maxComponent;\n\t}\n#endif\nstruct IncidentLight {\n\tvec3 color;\n\tvec3 direction;\n\tbool visible;\n};\nstruct ReflectedLight {\n\tvec3 directDiffuse;\n\tvec3 directSpecular;\n\tvec3 indirectDiffuse;\n\tvec3 indirectSpecular;\n};\nstruct GeometricContext {\n\tvec3 position;\n\tvec3 normal;\n\tvec3 viewDir;\n#ifdef CLEARCOAT\n\tvec3 clearcoatNormal;\n#endif\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\tfloat distance = dot( planeNormal, point - pointOnPlane );\n\treturn - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nmat3 transposeMat3( const in mat3 m ) {\n\tmat3 tmp;\n\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\n\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\n\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\n\treturn tmp;\n}\nfloat linearToRelativeLuminance( const in vec3 color ) {\n\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\n\treturn dot( weights, color.rgb );\n}"; + vertexShader: ShaderChunk.meshtoon_vert, + fragmentShader: ShaderChunk.meshtoon_frag - var cube_uv_reflection_fragment = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n\tvec3 absDirection = abs(direction);\n\tint face = -1;\n\tif( absDirection.x > absDirection.z ) {\n\t\tif(absDirection.x > absDirection.y )\n\t\t\tface = direction.x > 0.0 ? 0 : 3;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\telse {\n\t\tif(absDirection.z > absDirection.y )\n\t\t\tface = direction.z > 0.0 ? 2 : 5;\n\t\telse\n\t\t\tface = direction.y > 0.0 ? 1 : 4;\n\t}\n\treturn face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n\tfloat dxRoughness = dFdx(roughness);\n\tfloat dyRoughness = dFdy(roughness);\n\tvec3 dx = dFdx( vec * scale * dxRoughness );\n\tvec3 dy = dFdy( vec * scale * dyRoughness );\n\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\n\td = clamp(d, 1.0, cubeUV_rangeClamp);\n\tfloat mipLevel = 0.5 * log2(d);\n\treturn vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n\tfloat a = 16.0 * cubeUV_rcpTextureSize;\n\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n\tfloat powScale = exp2_packed.x * exp2_packed.y;\n\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n\tbool bRes = mipLevel == 0.0;\n\tscale = bRes && (scale < a) ? a : scale;\n\tvec3 r;\n\tvec2 offset;\n\tint face = getFaceFromDirection(direction);\n\tfloat rcpPowScale = 1.0 / powScale;\n\tif( face == 0) {\n\t\tr = vec3(direction.x, -direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 1) {\n\t\tr = vec3(direction.y, direction.x, direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 2) {\n\t\tr = vec3(direction.z, direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n\t}\n\telse if( face == 3) {\n\t\tr = vec3(direction.x, direction.z, direction.y);\n\t\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse if( face == 4) {\n\t\tr = vec3(direction.y, direction.x, -direction.z);\n\t\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\telse {\n\t\tr = vec3(direction.z, -direction.x, direction.y);\n\t\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n\t\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n\t}\n\tr = normalize(r);\n\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\n\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n\tvec2 base = offset + vec2( texelOffset );\n\treturn base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV( sampler2D envMap, vec3 reflectedDirection, float roughness ) {\n\tfloat roughnessVal = roughness* cubeUV_maxLods3;\n\tfloat r1 = floor(roughnessVal);\n\tfloat r2 = r1 + 1.0;\n\tfloat t = fract(roughnessVal);\n\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n\tfloat s = mipInfo.y;\n\tfloat level0 = mipInfo.x;\n\tfloat level1 = level0 + 1.0;\n\tlevel1 = level1 > 5.0 ? 5.0 : level1;\n\tlevel0 += min( floor( s + 0.5 ), 5.0 );\n\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n\tvec4 result = mix(color10, color20, t);\n\treturn vec4(result.rgb, 1.0);\n}\n#endif"; + }, - var defaultnormal_vertex = "vec3 transformedNormal = normalMatrix * objectNormal;\n#ifdef FLIP_SIDED\n\ttransformedNormal = - transformedNormal;\n#endif\n#ifdef USE_TANGENT\n\tvec3 transformedTangent = normalMatrix * objectTangent;\n\t#ifdef FLIP_SIDED\n\t\ttransformedTangent = - transformedTangent;\n\t#endif\n#endif"; + matcap: { - var displacementmap_pars_vertex = "#ifdef USE_DISPLACEMENTMAP\n\tuniform sampler2D displacementMap;\n\tuniform float displacementScale;\n\tuniform float displacementBias;\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + UniformsLib.fog, + { + matcap: { value: null } + } + ] ), - var displacementmap_vertex = "#ifdef USE_DISPLACEMENTMAP\n\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif"; + vertexShader: ShaderChunk.meshmatcap_vert, + fragmentShader: ShaderChunk.meshmatcap_frag - var emissivemap_fragment = "#ifdef USE_EMISSIVEMAP\n\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\n\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n\ttotalEmissiveRadiance *= emissiveColor.rgb;\n#endif"; + }, - var emissivemap_pars_fragment = "#ifdef USE_EMISSIVEMAP\n\tuniform sampler2D emissiveMap;\n#endif"; + points: { - var encodings_fragment = "gl_FragColor = linearToOutputTexel( gl_FragColor );"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.points, + UniformsLib.fog + ] ), - var encodings_pars_fragment = "\nvec4 LinearToLinear( in vec4 value ) {\n\treturn value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n\treturn vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n\tfloat maxComponent = max( max( value.r, value.g ), value.b );\n\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * value.a * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n\tM = ceil( M * 255.0 ) / 255.0;\n\treturn vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n\tfloat maxRGB = max( value.r, max( value.g, value.b ) );\n\tfloat D = max( maxRange / maxRGB, 1.0 );\n\tD = min( floor( D ) / 255.0, 1.0 );\n\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n\tvec3 Xp_Y_XYZp = cLogLuvM * value.rgb;\n\tXp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );\n\tvec4 vResult;\n\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n\tvResult.w = fract( Le );\n\tvResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;\n\treturn vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n\tfloat Le = value.z * 255.0 + value.w;\n\tvec3 Xp_Y_XYZp;\n\tXp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );\n\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n\tvec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;\n\treturn vec4( max( vRGB, 0.0 ), 1.0 );\n}"; + vertexShader: ShaderChunk.points_vert, + fragmentShader: ShaderChunk.points_frag - var envmap_fragment = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#else\n\t\tvec3 reflectVec = vReflect;\n\t#endif\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\tvec2 sampleUV;\n\t\treflectVec = normalize( reflectVec );\n\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\tvec4 envColor = texture2D( envMap, sampleUV );\n\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\treflectVec = normalize( reflectVec );\n\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\n\t\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n\t#else\n\t\tvec4 envColor = vec4( 0.0 );\n\t#endif\n\tenvColor = envMapTexelToLinear( envColor );\n\t#ifdef ENVMAP_BLENDING_MULTIPLY\n\t\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_MIX )\n\t\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\t#elif defined( ENVMAP_BLENDING_ADD )\n\t\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\n\t#endif\n#endif"; + }, - var envmap_common_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float envMapIntensity;\n\tuniform float flipEnvMap;\n\tuniform int maxMipLevel;\n\t#ifdef ENVMAP_TYPE_CUBE\n\t\tuniform samplerCube envMap;\n\t#else\n\t\tuniform sampler2D envMap;\n\t#endif\n\t\n#endif"; + dashed: { - var envmap_pars_fragment = "#ifdef USE_ENVMAP\n\tuniform float reflectivity;\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\tvarying vec3 vWorldPosition;\n\t\tuniform float refractionRatio;\n\t#else\n\t\tvarying vec3 vReflect;\n\t#endif\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.fog, + { + scale: { value: 1 }, + dashSize: { value: 1 }, + totalSize: { value: 2 } + } + ] ), - var envmap_pars_vertex = "#ifdef USE_ENVMAP\n\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) ||defined( PHONG )\n\t\t#define ENV_WORLDPOS\n\t#endif\n\t#ifdef ENV_WORLDPOS\n\t\t\n\t\tvarying vec3 vWorldPosition;\n\t#else\n\t\tvarying vec3 vReflect;\n\t\tuniform float refractionRatio;\n\t#endif\n#endif"; + vertexShader: ShaderChunk.linedashed_vert, + fragmentShader: ShaderChunk.linedashed_frag - var envmap_vertex = "#ifdef USE_ENVMAP\n\t#ifdef ENV_WORLDPOS\n\t\tvWorldPosition = worldPosition.xyz;\n\t#else\n\t\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\t\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t\tvReflect = reflect( cameraToVertex, worldNormal );\n\t\t#else\n\t\t\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\t\t#endif\n\t#endif\n#endif"; + }, - var fog_vertex = "#ifdef USE_FOG\n\tfogDepth = -mvPosition.z;\n#endif"; + depth: { - var fog_pars_vertex = "#ifdef USE_FOG\n\tvarying float fogDepth;\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.displacementmap + ] ), - var fog_fragment = "#ifdef USE_FOG\n\t#ifdef FOG_EXP2\n\t\tfloat fogFactor = 1.0 - exp( - fogDensity * fogDensity * fogDepth * fogDepth );\n\t#else\n\t\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\n\t#endif\n\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif"; + vertexShader: ShaderChunk.depth_vert, + fragmentShader: ShaderChunk.depth_frag - var fog_pars_fragment = "#ifdef USE_FOG\n\tuniform vec3 fogColor;\n\tvarying float fogDepth;\n\t#ifdef FOG_EXP2\n\t\tuniform float fogDensity;\n\t#else\n\t\tuniform float fogNear;\n\t\tuniform float fogFar;\n\t#endif\n#endif"; + }, - var gradientmap_pars_fragment = "#ifdef TOON\n\tuniform sampler2D gradientMap;\n\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\n\t\tfloat dotNL = dot( normal, lightDirection );\n\t\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\n\t\t#ifdef USE_GRADIENTMAP\n\t\t\treturn texture2D( gradientMap, coord ).rgb;\n\t\t#else\n\t\t\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\n\t\t#endif\n\t}\n#endif"; + normal: { - var lightmap_fragment = "#ifdef USE_LIGHTMAP\n\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.bumpmap, + UniformsLib.normalmap, + UniformsLib.displacementmap, + { + opacity: { value: 1.0 } + } + ] ), - var lightmap_pars_fragment = "#ifdef USE_LIGHTMAP\n\tuniform sampler2D lightMap;\n\tuniform float lightMapIntensity;\n#endif"; + vertexShader: ShaderChunk.meshnormal_vert, + fragmentShader: ShaderChunk.meshnormal_frag - var lights_lambert_vertex = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\nvIndirectFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n\tvLightBack = vec3( 0.0 );\n\tvIndirectBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_DIR_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n\t\tdotNL = dot( geometry.normal, directLight.direction );\n\t\tdirectLightColor_Diffuse = PI * directLight.color;\n\t\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n\t\t#endif\n\t}\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\tvIndirectFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvIndirectBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n\t\t#endif\n\t}\n#endif"; + }, - var lights_pars_begin = "uniform vec3 ambientLightColor;\nuniform vec3 lightProbe[ 9 ];\nvec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {\n\tfloat x = normal.x, y = normal.y, z = normal.z;\n\tvec3 result = shCoefficients[ 0 ] * 0.886227;\n\tresult += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;\n\tresult += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;\n\tresult += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;\n\tresult += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;\n\tresult += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;\n\tresult += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );\n\tresult += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;\n\tresult += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );\n\treturn result;\n}\nvec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in GeometricContext geometry ) {\n\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\tvec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe );\n\treturn irradiance;\n}\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n\tvec3 irradiance = ambientLightColor;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treturn irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n\tstruct DirectionalLight {\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tdirectLight.color = directionalLight.color;\n\t\tdirectLight.direction = directionalLight.direction;\n\t\tdirectLight.visible = true;\n\t}\n#endif\n#if NUM_POINT_LIGHTS > 0\n\tstruct PointLight {\n\t\tvec3 position;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t\tfloat shadowCameraNear;\n\t\tfloat shadowCameraFar;\n\t};\n\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = pointLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tdirectLight.color = pointLight.color;\n\t\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n\t\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\n\t}\n#endif\n#if NUM_SPOT_LIGHTS > 0\n\tstruct SpotLight {\n\t\tvec3 position;\n\t\tvec3 direction;\n\t\tvec3 color;\n\t\tfloat distance;\n\t\tfloat decay;\n\t\tfloat coneCos;\n\t\tfloat penumbraCos;\n\t\tint shadow;\n\t\tfloat shadowBias;\n\t\tfloat shadowRadius;\n\t\tvec2 shadowMapSize;\n\t};\n\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n\t\tvec3 lVector = spotLight.position - geometry.position;\n\t\tdirectLight.direction = normalize( lVector );\n\t\tfloat lightDistance = length( lVector );\n\t\tfloat angleCos = dot( directLight.direction, spotLight.direction );\n\t\tif ( angleCos > spotLight.coneCos ) {\n\t\t\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n\t\t\tdirectLight.color = spotLight.color;\n\t\t\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n\t\t\tdirectLight.visible = true;\n\t\t} else {\n\t\t\tdirectLight.color = vec3( 0.0 );\n\t\t\tdirectLight.visible = false;\n\t\t}\n\t}\n#endif\n#if NUM_RECT_AREA_LIGHTS > 0\n\tstruct RectAreaLight {\n\t\tvec3 color;\n\t\tvec3 position;\n\t\tvec3 halfWidth;\n\t\tvec3 halfHeight;\n\t};\n\tuniform sampler2D ltc_1;\tuniform sampler2D ltc_2;\n\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\n#endif\n#if NUM_HEMI_LIGHTS > 0\n\tstruct HemisphereLight {\n\t\tvec3 direction;\n\t\tvec3 skyColor;\n\t\tvec3 groundColor;\n\t};\n\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n\t\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\n\t\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n\t\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tirradiance *= PI;\n\t\t#endif\n\t\treturn irradiance;\n\t}\n#endif"; + sprite: { - var envmap_physical_pars_fragment = "#if defined( USE_ENVMAP )\n\t#ifdef ENVMAP_MODE_REFRACTION\n\t\tuniform float refractionRatio;\n\t#endif\n\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n\t\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryVec, 1.0 );\n\t\t#else\n\t\t\tvec4 envMapColor = vec4( 0.0 );\n\t\t#endif\n\t\treturn PI * envMapColor.rgb * envMapIntensity;\n\t}\n\tfloat getSpecularMIPLevel( const in float roughness, const in int maxMIPLevel ) {\n\t\tfloat maxMIPLevelScalar = float( maxMIPLevel );\n\t\tfloat sigma = PI * roughness * roughness / ( 1.0 + roughness );\n\t\tfloat desiredMIPLevel = maxMIPLevelScalar + log2( sigma );\n\t\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n\t}\n\tvec3 getLightProbeIndirectRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {\n\t\t#ifdef ENVMAP_MODE_REFLECTION\n\t\t vec3 reflectVec = reflect( -viewDir, normal );\n\t\t reflectVec = normalize( mix( reflectVec, normal, roughness * roughness) );\n\t\t#else\n\t\t vec3 reflectVec = refract( -viewDir, normal, refractionRatio );\n\t\t#endif\n\t\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n\t\tfloat specularMIPLevel = getSpecularMIPLevel( roughness, maxMIPLevel );\n\t\t#ifdef ENVMAP_TYPE_CUBE\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_CUBE_UV )\n\t\t\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n\t\t\tvec4 envMapColor = textureCubeUV( envMap, queryReflectVec, roughness );\n\t\t#elif defined( ENVMAP_TYPE_EQUIREC )\n\t\t\tvec2 sampleUV;\n\t\t\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\t\t\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#elif defined( ENVMAP_TYPE_SPHERE )\n\t\t\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\n\t\t\t#ifdef TEXTURE_LOD_EXT\n\t\t\t\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#else\n\t\t\t\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n\t\t\t#endif\n\t\t\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n\t\t#endif\n\t\treturn envMapColor.rgb * envMapIntensity;\n\t}\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.sprite, + UniformsLib.fog + ] ), - var lights_phong_fragment = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;"; + vertexShader: ShaderChunk.sprite_vert, + fragmentShader: ShaderChunk.sprite_frag - var lights_phong_pars_fragment = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n\tvec3\tdiffuseColor;\n\tvec3\tspecularColor;\n\tfloat\tspecularShininess;\n\tfloat\tspecularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\t#ifdef TOON\n\t\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\n\t#else\n\t\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\t\tvec3 irradiance = dotNL * directLight.color;\n\t#endif\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct\t\t\t\tRE_Direct_BlinnPhong\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material )\t(0)"; + }, - var lights_physical_fragment = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef REFLECTIVITY\n\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n#else\n\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#endif\n#ifdef CLEARCOAT\n\tmaterial.clearcoat = saturate( clearcoat );\tmaterial.clearcoatRoughness = clamp( clearcoatRoughness, 0.04, 1.0 );\n#endif\n#ifdef USE_SHEEN\n\tmaterial.sheenColor = sheen;\n#endif"; + background: { - var lights_physical_pars_fragment = "struct PhysicalMaterial {\n\tvec3\tdiffuseColor;\n\tfloat\tspecularRoughness;\n\tvec3\tspecularColor;\n#ifdef CLEARCOAT\n\tfloat clearcoat;\n\tfloat clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tvec3 sheenColor;\n#endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearcoatDHRApprox( const in float roughness, const in float dotNL ) {\n\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\n#if NUM_RECT_AREA_LIGHTS > 0\n\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\t\tvec3 normal = geometry.normal;\n\t\tvec3 viewDir = geometry.viewDir;\n\t\tvec3 position = geometry.position;\n\t\tvec3 lightPos = rectAreaLight.position;\n\t\tvec3 halfWidth = rectAreaLight.halfWidth;\n\t\tvec3 halfHeight = rectAreaLight.halfHeight;\n\t\tvec3 lightColor = rectAreaLight.color;\n\t\tfloat roughness = material.specularRoughness;\n\t\tvec3 rectCoords[ 4 ];\n\t\trectCoords[ 0 ] = lightPos + halfWidth - halfHeight;\t\trectCoords[ 1 ] = lightPos - halfWidth - halfHeight;\n\t\trectCoords[ 2 ] = lightPos - halfWidth + halfHeight;\n\t\trectCoords[ 3 ] = lightPos + halfWidth + halfHeight;\n\t\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\n\t\tvec4 t1 = texture2D( ltc_1, uv );\n\t\tvec4 t2 = texture2D( ltc_2, uv );\n\t\tmat3 mInv = mat3(\n\t\t\tvec3( t1.x, 0, t1.y ),\n\t\t\tvec3( 0, 1, 0 ),\n\t\t\tvec3( t1.z, 0, t1.w )\n\t\t);\n\t\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\n\t\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\n\t\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\n\t}\n#endif\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n\tvec3 irradiance = dotNL * directLight.color;\n\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\tirradiance *= PI;\n\t#endif\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );\n\t\tvec3 ccIrradiance = ccDotNL * directLight.color;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tccIrradiance *= PI;\n\t\t#endif\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t\treflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\t#ifdef USE_SHEEN\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(\n\t\t\tmaterial.specularRoughness,\n\t\t\tdirectLight.direction,\n\t\t\tgeometry,\n\t\t\tmaterial.sheenColor\n\t\t);\n\t#else\n\t\treflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);\n\t#endif\n\treflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {\n\t#ifdef CLEARCOAT\n\t\tfloat ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );\n\t\treflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );\n\t\tfloat ccDotNL = ccDotNV;\n\t\tfloat clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );\n\t#else\n\t\tfloat clearcoatDHR = 0.0;\n\t#endif\n\tfloat clearcoatInv = 1.0 - clearcoatDHR;\n\tvec3 singleScattering = vec3( 0.0 );\n\tvec3 multiScattering = vec3( 0.0 );\n\tvec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI;\n\tBRDF_Specular_Multiscattering_Environment( geometry, material.specularColor, material.specularRoughness, singleScattering, multiScattering );\n\tvec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );\n\treflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;\n\treflectedLight.indirectDiffuse += multiScattering * cosineWeightedIrradiance;\n\treflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;\n}\n#define RE_Direct\t\t\t\tRE_Direct_Physical\n#define RE_Direct_RectArea\t\tRE_Direct_RectArea_Physical\n#define RE_IndirectDiffuse\t\tRE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular\t\tRE_IndirectSpecular_Physical\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}"; + uniforms: { + uvTransform: { value: /*@__PURE__*/ new Matrix3() }, + t2D: { value: null }, + backgroundIntensity: { value: 1 } + }, - var lights_fragment_begin = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\n#ifdef CLEARCOAT\n\tgeometry.clearcoatNormal = clearcoatNormal;\n#endif\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )\n\t\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )\n\t\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n\t\t#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )\n\t\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t\t#endif\n\t\tRE_Direct( directLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\n\tRectAreaLight rectAreaLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\n\t\trectAreaLight = rectAreaLights[ i ];\n\t\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\n\t}\n#endif\n#if defined( RE_IndirectDiffuse )\n\tvec3 iblIrradiance = vec3( 0.0 );\n\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n\tirradiance += getLightProbeIrradiance( lightProbe, geometry );\n\t#if ( NUM_HEMI_LIGHTS > 0 )\n\t\t#pragma unroll_loop\n\t\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n\t\t\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n\t\t}\n\t#endif\n#endif\n#if defined( RE_IndirectSpecular )\n\tvec3 radiance = vec3( 0.0 );\n\tvec3 clearcoatRadiance = vec3( 0.0 );\n#endif"; + vertexShader: ShaderChunk.background_vert, + fragmentShader: ShaderChunk.background_frag - var lights_fragment_maps = "#if defined( RE_IndirectDiffuse )\n\t#ifdef USE_LIGHTMAP\n\t\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t\t#ifndef PHYSICALLY_CORRECT_LIGHTS\n\t\t\tlightMapIrradiance *= PI;\n\t\t#endif\n\t\tirradiance += lightMapIrradiance;\n\t#endif\n\t#if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV )\n\t\tiblIrradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\n\t#endif\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n\tradiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.normal, material.specularRoughness, maxMipLevel );\n\t#ifdef CLEARCOAT\n\t\tclearcoatRadiance += getLightProbeIndirectRadiance( geometry.viewDir, geometry.clearcoatNormal, material.clearcoatRoughness, maxMipLevel );\n\t#endif\n#endif"; + }, - var lights_fragment_end = "#if defined( RE_IndirectDiffuse )\n\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( RE_IndirectSpecular )\n\tRE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometry, material, reflectedLight );\n#endif"; + backgroundCube: { - var logdepthbuf_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\n#endif"; + uniforms: { + envMap: { value: null }, + flipEnvMap: { value: - 1 }, + backgroundBlurriness: { value: 0 }, + backgroundIntensity: { value: 1 } + }, - var logdepthbuf_pars_fragment = "#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\n\tuniform float logDepthBufFC;\n\tvarying float vFragDepth;\n#endif"; + vertexShader: ShaderChunk.backgroundCube_vert, + fragmentShader: ShaderChunk.backgroundCube_frag - var logdepthbuf_pars_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvarying float vFragDepth;\n\t#else\n\t\tuniform float logDepthBufFC;\n\t#endif\n#endif"; + }, - var logdepthbuf_vertex = "#ifdef USE_LOGDEPTHBUF\n\t#ifdef USE_LOGDEPTHBUF_EXT\n\t\tvFragDepth = 1.0 + gl_Position.w;\n\t#else\n\t\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\n\t\tgl_Position.z *= gl_Position.w;\n\t#endif\n#endif"; + cube: { - var map_fragment = "#ifdef USE_MAP\n\tvec4 texelColor = texture2D( map, vUv );\n\ttexelColor = mapTexelToLinear( texelColor );\n\tdiffuseColor *= texelColor;\n#endif"; + uniforms: { + tCube: { value: null }, + tFlip: { value: - 1 }, + opacity: { value: 1.0 } + }, - var map_pars_fragment = "#ifdef USE_MAP\n\tuniform sampler2D map;\n#endif"; + vertexShader: ShaderChunk.cube_vert, + fragmentShader: ShaderChunk.cube_frag - var map_particle_fragment = "#ifdef USE_MAP\n\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\n\tvec4 mapTexel = texture2D( map, uv );\n\tdiffuseColor *= mapTexelToLinear( mapTexel );\n#endif"; + }, - var map_particle_pars_fragment = "#ifdef USE_MAP\n\tuniform mat3 uvTransform;\n\tuniform sampler2D map;\n#endif"; + equirect: { - var metalnessmap_fragment = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\n\tmetalnessFactor *= texelMetalness.b;\n#endif"; + uniforms: { + tEquirect: { value: null }, + }, - var metalnessmap_pars_fragment = "#ifdef USE_METALNESSMAP\n\tuniform sampler2D metalnessMap;\n#endif"; + vertexShader: ShaderChunk.equirect_vert, + fragmentShader: ShaderChunk.equirect_frag - var morphnormal_vertex = "#ifdef USE_MORPHNORMALS\n\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif"; + }, - var morphtarget_pars_vertex = "#ifdef USE_MORPHTARGETS\n\t#ifndef USE_MORPHNORMALS\n\tuniform float morphTargetInfluences[ 8 ];\n\t#else\n\tuniform float morphTargetInfluences[ 4 ];\n\t#endif\n#endif"; + distanceRGBA: { - var morphtarget_vertex = "#ifdef USE_MORPHTARGETS\n\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\t#ifndef USE_MORPHNORMALS\n\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\t#endif\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.common, + UniformsLib.displacementmap, + { + referencePosition: { value: /*@__PURE__*/ new Vector3() }, + nearDistance: { value: 1 }, + farDistance: { value: 1000 } + } + ] ), - var normal_fragment_begin = "#ifdef FLAT_SHADED\n\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n\tvec3 normal = normalize( cross( fdx, fdy ) );\n#else\n\tvec3 normal = normalize( vNormal );\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\t#ifdef USE_TANGENT\n\t\tvec3 tangent = normalize( vTangent );\n\t\tvec3 bitangent = normalize( vBitangent );\n\t\t#ifdef DOUBLE_SIDED\n\t\t\ttangent = tangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t\tbitangent = bitangent * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t#endif\n#endif\nvec3 geometryNormal = normal;"; + vertexShader: ShaderChunk.distanceRGBA_vert, + fragmentShader: ShaderChunk.distanceRGBA_frag - var normal_fragment_maps = "#ifdef OBJECTSPACE_NORMALMAP\n\tnormal = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t#ifdef FLIP_SIDED\n\t\tnormal = - normal;\n\t#endif\n\t#ifdef DOUBLE_SIDED\n\t\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t#endif\n\tnormal = normalize( normalMatrix * normal );\n#elif defined( TANGENTSPACE_NORMALMAP )\n\t#ifdef USE_TANGENT\n\t\tmat3 vTBN = mat3( tangent, bitangent, normal );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = normalScale * mapN.xy;\n\t\tnormal = normalize( vTBN * mapN );\n\t#else\n\t\tnormal = perturbNormal2Arb( -vViewPosition, normal, normalScale, normalMap );\n\t#endif\n#elif defined( USE_BUMPMAP )\n\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif"; + }, - var normalmap_pars_fragment = "#ifdef USE_NORMALMAP\n\tuniform sampler2D normalMap;\n\tuniform vec2 normalScale;\n#endif\n#ifdef OBJECTSPACE_NORMALMAP\n\tuniform mat3 normalMatrix;\n#endif\n#if ! defined ( USE_TANGENT ) && ( defined ( TANGENTSPACE_NORMALMAP ) || defined ( USE_CLEARCOAT_NORMALMAP ) )\n\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec2 normalScale, in sampler2D normalMap ) {\n\t\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\n\t\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\n\t\tvec2 st0 = dFdx( vUv.st );\n\t\tvec2 st1 = dFdy( vUv.st );\n\t\tfloat scale = sign( st1.t * st0.s - st0.t * st1.s );\n\t\tvec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );\n\t\tvec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );\n\t\tvec3 N = normalize( surf_norm );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy *= normalScale;\n\t\t#ifdef DOUBLE_SIDED\n\t\t\tvec3 NfromST = cross( S, T );\n\t\t\tif( dot( NfromST, N ) > 0.0 ) {\n\t\t\t\tS *= -1.0;\n\t\t\t\tT *= -1.0;\n\t\t\t}\n\t\t#else\n\t\t\tmapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n\t\t#endif\n\t\tmat3 tsn = mat3( S, T, N );\n\t\treturn normalize( tsn * mapN );\n\t}\n#endif"; + shadow: { - var clearcoat_normal_fragment_begin = "#ifdef CLEARCOAT\n\tvec3 clearcoatNormal = geometryNormal;\n#endif"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + UniformsLib.lights, + UniformsLib.fog, + { + color: { value: /*@__PURE__*/ new Color( 0x00000 ) }, + opacity: { value: 1.0 } + }, + ] ), - var clearcoat_normal_fragment_maps = "#ifdef USE_CLEARCOAT_NORMALMAP\n\t#ifdef USE_TANGENT\n\t\tmat3 vTBN = mat3( tangent, bitangent, clearcoatNormal );\n\t\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n\t\tmapN.xy = clearcoatNormalScale * mapN.xy;\n\t\tclearcoatNormal = normalize( vTBN * mapN );\n\t#else\n\t\tclearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatNormalScale, clearcoatNormalMap );\n\t#endif\n#endif"; + vertexShader: ShaderChunk.shadow_vert, + fragmentShader: ShaderChunk.shadow_frag - var clearcoat_normalmap_pars_fragment = "#ifdef USE_CLEARCOAT_NORMALMAP\n\tuniform sampler2D clearcoatNormalMap;\n\tuniform vec2 clearcoatNormalScale;\n#endif"; + } - var packing = "vec3 packNormalToRGB( const in vec3 normal ) {\n\treturn normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n\treturn 2.0 * rgb.xyz - 1.0;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n\tvec4 r = vec4( fract( v * PackFactors ), v );\n\tr.yzw -= r.xyz * ShiftRight8;\treturn r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n\treturn dot( v, UnpackFactors );\n}\nvec4 encodeHalfRGBA ( vec2 v ) {\n\tvec4 encoded = vec4( 0.0 );\n\tconst vec2 offset = vec2( 1.0 / 255.0, 0.0 );\n\tencoded.xy = vec2( v.x, fract( v.x * 255.0 ) );\n\tencoded.xy = encoded.xy - ( encoded.yy * offset );\n\tencoded.zw = vec2( v.y, fract( v.y * 255.0 ) );\n\tencoded.zw = encoded.zw - ( encoded.ww * offset );\n\treturn encoded;\n}\nvec2 decodeHalfRGBA( vec4 v ) {\n\treturn vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n\treturn linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\n}"; +}; - var premultiplied_alpha_fragment = "#ifdef PREMULTIPLIED_ALPHA\n\tgl_FragColor.rgb *= gl_FragColor.a;\n#endif"; +ShaderLib.physical = { - var project_vertex = "vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\ngl_Position = projectionMatrix * mvPosition;"; + uniforms: /*@__PURE__*/ mergeUniforms( [ + ShaderLib.standard.uniforms, + { + clearcoat: { value: 0 }, + clearcoatMap: { value: null }, + clearcoatMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + clearcoatNormalMap: { value: null }, + clearcoatNormalMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + clearcoatNormalScale: { value: /*@__PURE__*/ new Vector2( 1, 1 ) }, + clearcoatRoughness: { value: 0 }, + clearcoatRoughnessMap: { value: null }, + clearcoatRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + iridescence: { value: 0 }, + iridescenceMap: { value: null }, + iridescenceMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + iridescenceIOR: { value: 1.3 }, + iridescenceThicknessMinimum: { value: 100 }, + iridescenceThicknessMaximum: { value: 400 }, + iridescenceThicknessMap: { value: null }, + iridescenceThicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + sheen: { value: 0 }, + sheenColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, + sheenColorMap: { value: null }, + sheenColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + sheenRoughness: { value: 1 }, + sheenRoughnessMap: { value: null }, + sheenRoughnessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + transmission: { value: 0 }, + transmissionMap: { value: null }, + transmissionMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + transmissionSamplerSize: { value: /*@__PURE__*/ new Vector2() }, + transmissionSamplerMap: { value: null }, + thickness: { value: 0 }, + thicknessMap: { value: null }, + thicknessMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + attenuationDistance: { value: 0 }, + attenuationColor: { value: /*@__PURE__*/ new Color( 0x000000 ) }, + specularColor: { value: /*@__PURE__*/ new Color( 1, 1, 1 ) }, + specularColorMap: { value: null }, + specularColorMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + specularIntensity: { value: 1 }, + specularIntensityMap: { value: null }, + specularIntensityMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + anisotropyVector: { value: /*@__PURE__*/ new Vector2() }, + anisotropyMap: { value: null }, + anisotropyMapTransform: { value: /*@__PURE__*/ new Matrix3() }, + } + ] ), - var dithering_fragment = "#ifdef DITHERING\n\tgl_FragColor.rgb = dithering( gl_FragColor.rgb );\n#endif"; + vertexShader: ShaderChunk.meshphysical_vert, + fragmentShader: ShaderChunk.meshphysical_frag - var dithering_pars_fragment = "#ifdef DITHERING\n\tvec3 dithering( vec3 color ) {\n\t\tfloat grid_position = rand( gl_FragCoord.xy );\n\t\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\n\t\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\n\t\treturn color + dither_shift_RGB;\n\t}\n#endif"; +}; - var roughnessmap_fragment = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\n\troughnessFactor *= texelRoughness.g;\n#endif"; +const _rgb = { r: 0, b: 0, g: 0 }; - var roughnessmap_pars_fragment = "#ifdef USE_ROUGHNESSMAP\n\tuniform sampler2D roughnessMap;\n#endif"; +function WebGLBackground( renderer, cubemaps, cubeuvmaps, state, objects, alpha, premultipliedAlpha ) { - var shadowmap_pars_fragment = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n\t\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n\t}\n\tvec2 texture2DDistribution( sampler2D shadow, vec2 uv ) {\n\t\treturn decodeHalfRGBA( texture2D( shadow, uv ) );\n\t}\n\tfloat VSMShadow (sampler2D shadow, vec2 uv, float compare ){\n\t\tfloat occlusion = 1.0;\n\t\tvec2 distribution = texture2DDistribution( shadow, uv );\n\t\tfloat hard_shadow = step( compare , distribution.x );\n\t\tif (hard_shadow != 1.0 ) {\n\t\t\tfloat distance = compare - distribution.x ;\n\t\t\tfloat variance = max( 0.00000, distribution.y * distribution.y );\n\t\t\tfloat softness_probability = variance / (variance + distance * distance );\t\t\tsoftness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 );\t\t\tocclusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 );\n\t\t}\n\t\treturn occlusion;\n\t}\n\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n\t\tconst vec2 offset = vec2( 0.0, 1.0 );\n\t\tvec2 texelSize = vec2( 1.0 ) / size;\n\t\tvec2 centroidUV = ( floor( uv * size - 0.5 ) + 0.5 ) * texelSize;\n\t\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n\t\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n\t\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n\t\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n\t\tvec2 f = fract( uv * size + 0.5 );\n\t\tfloat a = mix( lb, lt, f.y );\n\t\tfloat b = mix( rb, rt, f.y );\n\t\tfloat c = mix( a, b, f.x );\n\t\treturn c;\n\t}\n\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n\t\tfloat shadow = 1.0;\n\t\tshadowCoord.xyz /= shadowCoord.w;\n\t\tshadowCoord.z += shadowBias;\n\t\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n\t\tbool inFrustum = all( inFrustumVec );\n\t\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\t\tbool frustumTest = all( frustumTestVec );\n\t\tif ( frustumTest ) {\n\t\t#if defined( SHADOWMAP_TYPE_PCF )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tfloat dx2 = dx0 / 2.0;\n\t\t\tfloat dy2 = dy0 / 2.0;\n\t\t\tfloat dx3 = dx1 / 2.0;\n\t\t\tfloat dy3 = dy1 / 2.0;\n\t\t\tshadow = (\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 17.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\t\t\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n\t\t\tfloat dx0 = - texelSize.x * shadowRadius;\n\t\t\tfloat dy0 = - texelSize.y * shadowRadius;\n\t\t\tfloat dx1 = + texelSize.x * shadowRadius;\n\t\t\tfloat dy1 = + texelSize.y * shadowRadius;\n\t\t\tshadow = (\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n\t\t\t\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#elif defined( SHADOWMAP_TYPE_VSM )\n\t\t\tshadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#else\n\t\t\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n\t\t#endif\n\t\t}\n\t\treturn shadow;\n\t}\n\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\n\t\tvec3 absV = abs( v );\n\t\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n\t\tabsV *= scaleToCube;\n\t\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n\t\tvec2 planar = v.xy;\n\t\tfloat almostATexel = 1.5 * texelSizeY;\n\t\tfloat almostOne = 1.0 - almostATexel;\n\t\tif ( absV.z >= almostOne ) {\n\t\t\tif ( v.z > 0.0 )\n\t\t\t\tplanar.x = 4.0 - v.x;\n\t\t} else if ( absV.x >= almostOne ) {\n\t\t\tfloat signX = sign( v.x );\n\t\t\tplanar.x = v.z * signX + 2.0 * signX;\n\t\t} else if ( absV.y >= almostOne ) {\n\t\t\tfloat signY = sign( v.y );\n\t\t\tplanar.x = v.x + 2.0 * signY + 2.0;\n\t\t\tplanar.y = v.z * signY - 2.0;\n\t\t}\n\t\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n\t}\n\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\n\t\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n\t\tvec3 lightToPosition = shadowCoord.xyz;\n\t\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\t\tdp += shadowBias;\n\t\tvec3 bd3D = normalize( lightToPosition );\n\t\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM )\n\t\t\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n\t\t\treturn (\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n\t\t\t\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n\t\t\t) * ( 1.0 / 9.0 );\n\t\t#else\n\t\t\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n\t\t#endif\n\t}\n#endif"; + const clearColor = new Color( 0x000000 ); + let clearAlpha = alpha === true ? 0 : 1; - var shadowmap_pars_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ];\n\t\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHT_SHADOWS ];\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ];\n\t\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ];\n\t#endif\n#endif"; + let planeMesh; + let boxMesh; - var shadowmap_vertex = "#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n\t}\n\t#endif\n#endif"; + let currentBackground = null; + let currentBackgroundVersion = 0; + let currentTonemapping = null; - var shadowmask_pars_fragment = "float getShadowMask() {\n\tfloat shadow = 1.0;\n\t#ifdef USE_SHADOWMAP\n\t#if NUM_DIR_LIGHT_SHADOWS > 0\n\tDirectionalLight directionalLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) {\n\t\tdirectionalLight = directionalLights[ i ];\n\t\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_SPOT_LIGHT_SHADOWS > 0\n\tSpotLight spotLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) {\n\t\tspotLight = spotLights[ i ];\n\t\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n\t}\n\t#endif\n\t#if NUM_POINT_LIGHT_SHADOWS > 0\n\tPointLight pointLight;\n\t#pragma unroll_loop\n\tfor ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) {\n\t\tpointLight = pointLights[ i ];\n\t\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\n\t}\n\t#endif\n\t#endif\n\treturn shadow;\n}"; + function render( renderList, scene ) { - var skinbase_vertex = "#ifdef USE_SKINNING\n\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\n\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\n\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\n\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; + let forceClear = false; + let background = scene.isScene === true ? scene.background : null; - var skinning_pars_vertex = "#ifdef USE_SKINNING\n\tuniform mat4 bindMatrix;\n\tuniform mat4 bindMatrixInverse;\n\t#ifdef BONE_TEXTURE\n\t\tuniform highp sampler2D boneTexture;\n\t\tuniform int boneTextureSize;\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tfloat j = i * 4.0;\n\t\t\tfloat x = mod( j, float( boneTextureSize ) );\n\t\t\tfloat y = floor( j / float( boneTextureSize ) );\n\t\t\tfloat dx = 1.0 / float( boneTextureSize );\n\t\t\tfloat dy = 1.0 / float( boneTextureSize );\n\t\t\ty = dy * ( y + 0.5 );\n\t\t\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n\t\t\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n\t\t\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n\t\t\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\t\t\tmat4 bone = mat4( v1, v2, v3, v4 );\n\t\t\treturn bone;\n\t\t}\n\t#else\n\t\tuniform mat4 boneMatrices[ MAX_BONES ];\n\t\tmat4 getBoneMatrix( const in float i ) {\n\t\t\tmat4 bone = boneMatrices[ int(i) ];\n\t\t\treturn bone;\n\t\t}\n\t#endif\n#endif"; + if ( background && background.isTexture ) { - var skinning_vertex = "#ifdef USE_SKINNING\n\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n\tvec4 skinned = vec4( 0.0 );\n\tskinned += boneMatX * skinVertex * skinWeight.x;\n\tskinned += boneMatY * skinVertex * skinWeight.y;\n\tskinned += boneMatZ * skinVertex * skinWeight.z;\n\tskinned += boneMatW * skinVertex * skinWeight.w;\n\ttransformed = ( bindMatrixInverse * skinned ).xyz;\n#endif"; + const usePMREM = scene.backgroundBlurriness > 0; // use PMREM if the user wants to blur the background + background = ( usePMREM ? cubeuvmaps : cubemaps ).get( background ); - var skinnormal_vertex = "#ifdef USE_SKINNING\n\tmat4 skinMatrix = mat4( 0.0 );\n\tskinMatrix += skinWeight.x * boneMatX;\n\tskinMatrix += skinWeight.y * boneMatY;\n\tskinMatrix += skinWeight.z * boneMatZ;\n\tskinMatrix += skinWeight.w * boneMatW;\n\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\t#ifdef USE_TANGENT\n\t\tobjectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz;\n\t#endif\n#endif"; + } - var specularmap_fragment = "float specularStrength;\n#ifdef USE_SPECULARMAP\n\tvec4 texelSpecular = texture2D( specularMap, vUv );\n\tspecularStrength = texelSpecular.r;\n#else\n\tspecularStrength = 1.0;\n#endif"; + if ( background === null ) { - var specularmap_pars_fragment = "#ifdef USE_SPECULARMAP\n\tuniform sampler2D specularMap;\n#endif"; + setClear( clearColor, clearAlpha ); - var tonemapping_fragment = "#if defined( TONE_MAPPING )\n\tgl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif"; + } else if ( background && background.isColor ) { - var tonemapping_pars_fragment = "#ifndef saturate\n\t#define saturate(a) clamp( a, 0.0, 1.0 )\n#endif\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n\treturn toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\tcolor = max( vec3( 0.0 ), color - 0.004 );\n\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\nvec3 ACESFilmicToneMapping( vec3 color ) {\n\tcolor *= toneMappingExposure;\n\treturn saturate( ( color * ( 2.51 * color + 0.03 ) ) / ( color * ( 2.43 * color + 0.59 ) + 0.14 ) );\n}"; + setClear( background, 1 ); + forceClear = true; - var uv_pars_fragment = "#ifdef USE_UV\n\tvarying vec2 vUv;\n#endif"; + } - var uv_pars_vertex = "#ifdef USE_UV\n\tvarying vec2 vUv;\n\tuniform mat3 uvTransform;\n#endif"; + const environmentBlendMode = renderer.xr.getEnvironmentBlendMode(); - var uv_vertex = "#ifdef USE_UV\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n#endif"; + if ( environmentBlendMode === 'additive' ) { - var uv2_pars_fragment = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvarying vec2 vUv2;\n#endif"; + state.buffers.color.setClear( 0, 0, 0, 1, premultipliedAlpha ); - var uv2_pars_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tattribute vec2 uv2;\n\tvarying vec2 vUv2;\n#endif"; + } else if ( environmentBlendMode === 'alpha-blend' ) { - var uv2_vertex = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\tvUv2 = uv2;\n#endif"; + state.buffers.color.setClear( 0, 0, 0, 0, premultipliedAlpha ); - var worldpos_vertex = "#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\n\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n#endif"; + } - var background_frag = "uniform sampler2D t2D;\nvarying vec2 vUv;\nvoid main() {\n\tvec4 texColor = texture2D( t2D, vUv );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + if ( renderer.autoClear || forceClear ) { - var background_vert = "varying vec2 vUv;\nuniform mat3 uvTransform;\nvoid main() {\n\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\n\tgl_Position = vec4( position.xy, 1.0, 1.0 );\n}"; + renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); - var cube_frag = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldDirection;\nvoid main() {\n\tvec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\tgl_FragColor.a *= opacity;\n\t#include \n\t#include \n}"; + } - var cube_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n\tgl_Position.z = gl_Position.w;\n}"; + if ( background && ( background.isCubeTexture || background.mapping === CubeUVReflectionMapping ) ) { - var depth_frag = "#if DEPTH_PACKING == 3200\n\tuniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#if DEPTH_PACKING == 3200\n\t\tdiffuseColor.a = opacity;\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#if DEPTH_PACKING == 3200\n\t\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\n\t#elif DEPTH_PACKING == 3201\n\t\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n\t#endif\n}"; + if ( boxMesh === undefined ) { - var depth_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + boxMesh = new Mesh( + new BoxGeometry( 1, 1, 1 ), + new ShaderMaterial( { + name: 'BackgroundCubeMaterial', + uniforms: cloneUniforms( ShaderLib.backgroundCube.uniforms ), + vertexShader: ShaderLib.backgroundCube.vertexShader, + fragmentShader: ShaderLib.backgroundCube.fragmentShader, + side: BackSide, + depthTest: false, + depthWrite: false, + fog: false + } ) + ); - var distanceRGBA_frag = "#define DISTANCE\nuniform vec3 referencePosition;\nuniform float nearDistance;\nuniform float farDistance;\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main () {\n\t#include \n\tvec4 diffuseColor = vec4( 1.0 );\n\t#include \n\t#include \n\t#include \n\tfloat dist = length( vWorldPosition - referencePosition );\n\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\n\tdist = saturate( dist );\n\tgl_FragColor = packDepthToRGBA( dist );\n}"; + boxMesh.geometry.deleteAttribute( 'normal' ); + boxMesh.geometry.deleteAttribute( 'uv' ); - var distanceRGBA_vert = "#define DISTANCE\nvarying vec3 vWorldPosition;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#ifdef USE_DISPLACEMENTMAP\n\t\t#include \n\t\t#include \n\t\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvWorldPosition = worldPosition.xyz;\n}"; + boxMesh.onBeforeRender = function ( renderer, scene, camera ) { - var equirect_frag = "uniform sampler2D tEquirect;\nvarying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvec3 direction = normalize( vWorldDirection );\n\tvec2 sampleUV;\n\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\n\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n\tvec4 texColor = texture2D( tEquirect, sampleUV );\n\tgl_FragColor = mapTexelToLinear( texColor );\n\t#include \n\t#include \n}"; + this.matrixWorld.copyPosition( camera.matrixWorld ); - var equirect_vert = "varying vec3 vWorldDirection;\n#include \nvoid main() {\n\tvWorldDirection = transformDirection( position, modelMatrix );\n\t#include \n\t#include \n}"; + }; - var linedashed_frag = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\n\t\tdiscard;\n\t}\n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + // add "envMap" material property so the renderer can evaluate it like for built-in materials + Object.defineProperty( boxMesh.material, 'envMap', { - var linedashed_vert = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvLineDistance = scale * lineDistance;\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; + get: function () { - var meshbasic_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\t#ifdef USE_LIGHTMAP\n\t\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\t#else\n\t\treflectedLight.indirectDiffuse += vec3( 1.0 );\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\n\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + return this.uniforms.envMap.value; - var meshbasic_vert = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifdef USE_ENVMAP\n\t#include \n\t#include \n\t#include \n\t#include \n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + } - var meshlambert_frag = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.indirectDiffuse += ( gl_FrontFacing ) ? vIndirectFront : vIndirectBack;\n\t#else\n\t\treflectedLight.indirectDiffuse += vIndirectFront;\n\t#endif\n\t#include \n\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n\t#ifdef DOUBLE_SIDED\n\t\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n\t#else\n\t\treflectedLight.directDiffuse = vLightFront;\n\t#endif\n\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + } ); - var meshlambert_vert = "#define LAMBERT\nvarying vec3 vLightFront;\nvarying vec3 vIndirectFront;\n#ifdef DOUBLE_SIDED\n\tvarying vec3 vLightBack;\n\tvarying vec3 vIndirectBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + objects.update( boxMesh ); - var meshmatcap_frag = "#define MATCAP\nuniform vec3 diffuse;\nuniform float opacity;\nuniform sampler2D matcap;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 viewDir = normalize( vViewPosition );\n\tvec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) );\n\tvec3 y = cross( viewDir, x );\n\tvec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5;\n\t#ifdef USE_MATCAP\n\t\tvec4 matcapColor = texture2D( matcap, uv );\n\t\tmatcapColor = matcapTexelToLinear( matcapColor );\n\t#else\n\t\tvec4 matcapColor = vec4( 1.0 );\n\t#endif\n\tvec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + } - var meshmatcap_vert = "#define MATCAP\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#ifndef FLAT_SHADED\n\t\tvNormal = normalize( transformedNormal );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n}"; + boxMesh.material.uniforms.envMap.value = background; + boxMesh.material.uniforms.flipEnvMap.value = ( background.isCubeTexture && background.isRenderTargetTexture === false ) ? - 1 : 1; + boxMesh.material.uniforms.backgroundBlurriness.value = scene.backgroundBlurriness; + boxMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; + boxMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer; - var meshphong_frag = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#include \n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + if ( currentBackground !== background || + currentBackgroundVersion !== background.version || + currentTonemapping !== renderer.toneMapping ) { - var meshphong_vert = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + boxMesh.material.needsUpdate = true; - var meshphysical_frag = "#define STANDARD\n#ifdef PHYSICAL\n\t#define REFLECTIVITY\n\t#define CLEARCOAT\n\t#define TRANSPARENCY\n#endif\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifdef TRANSPARENCY\n\tuniform float transparency;\n#endif\n#ifdef REFLECTIVITY\n\tuniform float reflectivity;\n#endif\n#ifdef CLEARCOAT\n\tuniform float clearcoat;\n\tuniform float clearcoatRoughness;\n#endif\n#ifdef USE_SHEEN\n\tuniform vec3 sheen;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n\tvec3 totalEmissiveRadiance = emissive;\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n\t#ifdef TRANSPARENCY\n\t\tdiffuseColor.a *= saturate( 1. - transparency + linearToRelativeLuminance( reflectedLight.directSpecular + reflectedLight.indirectSpecular ) );\n\t#endif\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + currentBackground = background; + currentBackgroundVersion = background.version; + currentTonemapping = renderer.toneMapping; - var meshphysical_vert = "#define STANDARD\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\tvViewPosition = - mvPosition.xyz;\n\t#include \n\t#include \n\t#include \n}"; + } - var normal_frag = "#define NORMAL\nuniform float opacity;\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\n}"; + boxMesh.layers.enableAll(); - var normal_vert = "#define NORMAL\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvarying vec3 vViewPosition;\n#endif\n#ifndef FLAT_SHADED\n\tvarying vec3 vNormal;\n\t#ifdef USE_TANGENT\n\t\tvarying vec3 vTangent;\n\t\tvarying vec3 vBitangent;\n\t#endif\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#ifndef FLAT_SHADED\n\tvNormal = normalize( transformedNormal );\n\t#ifdef USE_TANGENT\n\t\tvTangent = normalize( transformedTangent );\n\t\tvBitangent = normalize( cross( vNormal, vTangent ) * tangent.w );\n\t#endif\n#endif\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( TANGENTSPACE_NORMALMAP )\n\tvViewPosition = - mvPosition.xyz;\n#endif\n}"; + // push to the pre-sorted opaque render list + renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); - var points_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + } else if ( background && background.isTexture ) { - var points_vert = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\tgl_PointSize = size;\n\t#ifdef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z );\n\t#endif\n\t#include \n\t#include \n\t#include \n\t#include \n}"; + if ( planeMesh === undefined ) { - var shadow_frag = "uniform vec3 color;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\n\t#include \n}"; + planeMesh = new Mesh( + new PlaneGeometry( 2, 2 ), + new ShaderMaterial( { + name: 'BackgroundMaterial', + uniforms: cloneUniforms( ShaderLib.background.uniforms ), + vertexShader: ShaderLib.background.vertexShader, + fragmentShader: ShaderLib.background.fragmentShader, + side: FrontSide, + depthTest: false, + depthWrite: false, + fog: false + } ) + ); - var shadow_vert = "#include \n#include \nvoid main() {\n\t#include \n\t#include \n\t#include \n\t#include \n\t#include \n}"; + planeMesh.geometry.deleteAttribute( 'normal' ); - var sprite_frag = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec3 outgoingLight = vec3( 0.0 );\n\tvec4 diffuseColor = vec4( diffuse, opacity );\n\t#include \n\t#include \n\t#include \n\toutgoingLight = diffuseColor.rgb;\n\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\n\t#include \n\t#include \n\t#include \n}"; + // add "map" material property so the renderer can evaluate it like for built-in materials + Object.defineProperty( planeMesh.material, 'map', { - var sprite_vert = "uniform float rotation;\nuniform vec2 center;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n\t#include \n\tvec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );\n\tvec2 scale;\n\tscale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );\n\tscale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );\n\t#ifndef USE_SIZEATTENUATION\n\t\tbool isPerspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 );\n\t\tif ( isPerspective ) scale *= - mvPosition.z;\n\t#endif\n\tvec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;\n\tvec2 rotatedPosition;\n\trotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;\n\trotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;\n\tmvPosition.xy += rotatedPosition;\n\tgl_Position = projectionMatrix * mvPosition;\n\t#include \n\t#include \n\t#include \n}"; + get: function () { - var ShaderChunk = { - alphamap_fragment: alphamap_fragment, - alphamap_pars_fragment: alphamap_pars_fragment, - alphatest_fragment: alphatest_fragment, - aomap_fragment: aomap_fragment, - aomap_pars_fragment: aomap_pars_fragment, - begin_vertex: begin_vertex, - beginnormal_vertex: beginnormal_vertex, - bsdfs: bsdfs, - bumpmap_pars_fragment: bumpmap_pars_fragment, - clipping_planes_fragment: clipping_planes_fragment, - clipping_planes_pars_fragment: clipping_planes_pars_fragment, - clipping_planes_pars_vertex: clipping_planes_pars_vertex, - clipping_planes_vertex: clipping_planes_vertex, - color_fragment: color_fragment, - color_pars_fragment: color_pars_fragment, - color_pars_vertex: color_pars_vertex, - color_vertex: color_vertex, - common: common, - cube_uv_reflection_fragment: cube_uv_reflection_fragment, - defaultnormal_vertex: defaultnormal_vertex, - displacementmap_pars_vertex: displacementmap_pars_vertex, - displacementmap_vertex: displacementmap_vertex, - emissivemap_fragment: emissivemap_fragment, - emissivemap_pars_fragment: emissivemap_pars_fragment, - encodings_fragment: encodings_fragment, - encodings_pars_fragment: encodings_pars_fragment, - envmap_fragment: envmap_fragment, - envmap_common_pars_fragment: envmap_common_pars_fragment, - envmap_pars_fragment: envmap_pars_fragment, - envmap_pars_vertex: envmap_pars_vertex, - envmap_physical_pars_fragment: envmap_physical_pars_fragment, - envmap_vertex: envmap_vertex, - fog_vertex: fog_vertex, - fog_pars_vertex: fog_pars_vertex, - fog_fragment: fog_fragment, - fog_pars_fragment: fog_pars_fragment, - gradientmap_pars_fragment: gradientmap_pars_fragment, - lightmap_fragment: lightmap_fragment, - lightmap_pars_fragment: lightmap_pars_fragment, - lights_lambert_vertex: lights_lambert_vertex, - lights_pars_begin: lights_pars_begin, - lights_phong_fragment: lights_phong_fragment, - lights_phong_pars_fragment: lights_phong_pars_fragment, - lights_physical_fragment: lights_physical_fragment, - lights_physical_pars_fragment: lights_physical_pars_fragment, - lights_fragment_begin: lights_fragment_begin, - lights_fragment_maps: lights_fragment_maps, - lights_fragment_end: lights_fragment_end, - logdepthbuf_fragment: logdepthbuf_fragment, - logdepthbuf_pars_fragment: logdepthbuf_pars_fragment, - logdepthbuf_pars_vertex: logdepthbuf_pars_vertex, - logdepthbuf_vertex: logdepthbuf_vertex, - map_fragment: map_fragment, - map_pars_fragment: map_pars_fragment, - map_particle_fragment: map_particle_fragment, - map_particle_pars_fragment: map_particle_pars_fragment, - metalnessmap_fragment: metalnessmap_fragment, - metalnessmap_pars_fragment: metalnessmap_pars_fragment, - morphnormal_vertex: morphnormal_vertex, - morphtarget_pars_vertex: morphtarget_pars_vertex, - morphtarget_vertex: morphtarget_vertex, - normal_fragment_begin: normal_fragment_begin, - normal_fragment_maps: normal_fragment_maps, - normalmap_pars_fragment: normalmap_pars_fragment, - clearcoat_normal_fragment_begin: clearcoat_normal_fragment_begin, - clearcoat_normal_fragment_maps: clearcoat_normal_fragment_maps, - clearcoat_normalmap_pars_fragment: clearcoat_normalmap_pars_fragment, - packing: packing, - premultiplied_alpha_fragment: premultiplied_alpha_fragment, - project_vertex: project_vertex, - dithering_fragment: dithering_fragment, - dithering_pars_fragment: dithering_pars_fragment, - roughnessmap_fragment: roughnessmap_fragment, - roughnessmap_pars_fragment: roughnessmap_pars_fragment, - shadowmap_pars_fragment: shadowmap_pars_fragment, - shadowmap_pars_vertex: shadowmap_pars_vertex, - shadowmap_vertex: shadowmap_vertex, - shadowmask_pars_fragment: shadowmask_pars_fragment, - skinbase_vertex: skinbase_vertex, - skinning_pars_vertex: skinning_pars_vertex, - skinning_vertex: skinning_vertex, - skinnormal_vertex: skinnormal_vertex, - specularmap_fragment: specularmap_fragment, - specularmap_pars_fragment: specularmap_pars_fragment, - tonemapping_fragment: tonemapping_fragment, - tonemapping_pars_fragment: tonemapping_pars_fragment, - uv_pars_fragment: uv_pars_fragment, - uv_pars_vertex: uv_pars_vertex, - uv_vertex: uv_vertex, - uv2_pars_fragment: uv2_pars_fragment, - uv2_pars_vertex: uv2_pars_vertex, - uv2_vertex: uv2_vertex, - worldpos_vertex: worldpos_vertex, - - background_frag: background_frag, - background_vert: background_vert, - cube_frag: cube_frag, - cube_vert: cube_vert, - depth_frag: depth_frag, - depth_vert: depth_vert, - distanceRGBA_frag: distanceRGBA_frag, - distanceRGBA_vert: distanceRGBA_vert, - equirect_frag: equirect_frag, - equirect_vert: equirect_vert, - linedashed_frag: linedashed_frag, - linedashed_vert: linedashed_vert, - meshbasic_frag: meshbasic_frag, - meshbasic_vert: meshbasic_vert, - meshlambert_frag: meshlambert_frag, - meshlambert_vert: meshlambert_vert, - meshmatcap_frag: meshmatcap_frag, - meshmatcap_vert: meshmatcap_vert, - meshphong_frag: meshphong_frag, - meshphong_vert: meshphong_vert, - meshphysical_frag: meshphysical_frag, - meshphysical_vert: meshphysical_vert, - normal_frag: normal_frag, - normal_vert: normal_vert, - points_frag: points_frag, - points_vert: points_vert, - shadow_frag: shadow_frag, - shadow_vert: shadow_vert, - sprite_frag: sprite_frag, - sprite_vert: sprite_vert - }; + return this.uniforms.t2D.value; - /** - * Uniforms library for shared webgl shaders - */ + } - var UniformsLib = { + } ); - common: { + objects.update( planeMesh ); - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, + } - map: { value: null }, - uvTransform: { value: new Matrix3() }, + planeMesh.material.uniforms.t2D.value = background; + planeMesh.material.uniforms.backgroundIntensity.value = scene.backgroundIntensity; + planeMesh.material.toneMapped = ColorManagement.getTransfer( background.colorSpace ) !== SRGBTransfer; - alphaMap: { value: null }, + if ( background.matrixAutoUpdate === true ) { - }, + background.updateMatrix(); - specularmap: { + } - specularMap: { value: null }, + planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); - }, + if ( currentBackground !== background || + currentBackgroundVersion !== background.version || + currentTonemapping !== renderer.toneMapping ) { - envmap: { + planeMesh.material.needsUpdate = true; - envMap: { value: null }, - flipEnvMap: { value: - 1 }, - reflectivity: { value: 1.0 }, - refractionRatio: { value: 0.98 }, - maxMipLevel: { value: 0 } + currentBackground = background; + currentBackgroundVersion = background.version; + currentTonemapping = renderer.toneMapping; - }, + } - aomap: { + planeMesh.layers.enableAll(); - aoMap: { value: null }, - aoMapIntensity: { value: 1 } + // push to the pre-sorted opaque render list + renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); - }, + } - lightmap: { + } - lightMap: { value: null }, - lightMapIntensity: { value: 1 } + function setClear( color, alpha ) { - }, + color.getRGB( _rgb, getUnlitUniformColorSpace( renderer ) ); - emissivemap: { + state.buffers.color.setClear( _rgb.r, _rgb.g, _rgb.b, alpha, premultipliedAlpha ); - emissiveMap: { value: null } + } - }, + return { - bumpmap: { + getClearColor: function () { - bumpMap: { value: null }, - bumpScale: { value: 1 } + return clearColor; }, + setClearColor: function ( color, alpha = 1 ) { - normalmap: { - - normalMap: { value: null }, - normalScale: { value: new Vector2( 1, 1 ) } + clearColor.set( color ); + clearAlpha = alpha; + setClear( clearColor, clearAlpha ); }, + getClearAlpha: function () { - displacementmap: { - - displacementMap: { value: null }, - displacementScale: { value: 1 }, - displacementBias: { value: 0 } + return clearAlpha; }, + setClearAlpha: function ( alpha ) { - roughnessmap: { - - roughnessMap: { value: null } + clearAlpha = alpha; + setClear( clearColor, clearAlpha ); }, + render: render - metalnessmap: { + }; - metalnessMap: { value: null } +} - }, +function WebGLBindingStates( gl, extensions, attributes, capabilities ) { - gradientmap: { + const maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); - gradientMap: { value: null } + const extension = capabilities.isWebGL2 ? null : extensions.get( 'OES_vertex_array_object' ); + const vaoAvailable = capabilities.isWebGL2 || extension !== null; - }, + const bindingStates = {}; - fog: { + const defaultState = createBindingState( null ); + let currentState = defaultState; + let forceUpdate = false; - fogDensity: { value: 0.00025 }, - fogNear: { value: 1 }, - fogFar: { value: 2000 }, - fogColor: { value: new Color( 0xffffff ) } + function setup( object, material, program, geometry, index ) { - }, + let updateBuffers = false; - lights: { - - ambientLightColor: { value: [] }, - - lightProbe: { value: [] }, - - directionalLights: { value: [], properties: { - direction: {}, - color: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - directionalShadowMap: { value: [] }, - directionalShadowMatrix: { value: [] }, - - spotLights: { value: [], properties: { - color: {}, - position: {}, - direction: {}, - distance: {}, - coneCos: {}, - penumbraCos: {}, - decay: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {} - } }, - - spotShadowMap: { value: [] }, - spotShadowMatrix: { value: [] }, - - pointLights: { value: [], properties: { - color: {}, - position: {}, - decay: {}, - distance: {}, - - shadow: {}, - shadowBias: {}, - shadowRadius: {}, - shadowMapSize: {}, - shadowCameraNear: {}, - shadowCameraFar: {} - } }, - - pointShadowMap: { value: [] }, - pointShadowMatrix: { value: [] }, - - hemisphereLights: { value: [], properties: { - direction: {}, - skyColor: {}, - groundColor: {} - } }, - - // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src - rectAreaLights: { value: [], properties: { - color: {}, - position: {}, - width: {}, - height: {} - } } + if ( vaoAvailable ) { - }, + const state = getBindingState( geometry, program, material ); - points: { + if ( currentState !== state ) { - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - size: { value: 1.0 }, - scale: { value: 1.0 }, - map: { value: null }, - uvTransform: { value: new Matrix3() } + currentState = state; + bindVertexArrayObject( currentState.object ); - }, + } - sprite: { + updateBuffers = needsUpdate( object, geometry, program, index ); - diffuse: { value: new Color( 0xeeeeee ) }, - opacity: { value: 1.0 }, - center: { value: new Vector2( 0.5, 0.5 ) }, - rotation: { value: 0.0 }, - map: { value: null }, - uvTransform: { value: new Matrix3() } + if ( updateBuffers ) saveCache( object, geometry, program, index ); - } + } else { - }; + const wireframe = ( material.wireframe === true ); - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - */ + if ( currentState.geometry !== geometry.id || + currentState.program !== program.id || + currentState.wireframe !== wireframe ) { - var ShaderLib = { + currentState.geometry = geometry.id; + currentState.program = program.id; + currentState.wireframe = wireframe; - basic: { + updateBuffers = true; - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.fog - ] ), + } - vertexShader: ShaderChunk.meshbasic_vert, - fragmentShader: ShaderChunk.meshbasic_frag + } - }, + if ( index !== null ) { - lambert: { + attributes.update( index, gl.ELEMENT_ARRAY_BUFFER ); - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) } - } - ] ), + } - vertexShader: ShaderChunk.meshlambert_vert, - fragmentShader: ShaderChunk.meshlambert_frag + if ( updateBuffers || forceUpdate ) { - }, + forceUpdate = false; - phong: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.specularmap, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.gradientmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - specular: { value: new Color( 0x111111 ) }, - shininess: { value: 30 } - } - ] ), - - vertexShader: ShaderChunk.meshphong_vert, - fragmentShader: ShaderChunk.meshphong_frag + setupVertexAttributes( object, material, program, geometry ); - }, + if ( index !== null ) { - standard: { - - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.envmap, - UniformsLib.aomap, - UniformsLib.lightmap, - UniformsLib.emissivemap, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.roughnessmap, - UniformsLib.metalnessmap, - UniformsLib.fog, - UniformsLib.lights, - { - emissive: { value: new Color( 0x000000 ) }, - roughness: { value: 0.5 }, - metalness: { value: 0.5 }, - envMapIntensity: { value: 1 } // temporary - } - ] ), - - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, attributes.get( index ).buffer ); - }, + } - matcap: { + } - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - UniformsLib.fog, - { - matcap: { value: null } - } - ] ), + } - vertexShader: ShaderChunk.meshmatcap_vert, - fragmentShader: ShaderChunk.meshmatcap_frag + function createVertexArrayObject() { - }, + if ( capabilities.isWebGL2 ) return gl.createVertexArray(); - points: { + return extension.createVertexArrayOES(); - uniforms: mergeUniforms( [ - UniformsLib.points, - UniformsLib.fog - ] ), + } - vertexShader: ShaderChunk.points_vert, - fragmentShader: ShaderChunk.points_frag + function bindVertexArrayObject( vao ) { - }, + if ( capabilities.isWebGL2 ) return gl.bindVertexArray( vao ); - dashed: { + return extension.bindVertexArrayOES( vao ); - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.fog, - { - scale: { value: 1 }, - dashSize: { value: 1 }, - totalSize: { value: 2 } - } - ] ), + } - vertexShader: ShaderChunk.linedashed_vert, - fragmentShader: ShaderChunk.linedashed_frag + function deleteVertexArrayObject( vao ) { - }, + if ( capabilities.isWebGL2 ) return gl.deleteVertexArray( vao ); - depth: { + return extension.deleteVertexArrayOES( vao ); - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap - ] ), + } - vertexShader: ShaderChunk.depth_vert, - fragmentShader: ShaderChunk.depth_frag + function getBindingState( geometry, program, material ) { - }, + const wireframe = ( material.wireframe === true ); - normal: { + let programMap = bindingStates[ geometry.id ]; - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.bumpmap, - UniformsLib.normalmap, - UniformsLib.displacementmap, - { - opacity: { value: 1.0 } - } - ] ), + if ( programMap === undefined ) { - vertexShader: ShaderChunk.normal_vert, - fragmentShader: ShaderChunk.normal_frag + programMap = {}; + bindingStates[ geometry.id ] = programMap; - }, + } - sprite: { + let stateMap = programMap[ program.id ]; - uniforms: mergeUniforms( [ - UniformsLib.sprite, - UniformsLib.fog - ] ), + if ( stateMap === undefined ) { - vertexShader: ShaderChunk.sprite_vert, - fragmentShader: ShaderChunk.sprite_frag + stateMap = {}; + programMap[ program.id ] = stateMap; - }, + } - background: { + let state = stateMap[ wireframe ]; - uniforms: { - uvTransform: { value: new Matrix3() }, - t2D: { value: null }, - }, + if ( state === undefined ) { - vertexShader: ShaderChunk.background_vert, - fragmentShader: ShaderChunk.background_frag + state = createBindingState( createVertexArrayObject() ); + stateMap[ wireframe ] = state; - }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ + } - cube: { + return state; - uniforms: { - tCube: { value: null }, - tFlip: { value: - 1 }, - opacity: { value: 1.0 } - }, + } - vertexShader: ShaderChunk.cube_vert, - fragmentShader: ShaderChunk.cube_frag + function createBindingState( vao ) { - }, + const newAttributes = []; + const enabledAttributes = []; + const attributeDivisors = []; - equirect: { + for ( let i = 0; i < maxVertexAttributes; i ++ ) { - uniforms: { - tEquirect: { value: null }, - }, + newAttributes[ i ] = 0; + enabledAttributes[ i ] = 0; + attributeDivisors[ i ] = 0; - vertexShader: ShaderChunk.equirect_vert, - fragmentShader: ShaderChunk.equirect_frag + } - }, + return { - distanceRGBA: { + // for backward compatibility on non-VAO support browser + geometry: null, + program: null, + wireframe: false, - uniforms: mergeUniforms( [ - UniformsLib.common, - UniformsLib.displacementmap, - { - referencePosition: { value: new Vector3() }, - nearDistance: { value: 1 }, - farDistance: { value: 1000 } - } - ] ), + newAttributes: newAttributes, + enabledAttributes: enabledAttributes, + attributeDivisors: attributeDivisors, + object: vao, + attributes: {}, + index: null - vertexShader: ShaderChunk.distanceRGBA_vert, - fragmentShader: ShaderChunk.distanceRGBA_frag + }; - }, + } - shadow: { + function needsUpdate( object, geometry, program, index ) { - uniforms: mergeUniforms( [ - UniformsLib.lights, - UniformsLib.fog, - { - color: { value: new Color( 0x00000 ) }, - opacity: { value: 1.0 } - }, - ] ), + const cachedAttributes = currentState.attributes; + const geometryAttributes = geometry.attributes; - vertexShader: ShaderChunk.shadow_vert, - fragmentShader: ShaderChunk.shadow_frag + let attributesNum = 0; - } + const programAttributes = program.getAttributes(); - }; + for ( const name in programAttributes ) { - ShaderLib.physical = { + const programAttribute = programAttributes[ name ]; - uniforms: mergeUniforms( [ - ShaderLib.standard.uniforms, - { - transparency: { value: 0 }, - clearcoat: { value: 0 }, - clearcoatRoughness: { value: 0 }, - sheen: { value: new Color( 0x000000 ) }, - clearcoatNormalScale: { value: new Vector2( 1, 1 ) }, - clearcoatNormalMap: { value: null }, - } - ] ), + if ( programAttribute.location >= 0 ) { - vertexShader: ShaderChunk.meshphysical_vert, - fragmentShader: ShaderChunk.meshphysical_frag + const cachedAttribute = cachedAttributes[ name ]; + let geometryAttribute = geometryAttributes[ name ]; - }; + if ( geometryAttribute === undefined ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; - function WebGLAnimation() { + } - var context = null; - var isAnimating = false; - var animationLoop = null; + if ( cachedAttribute === undefined ) return true; - function onAnimationFrame( time, frame ) { + if ( cachedAttribute.attribute !== geometryAttribute ) return true; - if ( isAnimating === false ) return; + if ( geometryAttribute && cachedAttribute.data !== geometryAttribute.data ) return true; - animationLoop( time, frame ); + attributesNum ++; - context.requestAnimationFrame( onAnimationFrame ); + } } - return { - - start: function () { + if ( currentState.attributesNum !== attributesNum ) return true; - if ( isAnimating === true ) return; - if ( animationLoop === null ) return; + if ( currentState.index !== index ) return true; - context.requestAnimationFrame( onAnimationFrame ); + return false; - isAnimating = true; + } - }, + function saveCache( object, geometry, program, index ) { - stop: function () { + const cache = {}; + const attributes = geometry.attributes; + let attributesNum = 0; - isAnimating = false; + const programAttributes = program.getAttributes(); - }, + for ( const name in programAttributes ) { - setAnimationLoop: function ( callback ) { + const programAttribute = programAttributes[ name ]; - animationLoop = callback; + if ( programAttribute.location >= 0 ) { - }, + let attribute = attributes[ name ]; - setContext: function ( value ) { + if ( attribute === undefined ) { - context = value; + if ( name === 'instanceMatrix' && object.instanceMatrix ) attribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) attribute = object.instanceColor; - } + } - }; + const data = {}; + data.attribute = attribute; - } + if ( attribute && attribute.data ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + data.data = attribute.data; - function WebGLAttributes( gl ) { + } - var buffers = new WeakMap(); + cache[ name ] = data; - function createBuffer( attribute, bufferType ) { + attributesNum ++; - var array = attribute.array; - var usage = attribute.dynamic ? 35048 : 35044; + } - var buffer = gl.createBuffer(); + } - gl.bindBuffer( bufferType, buffer ); - gl.bufferData( bufferType, array, usage ); + currentState.attributes = cache; + currentState.attributesNum = attributesNum; - attribute.onUploadCallback(); + currentState.index = index; - var type = 5126; + } - if ( array instanceof Float32Array ) { + function initAttributes() { - type = 5126; + const newAttributes = currentState.newAttributes; - } else if ( array instanceof Float64Array ) { + for ( let i = 0, il = newAttributes.length; i < il; i ++ ) { - console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' ); + newAttributes[ i ] = 0; - } else if ( array instanceof Uint16Array ) { + } - type = 5123; + } - } else if ( array instanceof Int16Array ) { + function enableAttribute( attribute ) { - type = 5122; + enableAttributeAndDivisor( attribute, 0 ); - } else if ( array instanceof Uint32Array ) { + } - type = 5125; + function enableAttributeAndDivisor( attribute, meshPerAttribute ) { - } else if ( array instanceof Int32Array ) { + const newAttributes = currentState.newAttributes; + const enabledAttributes = currentState.enabledAttributes; + const attributeDivisors = currentState.attributeDivisors; - type = 5124; + newAttributes[ attribute ] = 1; - } else if ( array instanceof Int8Array ) { + if ( enabledAttributes[ attribute ] === 0 ) { - type = 5120; + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; - } else if ( array instanceof Uint8Array ) { + } - type = 5121; + if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - } + const extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); - return { - buffer: buffer, - type: type, - bytesPerElement: array.BYTES_PER_ELEMENT, - version: attribute.version - }; + extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); + attributeDivisors[ attribute ] = meshPerAttribute; } - function updateBuffer( buffer, attribute, bufferType ) { + } - var array = attribute.array; - var updateRange = attribute.updateRange; + function disableUnusedAttributes() { - gl.bindBuffer( bufferType, buffer ); + const newAttributes = currentState.newAttributes; + const enabledAttributes = currentState.enabledAttributes; - if ( attribute.dynamic === false ) { + for ( let i = 0, il = enabledAttributes.length; i < il; i ++ ) { - gl.bufferData( bufferType, array, 35044 ); + if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - } else if ( updateRange.count === - 1 ) { + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; - // Not using update ranges + } - gl.bufferSubData( bufferType, 0, array ); + } - } else if ( updateRange.count === 0 ) { + } - console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); + function vertexAttribPointer( index, size, type, normalized, stride, offset, integer ) { - } else { + if ( integer === true ) { - gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT, - array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) ); + gl.vertexAttribIPointer( index, size, type, stride, offset ); - updateRange.count = - 1; // reset range + } else { - } + gl.vertexAttribPointer( index, size, type, normalized, stride, offset ); } - // + } - function get( attribute ) { + function setupVertexAttributes( object, material, program, geometry ) { - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) { - return buffers.get( attribute ); + if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return; } - function remove( attribute ) { + initAttributes(); - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + const geometryAttributes = geometry.attributes; - var data = buffers.get( attribute ); + const programAttributes = program.getAttributes(); - if ( data ) { + const materialDefaultAttributeValues = material.defaultAttributeValues; - gl.deleteBuffer( data.buffer ); + for ( const name in programAttributes ) { - buffers.delete( attribute ); + const programAttribute = programAttributes[ name ]; - } + if ( programAttribute.location >= 0 ) { - } + let geometryAttribute = geometryAttributes[ name ]; - function update( attribute, bufferType ) { + if ( geometryAttribute === undefined ) { - if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data; + if ( name === 'instanceMatrix' && object.instanceMatrix ) geometryAttribute = object.instanceMatrix; + if ( name === 'instanceColor' && object.instanceColor ) geometryAttribute = object.instanceColor; - var data = buffers.get( attribute ); + } - if ( data === undefined ) { + if ( geometryAttribute !== undefined ) { - buffers.set( attribute, createBuffer( attribute, bufferType ) ); + const normalized = geometryAttribute.normalized; + const size = geometryAttribute.itemSize; - } else if ( data.version < attribute.version ) { + const attribute = attributes.get( geometryAttribute ); - updateBuffer( data.buffer, attribute, bufferType ); + // TODO Attribute may not be available on context restore - data.version = attribute.version; + if ( attribute === undefined ) continue; - } + const buffer = attribute.buffer; + const type = attribute.type; + const bytesPerElement = attribute.bytesPerElement; - } + // check for integer attributes (WebGL 2 only) - return { + const integer = ( capabilities.isWebGL2 === true && ( type === gl.INT || type === gl.UNSIGNED_INT || geometryAttribute.gpuType === IntType ) ); - get: get, - remove: remove, - update: update + if ( geometryAttribute.isInterleavedBufferAttribute ) { - }; + const data = geometryAttribute.data; + const stride = data.stride; + const offset = geometryAttribute.offset; - } + if ( data.isInstancedInterleavedBuffer ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - // PlaneGeometry + enableAttributeAndDivisor( programAttribute.location + i, data.meshPerAttribute ); - function PlaneGeometry( width, height, widthSegments, heightSegments ) { + } - Geometry.call( this ); + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - this.type = 'PlaneGeometry'; + geometry._maxInstanceCount = data.meshPerAttribute * data.count; - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; + } - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - this.mergeVertices(); + } else { - } + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - PlaneGeometry.prototype = Object.create( Geometry.prototype ); - PlaneGeometry.prototype.constructor = PlaneGeometry; + enableAttribute( programAttribute.location + i ); - // PlaneBufferGeometry + } - function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { + } - BufferGeometry.call( this ); + gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); - this.type = 'PlaneBufferGeometry'; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + stride * bytesPerElement, + ( offset + ( size / programAttribute.locationSize ) * i ) * bytesPerElement, + integer + ); - width = width || 1; - height = height || 1; + } - var width_half = width / 2; - var height_half = height / 2; + } else { - var gridX = Math.floor( widthSegments ) || 1; - var gridY = Math.floor( heightSegments ) || 1; + if ( geometryAttribute.isInstancedBufferAttribute ) { - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - var segment_width = width / gridX; - var segment_height = height / gridY; + enableAttributeAndDivisor( programAttribute.location + i, geometryAttribute.meshPerAttribute ); - var ix, iy; + } - // buffers + if ( object.isInstancedMesh !== true && geometry._maxInstanceCount === undefined ) { - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + geometry._maxInstanceCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - // generate vertices, normals and uvs + } - for ( iy = 0; iy < gridY1; iy ++ ) { + } else { - var y = iy * segment_height - height_half; + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - for ( ix = 0; ix < gridX1; ix ++ ) { + enableAttribute( programAttribute.location + i ); - var x = ix * segment_width - width_half; + } - vertices.push( x, - y, 0 ); + } - normals.push( 0, 0, 1 ); + gl.bindBuffer( gl.ARRAY_BUFFER, buffer ); - uvs.push( ix / gridX ); - uvs.push( 1 - ( iy / gridY ) ); + for ( let i = 0; i < programAttribute.locationSize; i ++ ) { - } + vertexAttribPointer( + programAttribute.location + i, + size / programAttribute.locationSize, + type, + normalized, + size * bytesPerElement, + ( size / programAttribute.locationSize ) * i * bytesPerElement, + integer + ); - } + } - // indices + } - for ( iy = 0; iy < gridY; iy ++ ) { + } else if ( materialDefaultAttributeValues !== undefined ) { - for ( ix = 0; ix < gridX; ix ++ ) { + const value = materialDefaultAttributeValues[ name ]; - var a = ix + gridX1 * iy; - var b = ix + gridX1 * ( iy + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = ( ix + 1 ) + gridX1 * iy; + if ( value !== undefined ) { - // faces + switch ( value.length ) { - indices.push( a, b, d ); - indices.push( b, c, d ); + case 2: + gl.vertexAttrib2fv( programAttribute.location, value ); + break; + + case 3: + gl.vertexAttrib3fv( programAttribute.location, value ); + break; + + case 4: + gl.vertexAttrib4fv( programAttribute.location, value ); + break; + + default: + gl.vertexAttrib1fv( programAttribute.location, value ); + + } + + } + + } } } - // build geometry - - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + disableUnusedAttributes(); } - PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry; + function dispose() { - /** - * @author mrdoob / http://mrdoob.com/ - */ + reset(); - function WebGLBackground( renderer, state, objects, premultipliedAlpha ) { + for ( const geometryId in bindingStates ) { - var clearColor = new Color( 0x000000 ); - var clearAlpha = 0; + const programMap = bindingStates[ geometryId ]; - var planeMesh; - var boxMesh; - // Store the current background texture and its `version` - // so we can recompile the material accordingly. - var currentBackground = null; - var currentBackgroundVersion = 0; + for ( const programId in programMap ) { - function render( renderList, scene, camera, forceClear ) { + const stateMap = programMap[ programId ]; - var background = scene.background; + for ( const wireframe in stateMap ) { - // Ignore background in AR - // TODO: Reconsider this. + deleteVertexArrayObject( stateMap[ wireframe ].object ); - var vr = renderer.vr; - var session = vr.getSession && vr.getSession(); + delete stateMap[ wireframe ]; - if ( session && session.environmentBlendMode === 'additive' ) { + } - background = null; + delete programMap[ programId ]; } - if ( background === null ) { - - setClear( clearColor, clearAlpha ); - currentBackground = null; - currentBackgroundVersion = 0; + delete bindingStates[ geometryId ]; - } else if ( background && background.isColor ) { + } - setClear( background, 1 ); - forceClear = true; - currentBackground = null; - currentBackgroundVersion = 0; + } - } + function releaseStatesOfGeometry( geometry ) { - if ( renderer.autoClear || forceClear ) { + if ( bindingStates[ geometry.id ] === undefined ) return; - renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil ); + const programMap = bindingStates[ geometry.id ]; - } + for ( const programId in programMap ) { - if ( background && ( background.isCubeTexture || background.isWebGLRenderTargetCube ) ) { + const stateMap = programMap[ programId ]; - if ( boxMesh === undefined ) { + for ( const wireframe in stateMap ) { - boxMesh = new Mesh( - new BoxBufferGeometry( 1, 1, 1 ), - new ShaderMaterial( { - type: 'BackgroundCubeMaterial', - uniforms: cloneUniforms( ShaderLib.cube.uniforms ), - vertexShader: ShaderLib.cube.vertexShader, - fragmentShader: ShaderLib.cube.fragmentShader, - side: BackSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); + deleteVertexArrayObject( stateMap[ wireframe ].object ); - boxMesh.geometry.removeAttribute( 'normal' ); - boxMesh.geometry.removeAttribute( 'uv' ); + delete stateMap[ wireframe ]; - boxMesh.onBeforeRender = function ( renderer, scene, camera ) { + } - this.matrixWorld.copyPosition( camera.matrixWorld ); + delete programMap[ programId ]; - }; + } - // enable code injection for non-built-in material - Object.defineProperty( boxMesh.material, 'map', { + delete bindingStates[ geometry.id ]; - get: function () { + } - return this.uniforms.tCube.value; + function releaseStatesOfProgram( program ) { - } + for ( const geometryId in bindingStates ) { - } ); + const programMap = bindingStates[ geometryId ]; - objects.update( boxMesh ); + if ( programMap[ program.id ] === undefined ) continue; - } + const stateMap = programMap[ program.id ]; - var texture = background.isWebGLRenderTargetCube ? background.texture : background; - boxMesh.material.uniforms.tCube.value = texture; - boxMesh.material.uniforms.tFlip.value = ( background.isWebGLRenderTargetCube ) ? 1 : - 1; + for ( const wireframe in stateMap ) { - if ( currentBackground !== background || - currentBackgroundVersion !== texture.version ) { + deleteVertexArrayObject( stateMap[ wireframe ].object ); - boxMesh.material.needsUpdate = true; + delete stateMap[ wireframe ]; - currentBackground = background; - currentBackgroundVersion = texture.version; + } - } + delete programMap[ program.id ]; - // push to the pre-sorted opaque render list - renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null ); + } - } else if ( background && background.isTexture ) { + } - if ( planeMesh === undefined ) { + function reset() { - planeMesh = new Mesh( - new PlaneBufferGeometry( 2, 2 ), - new ShaderMaterial( { - type: 'BackgroundMaterial', - uniforms: cloneUniforms( ShaderLib.background.uniforms ), - vertexShader: ShaderLib.background.vertexShader, - fragmentShader: ShaderLib.background.fragmentShader, - side: FrontSide, - depthTest: false, - depthWrite: false, - fog: false - } ) - ); + resetDefaultState(); + forceUpdate = true; - planeMesh.geometry.removeAttribute( 'normal' ); + if ( currentState === defaultState ) return; - // enable code injection for non-built-in material - Object.defineProperty( planeMesh.material, 'map', { + currentState = defaultState; + bindVertexArrayObject( currentState.object ); - get: function () { + } - return this.uniforms.t2D.value; + // for backward-compatibility - } + function resetDefaultState() { - } ); + defaultState.geometry = null; + defaultState.program = null; + defaultState.wireframe = false; - objects.update( planeMesh ); + } - } + return { - planeMesh.material.uniforms.t2D.value = background; + setup: setup, + reset: reset, + resetDefaultState: resetDefaultState, + dispose: dispose, + releaseStatesOfGeometry: releaseStatesOfGeometry, + releaseStatesOfProgram: releaseStatesOfProgram, - if ( background.matrixAutoUpdate === true ) { + initAttributes: initAttributes, + enableAttribute: enableAttribute, + disableUnusedAttributes: disableUnusedAttributes - background.updateMatrix(); + }; - } +} - planeMesh.material.uniforms.uvTransform.value.copy( background.matrix ); +function WebGLBufferRenderer( gl, extensions, info, capabilities ) { - if ( currentBackground !== background || - currentBackgroundVersion !== background.version ) { + const isWebGL2 = capabilities.isWebGL2; - planeMesh.material.needsUpdate = true; + let mode; - currentBackground = background; - currentBackgroundVersion = background.version; + function setMode( value ) { - } + mode = value; + } - // push to the pre-sorted opaque render list - renderList.unshift( planeMesh, planeMesh.geometry, planeMesh.material, 0, 0, null ); + function render( start, count ) { - } + gl.drawArrays( mode, start, count ); - } + info.update( count, mode, 1 ); - function setClear( color, alpha ) { + } - state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha ); + function renderInstances( start, count, primcount ) { - } + if ( primcount === 0 ) return; - return { + let extension, methodName; - getClearColor: function () { + if ( isWebGL2 ) { - return clearColor; + extension = gl; + methodName = 'drawArraysInstanced'; - }, - setClearColor: function ( color, alpha ) { + } else { - clearColor.set( color ); - clearAlpha = alpha !== undefined ? alpha : 1; - setClear( clearColor, clearAlpha ); + extension = extensions.get( 'ANGLE_instanced_arrays' ); + methodName = 'drawArraysInstancedANGLE'; - }, - getClearAlpha: function () { + if ( extension === null ) { - return clearAlpha; + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - }, - setClearAlpha: function ( alpha ) { + } - clearAlpha = alpha; - setClear( clearColor, clearAlpha ); + } - }, - render: render + extension[ methodName ]( mode, start, count, primcount ); - }; + info.update( count, mode, primcount ); } - /** - * @author mrdoob / http://mrdoob.com/ - */ + // - function WebGLBufferRenderer( gl, extensions, info, capabilities ) { + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; - var mode; +} - function setMode( value ) { +function WebGLCapabilities( gl, extensions, parameters ) { - mode = value; + let maxAnisotropy; - } + function getMaxAnisotropy() { - function render( start, count ) { + if ( maxAnisotropy !== undefined ) return maxAnisotropy; - gl.drawArrays( mode, start, count ); + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - info.update( count, mode ); + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - } + maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - function renderInstances( geometry, start, count ) { + } else { - var extension, methodName; + maxAnisotropy = 0; - if ( capabilities.isWebGL2 ) { + } - extension = gl; - methodName = 'drawArraysInstanced'; + return maxAnisotropy; - } else { + } - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawArraysInstancedANGLE'; + function getMaxPrecision( precision ) { - if ( extension === null ) { + if ( precision === 'highp' ) { - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { - } + return 'highp'; } - extension[ methodName ]( mode, start, count, geometry.maxInstancedCount ); - - info.update( count, mode, geometry.maxInstancedCount ); + precision = 'mediump'; } - // + if ( precision === 'mediump' ) { - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { - } + return 'mediump'; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function WebGLCapabilities( gl, extensions, parameters ) { + } - var maxAnisotropy; + return 'lowp'; - function getMaxAnisotropy() { + } - if ( maxAnisotropy !== undefined ) return maxAnisotropy; + const isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl.constructor.name === 'WebGL2RenderingContext'; - var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + let precision = parameters.precision !== undefined ? parameters.precision : 'highp'; + const maxPrecision = getMaxPrecision( precision ); - if ( extension !== null ) { + if ( maxPrecision !== precision ) { - maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); + console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); + precision = maxPrecision; - } else { + } - maxAnisotropy = 0; + const drawBuffers = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' ); - } + const logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; - return maxAnisotropy; + const maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + const maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + const maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); + const maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); - } + const maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + const maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); + const maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); + const maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); - function getMaxPrecision( precision ) { + const vertexTextures = maxVertexTextures > 0; + const floatFragmentTextures = isWebGL2 || extensions.has( 'OES_texture_float' ); + const floatVertexTextures = vertexTextures && floatFragmentTextures; - if ( precision === 'highp' ) { + const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0; - if ( gl.getShaderPrecisionFormat( 35633, 36338 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36338 ).precision > 0 ) { + return { - return 'highp'; + isWebGL2: isWebGL2, - } + drawBuffers: drawBuffers, - precision = 'mediump'; + getMaxAnisotropy: getMaxAnisotropy, + getMaxPrecision: getMaxPrecision, - } + precision: precision, + logarithmicDepthBuffer: logarithmicDepthBuffer, - if ( precision === 'mediump' ) { + maxTextures: maxTextures, + maxVertexTextures: maxVertexTextures, + maxTextureSize: maxTextureSize, + maxCubemapSize: maxCubemapSize, - if ( gl.getShaderPrecisionFormat( 35633, 36337 ).precision > 0 && - gl.getShaderPrecisionFormat( 35632, 36337 ).precision > 0 ) { + maxAttributes: maxAttributes, + maxVertexUniforms: maxVertexUniforms, + maxVaryings: maxVaryings, + maxFragmentUniforms: maxFragmentUniforms, - return 'mediump'; + vertexTextures: vertexTextures, + floatFragmentTextures: floatFragmentTextures, + floatVertexTextures: floatVertexTextures, - } + maxSamples: maxSamples - } + }; - return 'lowp'; +} - } +function WebGLClipping( properties ) { - var isWebGL2 = typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext; + const scope = this; - var precision = parameters.precision !== undefined ? parameters.precision : 'highp'; - var maxPrecision = getMaxPrecision( precision ); + let globalState = null, + numGlobalPlanes = 0, + localClippingEnabled = false, + renderingShadows = false; - if ( maxPrecision !== precision ) { + const plane = new Plane(), + viewNormalMatrix = new Matrix3(), - console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' ); - precision = maxPrecision; + uniform = { value: null, needsUpdate: false }; - } + this.uniform = uniform; + this.numPlanes = 0; + this.numIntersection = 0; - var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true; + this.init = function ( planes, enableLocalClipping ) { - var maxTextures = gl.getParameter( 34930 ); - var maxVertexTextures = gl.getParameter( 35660 ); - var maxTextureSize = gl.getParameter( 3379 ); - var maxCubemapSize = gl.getParameter( 34076 ); + const enabled = + planes.length !== 0 || + enableLocalClipping || + // enable state of previous frame - the clipping code has to + // run another frame in order to reset the state: + numGlobalPlanes !== 0 || + localClippingEnabled; - var maxAttributes = gl.getParameter( 34921 ); - var maxVertexUniforms = gl.getParameter( 36347 ); - var maxVaryings = gl.getParameter( 36348 ); - var maxFragmentUniforms = gl.getParameter( 36349 ); + localClippingEnabled = enableLocalClipping; - var vertexTextures = maxVertexTextures > 0; - var floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); - var floatVertexTextures = vertexTextures && floatFragmentTextures; + numGlobalPlanes = planes.length; - var maxSamples = isWebGL2 ? gl.getParameter( 36183 ) : 0; + return enabled; - return { + }; - isWebGL2: isWebGL2, + this.beginShadows = function () { - getMaxAnisotropy: getMaxAnisotropy, - getMaxPrecision: getMaxPrecision, + renderingShadows = true; + projectPlanes( null ); - precision: precision, - logarithmicDepthBuffer: logarithmicDepthBuffer, + }; - maxTextures: maxTextures, - maxVertexTextures: maxVertexTextures, - maxTextureSize: maxTextureSize, - maxCubemapSize: maxCubemapSize, + this.endShadows = function () { - maxAttributes: maxAttributes, - maxVertexUniforms: maxVertexUniforms, - maxVaryings: maxVaryings, - maxFragmentUniforms: maxFragmentUniforms, + renderingShadows = false; - vertexTextures: vertexTextures, - floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures, + }; - maxSamples: maxSamples + this.setGlobalState = function ( planes, camera ) { - }; + globalState = projectPlanes( planes, camera, 0 ); - } + }; - /** - * @author tschw - */ + this.setState = function ( material, camera, useCache ) { - function WebGLClipping() { + const planes = material.clippingPlanes, + clipIntersection = material.clipIntersection, + clipShadows = material.clipShadows; - var scope = this, + const materialProperties = properties.get( material ); - globalState = null, - numGlobalPlanes = 0, - localClippingEnabled = false, - renderingShadows = false, + if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { - plane = new Plane(), - viewNormalMatrix = new Matrix3(), + // there's no local clipping - uniform = { value: null, needsUpdate: false }; + if ( renderingShadows ) { - this.uniform = uniform; - this.numPlanes = 0; - this.numIntersection = 0; + // there's no global clipping - this.init = function ( planes, enableLocalClipping, camera ) { + projectPlanes( null ); - var enabled = - planes.length !== 0 || - enableLocalClipping || - // enable state of previous frame - the clipping code has to - // run another frame in order to reset the state: - numGlobalPlanes !== 0 || - localClippingEnabled; + } else { - localClippingEnabled = enableLocalClipping; + resetGlobalState(); - globalState = projectPlanes( planes, camera, 0 ); - numGlobalPlanes = planes.length; + } - return enabled; + } else { - }; + const nGlobal = renderingShadows ? 0 : numGlobalPlanes, + lGlobal = nGlobal * 4; - this.beginShadows = function () { + let dstArray = materialProperties.clippingState || null; - renderingShadows = true; - projectPlanes( null ); + uniform.value = dstArray; // ensure unique state - }; + dstArray = projectPlanes( planes, camera, lGlobal, useCache ); - this.endShadows = function () { + for ( let i = 0; i !== lGlobal; ++ i ) { - renderingShadows = false; - resetGlobalState(); + dstArray[ i ] = globalState[ i ]; - }; + } - this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) { + materialProperties.clippingState = dstArray; + this.numIntersection = clipIntersection ? this.numPlanes : 0; + this.numPlanes += nGlobal; - if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) { + } - // there's no local clipping - if ( renderingShadows ) { + }; - // there's no global clipping + function resetGlobalState() { - projectPlanes( null ); + if ( uniform.value !== globalState ) { - } else { + uniform.value = globalState; + uniform.needsUpdate = numGlobalPlanes > 0; - resetGlobalState(); + } - } + scope.numPlanes = numGlobalPlanes; + scope.numIntersection = 0; - } else { + } - var nGlobal = renderingShadows ? 0 : numGlobalPlanes, - lGlobal = nGlobal * 4, + function projectPlanes( planes, camera, dstOffset, skipTransform ) { - dstArray = cache.clippingState || null; + const nPlanes = planes !== null ? planes.length : 0; + let dstArray = null; - uniform.value = dstArray; // ensure unique state + if ( nPlanes !== 0 ) { - dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); + dstArray = uniform.value; - for ( var i = 0; i !== lGlobal; ++ i ) { + if ( skipTransform !== true || dstArray === null ) { - dstArray[ i ] = globalState[ i ]; + const flatSize = dstOffset + nPlanes * 4, + viewMatrix = camera.matrixWorldInverse; - } + viewNormalMatrix.getNormalMatrix( viewMatrix ); - cache.clippingState = dstArray; - this.numIntersection = clipIntersection ? this.numPlanes : 0; - this.numPlanes += nGlobal; + if ( dstArray === null || dstArray.length < flatSize ) { - } + dstArray = new Float32Array( flatSize ); + } - }; + for ( let i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { - function resetGlobalState() { + plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); - if ( uniform.value !== globalState ) { + plane.normal.toArray( dstArray, i4 ); + dstArray[ i4 + 3 ] = plane.constant; - uniform.value = globalState; - uniform.needsUpdate = numGlobalPlanes > 0; + } } - scope.numPlanes = numGlobalPlanes; - scope.numIntersection = 0; + uniform.value = dstArray; + uniform.needsUpdate = true; } - function projectPlanes( planes, camera, dstOffset, skipTransform ) { + scope.numPlanes = nPlanes; + scope.numIntersection = 0; - var nPlanes = planes !== null ? planes.length : 0, - dstArray = null; + return dstArray; - if ( nPlanes !== 0 ) { + } - dstArray = uniform.value; +} - if ( skipTransform !== true || dstArray === null ) { +function WebGLCubeMaps( renderer ) { - var flatSize = dstOffset + nPlanes * 4, - viewMatrix = camera.matrixWorldInverse; + let cubemaps = new WeakMap(); - viewNormalMatrix.getNormalMatrix( viewMatrix ); + function mapTextureMapping( texture, mapping ) { - if ( dstArray === null || dstArray.length < flatSize ) { + if ( mapping === EquirectangularReflectionMapping ) { - dstArray = new Float32Array( flatSize ); + texture.mapping = CubeReflectionMapping; - } + } else if ( mapping === EquirectangularRefractionMapping ) { - for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) { + texture.mapping = CubeRefractionMapping; - plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix ); + } - plane.normal.toArray( dstArray, i4 ); - dstArray[ i4 + 3 ] = plane.constant; + return texture; - } + } - } + function get( texture ) { - uniform.value = dstArray; - uniform.needsUpdate = true; + if ( texture && texture.isTexture && texture.isRenderTargetTexture === false ) { - } + const mapping = texture.mapping; - scope.numPlanes = nPlanes; + if ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ) { - return dstArray; + if ( cubemaps.has( texture ) ) { - } + const cubemap = cubemaps.get( texture ).texture; + return mapTextureMapping( cubemap, texture.mapping ); - } + } else { - /** - * @author mrdoob / http://mrdoob.com/ - */ + const image = texture.image; - function WebGLExtensions( gl ) { + if ( image && image.height > 0 ) { - var extensions = {}; + const renderTarget = new WebGLCubeRenderTarget( image.height / 2 ); + renderTarget.fromEquirectangularTexture( renderer, texture ); + cubemaps.set( texture, renderTarget ); - return { + texture.addEventListener( 'dispose', onTextureDispose ); - get: function ( name ) { + return mapTextureMapping( renderTarget.texture, texture.mapping ); - if ( extensions[ name ] !== undefined ) { + } else { - return extensions[ name ]; + // image not yet ready. try the conversion next frame - } + return null; - var extension; + } - switch ( name ) { + } - case 'WEBGL_depth_texture': - extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); - break; + } - case 'EXT_texture_filter_anisotropic': - extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - break; + } - case 'WEBGL_compressed_texture_s3tc': - extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - break; + return texture; - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; + } - default: - extension = gl.getExtension( name ); + function onTextureDispose( event ) { - } + const texture = event.target; - if ( extension === null ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + const cubemap = cubemaps.get( texture ); - } + if ( cubemap !== undefined ) { + + cubemaps.delete( texture ); + cubemap.dispose(); - extensions[ name ] = extension; + } - return extension; + } - } + function dispose() { - }; + cubemaps = new WeakMap(); } - /** - * @author mrdoob / http://mrdoob.com/ - */ + return { + get: get, + dispose: dispose + }; - function WebGLGeometries( gl, attributes, info ) { +} - var geometries = new WeakMap(); - var wireframeAttributes = new WeakMap(); +class OrthographicCamera extends Camera { - function onGeometryDispose( event ) { + constructor( left = - 1, right = 1, top = 1, bottom = - 1, near = 0.1, far = 2000 ) { - var geometry = event.target; - var buffergeometry = geometries.get( geometry ); + super(); - if ( buffergeometry.index !== null ) { + this.isOrthographicCamera = true; - attributes.remove( buffergeometry.index ); + this.type = 'OrthographicCamera'; - } + this.zoom = 1; + this.view = null; - for ( var name in buffergeometry.attributes ) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - attributes.remove( buffergeometry.attributes[ name ] ); + this.near = near; + this.far = far; - } + this.updateProjectionMatrix(); - geometry.removeEventListener( 'dispose', onGeometryDispose ); + } - geometries.delete( geometry ); + copy( source, recursive ) { - var attribute = wireframeAttributes.get( buffergeometry ); + super.copy( source, recursive ); - if ( attribute ) { + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - attributes.remove( attribute ); - wireframeAttributes.delete( buffergeometry ); + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); - } + return this; - // + } - info.memory.geometries --; + setViewOffset( fullWidth, fullHeight, x, y, width, height ) { - } + if ( this.view === null ) { - function get( object, geometry ) { + this.view = { + enabled: true, + fullWidth: 1, + fullHeight: 1, + offsetX: 0, + offsetY: 0, + width: 1, + height: 1 + }; - var buffergeometry = geometries.get( geometry ); + } - if ( buffergeometry ) return buffergeometry; + this.view.enabled = true; + this.view.fullWidth = fullWidth; + this.view.fullHeight = fullHeight; + this.view.offsetX = x; + this.view.offsetY = y; + this.view.width = width; + this.view.height = height; - geometry.addEventListener( 'dispose', onGeometryDispose ); + this.updateProjectionMatrix(); - if ( geometry.isBufferGeometry ) { + } - buffergeometry = geometry; + clearViewOffset() { - } else if ( geometry.isGeometry ) { + if ( this.view !== null ) { - if ( geometry._bufferGeometry === undefined ) { + this.view.enabled = false; - geometry._bufferGeometry = new BufferGeometry().setFromObject( object ); + } - } + this.updateProjectionMatrix(); - buffergeometry = geometry._bufferGeometry; + } - } + updateProjectionMatrix() { - geometries.set( geometry, buffergeometry ); + const dx = ( this.right - this.left ) / ( 2 * this.zoom ); + const dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + const cx = ( this.right + this.left ) / 2; + const cy = ( this.top + this.bottom ) / 2; - info.memory.geometries ++; + let left = cx - dx; + let right = cx + dx; + let top = cy + dy; + let bottom = cy - dy; - return buffergeometry; + if ( this.view !== null && this.view.enabled ) { - } + const scaleW = ( this.right - this.left ) / this.view.fullWidth / this.zoom; + const scaleH = ( this.top - this.bottom ) / this.view.fullHeight / this.zoom; - function update( geometry ) { + left += scaleW * this.view.offsetX; + right = left + scaleW * this.view.width; + top -= scaleH * this.view.offsetY; + bottom = top - scaleH * this.view.height; - var index = geometry.index; - var geometryAttributes = geometry.attributes; + } - if ( index !== null ) { + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far, this.coordinateSystem ); - attributes.update( index, 34963 ); + this.projectionMatrixInverse.copy( this.projectionMatrix ).invert(); - } + } - for ( var name in geometryAttributes ) { + toJSON( meta ) { - attributes.update( geometryAttributes[ name ], 34962 ); + const data = super.toJSON( meta ); - } + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; - // morph targets + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - var morphAttributes = geometry.morphAttributes; + return data; - for ( var name in morphAttributes ) { + } - var array = morphAttributes[ name ]; +} - for ( var i = 0, l = array.length; i < l; i ++ ) { +const LOD_MIN = 4; - attributes.update( array[ i ], 34962 ); +// 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(); +const _clearColor = /*@__PURE__*/ new Color(); +let _oldTarget = null; - } +// Golden Ratio +const PHI = ( 1 + Math.sqrt( 5 ) ) / 2; +const INV_PHI = 1 / PHI; - function updateWireframeAttribute( geometry ) { +// 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( 1, 1, 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, 1 ), + /*@__PURE__*/ new Vector3( 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( - 1, 1, - 1 ), + /*@__PURE__*/ new Vector3( 0, PHI, INV_PHI ), + /*@__PURE__*/ new Vector3( 0, PHI, - INV_PHI ), + /*@__PURE__*/ new Vector3( INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( - INV_PHI, 0, PHI ), + /*@__PURE__*/ new Vector3( PHI, INV_PHI, 0 ), + /*@__PURE__*/ new Vector3( - PHI, INV_PHI, 0 ) ]; - var indices = []; +/** + * 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 +*/ - var geometryIndex = geometry.index; - var geometryPosition = geometry.attributes.position; - var version = 0; +class PMREMGenerator { - if ( geometryIndex !== null ) { + constructor( renderer ) { - var array = geometryIndex.array; - version = geometryIndex.version; + this._renderer = renderer; + this._pingPongRenderTarget = null; - for ( var i = 0, l = array.length; i < l; i += 3 ) { + this._lodMax = 0; + this._cubeSize = 0; + this._lodPlanes = []; + this._sizeLods = []; + this._sigmas = []; - var a = array[ i + 0 ]; - var b = array[ i + 1 ]; - var c = array[ i + 2 ]; + this._blurMaterial = null; + this._cubemapMaterial = null; + this._equirectMaterial = null; - indices.push( a, b, b, c, c, a ); + this._compileMaterial( this._blurMaterial ); - } + } - } else { + /** + * 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 ) { - var array = geometryPosition.array; - version = geometryPosition.version; + _oldTarget = this._renderer.getRenderTarget(); - for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + this._setSize( 256 ); - var a = i + 0; - var b = i + 1; - var c = i + 2; + const cubeUVRenderTarget = this._allocateTargets(); + cubeUVRenderTarget.depthBuffer = true; - indices.push( a, b, b, c, c, a ); + this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget ); - } + if ( sigma > 0 ) { - } + this._blur( cubeUVRenderTarget, 0, 0, sigma ); - var attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); - attribute.version = version; + } - attributes.update( attribute, 34963 ); + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - // + return cubeUVRenderTarget; - var previousAttribute = wireframeAttributes.get( geometry ); + } - if ( previousAttribute ) attributes.remove( previousAttribute ); + /** + * 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 ); - wireframeAttributes.set( geometry, attribute ); + } - } + /** + * 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 ) { - function getWireframeAttribute( geometry ) { + return this._fromTexture( cubemap, renderTarget ); - var currentAttribute = wireframeAttributes.get( geometry ); + } - if ( currentAttribute ) { + /** + * 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() { - var geometryIndex = geometry.index; + if ( this._cubemapMaterial === null ) { - if ( geometryIndex !== null ) { + this._cubemapMaterial = _getCubemapMaterial(); + this._compileMaterial( this._cubemapMaterial ); - // if the attribute is obsolete, create a new one + } - if ( currentAttribute.version < geometryIndex.version ) { + } - updateWireframeAttribute( geometry ); + /** + * 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 ); - } else { + } - updateWireframeAttribute( geometry ); + } - } + /** + * 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() { - return wireframeAttributes.get( geometry ); + this._dispose(); - } + if ( this._cubemapMaterial !== null ) this._cubemapMaterial.dispose(); + if ( this._equirectMaterial !== null ) this._equirectMaterial.dispose(); - return { + } - get: get, - update: update, + // private interface - getWireframeAttribute: getWireframeAttribute + _setSize( cubeSize ) { - }; + this._lodMax = Math.floor( Math.log2( cubeSize ) ); + this._cubeSize = Math.pow( 2, this._lodMax ); } - /** - * @author mrdoob / http://mrdoob.com/ - */ + _dispose() { - function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { + if ( this._blurMaterial !== null ) this._blurMaterial.dispose(); - var mode; + if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose(); - function setMode( value ) { + for ( let i = 0; i < this._lodPlanes.length; i ++ ) { - mode = value; + this._lodPlanes[ i ].dispose(); } - var type, bytesPerElement; + } - function setIndex( value ) { + _cleanup( outputTarget ) { - type = value.type; - bytesPerElement = value.bytesPerElement; + this._renderer.setRenderTarget( _oldTarget ); + outputTarget.scissorTest = false; + _setViewport( outputTarget, 0, 0, outputTarget.width, outputTarget.height ); - } + } + + _fromTexture( texture, renderTarget ) { - function render( start, count ) { + if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) { - gl.drawElements( mode, count, type, start * bytesPerElement ); + this._setSize( texture.image.length === 0 ? 16 : ( texture.image[ 0 ].width || texture.image[ 0 ].image.width ) ); - info.update( count, mode ); + } else { // Equirectangular + + this._setSize( texture.image.width / 4 ); } - function renderInstances( geometry, start, count ) { + _oldTarget = this._renderer.getRenderTarget(); - var extension, methodName; + const cubeUVRenderTarget = renderTarget || this._allocateTargets(); + this._textureToCubeUV( texture, cubeUVRenderTarget ); + this._applyPMREM( cubeUVRenderTarget ); + this._cleanup( cubeUVRenderTarget ); - if ( capabilities.isWebGL2 ) { + return cubeUVRenderTarget; - extension = gl; - methodName = 'drawElementsInstanced'; + } - } else { + _allocateTargets() { - extension = extensions.get( 'ANGLE_instanced_arrays' ); - methodName = 'drawElementsInstancedANGLE'; + const width = 3 * Math.max( this._cubeSize, 16 * 7 ); + const height = 4 * this._cubeSize; - if ( extension === null ) { + const params = { + magFilter: LinearFilter, + minFilter: LinearFilter, + generateMipmaps: false, + type: HalfFloatType, + format: RGBAFormat, + colorSpace: LinearSRGBColorSpace, + depthBuffer: false + }; - console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + const cubeUVRenderTarget = _createRenderTarget( width, height, params ); - } + if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width || this._pingPongRenderTarget.height !== height ) { - } + if ( this._pingPongRenderTarget !== null ) { - extension[ methodName ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount ); + this._dispose(); - info.update( count, mode, geometry.maxInstancedCount ); + } - } + this._pingPongRenderTarget = _createRenderTarget( width, height, params ); - // + const { _lodMax } = this; + ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) ); - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; + this._blurMaterial = _getBlurShader( _lodMax, width, height ); - } + } - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + return cubeUVRenderTarget; - function WebGLInfo( gl ) { + } - var memory = { - geometries: 0, - textures: 0 - }; + _compileMaterial( material ) { - var render = { - frame: 0, - calls: 0, - triangles: 0, - points: 0, - lines: 0 - }; + const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material ); + this._renderer.compile( tmpMesh, _flatCamera ); - function update( count, mode, instanceCount ) { + } - instanceCount = instanceCount || 1; + _sceneToCubeUV( scene, near, far, cubeUVRenderTarget ) { - render.calls ++; + const fov = 90; + const aspect = 1; + const cubeCamera = new PerspectiveCamera( fov, aspect, near, far ); + const upSign = [ 1, - 1, 1, 1, 1, 1 ]; + const forwardSign = [ 1, 1, 1, - 1, - 1, - 1 ]; + const renderer = this._renderer; - switch ( mode ) { + const originalAutoClear = renderer.autoClear; + const toneMapping = renderer.toneMapping; + renderer.getClearColor( _clearColor ); - case 4: - render.triangles += instanceCount * ( count / 3 ); - break; + renderer.toneMapping = NoToneMapping; + renderer.autoClear = false; - case 5: - case 6: - render.triangles += instanceCount * ( count - 2 ); - break; + const backgroundMaterial = new MeshBasicMaterial( { + name: 'PMREM.Background', + side: BackSide, + depthWrite: false, + depthTest: false, + } ); - case 1: - render.lines += instanceCount * ( count / 2 ); - break; + const backgroundBox = new Mesh( new BoxGeometry(), backgroundMaterial ); - case 3: - render.lines += instanceCount * ( count - 1 ); - break; + let useSolidColor = false; + const background = scene.background; - case 2: - render.lines += instanceCount * count; - break; + if ( background ) { - case 0: - render.points += instanceCount * count; - break; + if ( background.isColor ) { - default: - console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); - break; + backgroundMaterial.color.copy( background ); + scene.background = null; + useSolidColor = true; } - } - - function reset() { + } else { - render.frame ++; - render.calls = 0; - render.triangles = 0; - render.points = 0; - render.lines = 0; + backgroundMaterial.color.copy( _clearColor ); + useSolidColor = true; } - return { - memory: memory, - render: render, - programs: null, - autoReset: true, - reset: reset, - update: update - }; - - } + for ( let i = 0; i < 6; i ++ ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + const col = i % 3; - function absNumericalSort( a, b ) { + if ( col === 0 ) { - return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( forwardSign[ i ], 0, 0 ); - } + } else if ( col === 1 ) { - function WebGLMorphtargets( gl ) { + cubeCamera.up.set( 0, 0, upSign[ i ] ); + cubeCamera.lookAt( 0, forwardSign[ i ], 0 ); - var influencesList = {}; - var morphInfluences = new Float32Array( 8 ); + } else { - function update( object, geometry, material, program ) { + cubeCamera.up.set( 0, upSign[ i ], 0 ); + cubeCamera.lookAt( 0, 0, forwardSign[ i ] ); - var objectInfluences = object.morphTargetInfluences; + } - var length = objectInfluences.length; + const size = this._cubeSize; - var influences = influencesList[ geometry.id ]; + _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size ); - if ( influences === undefined ) { + renderer.setRenderTarget( cubeUVRenderTarget ); - // initialise list + if ( useSolidColor ) { - influences = []; + renderer.render( backgroundBox, cubeCamera ); - for ( var i = 0; i < length; i ++ ) { + } - influences[ i ] = [ i, 0 ]; + renderer.render( scene, cubeCamera ); - } + } - influencesList[ geometry.id ] = influences; + backgroundBox.geometry.dispose(); + backgroundBox.material.dispose(); - } + renderer.toneMapping = toneMapping; + renderer.autoClear = originalAutoClear; + scene.background = background; - var morphTargets = material.morphTargets && geometry.morphAttributes.position; - var morphNormals = material.morphNormals && geometry.morphAttributes.normal; + } - // Remove current morphAttributes + _textureToCubeUV( texture, cubeUVRenderTarget ) { - for ( var i = 0; i < length; i ++ ) { + const renderer = this._renderer; - var influence = influences[ i ]; + const isCubeTexture = ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ); - if ( influence[ 1 ] !== 0 ) { + if ( isCubeTexture ) { - if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i ); - if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i ); + if ( this._cubemapMaterial === null ) { - } + this._cubemapMaterial = _getCubemapMaterial(); } - // Collect influences + this._cubemapMaterial.uniforms.flipEnvMap.value = ( texture.isRenderTargetTexture === false ) ? - 1 : 1; - for ( var i = 0; i < length; i ++ ) { + } else { - var influence = influences[ i ]; + if ( this._equirectMaterial === null ) { - influence[ 0 ] = i; - influence[ 1 ] = objectInfluences[ i ]; + this._equirectMaterial = _getEquirectMaterial(); } - influences.sort( absNumericalSort ); + } - // Add morphAttributes + const material = isCubeTexture ? this._cubemapMaterial : this._equirectMaterial; + const mesh = new Mesh( this._lodPlanes[ 0 ], material ); - for ( var i = 0; i < 8; i ++ ) { + const uniforms = material.uniforms; - var influence = influences[ i ]; + uniforms[ 'envMap' ].value = texture; - if ( influence ) { + const size = this._cubeSize; - var index = influence[ 0 ]; - var value = influence[ 1 ]; + _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size ); - if ( value ) { + renderer.setRenderTarget( cubeUVRenderTarget ); + renderer.render( mesh, _flatCamera ); - if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] ); - if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] ); + } - morphInfluences[ i ] = value; - continue; + _applyPMREM( cubeUVRenderTarget ) { - } + const renderer = this._renderer; + const autoClear = renderer.autoClear; + renderer.autoClear = false; - } + for ( let i = 1; i < this._lodPlanes.length; i ++ ) { - morphInfluences[ i ] = 0; + const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] ); - } + const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ]; - program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); + this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis ); } - return { - - update: update - - }; + renderer.autoClear = autoClear; } /** - * @author mrdoob / http://mrdoob.com/ + * 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 ) { - function WebGLObjects( geometries, info ) { + const pingPongRenderTarget = this._pingPongRenderTarget; - var updateList = {}; + this._halfBlur( + cubeUVRenderTarget, + pingPongRenderTarget, + lodIn, + lodOut, + sigma, + 'latitudinal', + poleAxis ); - function update( object ) { + this._halfBlur( + pingPongRenderTarget, + cubeUVRenderTarget, + lodOut, + lodOut, + sigma, + 'longitudinal', + poleAxis ); - var frame = info.render.frame; + } - var geometry = object.geometry; - var buffergeometry = geometries.get( object, geometry ); + _halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) { - // Update once per frame + const renderer = this._renderer; + const blurMaterial = this._blurMaterial; - if ( updateList[ buffergeometry.id ] !== frame ) { + if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) { - if ( geometry.isGeometry ) { + console.error( + 'blur direction must be either latitudinal or longitudinal!' ); - buffergeometry.updateFromObject( object ); + } - } + // Number of standard deviations at which to cut off the discrete approximation. + const STANDARD_DEVIATIONS = 3; - geometries.update( buffergeometry ); + const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial ); + const blurUniforms = blurMaterial.uniforms; - updateList[ buffergeometry.id ] = frame; + 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 ) { - return buffergeometry; + console.warn( `sigmaRadians, ${ + sigmaRadians}, is too large and will clip, as it requested ${ + samples} samples when the maximum is set to ${MAX_SAMPLES}` ); } - function dispose() { + const weights = []; + let sum = 0; - updateList = {}; + for ( let i = 0; i < MAX_SAMPLES; ++ i ) { - } + const x = i / sigmaPixels; + const weight = Math.exp( - x * x / 2 ); + weights.push( weight ); - return { + if ( i === 0 ) { - update: update, - dispose: dispose + sum += weight; - }; + } else if ( i < samples ) { - } + sum += 2 * weight; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { + } - images = images !== undefined ? images : []; - mapping = mapping !== undefined ? mapping : CubeReflectionMapping; - format = format !== undefined ? format : RGBFormat; + for ( let i = 0; i < weights.length; i ++ ) { - Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + weights[ i ] = weights[ i ] / sum; - this.flipY = false; + } - } + blurUniforms[ 'envMap' ].value = targetIn.texture; + blurUniforms[ 'samples' ].value = samples; + blurUniforms[ 'weights' ].value = weights; + blurUniforms[ 'latitudinal' ].value = direction === 'latitudinal'; - CubeTexture.prototype = Object.create( Texture.prototype ); - CubeTexture.prototype.constructor = CubeTexture; + if ( poleAxis ) { - CubeTexture.prototype.isCubeTexture = true; + blurUniforms[ 'poleAxis' ].value = poleAxis; - Object.defineProperty( CubeTexture.prototype, 'images', { + } - get: function () { + const { _lodMax } = this; + blurUniforms[ 'dTheta' ].value = radiansPerPixel; + blurUniforms[ 'mipInt' ].value = _lodMax - lodIn; - return this.image; + 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 ); - set: function ( value ) { + } - this.image = value; +} - } - } ); - /** - * @author Takahiro https://github.com/takahirox - */ +function _createPlanes( lodMax ) { - function DataTexture2DArray( data, width, height, depth ) { + const lodPlanes = []; + const sizeLods = []; + const sigmas = []; - Texture.call( this, null ); + let lod = lodMax; - this.image = { data: data, width: width, height: height, depth: depth }; + const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length; - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + for ( let i = 0; i < totalLods; i ++ ) { - this.wrapR = ClampToEdgeWrapping; + const sizeLod = Math.pow( 2, lod ); + sizeLods.push( sizeLod ); + let sigma = 1.0 / sizeLod; - this.generateMipmaps = false; - this.flipY = false; + if ( i > lodMax - LOD_MIN ) { - } + sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ]; - DataTexture2DArray.prototype = Object.create( Texture.prototype ); - DataTexture2DArray.prototype.constructor = DataTexture2DArray; - DataTexture2DArray.prototype.isDataTexture2DArray = true; + } else if ( i === 0 ) { - /** - * @author Artur Trzesiok - */ + sigma = 0; - function DataTexture3D( data, width, height, depth ) { + } - // We're going to add .setXXX() methods for setting properties later. - // Users can still set in DataTexture3D directly. - // - // var texture = new THREE.DataTexture3D( data, width, height, depth ); - // texture.anisotropy = 16; - // - // See #14839 + sigmas.push( sigma ); - Texture.call( this, null ); + 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 ]; - this.image = { data: data, width: width, height: height, depth: depth }; + const cubeFaces = 6; + const vertices = 6; + const positionSize = 3; + const uvSize = 2; + const faceIndexSize = 1; - this.magFilter = NearestFilter; - this.minFilter = NearestFilter; + const position = new Float32Array( positionSize * vertices * cubeFaces ); + const uv = new Float32Array( uvSize * vertices * cubeFaces ); + const faceIndex = new Float32Array( faceIndexSize * vertices * cubeFaces ); - this.wrapR = ClampToEdgeWrapping; + for ( let face = 0; face < cubeFaces; face ++ ) { - this.generateMipmaps = false; - this.flipY = false; + 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 + ]; + position.set( coordinates, positionSize * vertices * face ); + uv.set( uv1, uvSize * vertices * face ); + const fill = [ face, face, face, face, face, face ]; + faceIndex.set( fill, faceIndexSize * vertices * face ); - } + } - DataTexture3D.prototype = Object.create( Texture.prototype ); - DataTexture3D.prototype.constructor = DataTexture3D; - DataTexture3D.prototype.isDataTexture3D = true; + 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 ); - /** - * @author tschw - * @author Mugen87 / https://github.com/Mugen87 - * @author mrdoob / http://mrdoob.com/ - * - * Uniforms of a program. - * Those form a tree structure with a special top-level container for the root, - * which you get by calling 'new WebGLUniforms( gl, program )'. - * - * - * Properties of inner nodes including the top-level container: - * - * .seq - array of nested uniforms - * .map - nested uniforms by name - * - * - * Methods of all nodes except the top-level container: - * - * .setValue( gl, value, [textures] ) - * - * uploads a uniform value(s) - * the 'textures' parameter is needed for sampler uniforms - * - * - * Static methods of the top-level container (textures factorizations): - * - * .upload( gl, seq, values, textures ) - * - * sets uniforms in 'seq' to 'values[id].value' - * - * .seqWithValue( seq, values ) : filteredSeq - * - * filters 'seq' entries with corresponding entry in values - * - * - * Methods of the top-level container (textures factorizations): - * - * .setValue( gl, name, value, textures ) - * - * sets uniform with name 'name' to 'value' - * - * .setOptional( gl, obj, prop ) - * - * like .set for an optional property of the object - * - */ + if ( lod > LOD_MIN ) { - var emptyTexture = new Texture(); - var emptyTexture2dArray = new DataTexture2DArray(); - var emptyTexture3d = new DataTexture3D(); - var emptyCubeTexture = new CubeTexture(); + lod --; - // --- Utilities --- + } - // Array Caches (provide typed arrays for temporary by size) + } - var arrayCacheF32 = []; - var arrayCacheI32 = []; + return { lodPlanes, sizeLods, sigmas }; - // Float32Array caches used for uploading Matrix uniforms +} - var mat4array = new Float32Array( 16 ); - var mat3array = new Float32Array( 9 ); - var mat2array = new Float32Array( 4 ); +function _createRenderTarget( width, height, params ) { - // Flattening for arrays of vectors and matrices + const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params ); + cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; + cubeUVRenderTarget.texture.name = 'PMREM.cubeUv'; + cubeUVRenderTarget.scissorTest = true; + return cubeUVRenderTarget; - function flatten( array, nBlocks, blockSize ) { +} - var firstElem = array[ 0 ]; +function _setViewport( target, x, y, width, height ) { - if ( firstElem <= 0 || firstElem > 0 ) return array; - // unoptimized: ! isNaN( firstElem ) - // see http://jacksondunstan.com/articles/983 + target.viewport.set( x, y, width, height ); + target.scissor.set( x, y, width, height ); - var n = nBlocks * blockSize, - r = arrayCacheF32[ n ]; +} - if ( r === undefined ) { +function _getBlurShader( lodMax, width, height ) { - r = new Float32Array( n ); - arrayCacheF32[ n ] = r; + const weights = new Float32Array( MAX_SAMPLES ); + const poleAxis = new Vector3( 0, 1, 0 ); + const shaderMaterial = new ShaderMaterial( { - } + name: 'SphericalGaussianBlur', - if ( nBlocks !== 0 ) { + defines: { + 'n': MAX_SAMPLES, + 'CUBEUV_TEXEL_WIDTH': 1.0 / width, + 'CUBEUV_TEXEL_HEIGHT': 1.0 / height, + 'CUBEUV_MAX_MIP': `${lodMax}.0`, + }, - firstElem.toArray( r, 0 ); + uniforms: { + 'envMap': { value: null }, + 'samples': { value: 1 }, + 'weights': { value: weights }, + 'latitudinal': { value: false }, + 'dTheta': { value: 0 }, + 'mipInt': { value: 0 }, + 'poleAxis': { value: poleAxis } + }, - for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { + vertexShader: _getCommonVertexShader(), - offset += blockSize; - array[ i ].toArray( r, offset ); + fragmentShader: /* glsl */` - } + precision mediump float; + precision mediump int; - } + varying vec3 vOutputDirection; - return r; + uniform sampler2D envMap; + uniform int samples; + uniform float weights[ n ]; + uniform bool latitudinal; + uniform float dTheta; + uniform float mipInt; + uniform vec3 poleAxis; - } + #define ENVMAP_TYPE_CUBE_UV + #include - function arraysEqual( a, b ) { + vec3 getSample( float theta, vec3 axis ) { - if ( a.length !== b.length ) return false; + float cosTheta = cos( theta ); + // Rodrigues' axis-angle rotation + vec3 sampleDirection = vOutputDirection * cosTheta + + cross( axis, vOutputDirection ) * sin( theta ) + + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - for ( var i = 0, l = a.length; i < l; i ++ ) { + return bilinearCubeUV( envMap, sampleDirection, mipInt ); - if ( a[ i ] !== b[ i ] ) return false; + } - } + void main() { - return true; + vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - } + if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - function copyArray( a, b ) { + axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - for ( var i = 0, l = b.length; i < l; i ++ ) { + } - a[ i ] = b[ i ]; + axis = normalize( axis ); - } + gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); + gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - } + for ( int i = 1; i < n; i++ ) { - // Texture unit allocation + if ( i >= samples ) { - function allocTexUnits( textures, n ) { + break; - var r = arrayCacheI32[ n ]; + } - if ( r === undefined ) { + float theta = dTheta * float( i ); + gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); + gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - r = new Int32Array( n ); - arrayCacheI32[ n ] = r; + } - } + } + `, - for ( var i = 0; i !== n; ++ i ) - r[ i ] = textures.allocateTextureUnit(); + blending: NoBlending, + depthTest: false, + depthWrite: false - return r; + } ); - } + return shaderMaterial; - // --- Setters --- +} - // Note: Defining these methods externally, because they come in a bunch - // and this way their names minify. +function _getEquirectMaterial() { - // Single scalar + return new ShaderMaterial( { - function setValueV1f( gl, v ) { + name: 'EquirectangularToCubeUV', - var cache = this.cache; + uniforms: { + 'envMap': { value: null } + }, - if ( cache[ 0 ] === v ) return; + vertexShader: _getCommonVertexShader(), - gl.uniform1f( this.addr, v ); + fragmentShader: /* glsl */` - cache[ 0 ] = v; + precision mediump float; + precision mediump int; - } + varying vec3 vOutputDirection; - // Single float vector (from flat array or THREE.VectorN) + uniform sampler2D envMap; - function setValueV2f( gl, v ) { + #include - var cache = this.cache; + void main() { - if ( v.x !== undefined ) { + vec3 outputDirection = normalize( vOutputDirection ); + vec2 uv = equirectUv( outputDirection ); - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { + gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); - gl.uniform2f( this.addr, v.x, v.y ); + } + `, - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; + blending: NoBlending, + depthTest: false, + depthWrite: false - } + } ); - } else { +} - if ( arraysEqual( cache, v ) ) return; +function _getCubemapMaterial() { - gl.uniform2fv( this.addr, v ); + return new ShaderMaterial( { - copyArray( cache, v ); + name: 'CubemapToCubeUV', - } + uniforms: { + 'envMap': { value: null }, + 'flipEnvMap': { value: - 1 } + }, - } + vertexShader: _getCommonVertexShader(), + + fragmentShader: /* glsl */` - function setValueV3f( gl, v ) { + precision mediump float; + precision mediump int; - var cache = this.cache; + uniform float flipEnvMap; - if ( v.x !== undefined ) { + varying vec3 vOutputDirection; - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { + uniform samplerCube envMap; - gl.uniform3f( this.addr, v.x, v.y, v.z ); + void main() { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; + gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } + `, - } else if ( v.r !== undefined ) { + blending: NoBlending, + depthTest: false, + depthWrite: false - if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { + } ); - gl.uniform3f( this.addr, v.r, v.g, v.b ); +} - cache[ 0 ] = v.r; - cache[ 1 ] = v.g; - cache[ 2 ] = v.b; +function _getCommonVertexShader() { - } + return /* glsl */` - } else { + precision mediump float; + precision mediump int; - if ( arraysEqual( cache, v ) ) return; + attribute float faceIndex; - gl.uniform3fv( this.addr, v ); + varying vec3 vOutputDirection; - copyArray( cache, v ); + // RH coordinate system; PMREM face-indexing convention + vec3 getDirection( vec2 uv, float face ) { - } + uv = 2.0 * uv - 1.0; - } + vec3 direction = vec3( uv, 1.0 ); - function setValueV4f( gl, v ) { + if ( face == 0.0 ) { - var cache = this.cache; + direction = direction.zyx; // ( 1, v, u ) pos x - if ( v.x !== undefined ) { + } else if ( face == 1.0 ) { - if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { + direction = direction.xzy; + direction.xz *= -1.0; // ( -u, 1, -v ) pos y - gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); + } else if ( face == 2.0 ) { - cache[ 0 ] = v.x; - cache[ 1 ] = v.y; - cache[ 2 ] = v.z; - cache[ 3 ] = v.w; + direction.x *= -1.0; // ( -u, v, 1 ) pos z - } + } else if ( face == 3.0 ) { - } else { + direction = direction.zyx; + direction.xz *= -1.0; // ( -1, v, -u ) neg x - if ( arraysEqual( cache, v ) ) return; + } else if ( face == 4.0 ) { - gl.uniform4fv( this.addr, v ); + direction = direction.xzy; + direction.xy *= -1.0; // ( -u, -1, v ) neg y - copyArray( cache, v ); + } else if ( face == 5.0 ) { - } + direction.z *= -1.0; // ( u, v, -1 ) neg z - } + } - // Single matrix (from flat array or MatrixN) + return direction; - function setValueM2( gl, v ) { + } - var cache = this.cache; - var elements = v.elements; + void main() { - if ( elements === undefined ) { + vOutputDirection = getDirection( uv, faceIndex ); + gl_Position = vec4( position, 1.0 ); - if ( arraysEqual( cache, v ) ) return; + } + `; - gl.uniformMatrix2fv( this.addr, false, v ); +} - copyArray( cache, v ); +function WebGLCubeUVMaps( renderer ) { - } else { + let cubeUVmaps = new WeakMap(); - if ( arraysEqual( cache, elements ) ) return; + let pmremGenerator = null; - mat2array.set( elements ); + function get( texture ) { - gl.uniformMatrix2fv( this.addr, false, mat2array ); + if ( texture && texture.isTexture ) { - copyArray( cache, elements ); + const mapping = texture.mapping; - } + const isEquirectMap = ( mapping === EquirectangularReflectionMapping || mapping === EquirectangularRefractionMapping ); + const isCubeMap = ( mapping === CubeReflectionMapping || mapping === CubeRefractionMapping ); - } + // equirect/cube map to cubeUV conversion - function setValueM3( gl, v ) { + if ( isEquirectMap || isCubeMap ) { - var cache = this.cache; - var elements = v.elements; + if ( texture.isRenderTargetTexture && texture.needsPMREMUpdate === true ) { - if ( elements === undefined ) { + texture.needsPMREMUpdate = false; - if ( arraysEqual( cache, v ) ) return; + let renderTarget = cubeUVmaps.get( texture ); - gl.uniformMatrix3fv( this.addr, false, v ); + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - copyArray( cache, v ); + renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture, renderTarget ) : pmremGenerator.fromCubemap( texture, renderTarget ); + cubeUVmaps.set( texture, renderTarget ); - } else { + return renderTarget.texture; - if ( arraysEqual( cache, elements ) ) return; + } else { - mat3array.set( elements ); + if ( cubeUVmaps.has( texture ) ) { - gl.uniformMatrix3fv( this.addr, false, mat3array ); + return cubeUVmaps.get( texture ).texture; - copyArray( cache, elements ); + } else { - } + const image = texture.image; - } + if ( ( isEquirectMap && image && image.height > 0 ) || ( isCubeMap && image && isCubeTextureComplete( image ) ) ) { - function setValueM4( gl, v ) { + if ( pmremGenerator === null ) pmremGenerator = new PMREMGenerator( renderer ); - var cache = this.cache; - var elements = v.elements; + const renderTarget = isEquirectMap ? pmremGenerator.fromEquirectangular( texture ) : pmremGenerator.fromCubemap( texture ); + cubeUVmaps.set( texture, renderTarget ); - if ( elements === undefined ) { + texture.addEventListener( 'dispose', onTextureDispose ); - if ( arraysEqual( cache, v ) ) return; + return renderTarget.texture; - gl.uniformMatrix4fv( this.addr, false, v ); + } else { - copyArray( cache, v ); + // image not yet ready. try the conversion next frame - } else { + return null; - if ( arraysEqual( cache, elements ) ) return; + } - mat4array.set( elements ); + } - gl.uniformMatrix4fv( this.addr, false, mat4array ); + } - copyArray( cache, elements ); + } } - } + return texture; - // Single texture (2D / Cube) + } - function setValueT1( gl, v, textures ) { + function isCubeTextureComplete( image ) { - var cache = this.cache; - var unit = textures.allocateTextureUnit(); + let count = 0; + const length = 6; - if ( cache[ 0 ] !== unit ) { + for ( let i = 0; i < length; i ++ ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + if ( image[ i ] !== undefined ) count ++; } - textures.safeSetTexture2D( v || emptyTexture, unit ); + return count === length; + } - function setValueT2DArray1( gl, v, textures ) { + function onTextureDispose( event ) { - var cache = this.cache; - var unit = textures.allocateTextureUnit(); + const texture = event.target; - if ( cache[ 0 ] !== unit ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + const cubemapUV = cubeUVmaps.get( texture ); - } + if ( cubemapUV !== undefined ) { + + cubeUVmaps.delete( texture ); + cubemapUV.dispose(); - textures.setTexture2DArray( v || emptyTexture2dArray, unit ); + } } - function setValueT3D1( gl, v, textures ) { + function dispose() { - var cache = this.cache; - var unit = textures.allocateTextureUnit(); + cubeUVmaps = new WeakMap(); - if ( cache[ 0 ] !== unit ) { + if ( pmremGenerator !== null ) { - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; + pmremGenerator.dispose(); + pmremGenerator = null; } - textures.setTexture3D( v || emptyTexture3d, unit ); - } - function setValueT6( gl, v, textures ) { - - var cache = this.cache; - var unit = textures.allocateTextureUnit(); + return { + get: get, + dispose: dispose + }; - if ( cache[ 0 ] !== unit ) { +} - gl.uniform1i( this.addr, unit ); - cache[ 0 ] = unit; +function WebGLExtensions( gl ) { - } + const extensions = {}; - textures.safeSetTextureCube( v || emptyCubeTexture, unit ); + function getExtension( name ) { - } + if ( extensions[ name ] !== undefined ) { - // Integer / Boolean vectors or arrays thereof (always flat arrays) + return extensions[ name ]; - function setValueV1i( gl, v ) { + } - var cache = this.cache; + let extension; - if ( cache[ 0 ] === v ) return; + switch ( name ) { - gl.uniform1i( this.addr, v ); + case 'WEBGL_depth_texture': + extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); + break; - cache[ 0 ] = v; + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; - } + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; - function setValueV2i( gl, v ) { + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - var cache = this.cache; + default: + extension = gl.getExtension( name ); - if ( arraysEqual( cache, v ) ) return; + } - gl.uniform2iv( this.addr, v ); + extensions[ name ] = extension; - copyArray( cache, v ); + return extension; } - function setValueV3i( gl, v ) { - - var cache = this.cache; + return { - if ( arraysEqual( cache, v ) ) return; + has: function ( name ) { - gl.uniform3iv( this.addr, v ); + return getExtension( name ) !== null; - copyArray( cache, v ); + }, - } + init: function ( capabilities ) { - function setValueV4i( gl, v ) { + if ( capabilities.isWebGL2 ) { - var cache = this.cache; + getExtension( 'EXT_color_buffer_float' ); - if ( arraysEqual( cache, v ) ) return; + } else { - gl.uniform4iv( this.addr, v ); + getExtension( 'WEBGL_depth_texture' ); + getExtension( 'OES_texture_float' ); + getExtension( 'OES_texture_half_float' ); + getExtension( 'OES_texture_half_float_linear' ); + getExtension( 'OES_standard_derivatives' ); + getExtension( 'OES_element_index_uint' ); + getExtension( 'OES_vertex_array_object' ); + getExtension( 'ANGLE_instanced_arrays' ); - copyArray( cache, v ); + } - } + getExtension( 'OES_texture_float_linear' ); + getExtension( 'EXT_color_buffer_half_float' ); + getExtension( 'WEBGL_multisampled_render_to_texture' ); - // Helper to pick the right setter for the singular case + }, - function getSingularSetter( type ) { + get: function ( name ) { - switch ( type ) { + const extension = getExtension( name ); - case 0x1406: return setValueV1f; // FLOAT - case 0x8b50: return setValueV2f; // _VEC2 - case 0x8b51: return setValueV3f; // _VEC3 - case 0x8b52: return setValueV4f; // _VEC4 + if ( extension === null ) { - case 0x8b5a: return setValueM2; // _MAT2 - case 0x8b5b: return setValueM3; // _MAT3 - case 0x8b5c: return setValueM4; // _MAT4 + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES - case 0x8b5f: return setValueT3D1; // SAMPLER_3D - case 0x8b60: return setValueT6; // SAMPLER_CUBE - case 0x8DC1: return setValueT2DArray1; // SAMPLER_2D_ARRAY + } - case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 + return extension; } - } + }; - // Array of scalars - function setValueV1fArray( gl, v ) { +} - gl.uniform1fv( this.addr, v ); +function WebGLGeometries( gl, attributes, info, bindingStates ) { - } + const geometries = {}; + const wireframeAttributes = new WeakMap(); - // Integer / Boolean vectors or arrays thereof (always flat arrays) - function setValueV1iArray( gl, v ) { + function onGeometryDispose( event ) { - gl.uniform1iv( this.addr, v ); + const geometry = event.target; - } + if ( geometry.index !== null ) { - function setValueV2iArray( gl, v ) { + attributes.remove( geometry.index ); - gl.uniform2iv( this.addr, v ); + } - } + for ( const name in geometry.attributes ) { - function setValueV3iArray( gl, v ) { + attributes.remove( geometry.attributes[ name ] ); - gl.uniform3iv( this.addr, v ); + } - } + for ( const name in geometry.morphAttributes ) { - function setValueV4iArray( gl, v ) { + const array = geometry.morphAttributes[ name ]; - gl.uniform4iv( this.addr, v ); + for ( let i = 0, l = array.length; i < l; i ++ ) { - } + attributes.remove( array[ i ] ); + } + + } - // Array of vectors (flat or from THREE classes) + geometry.removeEventListener( 'dispose', onGeometryDispose ); - function setValueV2fArray( gl, v ) { + delete geometries[ geometry.id ]; - var data = flatten( v, this.size, 2 ); + const attribute = wireframeAttributes.get( geometry ); - gl.uniform2fv( this.addr, data ); + if ( attribute ) { - } + attributes.remove( attribute ); + wireframeAttributes.delete( geometry ); - function setValueV3fArray( gl, v ) { + } - var data = flatten( v, this.size, 3 ); + bindingStates.releaseStatesOfGeometry( geometry ); - gl.uniform3fv( this.addr, data ); + if ( geometry.isInstancedBufferGeometry === true ) { - } + delete geometry._maxInstanceCount; - function setValueV4fArray( gl, v ) { + } - var data = flatten( v, this.size, 4 ); + // - gl.uniform4fv( this.addr, data ); + info.memory.geometries --; } - // Array of matrices (flat or from THREE clases) + function get( object, geometry ) { - function setValueM2Array( gl, v ) { + if ( geometries[ geometry.id ] === true ) return geometry; - var data = flatten( v, this.size, 4 ); + geometry.addEventListener( 'dispose', onGeometryDispose ); - gl.uniformMatrix2fv( this.addr, false, data ); + geometries[ geometry.id ] = true; - } + info.memory.geometries ++; - function setValueM3Array( gl, v ) { + return geometry; - var data = flatten( v, this.size, 9 ); + } - gl.uniformMatrix3fv( this.addr, false, data ); + function update( geometry ) { - } + const geometryAttributes = geometry.attributes; - function setValueM4Array( gl, v ) { + // Updating index buffer in VAO now. See WebGLBindingStates. - var data = flatten( v, this.size, 16 ); + for ( const name in geometryAttributes ) { - gl.uniformMatrix4fv( this.addr, false, data ); + attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER ); - } + } - // Array of textures (2D / Cube) + // morph targets - function setValueT1Array( gl, v, textures ) { + const morphAttributes = geometry.morphAttributes; - var n = v.length; + for ( const name in morphAttributes ) { - var units = allocTexUnits( textures, n ); + const array = morphAttributes[ name ]; - gl.uniform1iv( this.addr, units ); + for ( let i = 0, l = array.length; i < l; i ++ ) { - for ( var i = 0; i !== n; ++ i ) { + attributes.update( array[ i ], gl.ARRAY_BUFFER ); - textures.safeSetTexture2D( v[ i ] || emptyTexture, units[ i ] ); + } } } - function setValueT6Array( gl, v, textures ) { + function updateWireframeAttribute( geometry ) { - var n = v.length; + const indices = []; - var units = allocTexUnits( textures, n ); + const geometryIndex = geometry.index; + const geometryPosition = geometry.attributes.position; + let version = 0; - gl.uniform1iv( this.addr, units ); + if ( geometryIndex !== null ) { - for ( var i = 0; i !== n; ++ i ) { + const array = geometryIndex.array; + version = geometryIndex.version; - textures.safeSetTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); + 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 ); - // Helper to pick the right setter for a pure (bottom-level) array + } - function getPureArraySetter( type ) { + } else if ( geometryPosition !== undefined ) { - switch ( type ) { + const array = geometryPosition.array; + version = geometryPosition.version; - case 0x1406: return setValueV1fArray; // FLOAT - case 0x8b50: return setValueV2fArray; // _VEC2 - case 0x8b51: return setValueV3fArray; // _VEC3 - case 0x8b52: return setValueV4fArray; // _VEC4 + for ( let i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - case 0x8b5a: return setValueM2Array; // _MAT2 - case 0x8b5b: return setValueM3Array; // _MAT3 - case 0x8b5c: return setValueM4Array; // _MAT4 + const a = i + 0; + const b = i + 1; + const c = i + 2; - case 0x8b5e: return setValueT1Array; // SAMPLER_2D - case 0x8b60: return setValueT6Array; // SAMPLER_CUBE + indices.push( a, b, b, c, c, a ); - case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL - case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 - case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 - case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 + } - } + } else { - } + return; - // --- Uniform Classes --- + } - function SingleUniform( id, activeInfo, addr ) { + const attribute = new ( arrayNeedsUint32( indices ) ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 ); + attribute.version = version; - this.id = id; - this.addr = addr; - this.cache = []; - this.setValue = getSingularSetter( activeInfo.type ); + // Updating index buffer in VAO now. See WebGLBindingStates - // this.path = activeInfo.name; // DEBUG + // - } + const previousAttribute = wireframeAttributes.get( geometry ); - function PureArrayUniform( id, activeInfo, addr ) { + if ( previousAttribute ) attributes.remove( previousAttribute ); - this.id = id; - this.addr = addr; - this.cache = []; - this.size = activeInfo.size; - this.setValue = getPureArraySetter( activeInfo.type ); + // - // this.path = activeInfo.name; // DEBUG + wireframeAttributes.set( geometry, attribute ); } - PureArrayUniform.prototype.updateCache = function ( data ) { + function getWireframeAttribute( geometry ) { - var cache = this.cache; + const currentAttribute = wireframeAttributes.get( geometry ); - if ( data instanceof Float32Array && cache.length !== data.length ) { + if ( currentAttribute ) { - this.cache = new Float32Array( data.length ); + const geometryIndex = geometry.index; - } + if ( geometryIndex !== null ) { - copyArray( cache, data ); + // if the attribute is obsolete, create a new one - }; + if ( currentAttribute.version < geometryIndex.version ) { - function StructuredUniform( id ) { + updateWireframeAttribute( geometry ); - this.id = id; + } - this.seq = []; - this.map = {}; + } - } + } else { - StructuredUniform.prototype.setValue = function ( gl, value, textures ) { + updateWireframeAttribute( geometry ); - var seq = this.seq; + } - for ( var i = 0, n = seq.length; i !== n; ++ i ) { + return wireframeAttributes.get( geometry ); - var u = seq[ i ]; - u.setValue( gl, value[ u.id ], textures ); + } - } + return { + + get: get, + update: update, + + getWireframeAttribute: getWireframeAttribute }; - // --- Top-level --- +} - // Parser - builds up the property tree from the path strings +function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) { - var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g; + const isWebGL2 = capabilities.isWebGL2; - // extracts - // - the identifier (member name or array index) - // - followed by an optional right bracket (found when array index) - // - followed by an optional left bracket or dot (type of subscript) - // - // Note: These portions can be read in a non-overlapping fashion and - // allow straightforward parsing of the hierarchy that WebGL encodes - // in the uniform names. + let mode; - function addUniform( container, uniformObject ) { + function setMode( value ) { - container.seq.push( uniformObject ); - container.map[ uniformObject.id ] = uniformObject; + mode = value; } - function parseUniform( activeInfo, addr, container ) { + let type, bytesPerElement; - var path = activeInfo.name, - pathLength = path.length; + function setIndex( value ) { - // reset RegExp object, because of the early exit of a previous run - RePathPart.lastIndex = 0; + type = value.type; + bytesPerElement = value.bytesPerElement; - while ( true ) { - - var match = RePathPart.exec( path ), - matchEnd = RePathPart.lastIndex, + } - id = match[ 1 ], - idIsIndex = match[ 2 ] === ']', - subscript = match[ 3 ]; + function render( start, count ) { - if ( idIsIndex ) id = id | 0; // convert to integer + gl.drawElements( mode, count, type, start * bytesPerElement ); - if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { + info.update( count, mode, 1 ); - // bare name or "pure" bottom-level array "[0]" suffix + } - addUniform( container, subscript === undefined ? - new SingleUniform( id, activeInfo, addr ) : - new PureArrayUniform( id, activeInfo, addr ) ); + function renderInstances( start, count, primcount ) { - break; + if ( primcount === 0 ) return; - } else { + let extension, methodName; - // step into inner node / create it in case it doesn't exist + if ( isWebGL2 ) { - var map = container.map, next = map[ id ]; + extension = gl; + methodName = 'drawElementsInstanced'; - if ( next === undefined ) { + } else { - next = new StructuredUniform( id ); - addUniform( container, next ); + extension = extensions.get( 'ANGLE_instanced_arrays' ); + methodName = 'drawElementsInstancedANGLE'; - } + if ( extension === null ) { - container = next; + console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; } } - } + extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount ); - // Root Container + info.update( count, mode, primcount ); - function WebGLUniforms( gl, program ) { + } - this.seq = []; - this.map = {}; + // - var n = gl.getProgramParameter( program, 35718 ); + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; - for ( var i = 0; i < n; ++ i ) { +} - var info = gl.getActiveUniform( program, i ), - addr = gl.getUniformLocation( program, info.name ); +function WebGLInfo( gl ) { - parseUniform( info, addr, this ); + const memory = { + geometries: 0, + textures: 0 + }; - } + const render = { + frame: 0, + calls: 0, + triangles: 0, + points: 0, + lines: 0 + }; - } + function update( count, mode, instanceCount ) { - WebGLUniforms.prototype.setValue = function ( gl, name, value, textures ) { + render.calls ++; - var u = this.map[ name ]; + switch ( mode ) { - if ( u !== undefined ) u.setValue( gl, value, textures ); + case gl.TRIANGLES: + render.triangles += instanceCount * ( count / 3 ); + break; - }; + case gl.LINES: + render.lines += instanceCount * ( count / 2 ); + break; - WebGLUniforms.prototype.setOptional = function ( gl, object, name ) { + case gl.LINE_STRIP: + render.lines += instanceCount * ( count - 1 ); + break; - var v = object[ name ]; + case gl.LINE_LOOP: + render.lines += instanceCount * count; + break; - if ( v !== undefined ) this.setValue( gl, name, v ); + case gl.POINTS: + render.points += instanceCount * count; + break; - }; + default: + console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode ); + break; + } - // Static interface + } - WebGLUniforms.upload = function ( gl, seq, values, textures ) { + function reset() { - for ( var i = 0, n = seq.length; i !== n; ++ i ) { + render.calls = 0; + render.triangles = 0; + render.points = 0; + render.lines = 0; - var u = seq[ i ], - v = values[ u.id ]; + } - if ( v.needsUpdate !== false ) { + return { + memory: memory, + render: render, + programs: null, + autoReset: true, + reset: reset, + update: update + }; - // note: always updating when .needsUpdate is undefined - u.setValue( gl, v.value, textures ); +} - } +function numericalSort( a, b ) { - } + return a[ 0 ] - b[ 0 ]; - }; +} - WebGLUniforms.seqWithValue = function ( seq, values ) { +function absNumericalSort( a, b ) { - var r = []; + return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] ); - for ( var i = 0, n = seq.length; i !== n; ++ i ) { +} - var u = seq[ i ]; - if ( u.id in values ) r.push( u ); +function WebGLMorphtargets( gl, capabilities, textures ) { - } + const influencesList = {}; + const morphInfluences = new Float32Array( 8 ); + const morphTextures = new WeakMap(); + const morph = new Vector4(); - return r; + const workInfluences = []; - }; + for ( let i = 0; i < 8; i ++ ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + workInfluences[ i ] = [ i, 0 ]; - function WebGLShader( gl, type, string ) { + } - var shader = gl.createShader( type ); + function update( object, geometry, program ) { - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + const objectInfluences = object.morphTargetInfluences; - return shader; + if ( capabilities.isWebGL2 === true ) { - } + // instead of using attributes, the WebGL 2 code path encodes morph targets + // into an array of data textures. Each layer represents a single morph target. - /** - * @author mrdoob / http://mrdoob.com/ - */ + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - var programIdCount = 0; + let entry = morphTextures.get( geometry ); - function addLineNumbers( string ) { + if ( entry === undefined || entry.count !== morphTargetsCount ) { - var lines = string.split( '\n' ); + if ( entry !== undefined ) entry.texture.dispose(); - for ( var i = 0; i < lines.length; i ++ ) { + const hasMorphPosition = geometry.morphAttributes.position !== undefined; + const hasMorphNormals = geometry.morphAttributes.normal !== undefined; + const hasMorphColors = geometry.morphAttributes.color !== undefined; - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + const morphTargets = geometry.morphAttributes.position || []; + const morphNormals = geometry.morphAttributes.normal || []; + const morphColors = geometry.morphAttributes.color || []; - } + let vertexDataCount = 0; - return lines.join( '\n' ); + if ( hasMorphPosition === true ) vertexDataCount = 1; + if ( hasMorphNormals === true ) vertexDataCount = 2; + if ( hasMorphColors === true ) vertexDataCount = 3; - } + let width = geometry.attributes.position.count * vertexDataCount; + let height = 1; - function getEncodingComponents( encoding ) { + if ( width > capabilities.maxTextureSize ) { - switch ( encoding ) { + height = Math.ceil( width / capabilities.maxTextureSize ); + width = capabilities.maxTextureSize; - case LinearEncoding: - return [ 'Linear', '( value )' ]; - case sRGBEncoding: - return [ 'sRGB', '( value )' ]; - case RGBEEncoding: - return [ 'RGBE', '( value )' ]; - case RGBM7Encoding: - return [ 'RGBM', '( value, 7.0 )' ]; - case RGBM16Encoding: - return [ 'RGBM', '( value, 16.0 )' ]; - case RGBDEncoding: - return [ 'RGBD', '( value, 256.0 )' ]; - case GammaEncoding: - return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ]; - case LogLuvEncoding: - return [ 'LogLuv', '( value )' ]; - default: - throw new Error( 'unsupported encoding: ' + encoding ); + } - } + const buffer = new Float32Array( width * height * 4 * morphTargetsCount ); - } + const texture = new DataArrayTexture( buffer, width, height, morphTargetsCount ); + texture.type = FloatType; + texture.needsUpdate = true; - function getShaderErrors( gl, shader, type ) { + // fill buffer - var status = gl.getShaderParameter( shader, 35713 ); - var log = gl.getShaderInfoLog( shader ).trim(); + const vertexDataStride = vertexDataCount * 4; - if ( status && log === '' ) return ''; + for ( let i = 0; i < morphTargetsCount; i ++ ) { - // --enable-privileged-webgl-extension - // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + const morphTarget = morphTargets[ i ]; + const morphNormal = morphNormals[ i ]; + const morphColor = morphColors[ i ]; - var source = gl.getShaderSource( shader ); + const offset = width * height * 4 * i; - return 'THREE.WebGLShader: gl.getShaderInfoLog() ' + type + '\n' + log + addLineNumbers( source ); + for ( let j = 0; j < morphTarget.count; j ++ ) { - } + const stride = j * vertexDataStride; - function getTexelDecodingFunction( functionName, encoding ) { + if ( hasMorphPosition === true ) { - var components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }'; + morph.fromBufferAttribute( morphTarget, j ); - } + buffer[ offset + stride + 0 ] = morph.x; + buffer[ offset + stride + 1 ] = morph.y; + buffer[ offset + stride + 2 ] = morph.z; + buffer[ offset + stride + 3 ] = 0; - function getTexelEncodingFunction( functionName, encoding ) { + } - var components = getEncodingComponents( encoding ); - return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }'; + if ( hasMorphNormals === true ) { - } + morph.fromBufferAttribute( morphNormal, j ); - function getToneMappingFunction( functionName, toneMapping ) { + buffer[ offset + stride + 4 ] = morph.x; + buffer[ offset + stride + 5 ] = morph.y; + buffer[ offset + stride + 6 ] = morph.z; + buffer[ offset + stride + 7 ] = 0; - var toneMappingName; + } - switch ( toneMapping ) { + if ( hasMorphColors === true ) { - case LinearToneMapping: - toneMappingName = 'Linear'; - break; + morph.fromBufferAttribute( morphColor, j ); - case ReinhardToneMapping: - toneMappingName = 'Reinhard'; - break; + buffer[ offset + stride + 8 ] = morph.x; + buffer[ offset + stride + 9 ] = morph.y; + buffer[ offset + stride + 10 ] = morph.z; + buffer[ offset + stride + 11 ] = ( morphColor.itemSize === 4 ) ? morph.w : 1; - case Uncharted2ToneMapping: - toneMappingName = 'Uncharted2'; - break; + } - case CineonToneMapping: - toneMappingName = 'OptimizedCineon'; - break; + } - case ACESFilmicToneMapping: - toneMappingName = 'ACESFilmic'; - break; + } - default: - throw new Error( 'unsupported toneMapping: ' + toneMapping ); + entry = { + count: morphTargetsCount, + texture: texture, + size: new Vector2( width, height ) + }; - } + morphTextures.set( geometry, entry ); - return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; + function disposeTexture() { - } + texture.dispose(); - function generateExtensions( extensions, parameters, rendererExtensions ) { + morphTextures.delete( geometry ); - extensions = extensions || {}; + geometry.removeEventListener( 'dispose', disposeTexture ); - var chunks = [ - ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.tangentSpaceNormalMap || parameters.clearcoatNormalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', - ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', - ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', - ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '' - ]; + } - return chunks.filter( filterEmptyLine ).join( '\n' ); + geometry.addEventListener( 'dispose', disposeTexture ); - } + } - function generateDefines( defines ) { + // - var chunks = []; + let morphInfluencesSum = 0; - for ( var name in defines ) { + for ( let i = 0; i < objectInfluences.length; i ++ ) { - var value = defines[ name ]; + morphInfluencesSum += objectInfluences[ i ]; - if ( value === false ) continue; + } - chunks.push( '#define ' + name + ' ' + value ); + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - } + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', objectInfluences ); - return chunks.join( '\n' ); + program.getUniforms().setValue( gl, 'morphTargetsTexture', entry.texture, textures ); + program.getUniforms().setValue( gl, 'morphTargetsTextureSize', entry.size ); - } - function fetchAttributeLocations( gl, program ) { + } else { - var attributes = {}; + // When object doesn't have morph target influences defined, we treat it as a 0-length array + // This is important to make sure we set up morphTargetBaseInfluence / morphTargetInfluences - var n = gl.getProgramParameter( program, 35721 ); + const length = objectInfluences === undefined ? 0 : objectInfluences.length; - for ( var i = 0; i < n; i ++ ) { + let influences = influencesList[ geometry.id ]; - var info = gl.getActiveAttrib( program, i ); - var name = info.name; + if ( influences === undefined || influences.length !== length ) { - // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); + // initialise list - attributes[ name ] = gl.getAttribLocation( program, name ); + influences = []; - } + for ( let i = 0; i < length; i ++ ) { - return attributes; + influences[ i ] = [ i, 0 ]; - } + } - function filterEmptyLine( string ) { + influencesList[ geometry.id ] = influences; - return string !== ''; + } - } + // Collect influences - function replaceLightNums( string, parameters ) { + for ( let i = 0; i < length; i ++ ) { - return string - .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) - .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) - .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) - .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) - .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) - .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) - .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) - .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); + const influence = influences[ i ]; - } - - function replaceClippingPlaneNums( string, parameters ) { + influence[ 0 ] = i; + influence[ 1 ] = objectInfluences[ i ]; - return string - .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) - .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); + } - } + influences.sort( absNumericalSort ); - function parseIncludes( string ) { + for ( let i = 0; i < 8; i ++ ) { - var pattern = /^[ \t]*#include +<([\w\d./]+)>/gm; + if ( i < length && influences[ i ][ 1 ] ) { - function replace( match, include ) { + workInfluences[ i ][ 0 ] = influences[ i ][ 0 ]; + workInfluences[ i ][ 1 ] = influences[ i ][ 1 ]; - var replace = ShaderChunk[ include ]; + } else { - if ( replace === undefined ) { + workInfluences[ i ][ 0 ] = Number.MAX_SAFE_INTEGER; + workInfluences[ i ][ 1 ] = 0; - throw new Error( 'Can not resolve #include <' + include + '>' ); + } } - return parseIncludes( replace ); - - } + workInfluences.sort( numericalSort ); - return string.replace( pattern, replace ); + const morphTargets = geometry.morphAttributes.position; + const morphNormals = geometry.morphAttributes.normal; - } + let morphInfluencesSum = 0; - function unrollLoops( string ) { + for ( let i = 0; i < 8; i ++ ) { - var pattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; + const influence = workInfluences[ i ]; + const index = influence[ 0 ]; + const value = influence[ 1 ]; - function replace( match, start, end, snippet ) { + if ( index !== Number.MAX_SAFE_INTEGER && value ) { - var unroll = ''; + if ( morphTargets && geometry.getAttribute( 'morphTarget' + i ) !== morphTargets[ index ] ) { - for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { + geometry.setAttribute( 'morphTarget' + i, morphTargets[ index ] ); - unroll += snippet - .replace( /\[ i \]/g, '[ ' + i + ' ]' ) - .replace( /UNROLLED_LOOP_INDEX/g, i ); + } - } + if ( morphNormals && geometry.getAttribute( 'morphNormal' + i ) !== morphNormals[ index ] ) { - return unroll; + geometry.setAttribute( 'morphNormal' + i, morphNormals[ index ] ); - } + } - return string.replace( pattern, replace ); + morphInfluences[ i ] = value; + morphInfluencesSum += value; - } + } else { - function WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities ) { + if ( morphTargets && geometry.hasAttribute( 'morphTarget' + i ) === true ) { - var gl = renderer.getContext(); + geometry.deleteAttribute( 'morphTarget' + i ); - var defines = material.defines; + } - var vertexShader = shader.vertexShader; - var fragmentShader = shader.fragmentShader; + if ( morphNormals && geometry.hasAttribute( 'morphNormal' + i ) === true ) { - var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + geometry.deleteAttribute( 'morphNormal' + i ); - if ( parameters.shadowMapType === PCFShadowMap ) { + } - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + morphInfluences[ i ] = 0; - } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { + } - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + } - } else if ( parameters.shadowMapType === VSMShadowMap ) { + // GLSL shader uses formula baseinfluence * base + sum(target * influence) + // This allows us to switch between absolute morphs and relative morphs without changing shader code + // When baseinfluence = 1 - sum(influence), the above is equivalent to sum((target - base) * influence) + const morphBaseInfluence = geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum; - shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; + program.getUniforms().setValue( gl, 'morphTargetBaseInfluence', morphBaseInfluence ); + program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences ); } - var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + } - if ( parameters.envMap ) { + return { - switch ( material.envMap.mapping ) { + update: update - case CubeReflectionMapping: - case CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; + }; - case CubeUVReflectionMapping: - case CubeUVRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; - break; +} - case EquirectangularReflectionMapping: - case EquirectangularRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; - break; +function WebGLObjects( gl, geometries, attributes, info ) { - case SphericalReflectionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; - break; + let updateMap = new WeakMap(); - } + function update( object ) { - switch ( material.envMap.mapping ) { + const frame = info.render.frame; - case CubeRefractionMapping: - case EquirectangularRefractionMapping: - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; + const geometry = object.geometry; + const buffergeometry = geometries.get( object, geometry ); - } + // Update once per frame - switch ( material.combine ) { + if ( updateMap.get( buffergeometry ) !== frame ) { - case MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; + geometries.update( buffergeometry ); - case MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; + updateMap.set( buffergeometry, frame ); - case AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; + } - } + if ( object.isInstancedMesh ) { - } + if ( object.hasEventListener( 'dispose', onInstancedMeshDispose ) === false ) { - var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + object.addEventListener( 'dispose', onInstancedMeshDispose ); - // console.log( 'building new program ' ); + } - // + if ( updateMap.get( object ) !== frame ) { - var customExtensions = capabilities.isWebGL2 ? '' : generateExtensions( material.extensions, parameters, extensions ); + attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER ); - var customDefines = generateDefines( defines ); + if ( object.instanceColor !== null ) { - // + attributes.update( object.instanceColor, gl.ARRAY_BUFFER ); + + } - var program = gl.createProgram(); + updateMap.set( object, frame ); - var prefixVertex, prefixFragment; + } - if ( material.isRawShaderMaterial ) { + } - prefixVertex = [ + if ( object.isSkinnedMesh ) { - customDefines + const skeleton = object.skeleton; - ].filter( filterEmptyLine ).join( '\n' ); + if ( updateMap.get( skeleton ) !== frame ) { - if ( prefixVertex.length > 0 ) { + skeleton.update(); - prefixVertex += '\n'; + updateMap.set( skeleton, frame ); } - prefixFragment = [ + } - customExtensions, - customDefines + return buffergeometry; - ].filter( filterEmptyLine ).join( '\n' ); + } - if ( prefixFragment.length > 0 ) { + function dispose() { - prefixFragment += '\n'; + updateMap = new WeakMap(); - } + } - } else { + function onInstancedMeshDispose( event ) { - prefixVertex = [ + const instancedMesh = event.target; - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', + instancedMesh.removeEventListener( 'dispose', onInstancedMeshDispose ); - ( parameters.precision === 'highp' ) ? '#define HIGH_PRECISION' : '', + attributes.remove( instancedMesh.instanceMatrix ); - '#define SHADER_NAME ' + shader.name, + if ( instancedMesh.instanceColor !== null ) attributes.remove( instancedMesh.instanceColor ); + + } - customDefines, + return { - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + update: update, + dispose: dispose - '#define GAMMA_FACTOR ' + gammaFactorDefine, + }; - '#define MAX_BONES ' + parameters.maxBones, - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', +} - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', +/** + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [textures] ) + * + * uploads a uniform value(s) + * the 'textures' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (textures factorizations): + * + * .upload( gl, seq, values, textures ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * + * Methods of the top-level container (textures factorizations): + * + * .setValue( gl, name, value, textures ) + * + * sets uniform with name 'name' to 'value' + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + */ - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', +const emptyTexture = /*@__PURE__*/ new Texture(); +const emptyArrayTexture = /*@__PURE__*/ new DataArrayTexture(); +const empty3dTexture = /*@__PURE__*/ new Data3DTexture(); +const emptyCubeTexture = /*@__PURE__*/ new CubeTexture(); - parameters.flatShading ? '#define FLAT_SHADED' : '', +// --- Utilities --- - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', +// Array Caches (provide typed arrays for temporary by size) - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', +const arrayCacheF32 = []; +const arrayCacheI32 = []; - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', +// Float32Array caches used for uploading Matrix uniforms - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', +const mat4array = new Float32Array( 16 ); +const mat3array = new Float32Array( 9 ); +const mat2array = new Float32Array( 4 ); - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '', +// Flattening for arrays of vectors and matrices - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', +function flatten( array, nBlocks, blockSize ) { - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', + const firstElem = array[ 0 ]; - '#ifdef USE_TANGENT', + if ( firstElem <= 0 || firstElem > 0 ) return array; + // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 - ' attribute vec4 tangent;', + const n = nBlocks * blockSize; + let r = arrayCacheF32[ n ]; - '#endif', + if ( r === undefined ) { - '#ifdef USE_COLOR', + r = new Float32Array( n ); + arrayCacheF32[ n ] = r; - ' attribute vec3 color;', + } - '#endif', + if ( nBlocks !== 0 ) { - '#ifdef USE_MORPHTARGETS', + firstElem.toArray( r, 0 ); - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', + for ( let i = 1, offset = 0; i !== nBlocks; ++ i ) { - ' #ifdef USE_MORPHNORMALS', + offset += blockSize; + array[ i ].toArray( r, offset ); - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', + } - ' #else', + } - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', + return r; - ' #endif', +} - '#endif', +function arraysEqual( a, b ) { - '#ifdef USE_SKINNING', + if ( a.length !== b.length ) return false; - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', + for ( let i = 0, l = a.length; i < l; i ++ ) { - '#endif', + if ( a[ i ] !== b[ i ] ) return false; - '\n' + } - ].filter( filterEmptyLine ).join( '\n' ); + return true; - prefixFragment = [ +} - customExtensions, +function copyArray( a, b ) { - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', + for ( let i = 0, l = b.length; i < l; i ++ ) { - ( parameters.precision === 'highp' ) ? '#define HIGH_PRECISION' : '', + a[ i ] = b[ i ]; - '#define SHADER_NAME ' + shader.name, + } - customDefines, +} - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest + ( parameters.alphaTest % 1 ? '' : '.0' ) : '', // add '.0' if integer +// Texture unit allocation - '#define GAMMA_FACTOR ' + gammaFactorDefine, +function allocTexUnits( textures, n ) { - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp2 ) ? '#define FOG_EXP2' : '', + let r = arrayCacheI32[ n ]; - parameters.map ? '#define USE_MAP' : '', - parameters.matcap ? '#define USE_MATCAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - ( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '', - ( parameters.normalMap && parameters.tangentSpaceNormalMap ) ? '#define TANGENTSPACE_NORMALMAP' : '', - parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', - parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + if ( r === undefined ) { - parameters.sheen ? '#define USE_SHEEN' : '', + r = new Int32Array( n ); + arrayCacheI32[ n ] = r; - parameters.vertexTangents ? '#define USE_TANGENT' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', - parameters.vertexUvs ? '#define USE_UV' : '', + } - parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', + for ( let i = 0; i !== n; ++ i ) { - parameters.flatShading ? '#define FLAT_SHADED' : '', + r[ i ] = textures.allocateTextureUnit(); - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + return r; - parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', +} - parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '', +// --- Setters --- - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && ( capabilities.isWebGL2 || extensions.get( 'EXT_frag_depth' ) ) ? '#define USE_LOGDEPTHBUF_EXT' : '', +// Note: Defining these methods externally, because they come in a bunch +// and this way their names minify. - ( ( material.extensions ? material.extensions.shaderTextureLOD : false ) || parameters.envMap ) && ( capabilities.isWebGL2 || extensions.get( 'EXT_shader_texture_lod' ) ) ? '#define TEXTURE_LOD_EXT' : '', +// Single scalar - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', +function setValueV1f( gl, v ) { - ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', - ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below - ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', + const cache = this.cache; - parameters.dithering ? '#define DITHERING' : '', + if ( cache[ 0 ] === v ) return; - ( parameters.outputEncoding || parameters.mapEncoding || parameters.matcapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? - ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below - parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', - parameters.matcapEncoding ? getTexelDecodingFunction( 'matcapTexelToLinear', parameters.matcapEncoding ) : '', - parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', - parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', - parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '', + gl.uniform1f( this.addr, v ); - parameters.depthPacking ? '#define DEPTH_PACKING ' + material.depthPacking : '', + cache[ 0 ] = v; - '\n' +} - ].filter( filterEmptyLine ).join( '\n' ); +// Single float vector (from flat array or THREE.VectorN) - } +function setValueV2f( gl, v ) { - vertexShader = parseIncludes( vertexShader ); - vertexShader = replaceLightNums( vertexShader, parameters ); - vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); + const cache = this.cache; - fragmentShader = parseIncludes( fragmentShader ); - fragmentShader = replaceLightNums( fragmentShader, parameters ); - fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); + if ( v.x !== undefined ) { - vertexShader = unrollLoops( vertexShader ); - fragmentShader = unrollLoops( fragmentShader ); + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - if ( capabilities.isWebGL2 && ! material.isRawShaderMaterial ) { + gl.uniform2f( this.addr, v.x, v.y ); - var isGLSL3ShaderMaterial = false; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - var versionRegex = /^\s*#version\s+300\s+es\s*\n/; + } - if ( material.isShaderMaterial && - vertexShader.match( versionRegex ) !== null && - fragmentShader.match( versionRegex ) !== null ) { + } else { - isGLSL3ShaderMaterial = true; + if ( arraysEqual( cache, v ) ) return; - vertexShader = vertexShader.replace( versionRegex, '' ); - fragmentShader = fragmentShader.replace( versionRegex, '' ); + gl.uniform2fv( this.addr, v ); - } + copyArray( cache, v ); + + } - // GLSL 3.0 conversion - prefixVertex = [ - '#version 300 es\n', - '#define attribute in', - '#define varying out', - '#define texture2D texture' - ].join( '\n' ) + '\n' + prefixVertex; +} - prefixFragment = [ - '#version 300 es\n', - '#define varying in', - isGLSL3ShaderMaterial ? '' : 'out highp vec4 pc_fragColor;', - isGLSL3ShaderMaterial ? '' : '#define gl_FragColor pc_fragColor', - '#define gl_FragDepthEXT gl_FragDepth', - '#define texture2D texture', - '#define textureCube texture', - '#define texture2DProj textureProj', - '#define texture2DLodEXT textureLod', - '#define texture2DProjLodEXT textureProjLod', - '#define textureCubeLodEXT textureLod', - '#define texture2DGradEXT textureGrad', - '#define texture2DProjGradEXT textureProjGrad', - '#define textureCubeGradEXT textureGrad' - ].join( '\n' ) + '\n' + prefixFragment; +function setValueV3f( gl, v ) { - } + const cache = this.cache; - var vertexGlsl = prefixVertex + vertexShader; - var fragmentGlsl = prefixFragment + fragmentShader; + if ( v.x !== undefined ) { - // console.log( '*VERTEX*', vertexGlsl ); - // console.log( '*FRAGMENT*', fragmentGlsl ); + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - var glVertexShader = WebGLShader( gl, 35633, vertexGlsl ); - var glFragmentShader = WebGLShader( gl, 35632, fragmentGlsl ); + gl.uniform3f( this.addr, v.x, v.y, v.z ); - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - // Force a particular attribute to index 0. + } - if ( material.index0AttributeName !== undefined ) { + } else if ( v.r !== undefined ) { - gl.bindAttribLocation( program, 0, material.index0AttributeName ); + if ( cache[ 0 ] !== v.r || cache[ 1 ] !== v.g || cache[ 2 ] !== v.b ) { - } else if ( parameters.morphTargets === true ) { + gl.uniform3f( this.addr, v.r, v.g, v.b ); - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); + cache[ 0 ] = v.r; + cache[ 1 ] = v.g; + cache[ 2 ] = v.b; } - gl.linkProgram( program ); - - // check for link errors - if ( renderer.debug.checkShaderErrors ) { + } else { - var programLog = gl.getProgramInfoLog( program ).trim(); - var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); - var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); + if ( arraysEqual( cache, v ) ) return; - var runnable = true; - var haveDiagnostics = true; + gl.uniform3fv( this.addr, v ); - if ( gl.getProgramParameter( program, 35714 ) === false ) { + copyArray( cache, v ); - runnable = false; + } - var vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); - var fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); +} - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), '35715', gl.getProgramParameter( program, 35715 ), 'gl.getProgramInfoLog', programLog, vertexErrors, fragmentErrors ); +function setValueV4f( gl, v ) { - } else if ( programLog !== '' ) { + const cache = this.cache; - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + if ( v.x !== undefined ) { - } else if ( vertexLog === '' || fragmentLog === '' ) { + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - haveDiagnostics = false; + gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - } + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; - if ( haveDiagnostics ) { + } - this.diagnostics = { + } else { - runnable: runnable, - material: material, + if ( arraysEqual( cache, v ) ) return; - programLog: programLog, + gl.uniform4fv( this.addr, v ); - vertexShader: { + copyArray( cache, v ); - log: vertexLog, - prefix: prefixVertex + } - }, +} - fragmentShader: { +// Single matrix (from flat array or THREE.MatrixN) - log: fragmentLog, - prefix: prefixFragment +function setValueM2( gl, v ) { - } + const cache = this.cache; + const elements = v.elements; - }; + if ( elements === undefined ) { - } + if ( arraysEqual( cache, v ) ) return; - } + gl.uniformMatrix2fv( this.addr, false, v ); - // clean up + copyArray( cache, v ); - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); + } else { - // set up caching for uniform locations + if ( arraysEqual( cache, elements ) ) return; - var cachedUniforms; + mat2array.set( elements ); - this.getUniforms = function () { + gl.uniformMatrix2fv( this.addr, false, mat2array ); - if ( cachedUniforms === undefined ) { + copyArray( cache, elements ); - cachedUniforms = new WebGLUniforms( gl, program ); + } - } +} - return cachedUniforms; +function setValueM3( gl, v ) { - }; + const cache = this.cache; + const elements = v.elements; - // set up caching for attribute locations + if ( elements === undefined ) { - var cachedAttributes; + if ( arraysEqual( cache, v ) ) return; - this.getAttributes = function () { + gl.uniformMatrix3fv( this.addr, false, v ); - if ( cachedAttributes === undefined ) { + copyArray( cache, v ); - cachedAttributes = fetchAttributeLocations( gl, program ); + } else { - } + if ( arraysEqual( cache, elements ) ) return; - return cachedAttributes; + mat3array.set( elements ); - }; + gl.uniformMatrix3fv( this.addr, false, mat3array ); - // free resource + copyArray( cache, elements ); - this.destroy = function () { + } - gl.deleteProgram( program ); - this.program = undefined; +} - }; +function setValueM4( gl, v ) { - // + const cache = this.cache; + const elements = v.elements; - this.name = shader.name; - this.id = programIdCount ++; - this.code = code; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; + if ( elements === undefined ) { - return this; + if ( arraysEqual( cache, v ) ) return; - } + gl.uniformMatrix4fv( this.addr, false, v ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + copyArray( cache, v ); - function WebGLPrograms( renderer, extensions, capabilities ) { - - var programs = []; - - var shaderIDs = { - MeshDepthMaterial: 'depth', - MeshDistanceMaterial: 'distanceRGBA', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - MeshToonMaterial: 'phong', - MeshStandardMaterial: 'physical', - MeshPhysicalMaterial: 'physical', - MeshMatcapMaterial: 'matcap', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points', - ShadowMaterial: 'shadow', - SpriteMaterial: 'sprite' - }; + } else { - var parameterNames = [ - "precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding", - "lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatNormalMap", "displacementMap", "specularMap", - "roughnessMap", "metalnessMap", "gradientMap", - "alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp2", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "premultipliedAlpha", - "numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights", - "shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights', - "alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering", - "sheen" - ]; + if ( arraysEqual( cache, elements ) ) return; + mat4array.set( elements ); - function allocateBones( object ) { + gl.uniformMatrix4fv( this.addr, false, mat4array ); - var skeleton = object.skeleton; - var bones = skeleton.bones; + copyArray( cache, elements ); - if ( capabilities.floatVertexTextures ) { + } - return 1024; +} - } else { +// Single integer / boolean - // default for when object is not specified - // ( for example when prebuilding shader to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) +function setValueV1i( gl, v ) { - var nVertexUniforms = capabilities.maxVertexUniforms; - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + const cache = this.cache; - var maxBones = Math.min( nVertexMatrices, bones.length ); + if ( cache[ 0 ] === v ) return; - if ( maxBones < bones.length ) { + gl.uniform1i( this.addr, v ); - console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' ); - return 0; + cache[ 0 ] = v; - } +} - return maxBones; +// Single integer / boolean vector (from flat array or THREE.VectorN) - } +function setValueV2i( gl, v ) { - } + const cache = this.cache; - function getTextureEncodingFromMap( map, gammaOverrideLinear ) { + if ( v.x !== undefined ) { - var encoding; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - if ( ! map ) { + gl.uniform2i( this.addr, v.x, v.y ); - encoding = LinearEncoding; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - } else if ( map.isTexture ) { + } - encoding = map.encoding; + } else { - } else if ( map.isWebGLRenderTarget ) { + if ( arraysEqual( cache, v ) ) return; - console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); - encoding = map.texture.encoding; + gl.uniform2iv( this.addr, v ); - } + copyArray( cache, v ); - // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. - if ( encoding === LinearEncoding && gammaOverrideLinear ) { + } - encoding = GammaEncoding; +} - } +function setValueV3i( gl, v ) { - return encoding; + const cache = this.cache; - } + if ( v.x !== undefined ) { - this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) { + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - var shaderID = shaderIDs[ material.type ]; + gl.uniform3i( this.addr, v.x, v.y, v.z ); - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0; - var precision = capabilities.precision; + } - if ( material.precision !== null ) { + } else { - precision = capabilities.getMaxPrecision( material.precision ); + if ( arraysEqual( cache, v ) ) return; - if ( precision !== material.precision ) { + gl.uniform3iv( this.addr, v ); - console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); + copyArray( cache, v ); - } + } - } +} - var currentRenderTarget = renderer.getRenderTarget(); +function setValueV4i( gl, v ) { - var parameters = { + const cache = this.cache; - shaderID: shaderID, + if ( v.x !== undefined ) { - precision: precision, - supportsVertexTextures: capabilities.vertexTextures, - outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), - map: !! material.map, - mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), - matcap: !! material.matcap, - matcapEncoding: getTextureEncodingFromMap( material.matcap, renderer.gammaInput ), - envMap: !! material.envMap, - envMapMode: material.envMap && material.envMap.mapping, - envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), - envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ), - lightMap: !! material.lightMap, - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap, - tangentSpaceNormalMap: material.normalMapType === TangentSpaceNormalMap, - clearcoatNormalMap: !! material.clearcoatNormalMap, - displacementMap: !! material.displacementMap, - roughnessMap: !! material.roughnessMap, - metalnessMap: !! material.metalnessMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - gradientMap: !! material.gradientMap, + gl.uniform4i( this.addr, v.x, v.y, v.z, v.w ); - sheen: !! material.sheen, + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; - combine: material.combine, + } - vertexTangents: ( material.normalMap && material.vertexTangents ), - vertexColors: material.vertexColors, - vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap, + } else { - fog: !! fog, - useFog: material.fog, - fogExp2: ( fog && fog.isFogExp2 ), + if ( arraysEqual( cache, v ) ) return; - flatShading: material.flatShading, + gl.uniform4iv( this.addr, v ); - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, + copyArray( cache, v ); - skinning: material.skinning && maxBones > 0, - maxBones: maxBones, - useVertexTexture: capabilities.floatVertexTextures, + } - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, +} - numDirLights: lights.directional.length, - numPointLights: lights.point.length, - numSpotLights: lights.spot.length, - numRectAreaLights: lights.rectArea.length, - numHemiLights: lights.hemi.length, +// Single unsigned integer - numDirLightShadows: lights.directionalShadowMap.length, - numPointLightShadows: lights.pointShadowMap.length, - numSpotLightShadows: lights.spotShadowMap.length, +function setValueV1ui( gl, v ) { - numClippingPlanes: nClipPlanes, - numClipIntersection: nClipIntersection, + const cache = this.cache; - dithering: material.dithering, + if ( cache[ 0 ] === v ) return; - shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0, - shadowMapType: renderer.shadowMap.type, + gl.uniform1ui( this.addr, v ); - toneMapping: material.toneMapped ? renderer.toneMapping : NoToneMapping, - physicallyCorrectLights: renderer.physicallyCorrectLights, + cache[ 0 ] = v; - premultipliedAlpha: material.premultipliedAlpha, +} - alphaTest: material.alphaTest, - doubleSided: material.side === DoubleSide, - flipSided: material.side === BackSide, +// Single unsigned integer vector (from flat array or THREE.VectorN) - depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false +function setValueV2ui( gl, v ) { - }; + const cache = this.cache; - return parameters; + if ( v.x !== undefined ) { - }; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y ) { - this.getProgramCode = function ( material, parameters ) { + gl.uniform2ui( this.addr, v.x, v.y ); - var array = []; + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; - if ( parameters.shaderID ) { + } - array.push( parameters.shaderID ); + } else { - } else { + if ( arraysEqual( cache, v ) ) return; - array.push( material.fragmentShader ); - array.push( material.vertexShader ); + gl.uniform2uiv( this.addr, v ); - } + copyArray( cache, v ); - if ( material.defines !== undefined ) { + } - for ( var name in material.defines ) { +} - array.push( name ); - array.push( material.defines[ name ] ); +function setValueV3ui( gl, v ) { - } + const cache = this.cache; - } + if ( v.x !== undefined ) { - for ( var i = 0; i < parameterNames.length; i ++ ) { + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z ) { - array.push( parameters[ parameterNames[ i ] ] ); + gl.uniform3ui( this.addr, v.x, v.y, v.z ); - } + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; - array.push( material.onBeforeCompile.toString() ); + } - array.push( renderer.gammaOutput ); + } else { - array.push( renderer.gammaFactor ); + if ( arraysEqual( cache, v ) ) return; - return array.join(); + gl.uniform3uiv( this.addr, v ); - }; + copyArray( cache, v ); - this.acquireProgram = function ( material, shader, parameters, code ) { + } - var program; +} - // Check if code has been already compiled - for ( var p = 0, pl = programs.length; p < pl; p ++ ) { +function setValueV4ui( gl, v ) { - var programInfo = programs[ p ]; + const cache = this.cache; - if ( programInfo.code === code ) { + if ( v.x !== undefined ) { - program = programInfo; - ++ program.usedTimes; + if ( cache[ 0 ] !== v.x || cache[ 1 ] !== v.y || cache[ 2 ] !== v.z || cache[ 3 ] !== v.w ) { - break; + gl.uniform4ui( this.addr, v.x, v.y, v.z, v.w ); - } + cache[ 0 ] = v.x; + cache[ 1 ] = v.y; + cache[ 2 ] = v.z; + cache[ 3 ] = v.w; - } + } - if ( program === undefined ) { + } else { - program = new WebGLProgram( renderer, extensions, code, material, shader, parameters, capabilities ); - programs.push( program ); + if ( arraysEqual( cache, v ) ) return; - } + gl.uniform4uiv( this.addr, v ); - return program; + copyArray( cache, v ); - }; + } - this.releaseProgram = function ( program ) { +} - if ( -- program.usedTimes === 0 ) { - // Remove from unordered set - var i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); +// Single texture (2D / Cube) - // Free WebGL resources - program.destroy(); +function setValueT1( gl, v, textures ) { - } + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - }; + if ( cache[ 0 ] !== unit ) { - // Exposed for resource monitoring & error feedback via renderer.info: - this.programs = programs; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; } - /** - * @author fordacious / fordacious.github.io - */ + textures.setTexture2D( v || emptyTexture, unit ); - function WebGLProperties() { +} - var properties = new WeakMap(); +function setValueT3D1( gl, v, textures ) { - function get( object ) { + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - var map = properties.get( object ); + if ( cache[ 0 ] !== unit ) { - if ( map === undefined ) { + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - map = {}; - properties.set( object, map ); + } - } + textures.setTexture3D( v || empty3dTexture, unit ); - return map; +} - } +function setValueT6( gl, v, textures ) { - function remove( object ) { + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - properties.delete( object ); + if ( cache[ 0 ] !== unit ) { - } + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; - function update( object, key, value ) { + } - properties.get( object )[ key ] = value; + textures.setTextureCube( v || emptyCubeTexture, unit ); - } +} - function dispose() { +function setValueT2DArray1( gl, v, textures ) { - properties = new WeakMap(); + const cache = this.cache; + const unit = textures.allocateTextureUnit(); - } + if ( cache[ 0 ] !== unit ) { - return { - get: get, - remove: remove, - update: update, - dispose: dispose - }; + gl.uniform1i( this.addr, unit ); + cache[ 0 ] = unit; } - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function painterSortStable( a, b ) { + textures.setTexture2DArray( v || emptyArrayTexture, unit ); - if ( a.groupOrder !== b.groupOrder ) { +} - return a.groupOrder - b.groupOrder; +// Helper to pick the right setter for the singular case - } else if ( a.renderOrder !== b.renderOrder ) { +function getSingularSetter( type ) { - return a.renderOrder - b.renderOrder; + switch ( type ) { - } else if ( a.program !== b.program ) { + case 0x1406: return setValueV1f; // FLOAT + case 0x8b50: return setValueV2f; // _VEC2 + case 0x8b51: return setValueV3f; // _VEC3 + case 0x8b52: return setValueV4f; // _VEC4 - return a.program.id - b.program.id; + case 0x8b5a: return setValueM2; // _MAT2 + case 0x8b5b: return setValueM3; // _MAT3 + case 0x8b5c: return setValueM4; // _MAT4 - } else if ( a.material.id !== b.material.id ) { + case 0x1404: case 0x8b56: return setValueV1i; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2i; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3i; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4i; // _VEC4 - return a.material.id - b.material.id; + case 0x1405: return setValueV1ui; // UINT + case 0x8dc6: return setValueV2ui; // _VEC2 + case 0x8dc7: return setValueV3ui; // _VEC3 + case 0x8dc8: return setValueV4ui; // _VEC4 - } else if ( a.z !== b.z ) { + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1; - return a.z - b.z; + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3D1; - } else { - - return a.id - b.id; + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6; - } + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArray1; } - function reversePainterSortStable( a, b ) { +} - if ( a.groupOrder !== b.groupOrder ) { - return a.groupOrder - b.groupOrder; +// Array of scalars - } else if ( a.renderOrder !== b.renderOrder ) { +function setValueV1fArray( gl, v ) { - return a.renderOrder - b.renderOrder; + gl.uniform1fv( this.addr, v ); - } else if ( a.z !== b.z ) { +} - return b.z - a.z; +// Array of vectors (from flat array or array of THREE.VectorN) - } else { +function setValueV2fArray( gl, v ) { - return a.id - b.id; + const data = flatten( v, this.size, 2 ); - } + gl.uniform2fv( this.addr, data ); - } +} +function setValueV3fArray( gl, v ) { - function WebGLRenderList() { + const data = flatten( v, this.size, 3 ); - var renderItems = []; - var renderItemsIndex = 0; + gl.uniform3fv( this.addr, data ); - var opaque = []; - var transparent = []; +} - var defaultProgram = { id: - 1 }; +function setValueV4fArray( gl, v ) { - function init() { + const data = flatten( v, this.size, 4 ); - renderItemsIndex = 0; + gl.uniform4fv( this.addr, data ); - opaque.length = 0; - transparent.length = 0; +} - } +// Array of matrices (from flat array or array of THREE.MatrixN) - function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { +function setValueM2Array( gl, v ) { - var renderItem = renderItems[ renderItemsIndex ]; + const data = flatten( v, this.size, 4 ); - if ( renderItem === undefined ) { + gl.uniformMatrix2fv( this.addr, false, data ); - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - program: material.program || defaultProgram, - groupOrder: groupOrder, - renderOrder: object.renderOrder, - z: z, - group: group - }; +} - renderItems[ renderItemsIndex ] = renderItem; +function setValueM3Array( gl, v ) { - } else { + const data = flatten( v, this.size, 9 ); - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.program = material.program || defaultProgram; - renderItem.groupOrder = groupOrder; - renderItem.renderOrder = object.renderOrder; - renderItem.z = z; - renderItem.group = group; + gl.uniformMatrix3fv( this.addr, false, data ); - } +} - renderItemsIndex ++; +function setValueM4Array( gl, v ) { - return renderItem; + const data = flatten( v, this.size, 16 ); - } + gl.uniformMatrix4fv( this.addr, false, data ); - function push( object, geometry, material, groupOrder, z, group ) { +} - var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); +// Array of integer / boolean - ( material.transparent === true ? transparent : opaque ).push( renderItem ); +function setValueV1iArray( gl, v ) { - } + gl.uniform1iv( this.addr, v ); - function unshift( object, geometry, material, groupOrder, z, group ) { +} - var renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); +// Array of integer / boolean vectors (from flat array) - ( material.transparent === true ? transparent : opaque ).unshift( renderItem ); +function setValueV2iArray( gl, v ) { - } + gl.uniform2iv( this.addr, v ); - function sort() { +} - if ( opaque.length > 1 ) opaque.sort( painterSortStable ); - if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable ); +function setValueV3iArray( gl, v ) { - } + gl.uniform3iv( this.addr, v ); - return { - opaque: opaque, - transparent: transparent, +} - init: init, - push: push, - unshift: unshift, +function setValueV4iArray( gl, v ) { - sort: sort - }; + gl.uniform4iv( this.addr, v ); - } +} - function WebGLRenderLists() { +// Array of unsigned integer - var lists = new WeakMap(); +function setValueV1uiArray( gl, v ) { - function onSceneDispose( event ) { + gl.uniform1uiv( this.addr, v ); - var scene = event.target; +} - scene.removeEventListener( 'dispose', onSceneDispose ); +// Array of unsigned integer vectors (from flat array) - lists.delete( scene ); +function setValueV2uiArray( gl, v ) { - } + gl.uniform2uiv( this.addr, v ); - function get( scene, camera ) { +} - var cameras = lists.get( scene ); - var list; - if ( cameras === undefined ) { +function setValueV3uiArray( gl, v ) { - list = new WebGLRenderList(); - lists.set( scene, new WeakMap() ); - lists.get( scene ).set( camera, list ); + gl.uniform3uiv( this.addr, v ); - scene.addEventListener( 'dispose', onSceneDispose ); +} - } else { +function setValueV4uiArray( gl, v ) { - list = cameras.get( camera ); - if ( list === undefined ) { + gl.uniform4uiv( this.addr, v ); - list = new WebGLRenderList(); - cameras.set( camera, list ); +} - } - } +// Array of textures (2D / 3D / Cube / 2DArray) - return list; +function setValueT1Array( gl, v, textures ) { - } + const cache = this.cache; - function dispose() { + const n = v.length; - lists = new WeakMap(); + const units = allocTexUnits( textures, n ); - } + if ( ! arraysEqual( cache, units ) ) { - return { - get: get, - dispose: dispose - }; + gl.uniform1iv( this.addr, units ); + + copyArray( cache, units ); } - /** - * @author mrdoob / http://mrdoob.com/ - */ + for ( let i = 0; i !== n; ++ i ) { - function UniformsCache() { + textures.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); - var lights = {}; + } - return { +} - get: function ( light ) { +function setValueT3DArray( gl, v, textures ) { - if ( lights[ light.id ] !== undefined ) { + const cache = this.cache; - return lights[ light.id ]; + const n = v.length; - } + const units = allocTexUnits( textures, n ); - var uniforms; + if ( ! arraysEqual( cache, units ) ) { - switch ( light.type ) { + gl.uniform1iv( this.addr, units ); - case 'DirectionalLight': - uniforms = { - direction: new Vector3(), - color: new Color(), + copyArray( cache, units ); - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + } - case 'SpotLight': - uniforms = { - position: new Vector3(), - direction: new Vector3(), - color: new Color(), - distance: 0, - coneCos: 0, - penumbraCos: 0, - decay: 0, - - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2() - }; - break; + for ( let i = 0; i !== n; ++ i ) { - case 'PointLight': - uniforms = { - position: new Vector3(), - color: new Color(), - distance: 0, - decay: 0, - - shadow: false, - shadowBias: 0, - shadowRadius: 1, - shadowMapSize: new Vector2(), - shadowCameraNear: 1, - shadowCameraFar: 1000 - }; - break; + textures.setTexture3D( v[ i ] || empty3dTexture, units[ i ] ); - case 'HemisphereLight': - uniforms = { - direction: new Vector3(), - skyColor: new Color(), - groundColor: new Color() - }; - break; + } - case 'RectAreaLight': - uniforms = { - color: new Color(), - position: new Vector3(), - halfWidth: new Vector3(), - halfHeight: new Vector3() - // TODO (abelnation): set RectAreaLight shadow uniforms - }; - break; +} - } +function setValueT6Array( gl, v, textures ) { - lights[ light.id ] = uniforms; + const cache = this.cache; - return uniforms; + const n = v.length; - } + const units = allocTexUnits( textures, n ); - }; + if ( ! arraysEqual( cache, units ) ) { - } + gl.uniform1iv( this.addr, units ); + + copyArray( cache, units ); - var nextVersion = 0; + } - function shadowCastingLightsFirst( lightA, lightB ) { + for ( let i = 0; i !== n; ++ i ) { - return ( lightB.castShadow ? 1 : 0 ) - ( lightA.castShadow ? 1 : 0 ); + textures.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); } - function WebGLLights() { +} - var cache = new UniformsCache(); +function setValueT2DArrayArray( gl, v, textures ) { - var state = { + const cache = this.cache; - version: 0, + const n = v.length; - hash: { - directionalLength: - 1, - pointLength: - 1, - spotLength: - 1, - rectAreaLength: - 1, - hemiLength: - 1, + const units = allocTexUnits( textures, n ); - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1, - }, + if ( ! arraysEqual( cache, units ) ) { - ambient: [ 0, 0, 0 ], - probe: [], - directional: [], - directionalShadowMap: [], - directionalShadowMatrix: [], - spot: [], - spotShadowMap: [], - spotShadowMatrix: [], - rectArea: [], - point: [], - pointShadowMap: [], - pointShadowMatrix: [], - hemi: [], + gl.uniform1iv( this.addr, units ); - numDirectionalShadows: - 1, - numPointShadows: - 1, - numSpotShadows: - 1 + copyArray( cache, units ); - }; + } - for ( var i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); + for ( let i = 0; i !== n; ++ i ) { - var vector3 = new Vector3(); - var matrix4 = new Matrix4(); - var matrix42 = new Matrix4(); + textures.setTexture2DArray( v[ i ] || emptyArrayTexture, units[ i ] ); - function setup( lights, shadows, camera ) { + } - var r = 0, g = 0, b = 0; +} - for ( var i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - var directionalLength = 0; - var pointLength = 0; - var spotLength = 0; - var rectAreaLength = 0; - var hemiLength = 0; +// Helper to pick the right setter for a pure (bottom-level) array - var numDirectionalShadows = 0; - var numPointShadows = 0; - var numSpotShadows = 0; +function getPureArraySetter( type ) { - var viewMatrix = camera.matrixWorldInverse; + switch ( type ) { - lights.sort( shadowCastingLightsFirst ); + case 0x1406: return setValueV1fArray; // FLOAT + case 0x8b50: return setValueV2fArray; // _VEC2 + case 0x8b51: return setValueV3fArray; // _VEC3 + case 0x8b52: return setValueV4fArray; // _VEC4 - for ( var i = 0, l = lights.length; i < l; i ++ ) { + case 0x8b5a: return setValueM2Array; // _MAT2 + case 0x8b5b: return setValueM3Array; // _MAT3 + case 0x8b5c: return setValueM4Array; // _MAT4 - var light = lights[ i ]; + case 0x1404: case 0x8b56: return setValueV1iArray; // INT, BOOL + case 0x8b53: case 0x8b57: return setValueV2iArray; // _VEC2 + case 0x8b54: case 0x8b58: return setValueV3iArray; // _VEC3 + case 0x8b55: case 0x8b59: return setValueV4iArray; // _VEC4 - var color = light.color; - var intensity = light.intensity; - var distance = light.distance; + case 0x1405: return setValueV1uiArray; // UINT + case 0x8dc6: return setValueV2uiArray; // _VEC2 + case 0x8dc7: return setValueV3uiArray; // _VEC3 + case 0x8dc8: return setValueV4uiArray; // _VEC4 - var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; + case 0x8b5e: // SAMPLER_2D + case 0x8d66: // SAMPLER_EXTERNAL_OES + case 0x8dca: // INT_SAMPLER_2D + case 0x8dd2: // UNSIGNED_INT_SAMPLER_2D + case 0x8b62: // SAMPLER_2D_SHADOW + return setValueT1Array; - if ( light.isAmbientLight ) { + case 0x8b5f: // SAMPLER_3D + case 0x8dcb: // INT_SAMPLER_3D + case 0x8dd3: // UNSIGNED_INT_SAMPLER_3D + return setValueT3DArray; - r += color.r * intensity; - g += color.g * intensity; - b += color.b * intensity; + case 0x8b60: // SAMPLER_CUBE + case 0x8dcc: // INT_SAMPLER_CUBE + case 0x8dd4: // UNSIGNED_INT_SAMPLER_CUBE + case 0x8dc5: // SAMPLER_CUBE_SHADOW + return setValueT6Array; - } else if ( light.isLightProbe ) { + case 0x8dc1: // SAMPLER_2D_ARRAY + case 0x8dcf: // INT_SAMPLER_2D_ARRAY + case 0x8dd7: // UNSIGNED_INT_SAMPLER_2D_ARRAY + case 0x8dc4: // SAMPLER_2D_ARRAY_SHADOW + return setValueT2DArrayArray; - for ( var j = 0; j < 9; j ++ ) { + } - state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); +} - } +// --- Uniform Classes --- - } else if ( light.isDirectionalLight ) { +class SingleUniform { - var uniforms = cache.get( light ); + constructor( id, activeInfo, addr ) { - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + this.id = id; + this.addr = addr; + this.cache = []; + this.setValue = getSingularSetter( activeInfo.type ); - uniforms.shadow = light.castShadow; + // this.path = activeInfo.name; // DEBUG - if ( light.castShadow ) { + } - var shadow = light.shadow; +} - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; +class PureArrayUniform { - state.directionalShadowMap[ directionalLength ] = shadowMap; - state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + constructor( id, activeInfo, addr ) { - numDirectionalShadows ++; + this.id = id; + this.addr = addr; + this.cache = []; + this.size = activeInfo.size; + this.setValue = getPureArraySetter( activeInfo.type ); - } + // this.path = activeInfo.name; // DEBUG - state.directional[ directionalLength ] = uniforms; + } - directionalLength ++; +} - } else if ( light.isSpotLight ) { +class StructuredUniform { - var uniforms = cache.get( light ); + constructor( id ) { - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + this.id = id; - uniforms.color.copy( color ).multiplyScalar( intensity ); - uniforms.distance = distance; + this.seq = []; + this.map = {}; - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - vector3.setFromMatrixPosition( light.target.matrixWorld ); - uniforms.direction.sub( vector3 ); - uniforms.direction.transformDirection( viewMatrix ); + } - uniforms.coneCos = Math.cos( light.angle ); - uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); - uniforms.decay = light.decay; + setValue( gl, value, textures ) { - uniforms.shadow = light.castShadow; + const seq = this.seq; - if ( light.castShadow ) { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - var shadow = light.shadow; + const u = seq[ i ]; + u.setValue( gl, value[ u.id ], textures ); - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; + } - state.spotShadowMap[ spotLength ] = shadowMap; - state.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + } - numSpotShadows ++; +} - } +// --- Top-level --- - state.spot[ spotLength ] = uniforms; +// Parser - builds up the property tree from the path strings - spotLength ++; +const RePathPart = /(\w+)(\])?(\[|\.)?/g; - } else if ( light.isRectAreaLight ) { +// extracts +// - the identifier (member name or array index) +// - followed by an optional right bracket (found when array index) +// - followed by an optional left bracket or dot (type of subscript) +// +// Note: These portions can be read in a non-overlapping fashion and +// allow straightforward parsing of the hierarchy that WebGL encodes +// in the uniform names. - var uniforms = cache.get( light ); +function addUniform( container, uniformObject ) { - // (a) intensity is the total visible light emitted - //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) ); + container.seq.push( uniformObject ); + container.map[ uniformObject.id ] = uniformObject; - // (b) intensity is the brightness of the light - uniforms.color.copy( color ).multiplyScalar( intensity ); +} - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); +function parseUniform( activeInfo, addr, container ) { - // extract local rotation of light to derive width/height half vectors - matrix42.identity(); - matrix4.copy( light.matrixWorld ); - matrix4.premultiply( viewMatrix ); - matrix42.extractRotation( matrix4 ); + const path = activeInfo.name, + pathLength = path.length; - uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); - uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); + // reset RegExp object, because of the early exit of a previous run + RePathPart.lastIndex = 0; - uniforms.halfWidth.applyMatrix4( matrix42 ); - uniforms.halfHeight.applyMatrix4( matrix42 ); + while ( true ) { - // TODO (abelnation): RectAreaLight distance? - // uniforms.distance = distance; + const match = RePathPart.exec( path ), + matchEnd = RePathPart.lastIndex; - state.rectArea[ rectAreaLength ] = uniforms; + let id = match[ 1 ]; + const idIsIndex = match[ 2 ] === ']', + subscript = match[ 3 ]; - rectAreaLength ++; + if ( idIsIndex ) id = id | 0; // convert to integer - } else if ( light.isPointLight ) { + if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) { - var uniforms = cache.get( light ); + // bare name or "pure" bottom-level array "[0]" suffix - uniforms.position.setFromMatrixPosition( light.matrixWorld ); - uniforms.position.applyMatrix4( viewMatrix ); + addUniform( container, subscript === undefined ? + new SingleUniform( id, activeInfo, addr ) : + new PureArrayUniform( id, activeInfo, addr ) ); - uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); - uniforms.distance = light.distance; - uniforms.decay = light.decay; + break; - uniforms.shadow = light.castShadow; - - if ( light.castShadow ) { + } else { - var shadow = light.shadow; + // step into inner node / create it in case it doesn't exist - uniforms.shadowBias = shadow.bias; - uniforms.shadowRadius = shadow.radius; - uniforms.shadowMapSize = shadow.mapSize; - uniforms.shadowCameraNear = shadow.camera.near; - uniforms.shadowCameraFar = shadow.camera.far; + const map = container.map; + let next = map[ id ]; - state.pointShadowMap[ pointLength ] = shadowMap; - state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; + if ( next === undefined ) { - numPointShadows ++; + next = new StructuredUniform( id ); + addUniform( container, next ); - } + } - state.point[ pointLength ] = uniforms; + container = next; - pointLength ++; + } - } else if ( light.isHemisphereLight ) { + } - var uniforms = cache.get( light ); +} - uniforms.direction.setFromMatrixPosition( light.matrixWorld ); - uniforms.direction.transformDirection( viewMatrix ); - uniforms.direction.normalize(); +// Root Container - uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); - uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); +class WebGLUniforms { - state.hemi[ hemiLength ] = uniforms; + constructor( gl, program ) { - hemiLength ++; + this.seq = []; + this.map = {}; - } + const n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); - } + for ( let i = 0; i < n; ++ i ) { - state.ambient[ 0 ] = r; - state.ambient[ 1 ] = g; - state.ambient[ 2 ] = b; + const info = gl.getActiveUniform( program, i ), + addr = gl.getUniformLocation( program, info.name ); - var hash = state.hash; + parseUniform( info, addr, this ); - if ( hash.directionalLength !== directionalLength || - hash.pointLength !== pointLength || - hash.spotLength !== spotLength || - hash.rectAreaLength !== rectAreaLength || - hash.hemiLength !== hemiLength || - hash.numDirectionalShadows !== numDirectionalShadows || - hash.numPointShadows !== numPointShadows || - hash.numSpotShadows !== numSpotShadows ) { + } - state.directional.length = directionalLength; - state.spot.length = spotLength; - state.rectArea.length = rectAreaLength; - state.point.length = pointLength; - state.hemi.length = hemiLength; + } - state.directionalShadowMap.length = numDirectionalShadows; - state.pointShadowMap.length = numPointShadows; - state.spotShadowMap.length = numSpotShadows; - state.directionalShadowMatrix.length = numDirectionalShadows; - state.pointShadowMatrix.length = numPointShadows; - state.spotShadowMatrix.length = numSpotShadows; + setValue( gl, name, value, textures ) { - hash.directionalLength = directionalLength; - hash.pointLength = pointLength; - hash.spotLength = spotLength; - hash.rectAreaLength = rectAreaLength; - hash.hemiLength = hemiLength; + const u = this.map[ name ]; - hash.numDirectionalShadows = numDirectionalShadows; - hash.numPointShadows = numPointShadows; - hash.numSpotShadows = numSpotShadows; + if ( u !== undefined ) u.setValue( gl, value, textures ); - state.version = nextVersion ++; + } - } + setOptional( gl, object, name ) { - } + const v = object[ name ]; - return { - setup: setup, - state: state - }; + if ( v !== undefined ) this.setValue( gl, name, v ); } - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + static upload( gl, seq, values, textures ) { - function WebGLRenderState() { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - var lights = new WebGLLights(); + const u = seq[ i ], + v = values[ u.id ]; - var lightsArray = []; - var shadowsArray = []; + if ( v.needsUpdate !== false ) { - function init() { + // note: always updating when .needsUpdate is undefined + u.setValue( gl, v.value, textures ); - lightsArray.length = 0; - shadowsArray.length = 0; + } } - function pushLight( light ) { + } - lightsArray.push( light ); + static seqWithValue( seq, values ) { - } + const r = []; - function pushShadow( shadowLight ) { + for ( let i = 0, n = seq.length; i !== n; ++ i ) { - shadowsArray.push( shadowLight ); + const u = seq[ i ]; + if ( u.id in values ) r.push( u ); } - function setupLights( camera ) { - - lights.setup( lightsArray, shadowsArray, camera ); - - } + return r; - var state = { - lightsArray: lightsArray, - shadowsArray: shadowsArray, + } - lights: lights - }; +} - return { - init: init, - state: state, - setupLights: setupLights, +function WebGLShader( gl, type, string ) { - pushLight: pushLight, - pushShadow: pushShadow - }; + const shader = gl.createShader( type ); - } + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - function WebGLRenderStates() { + return shader; - var renderStates = new WeakMap(); +} - function onSceneDispose( event ) { +let programIdCount = 0; - var scene = event.target; +function handleSource( string, errorLine ) { - scene.removeEventListener( 'dispose', onSceneDispose ); + const lines = string.split( '\n' ); + const lines2 = []; - renderStates.delete( scene ); + const from = Math.max( errorLine - 6, 0 ); + const to = Math.min( errorLine + 6, lines.length ); - } + for ( let i = from; i < to; i ++ ) { - function get( scene, camera ) { + const line = i + 1; + lines2.push( `${line === errorLine ? '>' : ' '} ${line}: ${lines[ i ]}` ); - var renderState; + } - if ( renderStates.has( scene ) === false ) { + return lines2.join( '\n' ); - renderState = new WebGLRenderState(); - renderStates.set( scene, new WeakMap() ); - renderStates.get( scene ).set( camera, renderState ); +} - scene.addEventListener( 'dispose', onSceneDispose ); +function getEncodingComponents( colorSpace ) { - } else { + const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace ); + const encodingPrimaries = ColorManagement.getPrimaries( colorSpace ); - if ( renderStates.get( scene ).has( camera ) === false ) { + let gamutMapping; - renderState = new WebGLRenderState(); - renderStates.get( scene ).set( camera, renderState ); + if ( workingPrimaries === encodingPrimaries ) { - } else { + gamutMapping = ''; - renderState = renderStates.get( scene ).get( camera ); + } else if ( workingPrimaries === P3Primaries && encodingPrimaries === Rec709Primaries ) { - } + gamutMapping = 'LinearDisplayP3ToLinearSRGB'; - } + } else if ( workingPrimaries === Rec709Primaries && encodingPrimaries === P3Primaries ) { - return renderState; + gamutMapping = 'LinearSRGBToLinearDisplayP3'; - } + } - function dispose() { + switch ( colorSpace ) { - renderStates = new WeakMap(); + case LinearSRGBColorSpace: + case LinearDisplayP3ColorSpace: + return [ gamutMapping, 'LinearTransferOETF' ]; - } + case SRGBColorSpace: + case DisplayP3ColorSpace: + return [ gamutMapping, 'sRGBTransferOETF' ]; - return { - get: get, - dispose: dispose - }; + default: + console.warn( 'THREE.WebGLProgram: Unsupported color space:', colorSpace ); + return [ gamutMapping, 'LinearTransferOETF' ]; } - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author bhouston / https://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * - * opacity: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ +} - function MeshDepthMaterial( parameters ) { +function getShaderErrors( gl, shader, type ) { - Material.call( this ); + const status = gl.getShaderParameter( shader, gl.COMPILE_STATUS ); + const errors = gl.getShaderInfoLog( shader ).trim(); - this.type = 'MeshDepthMaterial'; + if ( status && errors === '' ) return ''; - this.depthPacking = BasicDepthPacking; + const errorMatches = /ERROR: 0:(\d+)/.exec( errors ); + if ( errorMatches ) { - this.skinning = false; - this.morphTargets = false; + // --enable-privileged-webgl-extension + // console.log( '**' + type + '**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - this.map = null; + const errorLine = parseInt( errorMatches[ 1 ] ); + return type.toUpperCase() + '\n\n' + errors + '\n\n' + handleSource( gl.getShaderSource( shader ), errorLine ); - this.alphaMap = null; + } else { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + return errors; - this.wireframe = false; - this.wireframeLinewidth = 1; + } - this.fog = false; - this.lights = false; +} - this.setValues( parameters ); +function getTexelEncodingFunction( functionName, colorSpace ) { - } + const components = getEncodingComponents( colorSpace ); + return `vec4 ${functionName}( vec4 value ) { return ${components[ 0 ]}( ${components[ 1 ]}( value ) ); }`; - MeshDepthMaterial.prototype = Object.create( Material.prototype ); - MeshDepthMaterial.prototype.constructor = MeshDepthMaterial; +} - MeshDepthMaterial.prototype.isMeshDepthMaterial = true; +function getToneMappingFunction( functionName, toneMapping ) { - MeshDepthMaterial.prototype.copy = function ( source ) { + let toneMappingName; - Material.prototype.copy.call( this, source ); + switch ( toneMapping ) { - this.depthPacking = source.depthPacking; + case LinearToneMapping: + toneMappingName = 'Linear'; + break; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + case ReinhardToneMapping: + toneMappingName = 'Reinhard'; + break; - this.map = source.map; + case CineonToneMapping: + toneMappingName = 'OptimizedCineon'; + break; - this.alphaMap = source.alphaMap; + case ACESFilmicToneMapping: + toneMappingName = 'ACESFilmic'; + break; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + case CustomToneMapping: + toneMappingName = 'Custom'; + break; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + default: + console.warn( 'THREE.WebGLProgram: Unsupported toneMapping:', toneMapping ); + toneMappingName = 'Linear'; - return this; + } - }; + return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }'; - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * - * referencePosition: , - * nearDistance: , - * farDistance: , - * - * skinning: , - * morphTargets: , - * - * map: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: - * - * } - */ +} - function MeshDistanceMaterial( parameters ) { +function generateExtensions( parameters ) { - Material.call( this ); + const chunks = [ + ( parameters.extensionDerivatives || !! parameters.envMapCubeUVHeight || parameters.bumpMap || parameters.normalMapTangentSpace || parameters.clearcoatNormalMap || parameters.flatShading || parameters.shaderID === 'physical' ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( parameters.extensionFragDepth || parameters.logarithmicDepthBuffer ) && parameters.rendererExtensionFragDepth ? '#extension GL_EXT_frag_depth : enable' : '', + ( parameters.extensionDrawBuffers && parameters.rendererExtensionDrawBuffers ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( parameters.extensionShaderTextureLOD || parameters.envMap || parameters.transmission ) && parameters.rendererExtensionShaderTextureLod ? '#extension GL_EXT_shader_texture_lod : enable' : '' + ]; - this.type = 'MeshDistanceMaterial'; + return chunks.filter( filterEmptyLine ).join( '\n' ); - this.referencePosition = new Vector3(); - this.nearDistance = 1; - this.farDistance = 1000; +} - this.skinning = false; - this.morphTargets = false; +function generateDefines( defines ) { - this.map = null; + const chunks = []; - this.alphaMap = null; + for ( const name in defines ) { - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + const value = defines[ name ]; - this.fog = false; - this.lights = false; + if ( value === false ) continue; - this.setValues( parameters ); + chunks.push( '#define ' + name + ' ' + value ); } - MeshDistanceMaterial.prototype = Object.create( Material.prototype ); - MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial; + return chunks.join( '\n' ); - MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true; +} - MeshDistanceMaterial.prototype.copy = function ( source ) { +function fetchAttributeLocations( gl, program ) { - Material.prototype.copy.call( this, source ); + const attributes = {}; - this.referencePosition.copy( source.referencePosition ); - this.nearDistance = source.nearDistance; - this.farDistance = source.farDistance; + const n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; + for ( let i = 0; i < n; i ++ ) { - this.map = source.map; + const info = gl.getActiveAttrib( program, i ); + const name = info.name; - this.alphaMap = source.alphaMap; + let locationSize = 1; + if ( info.type === gl.FLOAT_MAT2 ) locationSize = 2; + if ( info.type === gl.FLOAT_MAT3 ) locationSize = 3; + if ( info.type === gl.FLOAT_MAT4 ) locationSize = 4; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i ); - return this; + attributes[ name ] = { + type: info.type, + location: gl.getAttribLocation( program, name ), + locationSize: locationSize + }; - }; + } - var vsm_frag = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n float mean = 0.0;\n float squared_mean = 0.0;\n \n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n for ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n #ifdef HORIZONAL_PASS\n vec2 distribution = decodeHalfRGBA ( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n mean += distribution.x;\n squared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n #else\n float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n mean += depth;\n squared_mean += depth * depth;\n #endif\n }\n mean = mean * HALF_SAMPLE_RATE;\n squared_mean = squared_mean * HALF_SAMPLE_RATE;\n float std_dev = pow( squared_mean - mean * mean, 0.5 );\n gl_FragColor = encodeHalfRGBA( vec2( mean, std_dev ) );\n}"; + return attributes; - var vsm_vert = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; +} - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ +function filterEmptyLine( string ) { - function WebGLShadowMap( _renderer, _objects, maxTextureSize ) { + return string !== ''; - var _frustum = new Frustum(), +} - _shadowMapSize = new Vector2(), - _viewportSize = new Vector2(), +function replaceLightNums( string, parameters ) { - _viewport = new Vector4(), + const numSpotLightCoords = parameters.numSpotLightShadows + parameters.numSpotLightMaps - parameters.numSpotLightShadowsWithMaps; - _MorphingFlag = 1, - _SkinningFlag = 2, + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_SPOT_LIGHT_MAPS/g, parameters.numSpotLightMaps ) + .replace( /NUM_SPOT_LIGHT_COORDS/g, numSpotLightCoords ) + .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ) + .replace( /NUM_DIR_LIGHT_SHADOWS/g, parameters.numDirLightShadows ) + .replace( /NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS/g, parameters.numSpotLightShadowsWithMaps ) + .replace( /NUM_SPOT_LIGHT_SHADOWS/g, parameters.numSpotLightShadows ) + .replace( /NUM_POINT_LIGHT_SHADOWS/g, parameters.numPointLightShadows ); - _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, +} - _depthMaterials = new Array( _NumberOfMaterialVariants ), - _distanceMaterials = new Array( _NumberOfMaterialVariants ), +function replaceClippingPlaneNums( string, parameters ) { - _materialCache = {}; + return string + .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes ) + .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) ); - var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide }; +} - var shadowMaterialVertical = new ShaderMaterial( { +// Resolve Includes - defines: { - SAMPLE_RATE: 2.0 / 8.0, - HALF_SAMPLE_RATE: 1.0 / 8.0 - }, +const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm; - uniforms: { - shadow_pass: { value: null }, - resolution: { value: new Vector2() }, - radius: { value: 4.0 } - }, +function resolveIncludes( string ) { - vertexShader: vsm_vert, + return string.replace( includePattern, includeReplacer ); - fragmentShader: vsm_frag +} - } ); +const shaderChunkMap = new Map( [ + [ 'encodings_fragment', 'colorspace_fragment' ], // @deprecated, r154 + [ 'encodings_pars_fragment', 'colorspace_pars_fragment' ], // @deprecated, r154 + [ 'output_fragment', 'opaque_fragment' ], // @deprecated, r154 +] ); - var shadowMaterialHorizonal = shadowMaterialVertical.clone(); - shadowMaterialHorizonal.defines.HORIZONAL_PASS = 1; +function includeReplacer( match, include ) { - var fullScreenTri = new BufferGeometry(); - fullScreenTri.addAttribute( - "position", - new BufferAttribute( - new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), - 3 - ) - ); + let string = ShaderChunk[ include ]; - var fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); + if ( string === undefined ) { - // init + const newInclude = shaderChunkMap.get( include ); - for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { + if ( newInclude !== undefined ) { - var useMorphing = ( i & _MorphingFlag ) !== 0; - var useSkinning = ( i & _SkinningFlag ) !== 0; + string = ShaderChunk[ newInclude ]; + console.warn( 'THREE.WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.', include, newInclude ); + + } else { - var depthMaterial = new MeshDepthMaterial( { + throw new Error( 'Can not resolve #include <' + include + '>' ); - depthPacking: RGBADepthPacking, + } - morphTargets: useMorphing, - skinning: useSkinning + } - } ); + return resolveIncludes( string ); - _depthMaterials[ i ] = depthMaterial; +} - var distanceMaterial = new MeshDistanceMaterial( { +// Unroll Loops - morphTargets: useMorphing, - skinning: useSkinning +const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g; - } ); +function unrollLoops( string ) { - _distanceMaterials[ i ] = distanceMaterial; + return string.replace( unrollLoopPattern, loopReplacer ); - } +} - var scope = this; +function loopReplacer( match, start, end, snippet ) { - this.enabled = false; + let string = ''; - this.autoUpdate = true; - this.needsUpdate = false; + for ( let i = parseInt( start ); i < parseInt( end ); i ++ ) { - this.type = PCFShadowMap; + string += snippet + .replace( /\[\s*i\s*\]/g, '[ ' + i + ' ]' ) + .replace( /UNROLLED_LOOP_INDEX/g, i ); - this.render = function ( lights, scene, camera ) { + } - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + return string; - if ( lights.length === 0 ) return; +} - var currentRenderTarget = _renderer.getRenderTarget(); - var activeCubeFace = _renderer.getActiveCubeFace(); - var activeMipmapLevel = _renderer.getActiveMipmapLevel(); +// - var _state = _renderer.state; +function generatePrecision( parameters ) { - // Set GL state for depth map. - _state.setBlending( NoBlending ); - _state.buffers.color.setClear( 1, 1, 1, 1 ); - _state.buffers.depth.setTest( true ); - _state.setScissorTest( false ); + let precisionstring = 'precision ' + parameters.precision + ' float;\nprecision ' + parameters.precision + ' int;'; - // render depth map + if ( parameters.precision === 'highp' ) { - for ( var i = 0, il = lights.length; i < il; i ++ ) { + precisionstring += '\n#define HIGH_PRECISION'; - var light = lights[ i ]; - var shadow = light.shadow; + } else if ( parameters.precision === 'mediump' ) { - if ( shadow === undefined ) { + precisionstring += '\n#define MEDIUM_PRECISION'; - console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); - continue; + } else if ( parameters.precision === 'lowp' ) { - } + precisionstring += '\n#define LOW_PRECISION'; - _shadowMapSize.copy( shadow.mapSize ); + } - var shadowFrameExtents = shadow.getFrameExtents(); + return precisionstring; - _shadowMapSize.multiply( shadowFrameExtents ); +} - _viewportSize.copy( shadow.mapSize ); +function generateShadowMapTypeDefine( parameters ) { - if ( _shadowMapSize.x > maxTextureSize || _shadowMapSize.y > maxTextureSize ) { + let shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; - console.warn( 'THREE.WebGLShadowMap:', light, 'has shadow exceeding max texture size, reducing' ); + if ( parameters.shadowMapType === PCFShadowMap ) { - if ( _shadowMapSize.x > maxTextureSize ) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; - _viewportSize.x = Math.floor( maxTextureSize / shadowFrameExtents.x ); - _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; - shadow.mapSize.x = _viewportSize.x; + } else if ( parameters.shadowMapType === PCFSoftShadowMap ) { - } + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - if ( _shadowMapSize.y > maxTextureSize ) { + } else if ( parameters.shadowMapType === VSMShadowMap ) { - _viewportSize.y = Math.floor( maxTextureSize / shadowFrameExtents.y ); - _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; - shadow.mapSize.y = _viewportSize.y; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_VSM'; - } + } - } + return shadowMapTypeDefine; - if ( shadow.map === null && ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { +} - var pars = { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }; +function generateEnvMapTypeDefine( parameters ) { - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; + let envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + if ( parameters.envMap ) { - shadow.camera.updateProjectionMatrix(); + switch ( parameters.envMapMode ) { - } + case CubeReflectionMapping: + case CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; - if ( shadow.map === null ) { + case CubeUVReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; - var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat }; + } - shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - shadow.map.texture.name = light.name + ".shadowMap"; + } - shadow.camera.updateProjectionMatrix(); + return envMapTypeDefine; - } +} - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); +function generateEnvMapModeDefine( parameters ) { - var viewportCount = shadow.getViewportCount(); + let envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - for ( var vp = 0; vp < viewportCount; vp ++ ) { + if ( parameters.envMap ) { - var viewport = shadow.getViewport( vp ); + switch ( parameters.envMapMode ) { - _viewport.set( - _viewportSize.x * viewport.x, - _viewportSize.y * viewport.y, - _viewportSize.x * viewport.z, - _viewportSize.y * viewport.w - ); + case CubeRefractionMapping: - _state.viewport( _viewport ); + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; - shadow.updateMatrices( light, camera, vp ); + } - _frustum = shadow.getFrustum(); + } - renderObject( scene, camera, shadow.camera, light, this.type ); + return envMapModeDefine; - } +} - // do blur pass for VSM +function generateEnvMapBlendingDefine( parameters ) { - if ( ! shadow.isPointLightShadow && this.type === VSMShadowMap ) { + let envMapBlendingDefine = 'ENVMAP_BLENDING_NONE'; - VSMPass( shadow, camera ); + if ( parameters.envMap ) { - } + switch ( parameters.combine ) { - } + case MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - scope.needsUpdate = false; + case MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; - _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); + case AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; - }; + } - function VSMPass( shadow, camera ) { + } - var geometry = _objects.update( fullScreenMesh ); + return envMapBlendingDefine; - // vertical pass +} - shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; - shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; - shadowMaterialVertical.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.mapPass ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); +function generateCubeUVSize( parameters ) { - // horizonal pass + const imageHeight = parameters.envMapCubeUVHeight; - shadowMaterialHorizonal.uniforms.shadow_pass.value = shadow.mapPass.texture; - shadowMaterialHorizonal.uniforms.resolution.value = shadow.mapSize; - shadowMaterialHorizonal.uniforms.radius.value = shadow.radius; - _renderer.setRenderTarget( shadow.map ); - _renderer.clear(); - _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizonal, fullScreenMesh, null ); + if ( imageHeight === null ) return null; - } + const maxMip = Math.log2( imageHeight ) - 2; - function getDepthMaterial( object, material, light, shadowCameraNear, shadowCameraFar, type ) { + const texelHeight = 1.0 / imageHeight; - var geometry = object.geometry; + const texelWidth = 1.0 / ( 3 * Math.max( Math.pow( 2, maxMip ), 7 * 16 ) ); - var result = null; + return { texelWidth, texelHeight, maxMip }; - var materialVariants = _depthMaterials; - var customMaterial = object.customDepthMaterial; +} - if ( light.isPointLight ) { +function WebGLProgram( renderer, cacheKey, parameters, bindingStates ) { - materialVariants = _distanceMaterials; - customMaterial = object.customDistanceMaterial; + // TODO Send this event to Three.js DevTools + // console.log( 'WebGLProgram', cacheKey ); - } + const gl = renderer.getContext(); - if ( ! customMaterial ) { + const defines = parameters.defines; - var useMorphing = false; + let vertexShader = parameters.vertexShader; + let fragmentShader = parameters.fragmentShader; - if ( material.morphTargets ) { + const shadowMapTypeDefine = generateShadowMapTypeDefine( parameters ); + const envMapTypeDefine = generateEnvMapTypeDefine( parameters ); + const envMapModeDefine = generateEnvMapModeDefine( parameters ); + const envMapBlendingDefine = generateEnvMapBlendingDefine( parameters ); + const envMapCubeUVSize = generateCubeUVSize( parameters ); - if ( geometry && geometry.isBufferGeometry ) { + const customExtensions = parameters.isWebGL2 ? '' : generateExtensions( parameters ); - useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; + const customDefines = generateDefines( defines ); - } else if ( geometry && geometry.isGeometry ) { + const program = gl.createProgram(); - useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; + let prefixVertex, prefixFragment; + let versionString = parameters.glslVersion ? '#version ' + parameters.glslVersion + '\n' : ''; - } + if ( parameters.isRawShaderMaterial ) { - } + prefixVertex = [ - if ( object.isSkinnedMesh && material.skinning === false ) { + '#define SHADER_TYPE ' + parameters.shaderType, + '#define SHADER_NAME ' + parameters.shaderName, - console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object ); + customDefines - } + ].filter( filterEmptyLine ).join( '\n' ); - var useSkinning = object.isSkinnedMesh && material.skinning; + if ( prefixVertex.length > 0 ) { - var variantIndex = 0; + prefixVertex += '\n'; - if ( useMorphing ) variantIndex |= _MorphingFlag; - if ( useSkinning ) variantIndex |= _SkinningFlag; + } - result = materialVariants[ variantIndex ]; + prefixFragment = [ - } else { + customExtensions, - result = customMaterial; + '#define SHADER_TYPE ' + parameters.shaderType, + '#define SHADER_NAME ' + parameters.shaderName, - } + customDefines - if ( _renderer.localClippingEnabled && - material.clipShadows === true && - material.clippingPlanes.length !== 0 ) { + ].filter( filterEmptyLine ).join( '\n' ); - // in this case we need a unique material instance reflecting the - // appropriate state + if ( prefixFragment.length > 0 ) { - var keyA = result.uuid, keyB = material.uuid; + prefixFragment += '\n'; - var materialsForVariant = _materialCache[ keyA ]; + } - if ( materialsForVariant === undefined ) { + } else { - materialsForVariant = {}; - _materialCache[ keyA ] = materialsForVariant; + prefixVertex = [ - } + generatePrecision( parameters ), - var cachedMaterial = materialsForVariant[ keyB ]; + '#define SHADER_TYPE ' + parameters.shaderType, + '#define SHADER_NAME ' + parameters.shaderName, - if ( cachedMaterial === undefined ) { + customDefines, - cachedMaterial = result.clone(); - materialsForVariant[ keyB ] = cachedMaterial; + parameters.instancing ? '#define USE_INSTANCING' : '', + parameters.instancingColor ? '#define USE_INSTANCING_COLOR' : '', - } + parameters.useFog && parameters.fog ? '#define USE_FOG' : '', + parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', - result = cachedMaterial; + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '', + parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '', + parameters.displacementMap ? '#define USE_DISPLACEMENTMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - } + parameters.anisotropy ? '#define USE_ANISOTROPY' : '', + parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '', - result.visible = material.visible; - result.wireframe = material.wireframe; + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - if ( type === VSMShadowMap ) { + parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '', + parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '', - result.side = ( material.shadowSide != null ) ? material.shadowSide : material.side; + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '', - } else { + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.alphaHash ? '#define USE_ALPHAHASH' : '', - result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ]; + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - } + parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '', - result.clipShadows = material.clipShadows; - result.clippingPlanes = material.clippingPlanes; - result.clipIntersection = material.clipIntersection; + // - result.wireframeLinewidth = material.wireframeLinewidth; - result.linewidth = material.linewidth; + parameters.mapUv ? '#define MAP_UV ' + parameters.mapUv : '', + parameters.alphaMapUv ? '#define ALPHAMAP_UV ' + parameters.alphaMapUv : '', + parameters.lightMapUv ? '#define LIGHTMAP_UV ' + parameters.lightMapUv : '', + parameters.aoMapUv ? '#define AOMAP_UV ' + parameters.aoMapUv : '', + parameters.emissiveMapUv ? '#define EMISSIVEMAP_UV ' + parameters.emissiveMapUv : '', + parameters.bumpMapUv ? '#define BUMPMAP_UV ' + parameters.bumpMapUv : '', + parameters.normalMapUv ? '#define NORMALMAP_UV ' + parameters.normalMapUv : '', + parameters.displacementMapUv ? '#define DISPLACEMENTMAP_UV ' + parameters.displacementMapUv : '', - if ( light.isPointLight && result.isMeshDistanceMaterial ) { + parameters.metalnessMapUv ? '#define METALNESSMAP_UV ' + parameters.metalnessMapUv : '', + parameters.roughnessMapUv ? '#define ROUGHNESSMAP_UV ' + parameters.roughnessMapUv : '', - result.referencePosition.setFromMatrixPosition( light.matrixWorld ); - result.nearDistance = shadowCameraNear; - result.farDistance = shadowCameraFar; + parameters.anisotropyMapUv ? '#define ANISOTROPYMAP_UV ' + parameters.anisotropyMapUv : '', - } + parameters.clearcoatMapUv ? '#define CLEARCOATMAP_UV ' + parameters.clearcoatMapUv : '', + parameters.clearcoatNormalMapUv ? '#define CLEARCOAT_NORMALMAP_UV ' + parameters.clearcoatNormalMapUv : '', + parameters.clearcoatRoughnessMapUv ? '#define CLEARCOAT_ROUGHNESSMAP_UV ' + parameters.clearcoatRoughnessMapUv : '', - return result; + parameters.iridescenceMapUv ? '#define IRIDESCENCEMAP_UV ' + parameters.iridescenceMapUv : '', + parameters.iridescenceThicknessMapUv ? '#define IRIDESCENCE_THICKNESSMAP_UV ' + parameters.iridescenceThicknessMapUv : '', - } + parameters.sheenColorMapUv ? '#define SHEEN_COLORMAP_UV ' + parameters.sheenColorMapUv : '', + parameters.sheenRoughnessMapUv ? '#define SHEEN_ROUGHNESSMAP_UV ' + parameters.sheenRoughnessMapUv : '', - function renderObject( object, camera, shadowCamera, light, type ) { + parameters.specularMapUv ? '#define SPECULARMAP_UV ' + parameters.specularMapUv : '', + parameters.specularColorMapUv ? '#define SPECULAR_COLORMAP_UV ' + parameters.specularColorMapUv : '', + parameters.specularIntensityMapUv ? '#define SPECULAR_INTENSITYMAP_UV ' + parameters.specularIntensityMapUv : '', - if ( object.visible === false ) return; + parameters.transmissionMapUv ? '#define TRANSMISSIONMAP_UV ' + parameters.transmissionMapUv : '', + parameters.thicknessMapUv ? '#define THICKNESSMAP_UV ' + parameters.thicknessMapUv : '', - var visible = object.layers.test( camera.layers ); + // - if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { + parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUv1s ? '#define USE_UV1' : '', + parameters.vertexUv2s ? '#define USE_UV2' : '', + parameters.vertexUv3s ? '#define USE_UV3' : '', - if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { + parameters.pointsUvs ? '#define USE_POINTS_UV' : '', - object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + parameters.flatShading ? '#define FLAT_SHADED' : '', - var geometry = _objects.update( object ); - var material = object.material; + parameters.skinning ? '#define USE_SKINNING' : '', - if ( Array.isArray( material ) ) { + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + ( parameters.morphColors && parameters.isWebGL2 ) ? '#define USE_MORPHCOLORS' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE' : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_TEXTURE_STRIDE ' + parameters.morphTextureStride : '', + ( parameters.morphTargetsCount > 0 && parameters.isWebGL2 ) ? '#define MORPHTARGETS_COUNT ' + parameters.morphTargetsCount : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - var groups = geometry.groups; + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - for ( var k = 0, kl = groups.length; k < kl; k ++ ) { + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - var group = groups[ k ]; - var groupMaterial = material[ group.materialIndex ]; + parameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '', - if ( groupMaterial && groupMaterial.visible ) { + parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '', - var depthMaterial = getDepthMaterial( object, groupMaterial, light, shadowCamera.near, shadowCamera.far, type ); + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - } + '#ifdef USE_INSTANCING', - } + ' attribute mat4 instanceMatrix;', - } else if ( material.visible ) { + '#endif', - var depthMaterial = getDepthMaterial( object, material, light, shadowCamera.near, shadowCamera.far, type ); + '#ifdef USE_INSTANCING_COLOR', - _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); + ' attribute vec3 instanceColor;', - } + '#endif', - } + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - } + '#ifdef USE_UV1', - var children = object.children; + ' attribute vec2 uv1;', - for ( var i = 0, l = children.length; i < l; i ++ ) { + '#endif', - renderObject( children[ i ], camera, shadowCamera, light, type ); + '#ifdef USE_UV2', - } + ' attribute vec2 uv2;', - } + '#endif', - } + '#ifdef USE_UV3', - /** - * @author mrdoob / http://mrdoob.com/ - */ + ' attribute vec2 uv3;', - function WebGLState( gl, extensions, utils, capabilities ) { + '#endif', - function ColorBuffer() { + '#ifdef USE_TANGENT', - var locked = false; + ' attribute vec4 tangent;', - var color = new Vector4(); - var currentColorMask = null; - var currentColorClear = new Vector4( 0, 0, 0, 0 ); + '#endif', - return { + '#if defined( USE_COLOR_ALPHA )', - setMask: function ( colorMask ) { + ' attribute vec4 color;', - if ( currentColorMask !== colorMask && ! locked ) { + '#elif defined( USE_COLOR )', - gl.colorMask( colorMask, colorMask, colorMask, colorMask ); - currentColorMask = colorMask; + ' attribute vec3 color;', - } + '#endif', - }, + '#if ( defined( USE_MORPHTARGETS ) && ! defined( MORPHTARGETS_TEXTURE ) )', - setLocked: function ( lock ) { + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - locked = lock; + ' #ifdef USE_MORPHNORMALS', - }, + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - setClear: function ( r, g, b, a, premultipliedAlpha ) { + ' #else', - if ( premultipliedAlpha === true ) { + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - r *= a; g *= a; b *= a; + ' #endif', - } + '#endif', - color.set( r, g, b, a ); + '#ifdef USE_SKINNING', - if ( currentColorClear.equals( color ) === false ) { + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - gl.clearColor( r, g, b, a ); - currentColorClear.copy( color ); + '#endif', - } + '\n' - }, + ].filter( filterEmptyLine ).join( '\n' ); - reset: function () { + prefixFragment = [ - locked = false; + customExtensions, - currentColorMask = null; - currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state + generatePrecision( parameters ), - } + '#define SHADER_TYPE ' + parameters.shaderType, + '#define SHADER_NAME ' + parameters.shaderName, - }; + customDefines, - } + parameters.useFog && parameters.fog ? '#define USE_FOG' : '', + parameters.useFog && parameters.fogExp2 ? '#define FOG_EXP2' : '', - function DepthBuffer() { + parameters.map ? '#define USE_MAP' : '', + parameters.matcap ? '#define USE_MATCAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_WIDTH ' + envMapCubeUVSize.texelWidth : '', + envMapCubeUVSize ? '#define CUBEUV_TEXEL_HEIGHT ' + envMapCubeUVSize.texelHeight : '', + envMapCubeUVSize ? '#define CUBEUV_MAX_MIP ' + envMapCubeUVSize.maxMip + '.0' : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.normalMapObjectSpace ? '#define USE_NORMALMAP_OBJECTSPACE' : '', + parameters.normalMapTangentSpace ? '#define USE_NORMALMAP_TANGENTSPACE' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - var locked = false; + parameters.anisotropy ? '#define USE_ANISOTROPY' : '', + parameters.anisotropyMap ? '#define USE_ANISOTROPYMAP' : '', - var currentDepthMask = null; - var currentDepthFunc = null; - var currentDepthClear = null; + parameters.clearcoat ? '#define USE_CLEARCOAT' : '', + parameters.clearcoatMap ? '#define USE_CLEARCOATMAP' : '', + parameters.clearcoatRoughnessMap ? '#define USE_CLEARCOAT_ROUGHNESSMAP' : '', + parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '', - return { + parameters.iridescence ? '#define USE_IRIDESCENCE' : '', + parameters.iridescenceMap ? '#define USE_IRIDESCENCEMAP' : '', + parameters.iridescenceThicknessMap ? '#define USE_IRIDESCENCE_THICKNESSMAP' : '', - setTest: function ( depthTest ) { + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.specularColorMap ? '#define USE_SPECULAR_COLORMAP' : '', + parameters.specularIntensityMap ? '#define USE_SPECULAR_INTENSITYMAP' : '', - if ( depthTest ) { + parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', - enable( 2929 ); + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.alphaTest ? '#define USE_ALPHATEST' : '', + parameters.alphaHash ? '#define USE_ALPHAHASH' : '', - } else { + parameters.sheen ? '#define USE_SHEEN' : '', + parameters.sheenColorMap ? '#define USE_SHEEN_COLORMAP' : '', + parameters.sheenRoughnessMap ? '#define USE_SHEEN_ROUGHNESSMAP' : '', - disable( 2929 ); + parameters.transmission ? '#define USE_TRANSMISSION' : '', + parameters.transmissionMap ? '#define USE_TRANSMISSIONMAP' : '', + parameters.thicknessMap ? '#define USE_THICKNESSMAP' : '', - } + parameters.vertexTangents && parameters.flatShading === false ? '#define USE_TANGENT' : '', + parameters.vertexColors || parameters.instancingColor ? '#define USE_COLOR' : '', + parameters.vertexAlphas ? '#define USE_COLOR_ALPHA' : '', + parameters.vertexUv1s ? '#define USE_UV1' : '', + parameters.vertexUv2s ? '#define USE_UV2' : '', + parameters.vertexUv3s ? '#define USE_UV3' : '', - }, + parameters.pointsUvs ? '#define USE_POINTS_UV' : '', - setMask: function ( depthMask ) { + parameters.gradientMap ? '#define USE_GRADIENTMAP' : '', - if ( currentDepthMask !== depthMask && ! locked ) { + parameters.flatShading ? '#define FLAT_SHADED' : '', - gl.depthMask( depthMask ); - currentDepthMask = depthMask; + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - } + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - }, + parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '', - setFunc: function ( depthFunc ) { + parameters.numLightProbes > 0 ? '#define USE_LIGHT_PROBES' : '', - if ( currentDepthFunc !== depthFunc ) { + parameters.useLegacyLights ? '#define LEGACY_LIGHTS' : '', - if ( depthFunc ) { + parameters.decodeVideoTexture ? '#define DECODE_VIDEO_TEXTURE' : '', - switch ( depthFunc ) { + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + ( parameters.logarithmicDepthBuffer && parameters.rendererExtensionFragDepth ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - case NeverDepth: + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', + 'uniform bool isOrthographic;', - gl.depthFunc( 512 ); - break; + ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '', + ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below + ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '', - case AlwaysDepth: + parameters.dithering ? '#define DITHERING' : '', + parameters.opaque ? '#define OPAQUE' : '', - gl.depthFunc( 519 ); - break; + ShaderChunk[ 'colorspace_pars_fragment' ], // this code is required here because it is used by the various encoding/decoding function defined below + getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputColorSpace ), - case LessDepth: + parameters.useDepthPacking ? '#define DEPTH_PACKING ' + parameters.depthPacking : '', - gl.depthFunc( 513 ); - break; + '\n' - case LessEqualDepth: + ].filter( filterEmptyLine ).join( '\n' ); - gl.depthFunc( 515 ); - break; + } - case EqualDepth: + vertexShader = resolveIncludes( vertexShader ); + vertexShader = replaceLightNums( vertexShader, parameters ); + vertexShader = replaceClippingPlaneNums( vertexShader, parameters ); - gl.depthFunc( 514 ); - break; + fragmentShader = resolveIncludes( fragmentShader ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); + fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters ); - case GreaterEqualDepth: + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); - gl.depthFunc( 518 ); - break; + if ( parameters.isWebGL2 && parameters.isRawShaderMaterial !== true ) { - case GreaterDepth: + // GLSL 3.0 conversion for built-in materials and ShaderMaterial - gl.depthFunc( 516 ); - break; + versionString = '#version 300 es\n'; - case NotEqualDepth: + prefixVertex = [ + 'precision mediump sampler2DArray;', + '#define attribute in', + '#define varying out', + '#define texture2D texture' + ].join( '\n' ) + '\n' + prefixVertex; - gl.depthFunc( 517 ); - break; + prefixFragment = [ + '#define varying in', + ( parameters.glslVersion === GLSL3 ) ? '' : 'layout(location = 0) out highp vec4 pc_fragColor;', + ( parameters.glslVersion === GLSL3 ) ? '' : '#define gl_FragColor pc_fragColor', + '#define gl_FragDepthEXT gl_FragDepth', + '#define texture2D texture', + '#define textureCube texture', + '#define texture2DProj textureProj', + '#define texture2DLodEXT textureLod', + '#define texture2DProjLodEXT textureProjLod', + '#define textureCubeLodEXT textureLod', + '#define texture2DGradEXT textureGrad', + '#define texture2DProjGradEXT textureProjGrad', + '#define textureCubeGradEXT textureGrad' + ].join( '\n' ) + '\n' + prefixFragment; - default: + } - gl.depthFunc( 515 ); + const vertexGlsl = versionString + prefixVertex + vertexShader; + const fragmentGlsl = versionString + prefixFragment + fragmentShader; - } + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); - } else { + const glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + const glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); - gl.depthFunc( 515 ); + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); - } + // Force a particular attribute to index 0. - currentDepthFunc = depthFunc; + if ( parameters.index0AttributeName !== undefined ) { - } + gl.bindAttribLocation( program, 0, parameters.index0AttributeName ); - }, + } else if ( parameters.morphTargets === true ) { - setLocked: function ( lock ) { + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); - locked = lock; + } - }, + gl.linkProgram( program ); - setClear: function ( depth ) { + // check for link errors + if ( renderer.debug.checkShaderErrors ) { - if ( currentDepthClear !== depth ) { + const programLog = gl.getProgramInfoLog( program ).trim(); + const vertexLog = gl.getShaderInfoLog( glVertexShader ).trim(); + const fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim(); - gl.clearDepth( depth ); - currentDepthClear = depth; + let runnable = true; + let haveDiagnostics = true; - } + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { - }, + runnable = false; - reset: function () { + if ( typeof renderer.debug.onShaderError === 'function' ) { - locked = false; + renderer.debug.onShaderError( gl, program, glVertexShader, glFragmentShader ); - currentDepthMask = null; - currentDepthFunc = null; - currentDepthClear = null; + } else { - } + // default error reporting - }; + const vertexErrors = getShaderErrors( gl, glVertexShader, 'vertex' ); + const fragmentErrors = getShaderErrors( gl, glFragmentShader, 'fragment' ); - } + console.error( + 'THREE.WebGLProgram: Shader Error ' + gl.getError() + ' - ' + + 'VALIDATE_STATUS ' + gl.getProgramParameter( program, gl.VALIDATE_STATUS ) + '\n\n' + + 'Program Info Log: ' + programLog + '\n' + + vertexErrors + '\n' + + fragmentErrors + ); - function StencilBuffer() { + } - var locked = false; + } else if ( programLog !== '' ) { - var currentStencilMask = null; - var currentStencilFunc = null; - var currentStencilRef = null; - var currentStencilFuncMask = null; - var currentStencilFail = null; - var currentStencilZFail = null; - var currentStencilZPass = null; - var currentStencilClear = null; + console.warn( 'THREE.WebGLProgram: Program Info Log:', programLog ); - return { + } else if ( vertexLog === '' || fragmentLog === '' ) { - setTest: function ( stencilTest ) { + haveDiagnostics = false; - if ( ! locked ) { + } - if ( stencilTest ) { + if ( haveDiagnostics ) { - enable( 2960 ); + this.diagnostics = { - } else { + runnable: runnable, - disable( 2960 ); + programLog: programLog, - } + vertexShader: { - } + log: vertexLog, + prefix: prefixVertex }, - setMask: function ( stencilMask ) { + fragmentShader: { - if ( currentStencilMask !== stencilMask && ! locked ) { + log: fragmentLog, + prefix: prefixFragment - gl.stencilMask( stencilMask ); - currentStencilMask = stencilMask; + } - } + }; - }, + } - setFunc: function ( stencilFunc, stencilRef, stencilMask ) { + } - if ( currentStencilFunc !== stencilFunc || - currentStencilRef !== stencilRef || - currentStencilFuncMask !== stencilMask ) { + // Clean up - gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); + // Crashes in iOS9 and iOS10. #18402 + // gl.detachShader( program, glVertexShader ); + // gl.detachShader( program, glFragmentShader ); - currentStencilFunc = stencilFunc; - currentStencilRef = stencilRef; - currentStencilFuncMask = stencilMask; + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - } + // set up caching for uniform locations - }, + let cachedUniforms; - setOp: function ( stencilFail, stencilZFail, stencilZPass ) { + this.getUniforms = function () { - if ( currentStencilFail !== stencilFail || - currentStencilZFail !== stencilZFail || - currentStencilZPass !== stencilZPass ) { + if ( cachedUniforms === undefined ) { - gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); + cachedUniforms = new WebGLUniforms( gl, program ); - currentStencilFail = stencilFail; - currentStencilZFail = stencilZFail; - currentStencilZPass = stencilZPass; + } - } + return cachedUniforms; - }, + }; - setLocked: function ( lock ) { + // set up caching for attribute locations - locked = lock; + let cachedAttributes; - }, + this.getAttributes = function () { - setClear: function ( stencil ) { + if ( cachedAttributes === undefined ) { - if ( currentStencilClear !== stencil ) { + cachedAttributes = fetchAttributeLocations( gl, program ); - gl.clearStencil( stencil ); - currentStencilClear = stencil; + } - } + return cachedAttributes; - }, + }; - reset: function () { + // free resource - locked = false; + this.destroy = function () { - currentStencilMask = null; - currentStencilFunc = null; - currentStencilRef = null; - currentStencilFuncMask = null; - currentStencilFail = null; - currentStencilZFail = null; - currentStencilZPass = null; - currentStencilClear = null; + bindingStates.releaseStatesOfProgram( this ); - } + gl.deleteProgram( program ); + this.program = undefined; - }; + }; - } - - // - - var colorBuffer = new ColorBuffer(); - var depthBuffer = new DepthBuffer(); - var stencilBuffer = new StencilBuffer(); + // - var maxVertexAttributes = gl.getParameter( 34921 ); - var newAttributes = new Uint8Array( maxVertexAttributes ); - var enabledAttributes = new Uint8Array( maxVertexAttributes ); - var attributeDivisors = new Uint8Array( maxVertexAttributes ); + this.type = parameters.shaderType; + this.name = parameters.shaderName; + this.id = programIdCount ++; + this.cacheKey = cacheKey; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - var enabledCapabilities = {}; + return this; - var compressedTextureFormats = null; +} - var currentProgram = null; +let _id$1 = 0; - var currentBlendingEnabled = null; - var currentBlending = null; - var currentBlendEquation = null; - var currentBlendSrc = null; - var currentBlendDst = null; - var currentBlendEquationAlpha = null; - var currentBlendSrcAlpha = null; - var currentBlendDstAlpha = null; - var currentPremultipledAlpha = false; +class WebGLShaderCache { - var currentFlipSided = null; - var currentCullFace = null; + constructor() { - var currentLineWidth = null; + this.shaderCache = new Map(); + this.materialCache = new Map(); - var currentPolygonOffsetFactor = null; - var currentPolygonOffsetUnits = null; + } - var maxTextures = gl.getParameter( 35661 ); + update( material ) { - var lineWidthAvailable = false; - var version = 0; - var glVersion = gl.getParameter( 7938 ); + const vertexShader = material.vertexShader; + const fragmentShader = material.fragmentShader; - if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { + const vertexShaderStage = this._getShaderStage( vertexShader ); + const fragmentShaderStage = this._getShaderStage( fragmentShader ); - version = parseFloat( /^WebGL\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 1.0 ); + const materialShaders = this._getShaderCacheForMaterial( material ); - } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { + if ( materialShaders.has( vertexShaderStage ) === false ) { - version = parseFloat( /^OpenGL\ ES\ ([0-9])/.exec( glVersion )[ 1 ] ); - lineWidthAvailable = ( version >= 2.0 ); + materialShaders.add( vertexShaderStage ); + vertexShaderStage.usedTimes ++; } - var currentTextureSlot = null; - var currentBoundTextures = {}; + if ( materialShaders.has( fragmentShaderStage ) === false ) { + + materialShaders.add( fragmentShaderStage ); + fragmentShaderStage.usedTimes ++; - var currentScissor = new Vector4(); - var currentViewport = new Vector4(); + } - function createTexture( type, target, count ) { + return this; - var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. - var texture = gl.createTexture(); + } - gl.bindTexture( type, texture ); - gl.texParameteri( type, 10241, 9728 ); - gl.texParameteri( type, 10240, 9728 ); + remove( material ) { - for ( var i = 0; i < count; i ++ ) { + const materialShaders = this.materialCache.get( material ); - gl.texImage2D( target + i, 0, 6408, 1, 1, 0, 6408, 5121, data ); + for ( const shaderStage of materialShaders ) { - } + shaderStage.usedTimes --; - return texture; + if ( shaderStage.usedTimes === 0 ) this.shaderCache.delete( shaderStage.code ); } - var emptyTextures = {}; - emptyTextures[ 3553 ] = createTexture( 3553, 3553, 1 ); - emptyTextures[ 34067 ] = createTexture( 34067, 34069, 6 ); + this.materialCache.delete( material ); - // init - - colorBuffer.setClear( 0, 0, 0, 1 ); - depthBuffer.setClear( 1 ); - stencilBuffer.setClear( 0 ); + return this; - enable( 2929 ); - depthBuffer.setFunc( LessEqualDepth ); + } - setFlipSided( false ); - setCullFace( CullFaceBack ); - enable( 2884 ); + getVertexShaderID( material ) { - setBlending( NoBlending ); + return this._getShaderStage( material.vertexShader ).id; - // + } - function initAttributes() { + getFragmentShaderID( material ) { - for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { + return this._getShaderStage( material.fragmentShader ).id; - newAttributes[ i ] = 0; + } - } + dispose() { - } + this.shaderCache.clear(); + this.materialCache.clear(); - function enableAttribute( attribute ) { + } - enableAttributeAndDivisor( attribute, 0 ); + _getShaderCacheForMaterial( material ) { - } + const cache = this.materialCache; + let set = cache.get( material ); - function enableAttributeAndDivisor( attribute, meshPerAttribute ) { + if ( set === undefined ) { - newAttributes[ attribute ] = 1; + set = new Set(); + cache.set( material, set ); - if ( enabledAttributes[ attribute ] === 0 ) { + } - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; + return set; - } + } - if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { + _getShaderStage( code ) { - var extension = capabilities.isWebGL2 ? gl : extensions.get( 'ANGLE_instanced_arrays' ); + const cache = this.shaderCache; + let stage = cache.get( code ); - extension[ capabilities.isWebGL2 ? 'vertexAttribDivisor' : 'vertexAttribDivisorANGLE' ]( attribute, meshPerAttribute ); - attributeDivisors[ attribute ] = meshPerAttribute; + if ( stage === undefined ) { - } + stage = new WebGLShaderStage( code ); + cache.set( code, stage ); } - function disableUnusedAttributes() { + return stage; - for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { + } - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { +} - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; +class WebGLShaderStage { - } + constructor( code ) { - } + this.id = _id$1 ++; - } + this.code = code; + this.usedTimes = 0; - function enable( id ) { + } - if ( enabledCapabilities[ id ] !== true ) { +} - gl.enable( id ); - enabledCapabilities[ id ] = true; +function WebGLPrograms( renderer, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ) { - } + const _programLayers = new Layers(); + const _customShaders = new WebGLShaderCache(); + const programs = []; - } + const IS_WEBGL2 = capabilities.isWebGL2; + const logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; + const SUPPORTS_VERTEX_TEXTURES = capabilities.vertexTextures; - function disable( id ) { + let precision = capabilities.precision; - if ( enabledCapabilities[ id ] !== false ) { + const shaderIDs = { + MeshDepthMaterial: 'depth', + MeshDistanceMaterial: 'distanceRGBA', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + MeshToonMaterial: 'toon', + MeshStandardMaterial: 'physical', + MeshPhysicalMaterial: 'physical', + MeshMatcapMaterial: 'matcap', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points', + ShadowMaterial: 'shadow', + SpriteMaterial: 'sprite' + }; - gl.disable( id ); - enabledCapabilities[ id ] = false; + function getChannel( value ) { - } + if ( value === 0 ) return 'uv'; - } + return `uv${ value }`; + + } - function getCompressedTextureFormats() { + function getParameters( material, lights, shadows, scene, object ) { - if ( compressedTextureFormats === null ) { + const fog = scene.fog; + const geometry = object.geometry; + const environment = material.isMeshStandardMaterial ? scene.environment : null; - compressedTextureFormats = []; + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const envMapCubeUVHeight = ( !! envMap ) && ( envMap.mapping === CubeUVReflectionMapping ) ? envMap.image.height : null; - if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || - extensions.get( 'WEBGL_compressed_texture_s3tc' ) || - extensions.get( 'WEBGL_compressed_texture_etc1' ) || - extensions.get( 'WEBGL_compressed_texture_astc' ) ) { + const shaderID = shaderIDs[ material.type ]; - var formats = gl.getParameter( 34467 ); + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) - for ( var i = 0; i < formats.length; i ++ ) { + if ( material.precision !== null ) { - compressedTextureFormats.push( formats[ i ] ); + precision = capabilities.getMaxPrecision( material.precision ); - } + if ( precision !== material.precision ) { - } + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); } - return compressedTextureFormats; - } - function useProgram( program ) { + // - if ( currentProgram !== program ) { + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - gl.useProgram( program ); + let morphTextureStride = 0; - currentProgram = program; + if ( geometry.morphAttributes.position !== undefined ) morphTextureStride = 1; + if ( geometry.morphAttributes.normal !== undefined ) morphTextureStride = 2; + if ( geometry.morphAttributes.color !== undefined ) morphTextureStride = 3; - return true; + // - } + let vertexShader, fragmentShader; + let customVertexShaderID, customFragmentShaderID; - return false; + if ( shaderID ) { - } + const shader = ShaderLib[ shaderID ]; - function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { + vertexShader = shader.vertexShader; + fragmentShader = shader.fragmentShader; - if ( blending === NoBlending ) { + } else { - if ( currentBlendingEnabled ) { + vertexShader = material.vertexShader; + fragmentShader = material.fragmentShader; - disable( 3042 ); - currentBlendingEnabled = false; + _customShaders.update( material ); - } + customVertexShaderID = _customShaders.getVertexShaderID( material ); + customFragmentShaderID = _customShaders.getFragmentShaderID( material ); - return; + } - } + const currentRenderTarget = renderer.getRenderTarget(); - if ( ! currentBlendingEnabled ) { + const IS_INSTANCEDMESH = object.isInstancedMesh === true; - enable( 3042 ); - currentBlendingEnabled = true; + const HAS_MAP = !! material.map; + const HAS_MATCAP = !! material.matcap; + const HAS_ENVMAP = !! envMap; + const HAS_AOMAP = !! material.aoMap; + const HAS_LIGHTMAP = !! material.lightMap; + const HAS_BUMPMAP = !! material.bumpMap; + const HAS_NORMALMAP = !! material.normalMap; + const HAS_DISPLACEMENTMAP = !! material.displacementMap; + const HAS_EMISSIVEMAP = !! material.emissiveMap; - } + const HAS_METALNESSMAP = !! material.metalnessMap; + const HAS_ROUGHNESSMAP = !! material.roughnessMap; - if ( blending !== CustomBlending ) { + const HAS_ANISOTROPY = material.anisotropy > 0; + const HAS_CLEARCOAT = material.clearcoat > 0; + const HAS_IRIDESCENCE = material.iridescence > 0; + const HAS_SHEEN = material.sheen > 0; + const HAS_TRANSMISSION = material.transmission > 0; - if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + const HAS_ANISOTROPYMAP = HAS_ANISOTROPY && !! material.anisotropyMap; - if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { + const HAS_CLEARCOATMAP = HAS_CLEARCOAT && !! material.clearcoatMap; + const HAS_CLEARCOAT_NORMALMAP = HAS_CLEARCOAT && !! material.clearcoatNormalMap; + const HAS_CLEARCOAT_ROUGHNESSMAP = HAS_CLEARCOAT && !! material.clearcoatRoughnessMap; - gl.blendEquation( 32774 ); + const HAS_IRIDESCENCEMAP = HAS_IRIDESCENCE && !! material.iridescenceMap; + const HAS_IRIDESCENCE_THICKNESSMAP = HAS_IRIDESCENCE && !! material.iridescenceThicknessMap; - currentBlendEquation = AddEquation; - currentBlendEquationAlpha = AddEquation; + const HAS_SHEEN_COLORMAP = HAS_SHEEN && !! material.sheenColorMap; + const HAS_SHEEN_ROUGHNESSMAP = HAS_SHEEN && !! material.sheenRoughnessMap; - } + const HAS_SPECULARMAP = !! material.specularMap; + const HAS_SPECULAR_COLORMAP = !! material.specularColorMap; + const HAS_SPECULAR_INTENSITYMAP = !! material.specularIntensityMap; - if ( premultipliedAlpha ) { + const HAS_TRANSMISSIONMAP = HAS_TRANSMISSION && !! material.transmissionMap; + const HAS_THICKNESSMAP = HAS_TRANSMISSION && !! material.thicknessMap; - switch ( blending ) { + const HAS_GRADIENTMAP = !! material.gradientMap; - case NormalBlending: - gl.blendFuncSeparate( 1, 771, 1, 771 ); - break; + const HAS_ALPHAMAP = !! material.alphaMap; - case AdditiveBlending: - gl.blendFunc( 1, 1 ); - break; + const HAS_ALPHATEST = material.alphaTest > 0; - case SubtractiveBlending: - gl.blendFuncSeparate( 0, 0, 769, 771 ); - break; + const HAS_ALPHAHASH = !! material.alphaHash; - case MultiplyBlending: - gl.blendFuncSeparate( 0, 768, 0, 770 ); - break; + const HAS_EXTENSIONS = !! material.extensions; - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + const HAS_ATTRIBUTE_UV1 = !! geometry.attributes.uv1; + const HAS_ATTRIBUTE_UV2 = !! geometry.attributes.uv2; + const HAS_ATTRIBUTE_UV3 = !! geometry.attributes.uv3; - } + let toneMapping = NoToneMapping; - } else { + if ( material.toneMapped ) { - switch ( blending ) { + if ( currentRenderTarget === null || currentRenderTarget.isXRRenderTarget === true ) { - case NormalBlending: - gl.blendFuncSeparate( 770, 771, 1, 771 ); - break; + toneMapping = renderer.toneMapping; - case AdditiveBlending: - gl.blendFunc( 770, 1 ); - break; + } - case SubtractiveBlending: - gl.blendFunc( 0, 769 ); - break; + } - case MultiplyBlending: - gl.blendFunc( 0, 768 ); - break; + const parameters = { - default: - console.error( 'THREE.WebGLState: Invalid blending: ', blending ); - break; + isWebGL2: IS_WEBGL2, - } + shaderID: shaderID, + shaderType: material.type, + shaderName: material.name, - } + vertexShader: vertexShader, + fragmentShader: fragmentShader, + defines: material.defines, - currentBlendSrc = null; - currentBlendDst = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; + customVertexShaderID: customVertexShaderID, + customFragmentShaderID: customFragmentShaderID, - currentBlending = blending; - currentPremultipledAlpha = premultipliedAlpha; + isRawShaderMaterial: material.isRawShaderMaterial === true, + glslVersion: material.glslVersion, - } + precision: precision, - return; + instancing: IS_INSTANCEDMESH, + instancingColor: IS_INSTANCEDMESH && object.instanceColor !== null, - } + supportsVertexTextures: SUPPORTS_VERTEX_TEXTURES, + outputColorSpace: ( currentRenderTarget === null ) ? renderer.outputColorSpace : ( currentRenderTarget.isXRRenderTarget === true ? currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ), - // custom blending + map: HAS_MAP, + matcap: HAS_MATCAP, + envMap: HAS_ENVMAP, + envMapMode: HAS_ENVMAP && envMap.mapping, + envMapCubeUVHeight: envMapCubeUVHeight, + aoMap: HAS_AOMAP, + lightMap: HAS_LIGHTMAP, + bumpMap: HAS_BUMPMAP, + normalMap: HAS_NORMALMAP, + displacementMap: SUPPORTS_VERTEX_TEXTURES && HAS_DISPLACEMENTMAP, + emissiveMap: HAS_EMISSIVEMAP, - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + normalMapObjectSpace: HAS_NORMALMAP && material.normalMapType === ObjectSpaceNormalMap, + normalMapTangentSpace: HAS_NORMALMAP && material.normalMapType === TangentSpaceNormalMap, - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + metalnessMap: HAS_METALNESSMAP, + roughnessMap: HAS_ROUGHNESSMAP, - gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) ); + anisotropy: HAS_ANISOTROPY, + anisotropyMap: HAS_ANISOTROPYMAP, - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + clearcoat: HAS_CLEARCOAT, + clearcoatMap: HAS_CLEARCOATMAP, + clearcoatNormalMap: HAS_CLEARCOAT_NORMALMAP, + clearcoatRoughnessMap: HAS_CLEARCOAT_ROUGHNESSMAP, - } + iridescence: HAS_IRIDESCENCE, + iridescenceMap: HAS_IRIDESCENCEMAP, + iridescenceThicknessMap: HAS_IRIDESCENCE_THICKNESSMAP, - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + sheen: HAS_SHEEN, + sheenColorMap: HAS_SHEEN_COLORMAP, + sheenRoughnessMap: HAS_SHEEN_ROUGHNESSMAP, - gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) ); + specularMap: HAS_SPECULARMAP, + specularColorMap: HAS_SPECULAR_COLORMAP, + specularIntensityMap: HAS_SPECULAR_INTENSITYMAP, - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + transmission: HAS_TRANSMISSION, + transmissionMap: HAS_TRANSMISSIONMAP, + thicknessMap: HAS_THICKNESSMAP, - } + gradientMap: HAS_GRADIENTMAP, - currentBlending = blending; - currentPremultipledAlpha = null; + opaque: material.transparent === false && material.blending === NormalBlending, - } + alphaMap: HAS_ALPHAMAP, + alphaTest: HAS_ALPHATEST, + alphaHash: HAS_ALPHAHASH, - function setMaterial( material, frontFaceCW ) { + combine: material.combine, - material.side === DoubleSide - ? disable( 2884 ) - : enable( 2884 ); + // - var flipSided = ( material.side === BackSide ); - if ( frontFaceCW ) flipSided = ! flipSided; + mapUv: HAS_MAP && getChannel( material.map.channel ), + aoMapUv: HAS_AOMAP && getChannel( material.aoMap.channel ), + lightMapUv: HAS_LIGHTMAP && getChannel( material.lightMap.channel ), + bumpMapUv: HAS_BUMPMAP && getChannel( material.bumpMap.channel ), + normalMapUv: HAS_NORMALMAP && getChannel( material.normalMap.channel ), + displacementMapUv: HAS_DISPLACEMENTMAP && getChannel( material.displacementMap.channel ), + emissiveMapUv: HAS_EMISSIVEMAP && getChannel( material.emissiveMap.channel ), - setFlipSided( flipSided ); + metalnessMapUv: HAS_METALNESSMAP && getChannel( material.metalnessMap.channel ), + roughnessMapUv: HAS_ROUGHNESSMAP && getChannel( material.roughnessMap.channel ), - ( material.blending === NormalBlending && material.transparent === false ) - ? setBlending( NoBlending ) - : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); + anisotropyMapUv: HAS_ANISOTROPYMAP && getChannel( material.anisotropyMap.channel ), - depthBuffer.setFunc( material.depthFunc ); - depthBuffer.setTest( material.depthTest ); - depthBuffer.setMask( material.depthWrite ); - colorBuffer.setMask( material.colorWrite ); + clearcoatMapUv: HAS_CLEARCOATMAP && getChannel( material.clearcoatMap.channel ), + clearcoatNormalMapUv: HAS_CLEARCOAT_NORMALMAP && getChannel( material.clearcoatNormalMap.channel ), + clearcoatRoughnessMapUv: HAS_CLEARCOAT_ROUGHNESSMAP && getChannel( material.clearcoatRoughnessMap.channel ), - var stencilWrite = material.stencilWrite; - stencilBuffer.setTest( stencilWrite ); - if ( stencilWrite ) { + iridescenceMapUv: HAS_IRIDESCENCEMAP && getChannel( material.iridescenceMap.channel ), + iridescenceThicknessMapUv: HAS_IRIDESCENCE_THICKNESSMAP && getChannel( material.iridescenceThicknessMap.channel ), - stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilMask ); - stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); + sheenColorMapUv: HAS_SHEEN_COLORMAP && getChannel( material.sheenColorMap.channel ), + sheenRoughnessMapUv: HAS_SHEEN_ROUGHNESSMAP && getChannel( material.sheenRoughnessMap.channel ), - } + specularMapUv: HAS_SPECULARMAP && getChannel( material.specularMap.channel ), + specularColorMapUv: HAS_SPECULAR_COLORMAP && getChannel( material.specularColorMap.channel ), + specularIntensityMapUv: HAS_SPECULAR_INTENSITYMAP && getChannel( material.specularIntensityMap.channel ), - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + transmissionMapUv: HAS_TRANSMISSIONMAP && getChannel( material.transmissionMap.channel ), + thicknessMapUv: HAS_THICKNESSMAP && getChannel( material.thicknessMap.channel ), - } + alphaMapUv: HAS_ALPHAMAP && getChannel( material.alphaMap.channel ), - // + // - function setFlipSided( flipSided ) { + vertexTangents: !! geometry.attributes.tangent && ( HAS_NORMALMAP || HAS_ANISOTROPY ), + vertexColors: material.vertexColors, + vertexAlphas: material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4, + vertexUv1s: HAS_ATTRIBUTE_UV1, + vertexUv2s: HAS_ATTRIBUTE_UV2, + vertexUv3s: HAS_ATTRIBUTE_UV3, - if ( currentFlipSided !== flipSided ) { + pointsUvs: object.isPoints === true && !! geometry.attributes.uv && ( HAS_MAP || HAS_ALPHAMAP ), - if ( flipSided ) { + fog: !! fog, + useFog: material.fog === true, + fogExp2: ( fog && fog.isFogExp2 ), - gl.frontFace( 2304 ); + flatShading: material.flatShading === true, - } else { + sizeAttenuation: material.sizeAttenuation === true, + logarithmicDepthBuffer: logarithmicDepthBuffer, - gl.frontFace( 2305 ); + skinning: object.isSkinnedMesh === true, - } + morphTargets: geometry.morphAttributes.position !== undefined, + morphNormals: geometry.morphAttributes.normal !== undefined, + morphColors: geometry.morphAttributes.color !== undefined, + morphTargetsCount: morphTargetsCount, + morphTextureStride: morphTextureStride, - currentFlipSided = flipSided; + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numSpotLightMaps: lights.spotLightMap.length, + numRectAreaLights: lights.rectArea.length, + numHemiLights: lights.hemi.length, - } + numDirLightShadows: lights.directionalShadowMap.length, + numPointLightShadows: lights.pointShadowMap.length, + numSpotLightShadows: lights.spotShadowMap.length, + numSpotLightShadowsWithMaps: lights.numSpotLightShadowsWithMaps, - } + numLightProbes: lights.numLightProbes, - function setCullFace( cullFace ) { + numClippingPlanes: clipping.numPlanes, + numClipIntersection: clipping.numIntersection, - if ( cullFace !== CullFaceNone ) { + dithering: material.dithering, - enable( 2884 ); + shadowMapEnabled: renderer.shadowMap.enabled && shadows.length > 0, + shadowMapType: renderer.shadowMap.type, - if ( cullFace !== currentCullFace ) { + toneMapping: toneMapping, + useLegacyLights: renderer._useLegacyLights, - if ( cullFace === CullFaceBack ) { + decodeVideoTexture: HAS_MAP && ( material.map.isVideoTexture === true ) && ( ColorManagement.getTransfer( material.map.colorSpace ) === SRGBTransfer ), - gl.cullFace( 1029 ); + premultipliedAlpha: material.premultipliedAlpha, - } else if ( cullFace === CullFaceFront ) { + doubleSided: material.side === DoubleSide, + flipSided: material.side === BackSide, - gl.cullFace( 1028 ); + useDepthPacking: material.depthPacking >= 0, + depthPacking: material.depthPacking || 0, - } else { + index0AttributeName: material.index0AttributeName, - gl.cullFace( 1032 ); + extensionDerivatives: HAS_EXTENSIONS && material.extensions.derivatives === true, + extensionFragDepth: HAS_EXTENSIONS && material.extensions.fragDepth === true, + extensionDrawBuffers: HAS_EXTENSIONS && material.extensions.drawBuffers === true, + extensionShaderTextureLOD: HAS_EXTENSIONS && material.extensions.shaderTextureLOD === true, - } + rendererExtensionFragDepth: IS_WEBGL2 || extensions.has( 'EXT_frag_depth' ), + rendererExtensionDrawBuffers: IS_WEBGL2 || extensions.has( 'WEBGL_draw_buffers' ), + rendererExtensionShaderTextureLod: IS_WEBGL2 || extensions.has( 'EXT_shader_texture_lod' ), - } + customProgramCacheKey: material.customProgramCacheKey() - } else { + }; - disable( 2884 ); + return parameters; - } + } - currentCullFace = cullFace; + function getProgramCacheKey( parameters ) { - } + const array = []; + + if ( parameters.shaderID ) { - function setLineWidth( width ) { + array.push( parameters.shaderID ); - if ( width !== currentLineWidth ) { + } else { - if ( lineWidthAvailable ) gl.lineWidth( width ); + array.push( parameters.customVertexShaderID ); + array.push( parameters.customFragmentShaderID ); + + } + + if ( parameters.defines !== undefined ) { + + for ( const name in parameters.defines ) { + + array.push( name ); + array.push( parameters.defines[ name ] ); + + } + + } + + if ( parameters.isRawShaderMaterial === false ) { + + getProgramCacheKeyParameters( array, parameters ); + getProgramCacheKeyBooleans( array, parameters ); + array.push( renderer.outputColorSpace ); + + } + + array.push( parameters.customProgramCacheKey ); + + return array.join(); + + } + + function getProgramCacheKeyParameters( array, parameters ) { + + array.push( parameters.precision ); + array.push( parameters.outputColorSpace ); + array.push( parameters.envMapMode ); + array.push( parameters.envMapCubeUVHeight ); + array.push( parameters.mapUv ); + array.push( parameters.alphaMapUv ); + array.push( parameters.lightMapUv ); + array.push( parameters.aoMapUv ); + array.push( parameters.bumpMapUv ); + array.push( parameters.normalMapUv ); + array.push( parameters.displacementMapUv ); + array.push( parameters.emissiveMapUv ); + array.push( parameters.metalnessMapUv ); + array.push( parameters.roughnessMapUv ); + array.push( parameters.anisotropyMapUv ); + array.push( parameters.clearcoatMapUv ); + array.push( parameters.clearcoatNormalMapUv ); + array.push( parameters.clearcoatRoughnessMapUv ); + array.push( parameters.iridescenceMapUv ); + array.push( parameters.iridescenceThicknessMapUv ); + array.push( parameters.sheenColorMapUv ); + array.push( parameters.sheenRoughnessMapUv ); + array.push( parameters.specularMapUv ); + array.push( parameters.specularColorMapUv ); + array.push( parameters.specularIntensityMapUv ); + array.push( parameters.transmissionMapUv ); + array.push( parameters.thicknessMapUv ); + array.push( parameters.combine ); + array.push( parameters.fogExp2 ); + array.push( parameters.sizeAttenuation ); + array.push( parameters.morphTargetsCount ); + array.push( parameters.morphAttributeCount ); + array.push( parameters.numDirLights ); + array.push( parameters.numPointLights ); + array.push( parameters.numSpotLights ); + array.push( parameters.numSpotLightMaps ); + array.push( parameters.numHemiLights ); + array.push( parameters.numRectAreaLights ); + array.push( parameters.numDirLightShadows ); + array.push( parameters.numPointLightShadows ); + array.push( parameters.numSpotLightShadows ); + array.push( parameters.numSpotLightShadowsWithMaps ); + array.push( parameters.numLightProbes ); + array.push( parameters.shadowMapType ); + array.push( parameters.toneMapping ); + array.push( parameters.numClippingPlanes ); + array.push( parameters.numClipIntersection ); + array.push( parameters.depthPacking ); + + } + + function getProgramCacheKeyBooleans( array, parameters ) { + + _programLayers.disableAll(); + + if ( parameters.isWebGL2 ) + _programLayers.enable( 0 ); + if ( parameters.supportsVertexTextures ) + _programLayers.enable( 1 ); + if ( parameters.instancing ) + _programLayers.enable( 2 ); + if ( parameters.instancingColor ) + _programLayers.enable( 3 ); + if ( parameters.matcap ) + _programLayers.enable( 4 ); + if ( parameters.envMap ) + _programLayers.enable( 5 ); + if ( parameters.normalMapObjectSpace ) + _programLayers.enable( 6 ); + if ( parameters.normalMapTangentSpace ) + _programLayers.enable( 7 ); + if ( parameters.clearcoat ) + _programLayers.enable( 8 ); + if ( parameters.iridescence ) + _programLayers.enable( 9 ); + if ( parameters.alphaTest ) + _programLayers.enable( 10 ); + if ( parameters.vertexColors ) + _programLayers.enable( 11 ); + if ( parameters.vertexAlphas ) + _programLayers.enable( 12 ); + if ( parameters.vertexUv1s ) + _programLayers.enable( 13 ); + if ( parameters.vertexUv2s ) + _programLayers.enable( 14 ); + if ( parameters.vertexUv3s ) + _programLayers.enable( 15 ); + if ( parameters.vertexTangents ) + _programLayers.enable( 16 ); + if ( parameters.anisotropy ) + _programLayers.enable( 17 ); + + array.push( _programLayers.mask ); + _programLayers.disableAll(); + + if ( parameters.fog ) + _programLayers.enable( 0 ); + if ( parameters.useFog ) + _programLayers.enable( 1 ); + if ( parameters.flatShading ) + _programLayers.enable( 2 ); + if ( parameters.logarithmicDepthBuffer ) + _programLayers.enable( 3 ); + if ( parameters.skinning ) + _programLayers.enable( 4 ); + if ( parameters.morphTargets ) + _programLayers.enable( 5 ); + if ( parameters.morphNormals ) + _programLayers.enable( 6 ); + if ( parameters.morphColors ) + _programLayers.enable( 7 ); + if ( parameters.premultipliedAlpha ) + _programLayers.enable( 8 ); + if ( parameters.shadowMapEnabled ) + _programLayers.enable( 9 ); + if ( parameters.useLegacyLights ) + _programLayers.enable( 10 ); + if ( parameters.doubleSided ) + _programLayers.enable( 11 ); + if ( parameters.flipSided ) + _programLayers.enable( 12 ); + if ( parameters.useDepthPacking ) + _programLayers.enable( 13 ); + if ( parameters.dithering ) + _programLayers.enable( 14 ); + if ( parameters.transmission ) + _programLayers.enable( 15 ); + if ( parameters.sheen ) + _programLayers.enable( 16 ); + if ( parameters.opaque ) + _programLayers.enable( 17 ); + if ( parameters.pointsUvs ) + _programLayers.enable( 18 ); + if ( parameters.decodeVideoTexture ) + _programLayers.enable( 19 ); + + array.push( _programLayers.mask ); + + } + + function getUniforms( material ) { + + const shaderID = shaderIDs[ material.type ]; + let uniforms; + + if ( shaderID ) { + + const shader = ShaderLib[ shaderID ]; + uniforms = UniformsUtils.clone( shader.uniforms ); - currentLineWidth = width; + } else { - } + uniforms = material.uniforms; } - function setPolygonOffset( polygonOffset, factor, units ) { + return uniforms; - if ( polygonOffset ) { + } - enable( 32823 ); + function acquireProgram( parameters, cacheKey ) { - if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { + let program; - gl.polygonOffset( factor, units ); + // Check if code has been already compiled + for ( let p = 0, pl = programs.length; p < pl; p ++ ) { - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; + const preexistingProgram = programs[ p ]; - } + if ( preexistingProgram.cacheKey === cacheKey ) { - } else { + program = preexistingProgram; + ++ program.usedTimes; - disable( 32823 ); + break; } } - function setScissorTest( scissorTest ) { + if ( program === undefined ) { + + program = new WebGLProgram( renderer, cacheKey, parameters, bindingStates ); + programs.push( program ); + + } + + return program; - if ( scissorTest ) { + } - enable( 3089 ); + function releaseProgram( program ) { - } else { + if ( -- program.usedTimes === 0 ) { - disable( 3089 ); + // Remove from unordered set + const i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); - } + // Free WebGL resources + program.destroy(); } - // texture + } - function activeTexture( webglSlot ) { + function releaseShaderCache( material ) { - if ( webglSlot === undefined ) webglSlot = 33984 + maxTextures - 1; + _customShaders.remove( material ); - if ( currentTextureSlot !== webglSlot ) { + } - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; + function dispose() { - } + _customShaders.dispose(); - } + } + + return { + getParameters: getParameters, + getProgramCacheKey: getProgramCacheKey, + getUniforms: getUniforms, + acquireProgram: acquireProgram, + releaseProgram: releaseProgram, + releaseShaderCache: releaseShaderCache, + // Exposed for resource monitoring & error feedback via renderer.info: + programs: programs, + dispose: dispose + }; - function bindTexture( webglType, webglTexture ) { +} - if ( currentTextureSlot === null ) { +function WebGLProperties() { - activeTexture(); + let properties = new WeakMap(); - } + function get( object ) { - var boundTexture = currentBoundTextures[ currentTextureSlot ]; + let map = properties.get( object ); - if ( boundTexture === undefined ) { + if ( map === undefined ) { - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; + map = {}; + properties.set( object, map ); - } + } - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + return map; - gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); + } - boundTexture.type = webglType; - boundTexture.texture = webglTexture; + function remove( object ) { - } + properties.delete( object ); - } + } - function compressedTexImage2D() { + function update( object, key, value ) { - try { + properties.get( object )[ key ] = value; - gl.compressedTexImage2D.apply( gl, arguments ); + } - } catch ( error ) { + function dispose() { - console.error( 'THREE.WebGLState:', error ); + properties = new WeakMap(); - } + } - } + return { + get: get, + remove: remove, + update: update, + dispose: dispose + }; - function texImage2D() { +} - try { +function painterSortStable( a, b ) { - gl.texImage2D.apply( gl, arguments ); + if ( a.groupOrder !== b.groupOrder ) { - } catch ( error ) { + return a.groupOrder - b.groupOrder; - console.error( 'THREE.WebGLState:', error ); + } else if ( a.renderOrder !== b.renderOrder ) { - } + return a.renderOrder - b.renderOrder; - } + } else if ( a.material.id !== b.material.id ) { - function texImage3D() { + return a.material.id - b.material.id; - try { + } else if ( a.z !== b.z ) { - gl.texImage3D.apply( gl, arguments ); + return a.z - b.z; - } catch ( error ) { + } else { - console.error( 'THREE.WebGLState:', error ); + return a.id - b.id; - } + } - } +} - // +function reversePainterSortStable( a, b ) { - function scissor( scissor ) { + if ( a.groupOrder !== b.groupOrder ) { - if ( currentScissor.equals( scissor ) === false ) { + return a.groupOrder - b.groupOrder; - gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); - currentScissor.copy( scissor ); + } else if ( a.renderOrder !== b.renderOrder ) { - } + return a.renderOrder - b.renderOrder; - } + } else if ( a.z !== b.z ) { - function viewport( viewport ) { + return b.z - a.z; - if ( currentViewport.equals( viewport ) === false ) { + } else { - gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); - currentViewport.copy( viewport ); + return a.id - b.id; - } + } - } +} - // - function reset() { +function WebGLRenderList() { - for ( var i = 0; i < enabledAttributes.length; i ++ ) { + const renderItems = []; + let renderItemsIndex = 0; - if ( enabledAttributes[ i ] === 1 ) { + const opaque = []; + const transmissive = []; + const transparent = []; - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; + function init() { - } + renderItemsIndex = 0; - } + opaque.length = 0; + transmissive.length = 0; + transparent.length = 0; + + } - enabledCapabilities = {}; + function getNextRenderItem( object, geometry, material, groupOrder, z, group ) { - compressedTextureFormats = null; + let renderItem = renderItems[ renderItemsIndex ]; - currentTextureSlot = null; - currentBoundTextures = {}; + if ( renderItem === undefined ) { - currentProgram = null; + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + groupOrder: groupOrder, + renderOrder: object.renderOrder, + z: z, + group: group + }; - currentBlending = null; + renderItems[ renderItemsIndex ] = renderItem; - currentFlipSided = null; - currentCullFace = null; + } else { - colorBuffer.reset(); - depthBuffer.reset(); - stencilBuffer.reset(); + 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; } - return { + renderItemsIndex ++; - buffers: { - color: colorBuffer, - depth: depthBuffer, - stencil: stencilBuffer - }, + return renderItem; - initAttributes: initAttributes, - enableAttribute: enableAttribute, - enableAttributeAndDivisor: enableAttributeAndDivisor, - disableUnusedAttributes: disableUnusedAttributes, - enable: enable, - disable: disable, - getCompressedTextureFormats: getCompressedTextureFormats, + } - useProgram: useProgram, + function push( object, geometry, material, groupOrder, z, group ) { - setBlending: setBlending, - setMaterial: setMaterial, + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - setFlipSided: setFlipSided, - setCullFace: setCullFace, + if ( material.transmission > 0.0 ) { - setLineWidth: setLineWidth, - setPolygonOffset: setPolygonOffset, + transmissive.push( renderItem ); - setScissorTest: setScissorTest, + } else if ( material.transparent === true ) { - activeTexture: activeTexture, - bindTexture: bindTexture, - compressedTexImage2D: compressedTexImage2D, - texImage2D: texImage2D, - texImage3D: texImage3D, + transparent.push( renderItem ); - scissor: scissor, - viewport: viewport, + } else { - reset: reset + opaque.push( renderItem ); - }; + } } - /** - * @author mrdoob / http://mrdoob.com/ - */ + function unshift( object, geometry, material, groupOrder, z, group ) { - function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { + const renderItem = getNextRenderItem( object, geometry, material, groupOrder, z, group ); - var _videoTextures = new WeakMap(); - var _canvas; + if ( material.transmission > 0.0 ) { - // + transmissive.unshift( renderItem ); - var useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined'; + } else if ( material.transparent === true ) { - function createCanvas( width, height ) { + transparent.unshift( renderItem ); - // Use OffscreenCanvas when available. Specially needed in web workers + } else { - return useOffscreenCanvas ? - new OffscreenCanvas( width, height ) : - document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + opaque.unshift( renderItem ); } - function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { + } - var scale = 1; + function sort( customOpaqueSort, customTransparentSort ) { - // handle case if texture exceeds max size + if ( opaque.length > 1 ) opaque.sort( customOpaqueSort || painterSortStable ); + if ( transmissive.length > 1 ) transmissive.sort( customTransparentSort || reversePainterSortStable ); + if ( transparent.length > 1 ) transparent.sort( customTransparentSort || reversePainterSortStable ); - if ( image.width > maxSize || image.height > maxSize ) { + } - scale = maxSize / Math.max( image.width, image.height ); + function finish() { - } + // Clear references from inactive renderItems in the list - // only perform resize if necessary + for ( let i = renderItemsIndex, il = renderItems.length; i < il; i ++ ) { - if ( scale < 1 || needsPowerOfTwo === true ) { + const renderItem = renderItems[ i ]; - // only perform resize for certain image types + if ( renderItem.id === null ) break; - if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || - ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || - ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { + renderItem.id = null; + renderItem.object = null; + renderItem.geometry = null; + renderItem.material = null; + renderItem.group = null; - var floor = needsPowerOfTwo ? _Math.floorPowerOfTwo : Math.floor; + } - var width = floor( scale * image.width ); - var height = floor( scale * image.height ); + } - if ( _canvas === undefined ) _canvas = createCanvas( width, height ); + return { - // cube textures can't reuse the same canvas + opaque: opaque, + transmissive: transmissive, + transparent: transparent, - var canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; + init: init, + push: push, + unshift: unshift, + finish: finish, - canvas.width = width; - canvas.height = height; + sort: sort + }; - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); +} - console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); +function WebGLRenderLists() { - return canvas; + let lists = new WeakMap(); - } else { + function get( scene, renderCallDepth ) { - if ( 'data' in image ) { + const listArray = lists.get( scene ); + let list; - console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); + if ( listArray === undefined ) { - } + list = new WebGLRenderList(); + lists.set( scene, [ list ] ); - return image; + } else { - } + if ( renderCallDepth >= listArray.length ) { - } + list = new WebGLRenderList(); + listArray.push( list ); - return image; + } else { + + list = listArray[ renderCallDepth ]; + + } } - function isPowerOfTwo( image ) { + return list; - return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height ); + } - } + function dispose() { - function textureNeedsPowerOfTwo( texture ) { + lists = new WeakMap(); - if ( capabilities.isWebGL2 ) return false; + } - return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || - ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); + return { + get: get, + dispose: dispose + }; - } +} - function textureNeedsGenerateMipmaps( texture, supportsMips ) { +function UniformsCache() { - return texture.generateMipmaps && supportsMips && - texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; + const lights = {}; - } + return { - function generateMipmap( target, texture, width, height ) { + get: function ( light ) { - _gl.generateMipmap( target ); + if ( lights[ light.id ] !== undefined ) { - var textureProperties = properties.get( texture ); + return lights[ light.id ]; - // Note: Math.log( x ) * Math.LOG2E used instead of Math.log2( x ) which is not supported by IE11 - textureProperties.__maxMipLevel = Math.log( Math.max( width, height ) ) * Math.LOG2E; + } - } + let uniforms; - function getInternalFormat( glFormat, glType ) { + switch ( light.type ) { + + case 'DirectionalLight': + uniforms = { + direction: new Vector3(), + color: new Color() + }; + break; - if ( ! capabilities.isWebGL2 ) return glFormat; + case 'SpotLight': + uniforms = { + position: new Vector3(), + direction: new Vector3(), + color: new Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0 + }; + break; - var internalFormat = glFormat; + case 'PointLight': + uniforms = { + position: new Vector3(), + color: new Color(), + distance: 0, + decay: 0 + }; + break; - if ( glFormat === 6403 ) { + case 'HemisphereLight': + uniforms = { + direction: new Vector3(), + skyColor: new Color(), + groundColor: new Color() + }; + break; - if ( glType === 5126 ) internalFormat = 33326; - if ( glType === 5131 ) internalFormat = 33325; - if ( glType === 5121 ) internalFormat = 33321; + case 'RectAreaLight': + uniforms = { + color: new Color(), + position: new Vector3(), + halfWidth: new Vector3(), + halfHeight: new Vector3() + }; + break; } - if ( glFormat === 6407 ) { + lights[ light.id ] = uniforms; - if ( glType === 5126 ) internalFormat = 34837; - if ( glType === 5131 ) internalFormat = 34843; - if ( glType === 5121 ) internalFormat = 32849; + return uniforms; - } + } - if ( glFormat === 6408 ) { + }; - if ( glType === 5126 ) internalFormat = 34836; - if ( glType === 5131 ) internalFormat = 34842; - if ( glType === 5121 ) internalFormat = 32856; +} - } +function ShadowUniformsCache() { - if ( internalFormat === 33325 || internalFormat === 33326 || - internalFormat === 34842 || internalFormat === 34836 ) { + const lights = {}; - extensions.get( 'EXT_color_buffer_float' ); + return { - } else if ( internalFormat === 34843 || internalFormat === 34837 ) { + get: function ( light ) { - console.warn( 'THREE.WebGLRenderer: Floating point textures with RGB format not supported. Please use RGBA instead.' ); + if ( lights[ light.id ] !== undefined ) { + + return lights[ light.id ]; } - return internalFormat; + let uniforms; - } + switch ( light.type ) { - // Fallback filters for non-power-of-2 textures + case 'DirectionalLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - function filterFallback( f ) { + case 'SpotLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2() + }; + break; - if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { + case 'PointLight': + uniforms = { + shadowBias: 0, + shadowNormalBias: 0, + shadowRadius: 1, + shadowMapSize: new Vector2(), + shadowCameraNear: 1, + shadowCameraFar: 1000 + }; + break; - return 9728; + // TODO (abelnation): set RectAreaLight shadow uniforms } - return 9729; + lights[ light.id ] = uniforms; + + return uniforms; } - // + }; - function onTextureDispose( event ) { +} - var texture = event.target; - texture.removeEventListener( 'dispose', onTextureDispose ); - deallocateTexture( texture ); +let nextVersion = 0; - if ( texture.isVideoTexture ) { +function shadowCastingAndTexturingLightsFirst( lightA, lightB ) { - _videoTextures.delete( texture ); + return ( lightB.castShadow ? 2 : 0 ) - ( lightA.castShadow ? 2 : 0 ) + ( lightB.map ? 1 : 0 ) - ( lightA.map ? 1 : 0 ); - } +} - info.memory.textures --; +function WebGLLights( extensions, capabilities ) { - } + const cache = new UniformsCache(); - function onRenderTargetDispose( event ) { + const shadowCache = ShadowUniformsCache(); - var renderTarget = event.target; + const state = { - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + version: 0, - deallocateRenderTarget( renderTarget ); + hash: { + directionalLength: - 1, + pointLength: - 1, + spotLength: - 1, + rectAreaLength: - 1, + hemiLength: - 1, - info.memory.textures --; + numDirectionalShadows: - 1, + numPointShadows: - 1, + numSpotShadows: - 1, + numSpotMaps: - 1, + + numLightProbes: - 1 + }, + + ambient: [ 0, 0, 0 ], + probe: [], + directional: [], + directionalShadow: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotLightMap: [], + spotShadow: [], + spotShadowMap: [], + spotLightMatrix: [], + rectArea: [], + rectAreaLTC1: null, + rectAreaLTC2: null, + point: [], + pointShadow: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [], + numSpotLightShadowsWithMaps: 0, + numLightProbes: 0 - } + }; - // + for ( let i = 0; i < 9; i ++ ) state.probe.push( new Vector3() ); - function deallocateTexture( texture ) { + const vector3 = new Vector3(); + const matrix4 = new Matrix4(); + const matrix42 = new Matrix4(); - var textureProperties = properties.get( texture ); + function setup( lights, useLegacyLights ) { - if ( textureProperties.__webglInit === undefined ) return; + let r = 0, g = 0, b = 0; - _gl.deleteTexture( textureProperties.__webglTexture ); + for ( let i = 0; i < 9; i ++ ) state.probe[ i ].set( 0, 0, 0 ); - properties.remove( texture ); + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - } + let numDirectionalShadows = 0; + let numPointShadows = 0; + let numSpotShadows = 0; + let numSpotMaps = 0; + let numSpotShadowsWithMaps = 0; - function deallocateRenderTarget( renderTarget ) { + let numLightProbes = 0; - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); + // ordering : [shadow casting + map texturing, map texturing, shadow casting, none ] + lights.sort( shadowCastingAndTexturingLightsFirst ); - if ( ! renderTarget ) return; + // artist-friendly light intensity scaling factor + const scaleFactor = ( useLegacyLights === true ) ? Math.PI : 1; - if ( textureProperties.__webglTexture !== undefined ) { + for ( let i = 0, l = lights.length; i < l; i ++ ) { - _gl.deleteTexture( textureProperties.__webglTexture ); + const light = lights[ i ]; - } + const color = light.color; + const intensity = light.intensity; + const distance = light.distance; - if ( renderTarget.depthTexture ) { + const shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - renderTarget.depthTexture.dispose(); + if ( light.isAmbientLight ) { - } + r += color.r * intensity * scaleFactor; + g += color.g * intensity * scaleFactor; + b += color.b * intensity * scaleFactor; - if ( renderTarget.isWebGLRenderTargetCube ) { + } else if ( light.isLightProbe ) { - for ( var i = 0; i < 6; i ++ ) { + for ( let j = 0; j < 9; j ++ ) { - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); + state.probe[ j ].addScaledVector( light.sh.coefficients[ j ], intensity ); } - } else { + numLightProbes ++; - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); + } else if ( light.isDirectionalLight ) { - } + const uniforms = cache.get( light ); - properties.remove( renderTarget.texture ); - properties.remove( renderTarget ); + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); - } + if ( light.castShadow ) { - // + const shadow = light.shadow; - var textureUnits = 0; + const shadowUniforms = shadowCache.get( light ); - function resetTextureUnits() { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - textureUnits = 0; + state.directionalShadow[ directionalLength ] = shadowUniforms; + state.directionalShadowMap[ directionalLength ] = shadowMap; + state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; - } + numDirectionalShadows ++; - function allocateTextureUnit() { + } - var textureUnit = textureUnits; + state.directional[ directionalLength ] = uniforms; - if ( textureUnit >= capabilities.maxTextures ) { + directionalLength ++; - console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + } else if ( light.isSpotLight ) { - } + const uniforms = cache.get( light ); - textureUnits += 1; + uniforms.position.setFromMatrixPosition( light.matrixWorld ); - return textureUnit; + uniforms.color.copy( color ).multiplyScalar( intensity * scaleFactor ); + uniforms.distance = distance; - } + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = light.decay; - // + state.spot[ spotLength ] = uniforms; - function setTexture2D( texture, slot ) { + const shadow = light.shadow; - var textureProperties = properties.get( texture ); + if ( light.map ) { - if ( texture.isVideoTexture ) updateVideoTexture( texture ); + state.spotLightMap[ numSpotMaps ] = light.map; + numSpotMaps ++; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + // make sure the lightMatrix is up to date + // TODO : do it if required only + shadow.updateMatrices( light ); - var image = texture.image; + if ( light.castShadow ) numSpotShadowsWithMaps ++; - if ( image === undefined ) { + } - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined' ); + state.spotLightMatrix[ spotLength ] = shadow.matrix; - } else if ( image.complete === false ) { + if ( light.castShadow ) { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); + const shadowUniforms = shadowCache.get( light ); - } else { + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; - uploadTexture( textureProperties, texture, slot ); - return; + state.spotShadow[ spotLength ] = shadowUniforms; + state.spotShadowMap[ spotLength ] = shadowMap; + + numSpotShadows ++; } - } + spotLength ++; - state.activeTexture( 33984 + slot ); - state.bindTexture( 3553, textureProperties.__webglTexture ); + } else if ( light.isRectAreaLight ) { - } + const uniforms = cache.get( light ); - function setTexture2DArray( texture, slot ) { + uniforms.color.copy( color ).multiplyScalar( intensity ); - var textureProperties = properties.get( texture ); + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + state.rectArea[ rectAreaLength ] = uniforms; - uploadTexture( textureProperties, texture, slot ); - return; + rectAreaLength ++; - } + } else if ( light.isPointLight ) { - state.activeTexture( 33984 + slot ); - state.bindTexture( 35866, textureProperties.__webglTexture ); + const uniforms = cache.get( light ); - } + uniforms.color.copy( light.color ).multiplyScalar( light.intensity * scaleFactor ); + uniforms.distance = light.distance; + uniforms.decay = light.decay; - function setTexture3D( texture, slot ) { + if ( light.castShadow ) { - var textureProperties = properties.get( texture ); + const shadow = light.shadow; - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + const shadowUniforms = shadowCache.get( light ); - uploadTexture( textureProperties, texture, slot ); - return; + shadowUniforms.shadowBias = shadow.bias; + shadowUniforms.shadowNormalBias = shadow.normalBias; + shadowUniforms.shadowRadius = shadow.radius; + shadowUniforms.shadowMapSize = shadow.mapSize; + shadowUniforms.shadowCameraNear = shadow.camera.near; + shadowUniforms.shadowCameraFar = shadow.camera.far; - } + state.pointShadow[ pointLength ] = shadowUniforms; + state.pointShadowMap[ pointLength ] = shadowMap; + state.pointShadowMatrix[ pointLength ] = light.shadow.matrix; - state.activeTexture( 33984 + slot ); - state.bindTexture( 32879, textureProperties.__webglTexture ); + numPointShadows ++; - } + } - function setTextureCube( texture, slot ) { + state.point[ pointLength ] = uniforms; - if ( texture.image.length !== 6 ) return; + pointLength ++; - var textureProperties = properties.get( texture ); + } else if ( light.isHemisphereLight ) { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + const uniforms = cache.get( light ); - initTexture( textureProperties, texture ); + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity * scaleFactor ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity * scaleFactor ); - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + state.hemi[ hemiLength ] = uniforms; - _gl.pixelStorei( 37440, texture.flipY ); + hemiLength ++; - var isCompressed = ( texture && texture.isCompressedTexture ); - var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); + } - var cubeImage = []; + } - for ( var i = 0; i < 6; i ++ ) { + if ( rectAreaLength > 0 ) { - if ( ! isCompressed && ! isDataTexture ) { + if ( capabilities.isWebGL2 ) { - cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, capabilities.maxCubemapSize ); + // WebGL 2 - } else { + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + } else { - } + // WebGL 1 - } + if ( extensions.has( 'OES_texture_float_linear' ) === true ) { - var image = cubeImage[ 0 ], - supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( glFormat, glType ); + state.rectAreaLTC1 = UniformsLib.LTC_FLOAT_1; + state.rectAreaLTC2 = UniformsLib.LTC_FLOAT_2; - setTextureParameters( 34067, texture, supportsMips ); + } else if ( extensions.has( 'OES_texture_half_float_linear' ) === true ) { - var mipmaps; + state.rectAreaLTC1 = UniformsLib.LTC_HALF_1; + state.rectAreaLTC2 = UniformsLib.LTC_HALF_2; - if ( isCompressed ) { + } else { - for ( var i = 0; i < 6; i ++ ) { + console.error( 'THREE.WebGLRenderer: Unable to use RectAreaLight. Missing WebGL extensions.' ); - mipmaps = cubeImage[ i ].mipmaps; + } - for ( var j = 0; j < mipmaps.length; j ++ ) { + } - var mipmap = mipmaps[ j ]; + } - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + state.ambient[ 0 ] = r; + state.ambient[ 1 ] = g; + state.ambient[ 2 ] = b; - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + const hash = state.hash; - state.compressedTexImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + if ( hash.directionalLength !== directionalLength || + hash.pointLength !== pointLength || + hash.spotLength !== spotLength || + hash.rectAreaLength !== rectAreaLength || + hash.hemiLength !== hemiLength || + hash.numDirectionalShadows !== numDirectionalShadows || + hash.numPointShadows !== numPointShadows || + hash.numSpotShadows !== numSpotShadows || + hash.numSpotMaps !== numSpotMaps || + hash.numLightProbes !== numLightProbes ) { - } else { + state.directional.length = directionalLength; + state.spot.length = spotLength; + state.rectArea.length = rectAreaLength; + state.point.length = pointLength; + state.hemi.length = hemiLength; - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); + state.directionalShadow.length = numDirectionalShadows; + state.directionalShadowMap.length = numDirectionalShadows; + state.pointShadow.length = numPointShadows; + state.pointShadowMap.length = numPointShadows; + state.spotShadow.length = numSpotShadows; + state.spotShadowMap.length = numSpotShadows; + state.directionalShadowMatrix.length = numDirectionalShadows; + state.pointShadowMatrix.length = numPointShadows; + state.spotLightMatrix.length = numSpotShadows + numSpotMaps - numSpotShadowsWithMaps; + state.spotLightMap.length = numSpotMaps; + state.numSpotLightShadowsWithMaps = numSpotShadowsWithMaps; + state.numLightProbes = numLightProbes; - } + hash.directionalLength = directionalLength; + hash.pointLength = pointLength; + hash.spotLength = spotLength; + hash.rectAreaLength = rectAreaLength; + hash.hemiLength = hemiLength; - } else { + hash.numDirectionalShadows = numDirectionalShadows; + hash.numPointShadows = numPointShadows; + hash.numSpotShadows = numSpotShadows; + hash.numSpotMaps = numSpotMaps; - state.texImage2D( 34069 + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + hash.numLightProbes = numLightProbes; - } + state.version = nextVersion ++; - } + } - } + } - textureProperties.__maxMipLevel = mipmaps.length - 1; + function setupView( lights, camera ) { - } else { + let directionalLength = 0; + let pointLength = 0; + let spotLength = 0; + let rectAreaLength = 0; + let hemiLength = 0; - mipmaps = texture.mipmaps; + const viewMatrix = camera.matrixWorldInverse; - for ( var i = 0; i < 6; i ++ ) { + for ( let i = 0, l = lights.length; i < l; i ++ ) { - if ( isDataTexture ) { + const light = lights[ i ]; - state.texImage2D( 34069 + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + if ( light.isDirectionalLight ) { - for ( var j = 0; j < mipmaps.length; j ++ ) { + const uniforms = state.directional[ directionalLength ]; - var mipmap = mipmaps[ j ]; - var mipmapImage = mipmap.image[ i ].image; + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - state.texImage2D( 34069 + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); + directionalLength ++; - } + } else if ( light.isSpotLight ) { - } else { + const uniforms = state.spot[ spotLength ]; - state.texImage2D( 34069 + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - for ( var j = 0; j < mipmaps.length; j ++ ) { + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - var mipmap = mipmaps[ j ]; + spotLength ++; - state.texImage2D( 34069 + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); + } else if ( light.isRectAreaLight ) { - } + const uniforms = state.rectArea[ rectAreaLength ]; - } + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - } + // extract local rotation of light to derive width/height half vectors + matrix42.identity(); + matrix4.copy( light.matrixWorld ); + matrix4.premultiply( viewMatrix ); + matrix42.extractRotation( matrix4 ); - textureProperties.__maxMipLevel = mipmaps.length; + uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 ); + uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 ); - } + uniforms.halfWidth.applyMatrix4( matrix42 ); + uniforms.halfHeight.applyMatrix4( matrix42 ); - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + rectAreaLength ++; - // We assume images for cube map have the same size. - generateMipmap( 34067, texture, image.width, image.height ); + } else if ( light.isPointLight ) { - } + const uniforms = state.point[ pointLength ]; - textureProperties.__version = texture.version; + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - if ( texture.onUpdate ) texture.onUpdate( texture ); + pointLength ++; - } else { + } else if ( light.isHemisphereLight ) { + + const uniforms = state.hemi[ hemiLength ]; - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, textureProperties.__webglTexture ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + + hemiLength ++; } } - function setTextureCubeDynamic( texture, slot ) { - - state.activeTexture( 33984 + slot ); - state.bindTexture( 34067, properties.get( texture ).__webglTexture ); + } - } + return { + setup: setup, + setupView: setupView, + state: state + }; - function setTextureParameters( textureType, texture, supportsMips ) { +} - var extension; +function WebGLRenderState( extensions, capabilities ) { - if ( supportsMips ) { + const lights = new WebGLLights( extensions, capabilities ); - _gl.texParameteri( textureType, 10242, utils.convert( texture.wrapS ) ); - _gl.texParameteri( textureType, 10243, utils.convert( texture.wrapT ) ); + const lightsArray = []; + const shadowsArray = []; - if ( textureType === 32879 || textureType === 35866 ) { + function init() { - _gl.texParameteri( textureType, 32882, utils.convert( texture.wrapR ) ); + lightsArray.length = 0; + shadowsArray.length = 0; - } + } - _gl.texParameteri( textureType, 10240, utils.convert( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, utils.convert( texture.minFilter ) ); + function pushLight( light ) { - } else { + lightsArray.push( light ); - _gl.texParameteri( textureType, 10242, 33071 ); - _gl.texParameteri( textureType, 10243, 33071 ); + } - if ( textureType === 32879 || textureType === 35866 ) { + function pushShadow( shadowLight ) { - _gl.texParameteri( textureType, 32882, 33071 ); + shadowsArray.push( shadowLight ); - } + } - if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { + function setupLights( useLegacyLights ) { - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); + lights.setup( lightsArray, useLegacyLights ); - } + } - _gl.texParameteri( textureType, 10240, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, 10241, filterFallback( texture.minFilter ) ); + function setupLightsView( camera ) { - if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { + lights.setupView( lightsArray, camera ); - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); + } - } + const state = { + lightsArray: lightsArray, + shadowsArray: shadowsArray, - } + lights: lights + }; - extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + return { + init: init, + state: state, + setupLights: setupLights, + setupLightsView: setupLightsView, - if ( extension ) { + pushLight: pushLight, + pushShadow: pushShadow + }; - if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === HalfFloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_half_float_linear' ) ) === null ) return; +} - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { +function WebGLRenderStates( extensions, capabilities ) { - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; + let renderStates = new WeakMap(); - } + function get( scene, renderCallDepth = 0 ) { - } + const renderStateArray = renderStates.get( scene ); + let renderState; - } + if ( renderStateArray === undefined ) { - function initTexture( textureProperties, texture ) { + renderState = new WebGLRenderState( extensions, capabilities ); + renderStates.set( scene, [ renderState ] ); - if ( textureProperties.__webglInit === undefined ) { + } else { - textureProperties.__webglInit = true; + if ( renderCallDepth >= renderStateArray.length ) { - texture.addEventListener( 'dispose', onTextureDispose ); + renderState = new WebGLRenderState( extensions, capabilities ); + renderStateArray.push( renderState ); - textureProperties.__webglTexture = _gl.createTexture(); + } else { - info.memory.textures ++; + renderState = renderStateArray[ renderCallDepth ]; } } - function uploadTexture( textureProperties, texture, slot ) { - - var textureType = 3553; + return renderState; - if ( texture.isDataTexture2DArray ) textureType = 35866; - if ( texture.isDataTexture3D ) textureType = 32879; + } - initTexture( textureProperties, texture ); + function dispose() { - state.activeTexture( 33984 + slot ); - state.bindTexture( textureType, textureProperties.__webglTexture ); + renderStates = new WeakMap(); - _gl.pixelStorei( 37440, texture.flipY ); - _gl.pixelStorei( 37441, texture.premultiplyAlpha ); - _gl.pixelStorei( 3317, texture.unpackAlignment ); + } - var needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( texture.image ) === false; - var image = resizeImage( texture.image, needsPowerOfTwo, false, capabilities.maxTextureSize ); + return { + get: get, + dispose: dispose + }; - var supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2, - glFormat = utils.convert( texture.format ), - glType = utils.convert( texture.type ), - glInternalFormat = getInternalFormat( glFormat, glType ); +} - setTextureParameters( textureType, texture, supportsMips ); +class MeshDepthMaterial extends Material { - var mipmap, mipmaps = texture.mipmaps; + constructor( parameters ) { - if ( texture.isDepthTexture ) { + super(); - // populate depth texture with dummy data + this.isMeshDepthMaterial = true; - glInternalFormat = 6402; + this.type = 'MeshDepthMaterial'; - if ( texture.type === FloatType ) { + this.depthPacking = BasicDepthPacking; - if ( ! capabilities.isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' ); - glInternalFormat = 36012; + this.map = null; - } else if ( capabilities.isWebGL2 ) { + this.alphaMap = null; - // WebGL 2.0 requires signed internalformat for glTexImage2D - glInternalFormat = 33189; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.wireframe = false; + this.wireframeLinewidth = 1; - if ( texture.format === DepthFormat && glInternalFormat === 6402 ) { + this.setValues( parameters ); - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { + } - console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); + copy( source ) { - texture.type = UnsignedShortType; - glType = utils.convert( texture.type ); + super.copy( source ); - } + this.depthPacking = source.depthPacking; - } + this.map = source.map; - // Depth stencil textures need the DEPTH_STENCIL internal format - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.format === DepthStencilFormat ) { + this.alphaMap = source.alphaMap; - glInternalFormat = 34041; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are - // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. - // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) - if ( texture.type !== UnsignedInt248Type ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); + return this; - texture.type = UnsignedInt248Type; - glType = utils.convert( texture.type ); + } - } +} - } +class MeshDistanceMaterial extends Material { - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); + constructor( parameters ) { - } else if ( texture.isDataTexture ) { + super(); - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + this.isMeshDistanceMaterial = true; - if ( mipmaps.length > 0 && supportsMips ) { + this.type = 'MeshDistanceMaterial'; - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + this.map = null; - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + this.alphaMap = null; - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + this.setValues( parameters ); - } else { + } - state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + copy( source ) { - } + super.copy( source ); - } else if ( texture.isCompressedTexture ) { + this.map = source.map; - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + this.alphaMap = source.alphaMap; - mipmap = mipmaps[ i ]; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) { + return this; - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + } - state.compressedTexImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); +} - } else { +const vertex = "void main() {\n\tgl_Position = vec4( position, 1.0 );\n}"; - console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); +const fragment = "uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tconst float samples = float( VSM_SAMPLES );\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 );\n\tfloat uvStart = samples <= 1.0 ? 0.0 : - 1.0;\n\tfor ( float i = 0.0; i < samples; i ++ ) {\n\t\tfloat uvOffset = uvStart + i * uvStride;\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean / samples;\n\tsquared_mean = squared_mean / samples;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}"; - } +function WebGLShadowMap( _renderer, _objects, _capabilities ) { - } else { + let _frustum = new Frustum(); - state.texImage2D( 3553, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + const _shadowMapSize = new Vector2(), + _viewportSize = new Vector2(), - } + _viewport = new Vector4(), - } + _depthMaterial = new MeshDepthMaterial( { depthPacking: RGBADepthPacking } ), + _distanceMaterial = new MeshDistanceMaterial(), - textureProperties.__maxMipLevel = mipmaps.length - 1; + _materialCache = {}, - } else if ( texture.isDataTexture2DArray ) { + _maxTextureSize = _capabilities.maxTextureSize; - state.texImage3D( 35866, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + const shadowSide = { [ FrontSide ]: BackSide, [ BackSide ]: FrontSide, [ DoubleSide ]: DoubleSide }; - } else if ( texture.isDataTexture3D ) { + const shadowMaterialVertical = new ShaderMaterial( { + defines: { + VSM_SAMPLES: 8 + }, + uniforms: { + shadow_pass: { value: null }, + resolution: { value: new Vector2() }, + radius: { value: 4.0 } + }, - state.texImage3D( 32879, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - textureProperties.__maxMipLevel = 0; + vertexShader: vertex, + fragmentShader: fragment - } else { + } ); - // regular Texture (image, video, canvas) + const shadowMaterialHorizontal = shadowMaterialVertical.clone(); + shadowMaterialHorizontal.defines.HORIZONTAL_PASS = 1; - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + const fullScreenTri = new BufferGeometry(); + fullScreenTri.setAttribute( + 'position', + new BufferAttribute( + new Float32Array( [ - 1, - 1, 0.5, 3, - 1, 0.5, - 1, 3, 0.5 ] ), + 3 + ) + ); - if ( mipmaps.length > 0 && supportsMips ) { + const fullScreenMesh = new Mesh( fullScreenTri, shadowMaterialVertical ); - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + const scope = this; - mipmap = mipmaps[ i ]; - state.texImage2D( 3553, i, glInternalFormat, glFormat, glType, mipmap ); + this.enabled = false; - } + this.autoUpdate = true; + this.needsUpdate = false; - texture.generateMipmaps = false; - textureProperties.__maxMipLevel = mipmaps.length - 1; + this.type = PCFShadowMap; + let _previousType = this.type; - } else { + this.render = function ( lights, scene, camera ) { - state.texImage2D( 3553, 0, glInternalFormat, glFormat, glType, image ); - textureProperties.__maxMipLevel = 0; + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - } + if ( lights.length === 0 ) return; - } + const currentRenderTarget = _renderer.getRenderTarget(); + const activeCubeFace = _renderer.getActiveCubeFace(); + const activeMipmapLevel = _renderer.getActiveMipmapLevel(); - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + const _state = _renderer.state; - generateMipmap( 3553, texture, image.width, image.height ); + // Set GL state for depth map. + _state.setBlending( NoBlending ); + _state.buffers.color.setClear( 1, 1, 1, 1 ); + _state.buffers.depth.setTest( true ); + _state.setScissorTest( false ); - } + // check for shadow map type changes - textureProperties.__version = texture.version; + const toVSM = ( _previousType !== VSMShadowMap && this.type === VSMShadowMap ); + const fromVSM = ( _previousType === VSMShadowMap && this.type !== VSMShadowMap ); - if ( texture.onUpdate ) texture.onUpdate( texture ); + // render depth map - } + for ( let i = 0, il = lights.length; i < il; i ++ ) { - // Render targets + const light = lights[ i ]; + const shadow = light.shadow; - // Setup storage for target texture and bind it to correct framebuffer - function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) { + if ( shadow === undefined ) { - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( glFormat, glType ); - state.texImage2D( textureTarget, 0, glInternalFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - _gl.bindFramebuffer( 36160, framebuffer ); - _gl.framebufferTexture2D( 36160, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); - _gl.bindFramebuffer( 36160, null ); + console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + continue; - } + } - // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { + if ( shadow.autoUpdate === false && shadow.needsUpdate === false ) continue; - _gl.bindRenderbuffer( 36161, renderbuffer ); + _shadowMapSize.copy( shadow.mapSize ); - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + const shadowFrameExtents = shadow.getFrameExtents(); - if ( isMultisample ) { + _shadowMapSize.multiply( shadowFrameExtents ); - var samples = getRenderTargetSamples( renderTarget ); + _viewportSize.copy( shadow.mapSize ); - _gl.renderbufferStorageMultisample( 36161, samples, 33189, renderTarget.width, renderTarget.height ); + if ( _shadowMapSize.x > _maxTextureSize || _shadowMapSize.y > _maxTextureSize ) { - } else { + if ( _shadowMapSize.x > _maxTextureSize ) { - _gl.renderbufferStorage( 36161, 33189, renderTarget.width, renderTarget.height ); + _viewportSize.x = Math.floor( _maxTextureSize / shadowFrameExtents.x ); + _shadowMapSize.x = _viewportSize.x * shadowFrameExtents.x; + shadow.mapSize.x = _viewportSize.x; } - _gl.framebufferRenderbuffer( 36160, 36096, 36161, renderbuffer ); + if ( _shadowMapSize.y > _maxTextureSize ) { - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + _viewportSize.y = Math.floor( _maxTextureSize / shadowFrameExtents.y ); + _shadowMapSize.y = _viewportSize.y * shadowFrameExtents.y; + shadow.mapSize.y = _viewportSize.y; - if ( isMultisample ) { + } + + } - var samples = getRenderTargetSamples( renderTarget ); + if ( shadow.map === null || toVSM === true || fromVSM === true ) { - _gl.renderbufferStorageMultisample( 36161, samples, 35056, renderTarget.width, renderTarget.height ); + const pars = ( this.type !== VSMShadowMap ) ? { minFilter: NearestFilter, magFilter: NearestFilter } : {}; - } else { + if ( shadow.map !== null ) { - _gl.renderbufferStorage( 36161, 34041, renderTarget.width, renderTarget.height ); + shadow.map.dispose(); } + shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); + shadow.map.texture.name = light.name + '.shadowMap'; - _gl.framebufferRenderbuffer( 36160, 33306, 36161, renderbuffer ); + shadow.camera.updateProjectionMatrix(); - } else { + } - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( glFormat, glType ); + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); - if ( isMultisample ) { + const viewportCount = shadow.getViewportCount(); - var samples = getRenderTargetSamples( renderTarget ); + for ( let vp = 0; vp < viewportCount; vp ++ ) { - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + const viewport = shadow.getViewport( vp ); - } else { + _viewport.set( + _viewportSize.x * viewport.x, + _viewportSize.y * viewport.y, + _viewportSize.x * viewport.z, + _viewportSize.y * viewport.w + ); - _gl.renderbufferStorage( 36161, glInternalFormat, renderTarget.width, renderTarget.height ); + _state.viewport( _viewport ); - } + shadow.updateMatrices( light, vp ); - } + _frustum = shadow.getFrustum(); - _gl.bindRenderbuffer( 36161, null ); + renderObject( scene, camera, shadow.camera, light, this.type ); - } + } - // Setup resources for a Depth Texture for a FBO (needs an extension) - function setupDepthTexture( framebuffer, renderTarget ) { + // do blur pass for VSM - var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube ); - if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); + if ( shadow.isPointLightShadow !== true && this.type === VSMShadowMap ) { - _gl.bindFramebuffer( 36160, framebuffer ); + VSMPass( shadow, camera ); - if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { + } - throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); + shadow.needsUpdate = false; - } + } - // upload an empty depth texture with framebuffer size - if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || - renderTarget.depthTexture.image.width !== renderTarget.width || - renderTarget.depthTexture.image.height !== renderTarget.height ) { + _previousType = this.type; - renderTarget.depthTexture.image.width = renderTarget.width; - renderTarget.depthTexture.image.height = renderTarget.height; - renderTarget.depthTexture.needsUpdate = true; + scope.needsUpdate = false; - } + _renderer.setRenderTarget( currentRenderTarget, activeCubeFace, activeMipmapLevel ); - setTexture2D( renderTarget.depthTexture, 0 ); + }; - var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + function VSMPass( shadow, camera ) { - if ( renderTarget.depthTexture.format === DepthFormat ) { + const geometry = _objects.update( fullScreenMesh ); - _gl.framebufferTexture2D( 36160, 36096, 3553, webglDepthTexture, 0 ); + if ( shadowMaterialVertical.defines.VSM_SAMPLES !== shadow.blurSamples ) { - } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { + shadowMaterialVertical.defines.VSM_SAMPLES = shadow.blurSamples; + shadowMaterialHorizontal.defines.VSM_SAMPLES = shadow.blurSamples; - _gl.framebufferTexture2D( 36160, 33306, 3553, webglDepthTexture, 0 ); + shadowMaterialVertical.needsUpdate = true; + shadowMaterialHorizontal.needsUpdate = true; - } else { + } - throw new Error( 'Unknown depthTexture format' ); + if ( shadow.mapPass === null ) { - } + shadow.mapPass = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y ); } - // Setup GL resources for a non-texture depth buffer - function setupDepthRenderbuffer( renderTarget ) { + // vertical pass - var renderTargetProperties = properties.get( renderTarget ); + shadowMaterialVertical.uniforms.shadow_pass.value = shadow.map.texture; + shadowMaterialVertical.uniforms.resolution.value = shadow.mapSize; + shadowMaterialVertical.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.mapPass ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialVertical, fullScreenMesh, null ); - var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); + // horizontal pass - if ( renderTarget.depthTexture ) { + shadowMaterialHorizontal.uniforms.shadow_pass.value = shadow.mapPass.texture; + shadowMaterialHorizontal.uniforms.resolution.value = shadow.mapSize; + shadowMaterialHorizontal.uniforms.radius.value = shadow.radius; + _renderer.setRenderTarget( shadow.map ); + _renderer.clear(); + _renderer.renderBufferDirect( camera, null, geometry, shadowMaterialHorizontal, fullScreenMesh, null ); - if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - - setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); + } - } else { + function getDepthMaterial( object, material, light, type ) { - if ( isCube ) { + let result = null; - renderTargetProperties.__webglDepthbuffer = []; + const customMaterial = ( light.isPointLight === true ) ? object.customDistanceMaterial : object.customDepthMaterial; - for ( var i = 0; i < 6; i ++ ) { + if ( customMaterial !== undefined ) { - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer[ i ] ); - renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); + result = customMaterial; - } + } else { - } else { + result = ( light.isPointLight === true ) ? _distanceMaterial : _depthMaterial; - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglFramebuffer ); - renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); + if ( ( _renderer.localClippingEnabled && material.clipShadows === true && Array.isArray( material.clippingPlanes ) && material.clippingPlanes.length !== 0 ) || + ( material.displacementMap && material.displacementScale !== 0 ) || + ( material.alphaMap && material.alphaTest > 0 ) || + ( material.map && material.alphaTest > 0 ) ) { - } + // in this case we need a unique material instance reflecting the + // appropriate state - } + const keyA = result.uuid, keyB = material.uuid; - _gl.bindFramebuffer( 36160, null ); + let materialsForVariant = _materialCache[ keyA ]; - } + if ( materialsForVariant === undefined ) { - // Set up GL resources for the render target - function setupRenderTarget( renderTarget ) { + materialsForVariant = {}; + _materialCache[ keyA ] = materialsForVariant; - var renderTargetProperties = properties.get( renderTarget ); - var textureProperties = properties.get( renderTarget.texture ); + } - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + let cachedMaterial = materialsForVariant[ keyB ]; - textureProperties.__webglTexture = _gl.createTexture(); + if ( cachedMaterial === undefined ) { - info.memory.textures ++; + cachedMaterial = result.clone(); + materialsForVariant[ keyB ] = cachedMaterial; - var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); - var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); - var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2; + } - // Setup framebuffer + result = cachedMaterial; - if ( isCube ) { + } - renderTargetProperties.__webglFramebuffer = []; + } - for ( var i = 0; i < 6; i ++ ) { + result.visible = material.visible; + result.wireframe = material.wireframe; - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + if ( type === VSMShadowMap ) { - } + result.side = ( material.shadowSide !== null ) ? material.shadowSide : material.side; - } else { + } else { - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + result.side = ( material.shadowSide !== null ) ? material.shadowSide : shadowSide[ material.side ]; - if ( isMultisample ) { + } - if ( capabilities.isWebGL2 ) { + result.alphaMap = material.alphaMap; + result.alphaTest = material.alphaTest; + result.map = material.map; - renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); - renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; + result.clipIntersection = material.clipIntersection; - _gl.bindRenderbuffer( 36161, renderTargetProperties.__webglColorRenderbuffer ); - var glFormat = utils.convert( renderTarget.texture.format ); - var glType = utils.convert( renderTarget.texture.type ); - var glInternalFormat = getInternalFormat( glFormat, glType ); - var samples = getRenderTargetSamples( renderTarget ); - _gl.renderbufferStorageMultisample( 36161, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + result.displacementMap = material.displacementMap; + result.displacementScale = material.displacementScale; + result.displacementBias = material.displacementBias; - _gl.bindFramebuffer( 36160, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.framebufferRenderbuffer( 36160, 36064, 36161, renderTargetProperties.__webglColorRenderbuffer ); - _gl.bindRenderbuffer( 36161, null ); + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; - if ( renderTarget.depthBuffer ) { + if ( light.isPointLight === true && result.isMeshDistanceMaterial === true ) { - renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); - setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); + const materialProperties = _renderer.properties.get( result ); + materialProperties.light = light; - } + } - _gl.bindFramebuffer( 36160, null ); + return result; + } - } else { + function renderObject( object, camera, shadowCamera, light, type ) { - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + if ( object.visible === false ) return; - } + const visible = object.layers.test( camera.layers ); - } + if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) { - } + if ( ( object.castShadow || ( object.receiveShadow && type === VSMShadowMap ) ) && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) { - // Setup color buffer + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - if ( isCube ) { + const geometry = _objects.update( object ); + const material = object.material; - state.bindTexture( 34067, textureProperties.__webglTexture ); - setTextureParameters( 34067, renderTarget.texture, supportsMips ); + if ( Array.isArray( material ) ) { - for ( var i = 0; i < 6; i ++ ) { + const groups = geometry.groups; - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, 36064, 34069 + i ); + for ( let k = 0, kl = groups.length; k < kl; k ++ ) { - } + const group = groups[ k ]; + const groupMaterial = material[ group.materialIndex ]; - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { + if ( groupMaterial && groupMaterial.visible ) { - generateMipmap( 34067, renderTarget.texture, renderTarget.width, renderTarget.height ); + const depthMaterial = getDepthMaterial( object, groupMaterial, light, type ); - } + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - state.bindTexture( 34067, null ); + } - } else { + } - state.bindTexture( 3553, textureProperties.__webglTexture ); - setTextureParameters( 3553, renderTarget.texture, supportsMips ); - setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, 36064, 3553 ); + } else if ( material.visible ) { - if ( textureNeedsGenerateMipmaps( renderTarget.texture, supportsMips ) ) { + const depthMaterial = getDepthMaterial( object, material, light, type ); - generateMipmap( 3553, renderTarget.texture, renderTarget.width, renderTarget.height ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); } - state.bindTexture( 3553, null ); - } - // Setup depth and stencil buffers + } - if ( renderTarget.depthBuffer ) { + const children = object.children; - setupDepthRenderbuffer( renderTarget ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - } + renderObject( children[ i ], camera, shadowCamera, light, type ); } - function updateRenderTargetMipmap( renderTarget ) { + } - var texture = renderTarget.texture; - var supportsMips = isPowerOfTwo( renderTarget ) || capabilities.isWebGL2; +} - if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { +function WebGLState( gl, extensions, capabilities ) { - var target = renderTarget.isWebGLRenderTargetCube ? 34067 : 3553; - var webglTexture = properties.get( texture ).__webglTexture; + const isWebGL2 = capabilities.isWebGL2; - state.bindTexture( target, webglTexture ); - generateMipmap( target, texture, renderTarget.width, renderTarget.height ); - state.bindTexture( target, null ); + function ColorBuffer() { - } + let locked = false; - } + const color = new Vector4(); + let currentColorMask = null; + const currentColorClear = new Vector4( 0, 0, 0, 0 ); - function updateMultisampleRenderTarget( renderTarget ) { + return { - if ( renderTarget.isWebGLMultisampleRenderTarget ) { + setMask: function ( colorMask ) { - if ( capabilities.isWebGL2 ) { + if ( currentColorMask !== colorMask && ! locked ) { - var renderTargetProperties = properties.get( renderTarget ); + gl.colorMask( colorMask, colorMask, colorMask, colorMask ); + currentColorMask = colorMask; - _gl.bindFramebuffer( 36008, renderTargetProperties.__webglMultisampledFramebuffer ); - _gl.bindFramebuffer( 36009, renderTargetProperties.__webglFramebuffer ); + } - var width = renderTarget.width; - var height = renderTarget.height; - var mask = 16384; + }, - if ( renderTarget.depthBuffer ) mask |= 256; - if ( renderTarget.stencilBuffer ) mask |= 1024; + setLocked: function ( lock ) { - _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, 9728 ); + locked = lock; - } else { + }, - console.warn( 'THREE.WebGLRenderer: WebGLMultisampleRenderTarget can only be used with WebGL2.' ); + setClear: function ( r, g, b, a, premultipliedAlpha ) { - } + if ( premultipliedAlpha === true ) { - } + r *= a; g *= a; b *= a; - } + } - function getRenderTargetSamples( renderTarget ) { + color.set( r, g, b, a ); - return ( capabilities.isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? - Math.min( capabilities.maxSamples, renderTarget.samples ) : 0; + if ( currentColorClear.equals( color ) === false ) { - } + gl.clearColor( r, g, b, a ); + currentColorClear.copy( color ); - function updateVideoTexture( texture ) { + } - var frame = info.render.frame; + }, - // Check the last frame we updated the VideoTexture + reset: function () { - if ( _videoTextures.get( texture ) !== frame ) { + locked = false; - _videoTextures.set( texture, frame ); - texture.update(); + currentColorMask = null; + currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state } - } + }; - // backwards compatibility + } - var warnedTexture2D = false; - var warnedTextureCube = false; + function DepthBuffer() { - function safeSetTexture2D( texture, slot ) { + let locked = false; - if ( texture && texture.isWebGLRenderTarget ) { + let currentDepthMask = null; + let currentDepthFunc = null; + let currentDepthClear = null; - if ( warnedTexture2D === false ) { + return { - console.warn( "THREE.WebGLTextures.safeSetTexture2D: don't use render targets as textures. Use their .texture property instead." ); - warnedTexture2D = true; + setTest: function ( depthTest ) { - } + if ( depthTest ) { - texture = texture.texture; + enable( gl.DEPTH_TEST ); - } + } else { - setTexture2D( texture, slot ); + disable( gl.DEPTH_TEST ); - } + } - function safeSetTextureCube( texture, slot ) { + }, - if ( texture && texture.isWebGLRenderTargetCube ) { + setMask: function ( depthMask ) { - if ( warnedTextureCube === false ) { + if ( currentDepthMask !== depthMask && ! locked ) { - console.warn( "THREE.WebGLTextures.safeSetTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); - warnedTextureCube = true; + gl.depthMask( depthMask ); + currentDepthMask = depthMask; } - texture = texture.texture; + }, - } + setFunc: function ( depthFunc ) { - // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture - // TODO: unify these code paths - if ( ( texture && texture.isCubeTexture ) || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + if ( currentDepthFunc !== depthFunc ) { - // CompressedTexture can have Array in image :/ + switch ( depthFunc ) { - // this function alone should take care of cube textures - setTextureCube( texture, slot ); + case NeverDepth: - } else { + gl.depthFunc( gl.NEVER ); + break; - // assumed: texture property of THREE.WebGLRenderTargetCube - setTextureCubeDynamic( texture, slot ); + case AlwaysDepth: - } + gl.depthFunc( gl.ALWAYS ); + break; - } + case LessDepth: - // + gl.depthFunc( gl.LESS ); + break; - this.allocateTextureUnit = allocateTextureUnit; - this.resetTextureUnits = resetTextureUnits; + case LessEqualDepth: - this.setTexture2D = setTexture2D; - this.setTexture2DArray = setTexture2DArray; - this.setTexture3D = setTexture3D; - this.setTextureCube = setTextureCube; - this.setTextureCubeDynamic = setTextureCubeDynamic; - this.setupRenderTarget = setupRenderTarget; - this.updateRenderTargetMipmap = updateRenderTargetMipmap; - this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + gl.depthFunc( gl.LEQUAL ); + break; - this.safeSetTexture2D = safeSetTexture2D; - this.safeSetTextureCube = safeSetTextureCube; + case EqualDepth: - } + gl.depthFunc( gl.EQUAL ); + break; - /** - * @author thespite / http://www.twitter.com/thespite - */ + case GreaterEqualDepth: - function WebGLUtils( gl, extensions, capabilities ) { + gl.depthFunc( gl.GEQUAL ); + break; - function convert( p ) { + case GreaterDepth: - var extension; + gl.depthFunc( gl.GREATER ); + break; - if ( p === RepeatWrapping ) return 10497; - if ( p === ClampToEdgeWrapping ) return 33071; - if ( p === MirroredRepeatWrapping ) return 33648; + case NotEqualDepth: - if ( p === NearestFilter ) return 9728; - if ( p === NearestMipmapNearestFilter ) return 9984; - if ( p === NearestMipmapLinearFilter ) return 9986; + gl.depthFunc( gl.NOTEQUAL ); + break; - if ( p === LinearFilter ) return 9729; - if ( p === LinearMipmapNearestFilter ) return 9985; - if ( p === LinearMipmapLinearFilter ) return 9987; + default: - if ( p === UnsignedByteType ) return 5121; - if ( p === UnsignedShort4444Type ) return 32819; - if ( p === UnsignedShort5551Type ) return 32820; - if ( p === UnsignedShort565Type ) return 33635; + gl.depthFunc( gl.LEQUAL ); - if ( p === ByteType ) return 5120; - if ( p === ShortType ) return 5122; - if ( p === UnsignedShortType ) return 5123; - if ( p === IntType ) return 5124; - if ( p === UnsignedIntType ) return 5125; - if ( p === FloatType ) return 5126; + } - if ( p === HalfFloatType ) { + currentDepthFunc = depthFunc; - if ( capabilities.isWebGL2 ) return 5131; + } - extension = extensions.get( 'OES_texture_half_float' ); + }, - if ( extension !== null ) return extension.HALF_FLOAT_OES; + setLocked: function ( lock ) { - } + locked = lock; - if ( p === AlphaFormat ) return 6406; - if ( p === RGBFormat ) return 6407; - if ( p === RGBAFormat ) return 6408; - if ( p === LuminanceFormat ) return 6409; - if ( p === LuminanceAlphaFormat ) return 6410; - if ( p === DepthFormat ) return 6402; - if ( p === DepthStencilFormat ) return 34041; - if ( p === RedFormat ) return 6403; + }, - if ( p === AddEquation ) return 32774; - if ( p === SubtractEquation ) return 32778; - if ( p === ReverseSubtractEquation ) return 32779; + setClear: function ( depth ) { - if ( p === ZeroFactor ) return 0; - if ( p === OneFactor ) return 1; - if ( p === SrcColorFactor ) return 768; - if ( p === OneMinusSrcColorFactor ) return 769; - if ( p === SrcAlphaFactor ) return 770; - if ( p === OneMinusSrcAlphaFactor ) return 771; - if ( p === DstAlphaFactor ) return 772; - if ( p === OneMinusDstAlphaFactor ) return 773; + if ( currentDepthClear !== depth ) { - if ( p === DstColorFactor ) return 774; - if ( p === OneMinusDstColorFactor ) return 775; - if ( p === SrcAlphaSaturateFactor ) return 776; + gl.clearDepth( depth ); + currentDepthClear = depth; - if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || - p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { + } - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + }, - if ( extension !== null ) { + reset: function () { - 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; + locked = false; - } + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; } - 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; + function StencilBuffer() { - } + let locked = false; - } + let currentStencilMask = null; + let currentStencilFunc = null; + let currentStencilRef = null; + let currentStencilFuncMask = null; + let currentStencilFail = null; + let currentStencilZFail = null; + let currentStencilZPass = null; + let currentStencilClear = null; - if ( p === RGB_ETC1_Format ) { + return { - extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); + setTest: function ( stencilTest ) { - if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL; + if ( ! locked ) { - } + if ( stencilTest ) { - 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 ) { + enable( gl.STENCIL_TEST ); - extension = extensions.get( 'WEBGL_compressed_texture_astc' ); + } else { - if ( extension !== null ) { + disable( gl.STENCIL_TEST ); - return p; + } } - } + }, - if ( p === MinEquation || p === MaxEquation ) { + setMask: function ( stencilMask ) { - if ( capabilities.isWebGL2 ) { + if ( currentStencilMask !== stencilMask && ! locked ) { - if ( p === MinEquation ) return 32775; - if ( p === MaxEquation ) return 32776; + gl.stencilMask( stencilMask ); + currentStencilMask = stencilMask; } - extension = extensions.get( 'EXT_blend_minmax' ); - - if ( extension !== null ) { + }, - if ( p === MinEquation ) return extension.MIN_EXT; - if ( p === MaxEquation ) return extension.MAX_EXT; + setFunc: function ( stencilFunc, stencilRef, stencilMask ) { - } + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilFuncMask !== stencilMask ) { - } + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - if ( p === UnsignedInt248Type ) { + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; - if ( capabilities.isWebGL2 ) return 34042; + } - extension = extensions.get( 'WEBGL_depth_texture' ); + }, - if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL; + setOp: function ( stencilFail, stencilZFail, stencilZPass ) { - } + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { - return 0; + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - } + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; - return { convert: convert }; + } - } + }, - /** - * @author mrdoob / http://mrdoob.com/ - */ + setLocked: function ( lock ) { - function Group() { + locked = lock; - Object3D.call( this ); + }, - this.type = 'Group'; + setClear: function ( stencil ) { - } + if ( currentStencilClear !== stencil ) { - Group.prototype = Object.assign( Object.create( Object3D.prototype ), { + gl.clearStencil( stencil ); + currentStencilClear = stencil; - constructor: Group, + } - isGroup: true + }, - } ); + reset: function () { - /** - * @author mrdoob / http://mrdoob.com/ - */ + locked = false; - function ArrayCamera( array ) { + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; - PerspectiveCamera.call( this ); + } - this.cameras = array || []; + }; } - ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), { + // - constructor: ArrayCamera, + const colorBuffer = new ColorBuffer(); + const depthBuffer = new DepthBuffer(); + const stencilBuffer = new StencilBuffer(); - isArrayCamera: true + const uboBindings = new WeakMap(); + const uboProgramMap = new WeakMap(); - } ); + let enabledCapabilities = {}; - /** - * @author jsantell / https://www.jsantell.com/ - * @author mrdoob / http://mrdoob.com/ - */ + let currentBoundFramebuffers = {}; + let currentDrawbuffers = new WeakMap(); + let defaultDrawbuffers = []; - var cameraLPos = new Vector3(); - var cameraRPos = new Vector3(); + let currentProgram = null; - /** - * Assumes 2 cameras that are parallel and share an X-axis, and that - * the cameras' projection and world matrices have already been set. - * And that near and far planes are identical for both cameras. - * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 - */ - function setProjectionFromUnion( camera, cameraL, cameraR ) { + let currentBlendingEnabled = false; + let currentBlending = null; + let currentBlendEquation = null; + let currentBlendSrc = null; + let currentBlendDst = null; + let currentBlendEquationAlpha = null; + let currentBlendSrcAlpha = null; + let currentBlendDstAlpha = null; + let currentPremultipledAlpha = false; - cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); - cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); + let currentFlipSided = null; + let currentCullFace = null; - var ipd = cameraLPos.distanceTo( cameraRPos ); + let currentLineWidth = null; - var projL = cameraL.projectionMatrix.elements; - var projR = cameraR.projectionMatrix.elements; + let currentPolygonOffsetFactor = null; + let currentPolygonOffsetUnits = null; - // VR systems will have identical far and near planes, and - // most likely identical top and bottom frustum extents. - // Use the left camera for these values. - var near = projL[ 14 ] / ( projL[ 10 ] - 1 ); - var far = projL[ 14 ] / ( projL[ 10 ] + 1 ); - var topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; - var bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; + const maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ); - var leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; - var rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; - var left = near * leftFov; - var right = near * rightFov; + let lineWidthAvailable = false; + let version = 0; + const glVersion = gl.getParameter( gl.VERSION ); - // Calculate the new camera's position offset from the - // left camera. xOffset should be roughly half `ipd`. - var zOffset = ipd / ( - leftFov + rightFov ); - var xOffset = zOffset * - leftFov; + if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) { - // TODO: Better way to apply this offset? - cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); - camera.translateX( xOffset ); - camera.translateZ( zOffset ); - camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + version = parseFloat( /^WebGL (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 1.0 ); - // Find the union of the frustum values of the cameras and scale - // the values so that the near plane's position does not change in world space, - // although must now be relative to the new union camera. - var near2 = near + zOffset; - var far2 = far + zOffset; - var left2 = left - xOffset; - var right2 = right + ( ipd - xOffset ); - var top2 = topFov * far / far2 * near2; - var bottom2 = bottomFov * far / far2 * near2; + } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) { - camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + version = parseFloat( /^OpenGL ES (\d)/.exec( glVersion )[ 1 ] ); + lineWidthAvailable = ( version >= 2.0 ); } - /** - * @author mrdoob / http://mrdoob.com/ - */ + let currentTextureSlot = null; + let currentBoundTextures = {}; - function WebVRManager( renderer ) { + const scissorParam = gl.getParameter( gl.SCISSOR_BOX ); + const viewportParam = gl.getParameter( gl.VIEWPORT ); - var renderWidth, renderHeight; - var scope = this; + const currentScissor = new Vector4().fromArray( scissorParam ); + const currentViewport = new Vector4().fromArray( viewportParam ); - var device = null; - var frameData = null; + function createTexture( type, target, count, dimensions ) { - var poseTarget = null; + const data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. + const texture = gl.createTexture(); - var controllers = []; - var standingMatrix = new Matrix4(); - var standingMatrixInverse = new Matrix4(); + gl.bindTexture( type, texture ); + gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - var framebufferScaleFactor = 1.0; + for ( let i = 0; i < count; i ++ ) { - var referenceSpaceType = 'local-floor'; + if ( isWebGL2 && ( type === gl.TEXTURE_3D || type === gl.TEXTURE_2D_ARRAY ) ) { - if ( typeof window !== 'undefined' && 'VRFrameData' in window ) { + gl.texImage3D( target, 0, gl.RGBA, 1, 1, dimensions, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); - frameData = new window.VRFrameData(); - window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false ); + } else { - } + gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); - var matrixWorldInverse = new Matrix4(); - var tempQuaternion = new Quaternion(); - var tempPosition = new Vector3(); + } - var cameraL = new PerspectiveCamera(); - cameraL.viewport = new Vector4(); - cameraL.layers.enable( 1 ); + } - var cameraR = new PerspectiveCamera(); - cameraR.viewport = new Vector4(); - cameraR.layers.enable( 2 ); + return texture; - var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); + } - // + const emptyTextures = {}; + emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); + emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); - function isPresenting() { + if ( isWebGL2 ) { - return device !== null && device.isPresenting === true; + emptyTextures[ gl.TEXTURE_2D_ARRAY ] = createTexture( gl.TEXTURE_2D_ARRAY, gl.TEXTURE_2D_ARRAY, 1, 1 ); + emptyTextures[ gl.TEXTURE_3D ] = createTexture( gl.TEXTURE_3D, gl.TEXTURE_3D, 1, 1 ); - } + } - var currentSize = new Vector2(), currentPixelRatio; + // init - function onVRDisplayPresentChange() { + colorBuffer.setClear( 0, 0, 0, 1 ); + depthBuffer.setClear( 1 ); + stencilBuffer.setClear( 0 ); - if ( isPresenting() ) { + enable( gl.DEPTH_TEST ); + depthBuffer.setFunc( LessEqualDepth ); - var eyeParameters = device.getEyeParameters( 'left' ); - renderWidth = 2 * eyeParameters.renderWidth * framebufferScaleFactor; - renderHeight = eyeParameters.renderHeight * framebufferScaleFactor; + setFlipSided( false ); + setCullFace( CullFaceBack ); + enable( gl.CULL_FACE ); - currentPixelRatio = renderer.getPixelRatio(); - renderer.getSize( currentSize ); + setBlending( NoBlending ); - renderer.setDrawingBufferSize( renderWidth, renderHeight, 1 ); + // - cameraL.viewport.set( 0, 0, renderWidth / 2, renderHeight ); - cameraR.viewport.set( renderWidth / 2, 0, renderWidth / 2, renderHeight ); + function enable( id ) { - animation.start(); + if ( enabledCapabilities[ id ] !== true ) { - scope.dispatchEvent( { type: 'sessionstart' } ); + gl.enable( id ); + enabledCapabilities[ id ] = true; - } else { + } - if ( scope.enabled ) { + } - renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio ); + function disable( id ) { - } + if ( enabledCapabilities[ id ] !== false ) { - animation.stop(); + gl.disable( id ); + enabledCapabilities[ id ] = false; - scope.dispatchEvent( { type: 'sessionend' } ); + } - } + } - } + function bindFramebuffer( target, framebuffer ) { - // + if ( currentBoundFramebuffers[ target ] !== framebuffer ) { + + gl.bindFramebuffer( target, framebuffer ); - var triggers = []; + currentBoundFramebuffers[ target ] = framebuffer; - function findGamepad( id ) { + if ( isWebGL2 ) { - var gamepads = navigator.getGamepads && navigator.getGamepads(); + // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER - for ( var i = 0, j = 0, l = gamepads.length; i < l; i ++ ) { + if ( target === gl.DRAW_FRAMEBUFFER ) { - var gamepad = gamepads[ i ]; + currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer; - if ( gamepad && ( gamepad.id === 'Daydream Controller' || - gamepad.id === 'Gear VR Controller' || gamepad.id === 'Oculus Go Controller' || - gamepad.id === 'OpenVR Gamepad' || gamepad.id.startsWith( 'Oculus Touch' ) || - gamepad.id.startsWith( 'HTC Vive Focus' ) || - gamepad.id.startsWith( 'Spatial Controller' ) ) ) { + } - if ( j === id ) return gamepad; + if ( target === gl.FRAMEBUFFER ) { - j ++; + currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer; } } + return true; + } - function updateControllers() { + return false; - for ( var i = 0; i < controllers.length; i ++ ) { + } - var controller = controllers[ i ]; + function drawBuffers( renderTarget, framebuffer ) { - var gamepad = findGamepad( i ); + let drawBuffers = defaultDrawbuffers; - if ( gamepad !== undefined && gamepad.pose !== undefined ) { + let needsUpdate = false; - if ( gamepad.pose === null ) return; + if ( renderTarget ) { - // Pose + drawBuffers = currentDrawbuffers.get( framebuffer ); - var pose = gamepad.pose; + if ( drawBuffers === undefined ) { - if ( pose.hasPosition === false ) controller.position.set( 0.2, - 0.6, - 0.05 ); + drawBuffers = []; + currentDrawbuffers.set( framebuffer, drawBuffers ); - if ( pose.position !== null ) controller.position.fromArray( pose.position ); - if ( pose.orientation !== null ) controller.quaternion.fromArray( pose.orientation ); - controller.matrix.compose( controller.position, controller.quaternion, controller.scale ); - controller.matrix.premultiply( standingMatrix ); - controller.matrix.decompose( controller.position, controller.quaternion, controller.scale ); - controller.matrixWorldNeedsUpdate = true; - controller.visible = true; + } - // Trigger + if ( renderTarget.isWebGLMultipleRenderTargets ) { - var buttonId = gamepad.id === 'Daydream Controller' ? 0 : 1; + const textures = renderTarget.texture; - if ( triggers[ i ] === undefined ) triggers[ i ] = false; + if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { - if ( triggers[ i ] !== gamepad.buttons[ buttonId ].pressed ) { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - triggers[ i ] = gamepad.buttons[ buttonId ].pressed; + drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i; - if ( triggers[ i ] === true ) { + } - controller.dispatchEvent( { type: 'selectstart' } ); + drawBuffers.length = textures.length; - } else { + needsUpdate = true; - controller.dispatchEvent( { type: 'selectend' } ); - controller.dispatchEvent( { type: 'select' } ); + } - } + } else { - } + if ( drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) { - } else { + drawBuffers[ 0 ] = gl.COLOR_ATTACHMENT0; - controller.visible = false; + needsUpdate = true; } } - } + } else { - function updateViewportFromBounds( viewport, bounds ) { + if ( drawBuffers[ 0 ] !== gl.BACK ) { - if ( bounds !== null && bounds.length === 4 ) { + drawBuffers[ 0 ] = gl.BACK; - viewport.set( bounds[ 0 ] * renderWidth, bounds[ 1 ] * renderHeight, bounds[ 2 ] * renderWidth, bounds[ 3 ] * renderHeight ); + needsUpdate = true; } } - // + if ( needsUpdate ) { - this.enabled = false; + if ( capabilities.isWebGL2 ) { - this.getController = function ( id ) { + gl.drawBuffers( drawBuffers ); - var controller = controllers[ id ]; + } else { - if ( controller === undefined ) { + extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( drawBuffers ); - controller = new Group(); - controller.matrixAutoUpdate = false; - controller.visible = false; + } - controllers[ id ] = controller; + } - } - return controller; + } - }; + function useProgram( program ) { - this.getDevice = function () { + if ( currentProgram !== program ) { - return device; + gl.useProgram( program ); - }; + currentProgram = program; - this.setDevice = function ( value ) { + return true; - if ( value !== undefined ) device = value; + } - animation.setContext( value ); + return false; - }; + } - this.setFramebufferScaleFactor = function ( value ) { + const equationToGL = { + [ AddEquation ]: gl.FUNC_ADD, + [ SubtractEquation ]: gl.FUNC_SUBTRACT, + [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT + }; - framebufferScaleFactor = value; + if ( isWebGL2 ) { - }; + equationToGL[ MinEquation ] = gl.MIN; + equationToGL[ MaxEquation ] = gl.MAX; - this.setReferenceSpaceType = function ( value ) { + } else { - referenceSpaceType = value; + const extension = extensions.get( 'EXT_blend_minmax' ); - }; + if ( extension !== null ) { - this.setPoseTarget = function ( object ) { + equationToGL[ MinEquation ] = extension.MIN_EXT; + equationToGL[ MaxEquation ] = extension.MAX_EXT; - if ( object !== undefined ) poseTarget = object; + } - }; + } - this.getCamera = function ( camera ) { + const 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 + }; - var userHeight = referenceSpaceType === 'local-floor' ? 1.6 : 0; + function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { - if ( isPresenting() === false ) { + if ( blending === NoBlending ) { - camera.position.set( 0, userHeight, 0 ); - camera.rotation.set( 0, 0, 0 ); + if ( currentBlendingEnabled === true ) { - return camera; + disable( gl.BLEND ); + currentBlendingEnabled = false; } - device.depthNear = camera.near; - device.depthFar = camera.far; + return; + + } - device.getFrameData( frameData ); + if ( currentBlendingEnabled === false ) { - // + enable( gl.BLEND ); + currentBlendingEnabled = true; - if ( referenceSpaceType === 'local-floor' ) { + } - var stageParameters = device.stageParameters; + if ( blending !== CustomBlending ) { - if ( stageParameters ) { + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { - standingMatrix.fromArray( stageParameters.sittingToStandingTransform ); + if ( currentBlendEquation !== AddEquation || currentBlendEquationAlpha !== AddEquation ) { - } else { + gl.blendEquation( gl.FUNC_ADD ); - standingMatrix.makeTranslation( 0, userHeight, 0 ); + currentBlendEquation = AddEquation; + currentBlendEquationAlpha = AddEquation; } - } + if ( premultipliedAlpha ) { + switch ( blending ) { - var pose = frameData.pose; - var poseObject = poseTarget !== null ? poseTarget : camera; + case NormalBlending: + gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); + break; - // We want to manipulate poseObject by its position and quaternion components since users may rely on them. - poseObject.matrix.copy( standingMatrix ); - poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale ); + case AdditiveBlending: + gl.blendFunc( gl.ONE, gl.ONE ); + break; - if ( pose.orientation !== null ) { + case SubtractiveBlending: + gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); + break; - tempQuaternion.fromArray( pose.orientation ); - poseObject.quaternion.multiply( tempQuaternion ); + case MultiplyBlending: + gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); + break; - } + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - if ( pose.position !== null ) { + } - tempQuaternion.setFromRotationMatrix( standingMatrix ); - tempPosition.fromArray( pose.position ); - tempPosition.applyQuaternion( tempQuaternion ); - poseObject.position.add( tempPosition ); + } else { - } + switch ( blending ) { - poseObject.updateMatrixWorld(); + 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; - cameraL.near = camera.near; - cameraR.near = camera.near; + case SubtractiveBlending: + gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE ); + break; - cameraL.far = camera.far; - cameraR.far = camera.far; + case MultiplyBlending: + gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); + break; - cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix ); - cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix ); + default: + console.error( 'THREE.WebGLState: Invalid blending: ', blending ); + break; - // TODO (mrdoob) Double check this code + } - standingMatrixInverse.getInverse( standingMatrix ); + } - if ( referenceSpaceType === 'local-floor' ) { + currentBlendSrc = null; + currentBlendDst = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; - cameraL.matrixWorldInverse.multiply( standingMatrixInverse ); - cameraR.matrixWorldInverse.multiply( standingMatrixInverse ); + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; } - var parent = poseObject.parent; - - if ( parent !== null ) { + return; - matrixWorldInverse.getInverse( parent.matrixWorld ); + } - cameraL.matrixWorldInverse.multiply( matrixWorldInverse ); - cameraR.matrixWorldInverse.multiply( matrixWorldInverse ); + // custom blending - } + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - // envMap and Mirror needs camera.matrixWorld + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse ); - cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse ); + gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] ); - cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix ); - cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix ); + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - setProjectionFromUnion( cameraVR, cameraL, cameraR ); + } - // + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - var layers = device.getLayers(); + gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] ); - if ( layers.length ) { + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; - var layer = layers[ 0 ]; + } - updateViewportFromBounds( cameraL.viewport, layer.leftBounds ); - updateViewportFromBounds( cameraR.viewport, layer.rightBounds ); + currentBlending = blending; + currentPremultipledAlpha = false; - } + } - updateControllers(); + function setMaterial( material, frontFaceCW ) { - return cameraVR; + material.side === DoubleSide + ? disable( gl.CULL_FACE ) + : enable( gl.CULL_FACE ); - }; + let flipSided = ( material.side === BackSide ); + if ( frontFaceCW ) flipSided = ! flipSided; - this.getStandingMatrix = function () { + setFlipSided( flipSided ); - return standingMatrix; + ( material.blending === NormalBlending && material.transparent === false ) + ? setBlending( NoBlending ) + : setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - }; + depthBuffer.setFunc( material.depthFunc ); + depthBuffer.setTest( material.depthTest ); + depthBuffer.setMask( material.depthWrite ); + colorBuffer.setMask( material.colorWrite ); - this.isPresenting = isPresenting; + const stencilWrite = material.stencilWrite; + stencilBuffer.setTest( stencilWrite ); + if ( stencilWrite ) { - // Animation Loop + stencilBuffer.setMask( material.stencilWriteMask ); + stencilBuffer.setFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask ); + stencilBuffer.setOp( material.stencilFail, material.stencilZFail, material.stencilZPass ); - var animation = new WebGLAnimation(); + } - this.setAnimationLoop = function ( callback ) { + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - animation.setAnimationLoop( callback ); + material.alphaToCoverage === true + ? enable( gl.SAMPLE_ALPHA_TO_COVERAGE ) + : disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); - if ( isPresenting() ) animation.start(); + } - }; + // - this.submitFrame = function () { + function setFlipSided( flipSided ) { - if ( isPresenting() ) device.submitFrame(); + if ( currentFlipSided !== flipSided ) { - }; + if ( flipSided ) { - this.dispose = function () { + gl.frontFace( gl.CW ); - if ( typeof window !== 'undefined' ) { + } else { - window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange ); + gl.frontFace( gl.CCW ); } - }; + currentFlipSided = flipSided; - // DEPRECATED + } - this.setFrameOfReferenceType = function () { + } - console.warn( 'THREE.WebVRManager: setFrameOfReferenceType() has been deprecated.' ); + function setCullFace( cullFace ) { - }; + if ( cullFace !== CullFaceNone ) { - } + enable( gl.CULL_FACE ); - Object.assign( WebVRManager.prototype, EventDispatcher.prototype ); + if ( cullFace !== currentCullFace ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( cullFace === CullFaceBack ) { + + gl.cullFace( gl.BACK ); - function WebXRManager( renderer, gl ) { + } else if ( cullFace === CullFaceFront ) { - var scope = this; + gl.cullFace( gl.FRONT ); - var session = null; + } else { - var referenceSpace = null; - var referenceSpaceType = 'local-floor'; + gl.cullFace( gl.FRONT_AND_BACK ); - var pose = null; + } - var controllers = []; - var inputSources = []; + } - function isPresenting() { + } else { - return session !== null && referenceSpace !== null; + disable( gl.CULL_FACE ); } - // - - var cameraL = new PerspectiveCamera(); - cameraL.layers.enable( 1 ); - cameraL.viewport = new Vector4(); + currentCullFace = cullFace; - var cameraR = new PerspectiveCamera(); - cameraR.layers.enable( 2 ); - cameraR.viewport = new Vector4(); + } - var cameraVR = new ArrayCamera( [ cameraL, cameraR ] ); - cameraVR.layers.enable( 1 ); - cameraVR.layers.enable( 2 ); + function setLineWidth( width ) { - // + if ( width !== currentLineWidth ) { - this.enabled = false; + if ( lineWidthAvailable ) gl.lineWidth( width ); - this.getController = function ( id ) { + currentLineWidth = width; - var controller = controllers[ id ]; + } - if ( controller === undefined ) { + } - controller = new Group(); - controller.matrixAutoUpdate = false; - controller.visible = false; + function setPolygonOffset( polygonOffset, factor, units ) { - controllers[ id ] = controller; + if ( polygonOffset ) { - } + enable( gl.POLYGON_OFFSET_FILL ); - return controller; + if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - }; + gl.polygonOffset( factor, units ); - // + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; - function onSessionEvent( event ) { + } - for ( var i = 0; i < controllers.length; i ++ ) { + } else { - if ( inputSources[ i ] === event.inputSource ) { + disable( gl.POLYGON_OFFSET_FILL ); - controllers[ i ].dispatchEvent( { type: event.type } ); + } - } + } - } + function setScissorTest( scissorTest ) { - } + if ( scissorTest ) { - function onSessionEnd() { + enable( gl.SCISSOR_TEST ); - renderer.setFramebuffer( null ); - renderer.setRenderTarget( renderer.getRenderTarget() ); // Hack #15830 - animation.stop(); + } else { - scope.dispatchEvent( { type: 'sessionend' } ); + disable( gl.SCISSOR_TEST ); } - function onRequestReferenceSpace( value ) { + } - referenceSpace = value; + // texture - animation.setContext( session ); - animation.start(); + function activeTexture( webglSlot ) { - scope.dispatchEvent( { type: 'sessionstart' } ); + if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; - } + if ( currentTextureSlot !== webglSlot ) { - this.setFramebufferScaleFactor = function ( value ) { + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; - }; + } - this.setReferenceSpaceType = function ( value ) { + } - referenceSpaceType = value; + function bindTexture( webglType, webglTexture, webglSlot ) { - }; + if ( webglSlot === undefined ) { - this.getSession = function () { + if ( currentTextureSlot === null ) { - return session; + webglSlot = gl.TEXTURE0 + maxTextures - 1; - }; + } else { - this.setSession = function ( value ) { + webglSlot = currentTextureSlot; - session = value; + } - if ( session !== null ) { + } - session.addEventListener( 'select', onSessionEvent ); - session.addEventListener( 'selectstart', onSessionEvent ); - session.addEventListener( 'selectend', onSessionEvent ); - session.addEventListener( 'end', onSessionEnd ); + let boundTexture = currentBoundTextures[ webglSlot ]; - session.updateRenderState( { baseLayer: new XRWebGLLayer( session, gl ) } ); + if ( boundTexture === undefined ) { - session.requestReferenceSpace( referenceSpaceType ).then( onRequestReferenceSpace ); + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ webglSlot ] = boundTexture; - // + } - inputSources = session.inputSources; + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - session.addEventListener( 'inputsourceschange', function () { + if ( currentTextureSlot !== webglSlot ) { - inputSources = session.inputSources; - console.log( inputSources ); + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; - for ( var i = 0; i < controllers.length; i ++ ) { + } - var controller = controllers[ i ]; - controller.userData.inputSource = inputSources[ i ]; + gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] ); - } + boundTexture.type = webglType; + boundTexture.texture = webglTexture; - } ); + } - } + } - }; + function unbindTexture() { - function updateCamera( camera, parent ) { + const boundTexture = currentBoundTextures[ currentTextureSlot ]; - if ( parent === null ) { + if ( boundTexture !== undefined && boundTexture.type !== undefined ) { - camera.matrixWorld.copy( camera.matrix ); + gl.bindTexture( boundTexture.type, null ); - } else { + boundTexture.type = undefined; + boundTexture.texture = undefined; - camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); + } - } + } + + function compressedTexImage2D() { + + try { + + gl.compressedTexImage2D.apply( gl, arguments ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + } catch ( error ) { + + console.error( 'THREE.WebGLState:', error ); } - this.getCamera = function ( camera ) { + } - if ( isPresenting() ) { + function compressedTexImage3D() { - var parent = camera.parent; - var cameras = cameraVR.cameras; + try { - updateCamera( cameraVR, parent ); + gl.compressedTexImage3D.apply( gl, arguments ); - for ( var i = 0; i < cameras.length; i ++ ) { + } catch ( error ) { - updateCamera( cameras[ i ], parent ); + console.error( 'THREE.WebGLState:', error ); - } + } - // update camera and its children + } - camera.matrixWorld.copy( cameraVR.matrixWorld ); + function texSubImage2D() { - var children = camera.children; + try { - for ( var i = 0, l = children.length; i < l; i ++ ) { + gl.texSubImage2D.apply( gl, arguments ); - children[ i ].updateMatrixWorld( true ); + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); - setProjectionFromUnion( cameraVR, cameraL, cameraR ); + } - return cameraVR; + } - } + function texSubImage3D() { - return camera; + try { - }; + gl.texSubImage3D.apply( gl, arguments ); - this.isPresenting = isPresenting; + } catch ( error ) { - // Animation Loop + console.error( 'THREE.WebGLState:', error ); - var onAnimationFrameCallback = null; + } - function onAnimationFrame( time, frame ) { + } - pose = frame.getViewerPose( referenceSpace ); + function compressedTexSubImage2D() { - if ( pose !== null ) { + try { - var views = pose.views; - var baseLayer = session.renderState.baseLayer; + gl.compressedTexSubImage2D.apply( gl, arguments ); - renderer.setFramebuffer( baseLayer.framebuffer ); + } catch ( error ) { - for ( var i = 0; i < views.length; i ++ ) { + console.error( 'THREE.WebGLState:', error ); - var view = views[ i ]; - var viewport = baseLayer.getViewport( view ); - var viewMatrix = view.transform.inverse.matrix; + } - var camera = cameraVR.cameras[ i ]; - camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix ); - camera.projectionMatrix.fromArray( view.projectionMatrix ); - camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); + } - if ( i === 0 ) { + function compressedTexSubImage3D() { - cameraVR.matrix.copy( camera.matrix ); + try { - } + gl.compressedTexSubImage3D.apply( gl, arguments ); - } + } catch ( error ) { - } + console.error( 'THREE.WebGLState:', error ); - // + } - for ( var i = 0; i < controllers.length; i ++ ) { + } - var controller = controllers[ i ]; + function texStorage2D() { - var inputSource = inputSources[ i ]; + try { - if ( inputSource ) { + gl.texStorage2D.apply( gl, arguments ); - var inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); + } catch ( error ) { - if ( inputPose !== null ) { + console.error( 'THREE.WebGLState:', error ); - controller.matrix.fromArray( inputPose.transform.matrix ); - controller.matrix.decompose( controller.position, controller.rotation, controller.scale ); - controller.visible = true; + } - continue; + } - } + function texStorage3D() { - } + try { - controller.visible = false; + gl.texStorage3D.apply( gl, arguments ); - } + } catch ( error ) { - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); + console.error( 'THREE.WebGLState:', error ); } - var animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + } - this.setAnimationLoop = function ( callback ) { + function texImage2D() { - onAnimationFrameCallback = callback; + try { - }; + gl.texImage2D.apply( gl, arguments ); - this.dispose = function () {}; + } catch ( error ) { - // DEPRECATED + console.error( 'THREE.WebGLState:', error ); - this.getStandingMatrix = function () { + } - console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' ); - return new Matrix4(); + } - }; + function texImage3D() { - this.getDevice = function () { + try { - console.warn( 'THREE.WebXRManager: getDevice() has been deprecated.' ); + gl.texImage3D.apply( gl, arguments ); - }; + } catch ( error ) { - this.setDevice = function () { + console.error( 'THREE.WebGLState:', error ); - console.warn( 'THREE.WebXRManager: setDevice() has been deprecated.' ); + } - }; + } - this.setFrameOfReferenceType = function () { + // - console.warn( 'THREE.WebXRManager: setFrameOfReferenceType() has been deprecated.' ); + function scissor( scissor ) { - }; + if ( currentScissor.equals( scissor ) === false ) { - this.submitFrame = function () {}; + gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w ); + currentScissor.copy( scissor ); + + } } - Object.assign( WebXRManager.prototype, EventDispatcher.prototype ); + function viewport( viewport ) { - /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - * @author tschw - */ + if ( currentViewport.equals( viewport ) === false ) { - function WebGLRenderer( parameters ) { + gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w ); + currentViewport.copy( viewport ); - parameters = parameters || {}; + } - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, + } - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default', - _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false; + function updateUBOMapping( uniformsGroup, program ) { - var currentRenderList = null; - var currentRenderState = null; + let mapping = uboProgramMap.get( program ); - // public properties + if ( mapping === undefined ) { - this.domElement = _canvas; + mapping = new WeakMap(); - // Debug configuration container - this.debug = { + uboProgramMap.set( program, mapping ); - /** - * Enables error checking and reporting when shader programs are being compiled - * @type {boolean} - */ - checkShaderErrors: true - }; + } - // clearing + let blockIndex = mapping.get( uniformsGroup ); - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + if ( blockIndex === undefined ) { - // scene graph + blockIndex = gl.getUniformBlockIndex( program, uniformsGroup.name ); - this.sortObjects = true; + mapping.set( uniformsGroup, blockIndex ); - // user-defined clipping + } - this.clippingPlanes = []; - this.localClippingEnabled = false; + } - // physically based shading + function uniformBlockBinding( uniformsGroup, program ) { - this.gammaFactor = 2.0; // for backwards compatibility - this.gammaInput = false; - this.gammaOutput = false; + const mapping = uboProgramMap.get( program ); + const blockIndex = mapping.get( uniformsGroup ); - // physical lights + if ( uboBindings.get( program ) !== blockIndex ) { - this.physicallyCorrectLights = false; + // bind shader specific block index to global block point + gl.uniformBlockBinding( program, blockIndex, uniformsGroup.__bindingPointIndex ); - // tone mapping + uboBindings.set( program, blockIndex ); - this.toneMapping = LinearToneMapping; - this.toneMappingExposure = 1.0; - this.toneMappingWhitePoint = 1.0; + } - // morphs + } - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; + // - // internal properties + function reset() { - var _this = this, + // reset state - _isContextLost = false, + gl.disable( gl.BLEND ); + gl.disable( gl.CULL_FACE ); + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.POLYGON_OFFSET_FILL ); + gl.disable( gl.SCISSOR_TEST ); + gl.disable( gl.STENCIL_TEST ); + gl.disable( gl.SAMPLE_ALPHA_TO_COVERAGE ); - // internal state cache + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ONE, gl.ZERO ); + gl.blendFuncSeparate( gl.ONE, gl.ZERO, gl.ONE, gl.ZERO ); - _framebuffer = null, + gl.colorMask( true, true, true, true ); + gl.clearColor( 0, 0, 0, 0 ); - _currentActiveCubeFace = 0, - _currentActiveMipmapLevel = 0, - _currentRenderTarget = null, - _currentFramebuffer = null, - _currentMaterialId = - 1, + gl.depthMask( true ); + gl.depthFunc( gl.LESS ); + gl.clearDepth( 1 ); - // geometry and program caching + gl.stencilMask( 0xffffffff ); + gl.stencilFunc( gl.ALWAYS, 0, 0xffffffff ); + gl.stencilOp( gl.KEEP, gl.KEEP, gl.KEEP ); + gl.clearStencil( 0 ); - _currentGeometryProgram = { - geometry: null, - program: null, - wireframe: false - }, + gl.cullFace( gl.BACK ); + gl.frontFace( gl.CCW ); - _currentCamera = null, - _currentArrayCamera = null, + gl.polygonOffset( 0, 0 ); - _currentViewport = new Vector4(), - _currentScissor = new Vector4(), - _currentScissorTest = null, + gl.activeTexture( gl.TEXTURE0 ); - // + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); - _width = _canvas.width, - _height = _canvas.height, + if ( isWebGL2 === true ) { - _pixelRatio = 1, + gl.bindFramebuffer( gl.DRAW_FRAMEBUFFER, null ); + gl.bindFramebuffer( gl.READ_FRAMEBUFFER, null ); - _viewport = new Vector4( 0, 0, _width, _height ), - _scissor = new Vector4( 0, 0, _width, _height ), - _scissorTest = false, + } - // frustum + gl.useProgram( null ); - _frustum = new Frustum(), + gl.lineWidth( 1 ); - // clipping + gl.scissor( 0, 0, gl.canvas.width, gl.canvas.height ); + gl.viewport( 0, 0, gl.canvas.width, gl.canvas.height ); - _clipping = new WebGLClipping(), - _clippingEnabled = false, - _localClippingEnabled = false, + // reset internals - // camera matrices cache + enabledCapabilities = {}; - _projScreenMatrix = new Matrix4(), + currentTextureSlot = null; + currentBoundTextures = {}; - _vector3 = new Vector3(); + currentBoundFramebuffers = {}; + currentDrawbuffers = new WeakMap(); + defaultDrawbuffers = []; - function getTargetPixelRatio() { + currentProgram = null; - return _currentRenderTarget === null ? _pixelRatio : 1; + currentBlendingEnabled = false; + currentBlending = null; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; + currentPremultipledAlpha = false; - } + currentFlipSided = null; + currentCullFace = null; - // initialize + currentLineWidth = null; - var _gl; + currentPolygonOffsetFactor = null; + currentPolygonOffsetUnits = null; - try { + currentScissor.set( 0, 0, gl.canvas.width, gl.canvas.height ); + currentViewport.set( 0, 0, gl.canvas.width, gl.canvas.height ); - var contextAttributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer, - powerPreference: _powerPreference, - failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat, - xrCompatible: true - }; + colorBuffer.reset(); + depthBuffer.reset(); + stencilBuffer.reset(); - // event listeners must be registered before WebGL context is created, see #12753 + } - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + return { - _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes ); + buffers: { + color: colorBuffer, + depth: depthBuffer, + stencil: stencilBuffer + }, - if ( _gl === null ) { + enable: enable, + disable: disable, - if ( _canvas.getContext( 'webgl' ) !== null ) { + bindFramebuffer: bindFramebuffer, + drawBuffers: drawBuffers, - throw new Error( 'Error creating WebGL context with your selected attributes.' ); + useProgram: useProgram, - } else { + setBlending: setBlending, + setMaterial: setMaterial, - throw new Error( 'Error creating WebGL context.' ); + setFlipSided: setFlipSided, + setCullFace: setCullFace, - } + setLineWidth: setLineWidth, + setPolygonOffset: setPolygonOffset, - } + setScissorTest: setScissorTest, - // Some experimental-webgl implementations do not have getShaderPrecisionFormat + activeTexture: activeTexture, + bindTexture: bindTexture, + unbindTexture: unbindTexture, + compressedTexImage2D: compressedTexImage2D, + compressedTexImage3D: compressedTexImage3D, + texImage2D: texImage2D, + texImage3D: texImage3D, - if ( _gl.getShaderPrecisionFormat === undefined ) { + updateUBOMapping: updateUBOMapping, + uniformBlockBinding: uniformBlockBinding, - _gl.getShaderPrecisionFormat = function () { + texStorage2D: texStorage2D, + texStorage3D: texStorage3D, + texSubImage2D: texSubImage2D, + texSubImage3D: texSubImage3D, + compressedTexSubImage2D: compressedTexSubImage2D, + compressedTexSubImage3D: compressedTexSubImage3D, - return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + scissor: scissor, + viewport: viewport, - }; + reset: reset - } + }; - } catch ( error ) { +} - console.error( 'THREE.WebGLRenderer: ' + error.message ); - throw error; +function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) { - } + const isWebGL2 = capabilities.isWebGL2; + const maxTextures = capabilities.maxTextures; + const maxCubemapSize = capabilities.maxCubemapSize; + const maxTextureSize = capabilities.maxTextureSize; + const maxSamples = capabilities.maxSamples; + const multisampledRTTExt = extensions.has( 'WEBGL_multisampled_render_to_texture' ) ? extensions.get( 'WEBGL_multisampled_render_to_texture' ) : null; + const supportsInvalidateFramebuffer = typeof navigator === 'undefined' ? false : /OculusBrowser/g.test( navigator.userAgent ); - var extensions, capabilities, state, info; - var properties, textures, attributes, geometries, objects; - var programCache, renderLists, renderStates; + const _videoTextures = new WeakMap(); + let _canvas; - var background, morphtargets, bufferRenderer, indexedBufferRenderer; + const _sources = new WeakMap(); // maps WebglTexture objects to instances of Source - var utils; + // cordova iOS (as of 5.0) still uses UIWebView, which provides OffscreenCanvas, + // also OffscreenCanvas.getContext("webgl"), but not OffscreenCanvas.getContext("2d")! + // Some implementations may only implement OffscreenCanvas partially (e.g. lacking 2d). - function initGLContext() { + let useOffscreenCanvas = false; - extensions = new WebGLExtensions( _gl ); + try { - capabilities = new WebGLCapabilities( _gl, extensions, parameters ); + useOffscreenCanvas = typeof OffscreenCanvas !== 'undefined' + // eslint-disable-next-line compat/compat + && ( new OffscreenCanvas( 1, 1 ).getContext( '2d' ) ) !== null; - if ( ! capabilities.isWebGL2 ) { + } catch ( err ) { - extensions.get( 'WEBGL_depth_texture' ); - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'OES_element_index_uint' ); - extensions.get( 'ANGLE_instanced_arrays' ); + // Ignore any errors - } + } - extensions.get( 'OES_texture_float_linear' ); + function createCanvas( width, height ) { - utils = new WebGLUtils( _gl, extensions, capabilities ); + // Use OffscreenCanvas when available. Specially needed in web workers - state = new WebGLState( _gl, extensions, utils, capabilities ); - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + return useOffscreenCanvas ? + // eslint-disable-next-line compat/compat + new OffscreenCanvas( width, height ) : createElementNS( 'canvas' ); - info = new WebGLInfo( _gl ); - properties = new WebGLProperties(); - textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); - attributes = new WebGLAttributes( _gl ); - geometries = new WebGLGeometries( _gl, attributes, info ); - objects = new WebGLObjects( geometries, info ); - morphtargets = new WebGLMorphtargets( _gl ); - programCache = new WebGLPrograms( _this, extensions, capabilities ); - renderLists = new WebGLRenderLists(); - renderStates = new WebGLRenderStates(); + } - background = new WebGLBackground( _this, state, objects, _premultipliedAlpha ); + function resizeImage( image, needsPowerOfTwo, needsNewCanvas, maxSize ) { - bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); - indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); + let scale = 1; - info.programs = programCache.programs; + // handle case if texture exceeds max size - _this.capabilities = capabilities; - _this.extensions = extensions; - _this.properties = properties; - _this.renderLists = renderLists; - _this.state = state; - _this.info = info; + if ( image.width > maxSize || image.height > maxSize ) { + + scale = maxSize / Math.max( image.width, image.height ); } - initGLContext(); + // only perform resize if necessary - // vr + if ( scale < 1 || needsPowerOfTwo === true ) { - var vr = ( typeof navigator !== 'undefined' && 'xr' in navigator && 'supportsSession' in navigator.xr ) ? new WebXRManager( _this, _gl ) : new WebVRManager( _this ); + // only perform resize for certain image types - this.vr = vr; + if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || + ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) || + ( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) { - // shadow map + const floor = needsPowerOfTwo ? floorPowerOfTwo : Math.floor; - var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize ); + const width = floor( scale * image.width ); + const height = floor( scale * image.height ); - this.shadowMap = shadowMap; + if ( _canvas === undefined ) _canvas = createCanvas( width, height ); - // API + // cube textures can't reuse the same canvas - this.getContext = function () { + const canvas = needsNewCanvas ? createCanvas( width, height ) : _canvas; - return _gl; + canvas.width = width; + canvas.height = height; - }; + const context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - this.getContextAttributes = function () { + console.warn( 'THREE.WebGLRenderer: Texture has been resized from (' + image.width + 'x' + image.height + ') to (' + width + 'x' + height + ').' ); - return _gl.getContextAttributes(); + return canvas; - }; + } else { - this.forceContextLoss = function () { + if ( 'data' in image ) { - var extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.loseContext(); + console.warn( 'THREE.WebGLRenderer: Image in DataTexture is too big (' + image.width + 'x' + image.height + ').' ); - }; + } - this.forceContextRestore = function () { + return image; - var extension = extensions.get( 'WEBGL_lose_context' ); - if ( extension ) extension.restoreContext(); + } - }; + } - this.getPixelRatio = function () { + return image; - return _pixelRatio; + } - }; + function isPowerOfTwo$1( image ) { - this.setPixelRatio = function ( value ) { + return isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ); - if ( value === undefined ) return; + } - _pixelRatio = value; + function textureNeedsPowerOfTwo( texture ) { - this.setSize( _width, _height, false ); + if ( isWebGL2 ) return false; - }; + return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) || + ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ); - this.getSize = function ( target ) { + } - if ( target === undefined ) { + function textureNeedsGenerateMipmaps( texture, supportsMips ) { - console.warn( 'WebGLRenderer: .getsize() now requires a Vector2 as an argument' ); + return texture.generateMipmaps && supportsMips && + texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter; - target = new Vector2(); + } - } + function generateMipmap( target ) { - return target.set( _width, _height ); + _gl.generateMipmap( target ); - }; + } - this.setSize = function ( width, height, updateStyle ) { + function getInternalFormat( internalFormatName, glFormat, glType, colorSpace, forceLinearTransfer = false ) { - if ( vr.isPresenting() ) { + if ( isWebGL2 === false ) return glFormat; - console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); - return; + if ( internalFormatName !== null ) { - } + if ( _gl[ internalFormatName ] !== undefined ) return _gl[ internalFormatName ]; - _width = width; - _height = height; + console.warn( 'THREE.WebGLRenderer: Attempt to use non-existing WebGL internal format \'' + internalFormatName + '\'' ); - _canvas.width = Math.floor( width * _pixelRatio ); - _canvas.height = Math.floor( height * _pixelRatio ); + } - if ( updateStyle !== false ) { + let internalFormat = glFormat; - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + 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; - this.setViewport( 0, 0, width, height ); + } - }; + if ( glFormat === _gl.RED_INTEGER ) { - this.getDrawingBufferSize = function ( target ) { + 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 ( target === undefined ) { + } - console.warn( 'WebGLRenderer: .getdrawingBufferSize() now requires a Vector2 as an argument' ); + if ( glFormat === _gl.RG ) { - target = new Vector2(); + if ( glType === _gl.FLOAT ) internalFormat = _gl.RG32F; + if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RG16F; + if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = _gl.RG8; - } + } - return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); + if ( glFormat === _gl.RGBA ) { - }; + const transfer = forceLinearTransfer ? LinearTransfer : ColorManagement.getTransfer( colorSpace ); - this.setDrawingBufferSize = function ( width, height, pixelRatio ) { + if ( glType === _gl.FLOAT ) internalFormat = _gl.RGBA32F; + if ( glType === _gl.HALF_FLOAT ) internalFormat = _gl.RGBA16F; + if ( glType === _gl.UNSIGNED_BYTE ) internalFormat = ( transfer === SRGBTransfer ) ? _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; - _width = width; - _height = height; + } - _pixelRatio = pixelRatio; + if ( internalFormat === _gl.R16F || internalFormat === _gl.R32F || + internalFormat === _gl.RG16F || internalFormat === _gl.RG32F || + internalFormat === _gl.RGBA16F || internalFormat === _gl.RGBA32F ) { - _canvas.width = Math.floor( width * pixelRatio ); - _canvas.height = Math.floor( height * pixelRatio ); + extensions.get( 'EXT_color_buffer_float' ); - this.setViewport( 0, 0, width, height ); + } - }; + return internalFormat; - this.getCurrentViewport = function ( target ) { + } - if ( target === undefined ) { + function getMipLevels( texture, image, supportsMips ) { - console.warn( 'WebGLRenderer: .getCurrentViewport() now requires a Vector4 as an argument' ); + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) === true || ( texture.isFramebufferTexture && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) ) { - target = new Vector4(); + return Math.log2( Math.max( image.width, image.height ) ) + 1; - } + } else if ( texture.mipmaps !== undefined && texture.mipmaps.length > 0 ) { - return target.copy( _currentViewport ); + // user-defined mipmaps - }; + return texture.mipmaps.length; - this.getViewport = function ( target ) { + } else if ( texture.isCompressedTexture && Array.isArray( texture.image ) ) { - return target.copy( _viewport ); + return image.mipmaps.length; - }; + } else { - this.setViewport = function ( x, y, width, height ) { + // texture without mipmaps (only base level) - if ( x.isVector4 ) { + return 1; - _viewport.set( x.x, x.y, x.z, x.w ); + } - } else { + } - _viewport.set( x, y, width, height ); + // Fallback filters for non-power-of-2 textures - } + function filterFallback( f ) { - state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); + if ( f === NearestFilter || f === NearestMipmapNearestFilter || f === NearestMipmapLinearFilter ) { - }; + return _gl.NEAREST; - this.getScissor = function ( target ) { + } - return target.copy( _scissor ); + return _gl.LINEAR; - }; + } - this.setScissor = function ( x, y, width, height ) { + // - if ( x.isVector4 ) { + function onTextureDispose( event ) { - _scissor.set( x.x, x.y, x.z, x.w ); + const texture = event.target; - } else { + texture.removeEventListener( 'dispose', onTextureDispose ); - _scissor.set( x, y, width, height ); + deallocateTexture( texture ); - } + if ( texture.isVideoTexture ) { - state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); + _videoTextures.delete( texture ); - }; + } - this.getScissorTest = function () { + } - return _scissorTest; + function onRenderTargetDispose( event ) { - }; + const renderTarget = event.target; - this.setScissorTest = function ( boolean ) { + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - state.setScissorTest( _scissorTest = boolean ); + deallocateRenderTarget( renderTarget ); - }; + } - // Clearing + // - this.getClearColor = function () { + function deallocateTexture( texture ) { - return background.getClearColor(); + const textureProperties = properties.get( texture ); - }; + if ( textureProperties.__webglInit === undefined ) return; - this.setClearColor = function () { + // check if it's necessary to remove the WebGLTexture object - background.setClearColor.apply( background, arguments ); + const source = texture.source; + const webglTextures = _sources.get( source ); - }; + if ( webglTextures ) { - this.getClearAlpha = function () { + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; + webglTexture.usedTimes --; - return background.getClearAlpha(); + // the WebGLTexture object is not used anymore, remove it - }; + if ( webglTexture.usedTimes === 0 ) { - this.setClearAlpha = function () { + deleteTexture( texture ); - background.setClearAlpha.apply( background, arguments ); + } - }; + // remove the weak map entry if no WebGLTexture uses the source anymore - this.clear = function ( color, depth, stencil ) { + if ( Object.keys( webglTextures ).length === 0 ) { - var bits = 0; + _sources.delete( source ); - if ( color === undefined || color ) bits |= 16384; - if ( depth === undefined || depth ) bits |= 256; - if ( stencil === undefined || stencil ) bits |= 1024; + } - _gl.clear( bits ); + } - }; + properties.remove( texture ); - this.clearColor = function () { + } - this.clear( true, false, false ); + function deleteTexture( texture ) { - }; + const textureProperties = properties.get( texture ); + _gl.deleteTexture( textureProperties.__webglTexture ); - this.clearDepth = function () { + const source = texture.source; + const webglTextures = _sources.get( source ); + delete webglTextures[ textureProperties.__cacheKey ]; - this.clear( false, true, false ); + info.memory.textures --; - }; + } - this.clearStencil = function () { + function deallocateRenderTarget( renderTarget ) { - this.clear( false, false, true ); + const texture = renderTarget.texture; - }; + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - // + if ( textureProperties.__webglTexture !== undefined ) { - this.dispose = function () { + _gl.deleteTexture( textureProperties.__webglTexture ); - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + info.memory.textures --; - renderLists.dispose(); - renderStates.dispose(); - properties.dispose(); - objects.dispose(); + } - vr.dispose(); + if ( renderTarget.depthTexture ) { - animation.stop(); + renderTarget.depthTexture.dispose(); - }; + } - // Events + if ( renderTarget.isWebGLCubeRenderTarget ) { - function onContextLost( event ) { + for ( let i = 0; i < 6; i ++ ) { - event.preventDefault(); + if ( Array.isArray( renderTargetProperties.__webglFramebuffer[ i ] ) ) { - console.log( 'THREE.WebGLRenderer: Context Lost.' ); + for ( let level = 0; level < renderTargetProperties.__webglFramebuffer[ i ].length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ][ level ] ); - _isContextLost = true; + } else { - } + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - function onContextRestore( /* event */ ) { + } - console.log( 'THREE.WebGLRenderer: Context Restored.' ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - _isContextLost = false; + } - initGLContext(); + } else { - } + if ( Array.isArray( renderTargetProperties.__webglFramebuffer ) ) { - function onMaterialDispose( event ) { + for ( let level = 0; level < renderTargetProperties.__webglFramebuffer.length; level ++ ) _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ level ] ); - var material = event.target; + } else { - material.removeEventListener( 'dispose', onMaterialDispose ); + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - deallocateMaterial( material ); + } - } + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); + if ( renderTargetProperties.__webglMultisampledFramebuffer ) _gl.deleteFramebuffer( renderTargetProperties.__webglMultisampledFramebuffer ); - // Buffer deallocation + if ( renderTargetProperties.__webglColorRenderbuffer ) { - function deallocateMaterial( material ) { + for ( let i = 0; i < renderTargetProperties.__webglColorRenderbuffer.length; i ++ ) { - releaseMaterialProgramReference( material ); + if ( renderTargetProperties.__webglColorRenderbuffer[ i ] ) _gl.deleteRenderbuffer( renderTargetProperties.__webglColorRenderbuffer[ i ] ); - properties.remove( material ); + } + + } + + if ( renderTargetProperties.__webglDepthRenderbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthRenderbuffer ); } + if ( renderTarget.isWebGLMultipleRenderTargets ) { + + for ( let i = 0, il = texture.length; i < il; i ++ ) { + + const attachmentProperties = properties.get( texture[ i ] ); - function releaseMaterialProgramReference( material ) { + if ( attachmentProperties.__webglTexture ) { - var programInfo = properties.get( material ).program; + _gl.deleteTexture( attachmentProperties.__webglTexture ); - material.program = undefined; + info.memory.textures --; - if ( programInfo !== undefined ) { + } - programCache.releaseProgram( programInfo ); + properties.remove( texture[ i ] ); } } - // Buffer rendering + properties.remove( texture ); + properties.remove( renderTarget ); - function renderObjectImmediate( object, program ) { + } - object.render( function ( object ) { + // - _this.renderBufferImmediate( object, program ); + let textureUnits = 0; - } ); + function resetTextureUnits() { - } + textureUnits = 0; - this.renderBufferImmediate = function ( object, program ) { + } - state.initAttributes(); + function allocateTextureUnit() { - var buffers = properties.get( object ); + const textureUnit = textureUnits; - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + if ( textureUnit >= maxTextures ) { - var programAttributes = program.getAttributes(); + console.warn( 'THREE.WebGLTextures: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + maxTextures ); - if ( object.hasPositions ) { + } - _gl.bindBuffer( 34962, buffers.position ); - _gl.bufferData( 34962, object.positionArray, 35048 ); + textureUnits += 1; - state.enableAttribute( programAttributes.position ); - _gl.vertexAttribPointer( programAttributes.position, 3, 5126, false, 0, 0 ); + return textureUnit; - } + } - if ( object.hasNormals ) { + function getTextureCacheKey( texture ) { - _gl.bindBuffer( 34962, buffers.normal ); - _gl.bufferData( 34962, object.normalArray, 35048 ); + const array = []; - state.enableAttribute( programAttributes.normal ); - _gl.vertexAttribPointer( programAttributes.normal, 3, 5126, false, 0, 0 ); + array.push( texture.wrapS ); + array.push( texture.wrapT ); + array.push( texture.wrapR || 0 ); + array.push( texture.magFilter ); + array.push( texture.minFilter ); + array.push( texture.anisotropy ); + array.push( texture.internalFormat ); + array.push( texture.format ); + array.push( texture.type ); + array.push( texture.generateMipmaps ); + array.push( texture.premultiplyAlpha ); + array.push( texture.flipY ); + array.push( texture.unpackAlignment ); + array.push( texture.colorSpace ); - } + return array.join(); - if ( object.hasUvs ) { + } - _gl.bindBuffer( 34962, buffers.uv ); - _gl.bufferData( 34962, object.uvArray, 35048 ); + // - state.enableAttribute( programAttributes.uv ); - _gl.vertexAttribPointer( programAttributes.uv, 2, 5126, false, 0, 0 ); + function setTexture2D( texture, slot ) { - } + const textureProperties = properties.get( texture ); - if ( object.hasColors ) { + if ( texture.isVideoTexture ) updateVideoTexture( texture ); - _gl.bindBuffer( 34962, buffers.color ); - _gl.bufferData( 34962, object.colorArray, 35048 ); + if ( texture.isRenderTargetTexture === false && texture.version > 0 && textureProperties.__version !== texture.version ) { - state.enableAttribute( programAttributes.color ); - _gl.vertexAttribPointer( programAttributes.color, 3, 5126, false, 0, 0 ); + const image = texture.image; - } + if ( image === null ) { - state.disableUnusedAttributes(); + console.warn( 'THREE.WebGLRenderer: Texture marked for update but no image data found.' ); - _gl.drawArrays( 4, 0, object.count ); + } else if ( image.complete === false ) { - object.count = 0; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete' ); - }; + } else { - this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { + uploadTexture( textureProperties, texture, slot ); + return; - var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); + } - state.setMaterial( material, frontFaceCW ); + } - var program = setProgram( camera, fog, material, object ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - var updateBuffers = false; + } - if ( _currentGeometryProgram.geometry !== geometry.id || - _currentGeometryProgram.program !== program.id || - _currentGeometryProgram.wireframe !== ( material.wireframe === true ) ) { + function setTexture2DArray( texture, slot ) { - _currentGeometryProgram.geometry = geometry.id; - _currentGeometryProgram.program = program.id; - _currentGeometryProgram.wireframe = material.wireframe === true; - updateBuffers = true; + const textureProperties = properties.get( texture ); - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - if ( object.morphTargetInfluences ) { + uploadTexture( textureProperties, texture, slot ); + return; - morphtargets.update( object, geometry, material, program ); + } - updateBuffers = true; + state.bindTexture( _gl.TEXTURE_2D_ARRAY, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - } + } - // + function setTexture3D( texture, slot ) { - var index = geometry.index; - var position = geometry.attributes.position; - var rangeFactor = 1; + const textureProperties = properties.get( texture ); - if ( material.wireframe === true ) { + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - index = geometries.getWireframeAttribute( geometry ); - rangeFactor = 2; + uploadTexture( textureProperties, texture, slot ); + return; - } + } - var attribute; - var renderer = bufferRenderer; + state.bindTexture( _gl.TEXTURE_3D, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - if ( index !== null ) { + } - attribute = attributes.get( index ); + function setTextureCube( texture, slot ) { - renderer = indexedBufferRenderer; - renderer.setIndex( attribute ); + const textureProperties = properties.get( texture ); - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + + uploadCubeTexture( textureProperties, texture, slot ); + return; - if ( updateBuffers ) { + } - setupVertexAttributes( material, program, geometry ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - if ( index !== null ) { + } - _gl.bindBuffer( 34963, attribute.buffer ); + const wrappingToGL = { + [ RepeatWrapping ]: _gl.REPEAT, + [ ClampToEdgeWrapping ]: _gl.CLAMP_TO_EDGE, + [ MirroredRepeatWrapping ]: _gl.MIRRORED_REPEAT + }; - } + const 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 + }; - // + const 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 + }; - var dataCount = Infinity; + function setTextureParameters( textureType, texture, supportsMips ) { - if ( index !== null ) { + if ( supportsMips ) { - dataCount = index.count; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, wrappingToGL[ texture.wrapS ] ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, wrappingToGL[ texture.wrapT ] ); - } else if ( position !== undefined ) { + if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { - dataCount = position.count; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, wrappingToGL[ texture.wrapR ] ); } - var rangeStart = geometry.drawRange.start * rangeFactor; - var rangeCount = geometry.drawRange.count * rangeFactor; + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterToGL[ texture.magFilter ] ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterToGL[ texture.minFilter ] ); - var groupStart = group !== null ? group.start * rangeFactor : 0; - var groupCount = group !== null ? group.count * rangeFactor : Infinity; + } else { - var drawStart = Math.max( rangeStart, groupStart ); - var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); + if ( textureType === _gl.TEXTURE_3D || textureType === _gl.TEXTURE_2D_ARRAY ) { - if ( drawCount === 0 ) return; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_R, _gl.CLAMP_TO_EDGE ); - // + } - if ( object.isMesh ) { + if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) { - if ( material.wireframe === true ) { + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.' ); - state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); - renderer.setMode( 1 ); + } - } else { + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); - switch ( object.drawMode ) { + if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) { - case TrianglesDrawMode: - renderer.setMode( 4 ); - break; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.' ); - case TriangleStripDrawMode: - renderer.setMode( 5 ); - break; + } - case TriangleFanDrawMode: - renderer.setMode( 6 ); - break; + } - } + 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 ] ); + } - } else if ( object.isLine ) { + if ( extensions.has( 'EXT_texture_filter_anisotropic' ) === true ) { - var lineWidth = material.linewidth; + const extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + 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 ( isWebGL2 === false && ( texture.type === HalfFloatType && extensions.has( 'OES_texture_half_float_linear' ) === false ) ) return; // verify extension for WebGL 1 only - state.setLineWidth( lineWidth * getTargetPixelRatio() ); + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - if ( object.isLineSegments ) { + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; - renderer.setMode( 1 ); + } - } else if ( object.isLineLoop ) { + } - renderer.setMode( 2 ); + } - } else { + function initTexture( textureProperties, texture ) { - renderer.setMode( 3 ); + let forceUpload = false; - } + if ( textureProperties.__webglInit === undefined ) { - } else if ( object.isPoints ) { + textureProperties.__webglInit = true; - renderer.setMode( 0 ); + texture.addEventListener( 'dispose', onTextureDispose ); - } else if ( object.isSprite ) { + } - renderer.setMode( 4 ); + // create Source <-> WebGLTextures mapping if necessary - } + const source = texture.source; + let webglTextures = _sources.get( source ); - if ( geometry && geometry.isInstancedBufferGeometry ) { + if ( webglTextures === undefined ) { - if ( geometry.maxInstancedCount > 0 ) { + webglTextures = {}; + _sources.set( source, webglTextures ); - renderer.renderInstances( geometry, drawStart, drawCount ); + } - } + // check if there is already a WebGLTexture object for the given texture parameters - } else { + const textureCacheKey = getTextureCacheKey( texture ); - renderer.render( drawStart, drawCount ); + if ( textureCacheKey !== textureProperties.__cacheKey ) { - } + // if not, create a new instance of WebGLTexture - }; + if ( webglTextures[ textureCacheKey ] === undefined ) { - function setupVertexAttributes( material, program, geometry ) { + // create new entry - if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) { + webglTextures[ textureCacheKey ] = { + texture: _gl.createTexture(), + usedTimes: 0 + }; - if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) { + info.memory.textures ++; - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + // when a new instance of WebGLTexture was created, a texture upload is required + // even if the image contents are identical - } + forceUpload = true; } - state.initAttributes(); + webglTextures[ textureCacheKey ].usedTimes ++; - var geometryAttributes = geometry.attributes; + // every time the texture cache key changes, it's necessary to check if an instance of + // WebGLTexture can be deleted in order to avoid a memory leak. - var programAttributes = program.getAttributes(); + const webglTexture = webglTextures[ textureProperties.__cacheKey ]; - var materialDefaultAttributeValues = material.defaultAttributeValues; + if ( webglTexture !== undefined ) { - for ( var name in programAttributes ) { + webglTextures[ textureProperties.__cacheKey ].usedTimes --; - var programAttribute = programAttributes[ name ]; + if ( webglTexture.usedTimes === 0 ) { - if ( programAttribute >= 0 ) { + deleteTexture( texture ); - var geometryAttribute = geometryAttributes[ name ]; + } - if ( geometryAttribute !== undefined ) { + } - var normalized = geometryAttribute.normalized; - var size = geometryAttribute.itemSize; + // store references to cache key and WebGLTexture object - var attribute = attributes.get( geometryAttribute ); + textureProperties.__cacheKey = textureCacheKey; + textureProperties.__webglTexture = webglTextures[ textureCacheKey ].texture; - // TODO Attribute may not be available on context restore + } - if ( attribute === undefined ) continue; + return forceUpload; - var buffer = attribute.buffer; - var type = attribute.type; - var bytesPerElement = attribute.bytesPerElement; + } - if ( geometryAttribute.isInterleavedBufferAttribute ) { + function uploadTexture( textureProperties, texture, slot ) { - var data = geometryAttribute.data; - var stride = data.stride; - var offset = geometryAttribute.offset; + let textureType = _gl.TEXTURE_2D; - if ( data && data.isInstancedInterleavedBuffer ) { + if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) textureType = _gl.TEXTURE_2D_ARRAY; + if ( texture.isData3DTexture ) textureType = _gl.TEXTURE_3D; - state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute ); + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - if ( geometry.maxInstancedCount === undefined ) { + state.bindTexture( textureType, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - geometry.maxInstancedCount = data.meshPerAttribute * data.count; + const sourceProperties = properties.get( source ); - } + if ( source.version !== sourceProperties.__version || forceUpload === true ) { - } else { + state.activeTexture( _gl.TEXTURE0 + slot ); - state.enableAttribute( programAttribute ); + const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace ); + const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace ); + const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; - } + _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, unpackConversion ); - _gl.bindBuffer( 34962, buffer ); - _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, offset * bytesPerElement ); + const needsPowerOfTwo = textureNeedsPowerOfTwo( texture ) && isPowerOfTwo$1( texture.image ) === false; + let image = resizeImage( texture.image, needsPowerOfTwo, false, maxTextureSize ); + image = verifyColorSpace( texture, image ); - } else { + const supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.colorSpace ); - if ( geometryAttribute.isInstancedBufferAttribute ) { + let glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, texture.isVideoTexture ); - state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute ); + setTextureParameters( textureType, texture, supportsMips ); - if ( geometry.maxInstancedCount === undefined ) { + let mipmap; + const mipmaps = texture.mipmaps; - geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); + const levels = getMipLevels( texture, image, supportsMips ); - } + if ( texture.isDepthTexture ) { - } else { + // populate depth texture with dummy data - state.enableAttribute( programAttribute ); + glInternalFormat = _gl.DEPTH_COMPONENT; - } + if ( isWebGL2 ) { - _gl.bindBuffer( 34962, buffer ); - _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, 0 ); + if ( texture.type === FloatType ) { - } + glInternalFormat = _gl.DEPTH_COMPONENT32F; - } else if ( materialDefaultAttributeValues !== undefined ) { + } else if ( texture.type === UnsignedIntType ) { - var value = materialDefaultAttributeValues[ name ]; + glInternalFormat = _gl.DEPTH_COMPONENT24; - if ( value !== undefined ) { + } else if ( texture.type === UnsignedInt248Type ) { - switch ( value.length ) { + glInternalFormat = _gl.DEPTH24_STENCIL8; - case 2: - _gl.vertexAttrib2fv( programAttribute, value ); - break; + } else { - case 3: - _gl.vertexAttrib3fv( programAttribute, value ); - break; + glInternalFormat = _gl.DEPTH_COMPONENT16; // WebGL2 requires sized internalformat for glTexImage2D - case 4: - _gl.vertexAttrib4fv( programAttribute, value ); - break; + } - default: - _gl.vertexAttrib1fv( programAttribute, value ); + } else { - } + if ( texture.type === FloatType ) { - } + console.error( 'WebGLRenderer: Floating point depth texture requires WebGL2.' ); } } - } + // validation checks for WebGL 1 - state.disableUnusedAttributes(); + if ( texture.format === DepthFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { - } + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) { - // Compile + console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' ); - this.compile = function ( scene, camera ) { + texture.type = UnsignedIntType; + glType = utils.convert( texture.type ); - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); + } - scene.traverse( function ( object ) { + } - if ( object.isLight ) { + if ( texture.format === DepthStencilFormat && glInternalFormat === _gl.DEPTH_COMPONENT ) { - currentRenderState.pushLight( object ); + // Depth stencil textures need the DEPTH_STENCIL internal format + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + glInternalFormat = _gl.DEPTH_STENCIL; - if ( object.castShadow ) { + // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are + // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL. + // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/) + if ( texture.type !== UnsignedInt248Type ) { - currentRenderState.pushShadow( object ); + console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' ); + + texture.type = UnsignedInt248Type; + glType = utils.convert( texture.type ); } } - } ); - - currentRenderState.setupLights( camera ); - - scene.traverse( function ( object ) { - - if ( object.material ) { - - if ( Array.isArray( object.material ) ) { + // - for ( var i = 0; i < object.material.length; i ++ ) { + if ( allocateMemory ) { - initMaterial( object.material[ i ], scene.fog, object ); + if ( useTexStorage ) { - } + state.texStorage2D( _gl.TEXTURE_2D, 1, glInternalFormat, image.width, image.height ); } else { - initMaterial( object.material, scene.fog, object ); + state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ); } } - } ); + } else if ( texture.isDataTexture ) { - }; + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - // Animation Loop + if ( mipmaps.length > 0 && supportsMips ) { - var onAnimationFrameCallback = null; + if ( useTexStorage && allocateMemory ) { - function onAnimationFrame( time ) { - - if ( vr.isPresenting() ) return; - if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); - - } + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - var animation = new WebGLAnimation(); - animation.setAnimationLoop( onAnimationFrame ); + } - if ( typeof window !== 'undefined' ) animation.setContext( window ); + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - this.setAnimationLoop = function ( callback ) { + mipmap = mipmaps[ i ]; - onAnimationFrameCallback = callback; - vr.setAnimationLoop( callback ); + if ( useTexStorage ) { - animation.start(); + state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - }; + } else { - // Rendering + state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - this.render = function ( scene, camera ) { + } - var renderTarget, forceClear; + } - if ( arguments[ 2 ] !== undefined ) { + texture.generateMipmaps = false; - console.warn( 'THREE.WebGLRenderer.render(): the renderTarget argument has been removed. Use .setRenderTarget() instead.' ); - renderTarget = arguments[ 2 ]; + } else { - } + if ( useTexStorage ) { - if ( arguments[ 3 ] !== undefined ) { + if ( allocateMemory ) { - console.warn( 'THREE.WebGLRenderer.render(): the forceClear argument has been removed. Use .clear() instead.' ); - forceClear = arguments[ 3 ]; + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); - } + } - if ( ! ( camera && camera.isCamera ) ) { + state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, image.width, image.height, glFormat, glType, image.data ); - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + } else { - } + state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data ); - if ( _isContextLost ) return; + } - // reset caching for this frame + } - _currentGeometryProgram.geometry = null; - _currentGeometryProgram.program = null; - _currentGeometryProgram.wireframe = false; - _currentMaterialId = - 1; - _currentCamera = null; + } else if ( texture.isCompressedTexture ) { - // update scene graph + if ( texture.isCompressedArrayTexture ) { - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + if ( useTexStorage && allocateMemory ) { - // update camera matrices and frustum + state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height, image.depth ); - if ( camera.parent === null ) camera.updateMatrixWorld(); + } - if ( vr.enabled ) { + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - camera = vr.getCamera( camera ); + mipmap = mipmaps[ i ]; - } + if ( texture.format !== RGBAFormat ) { - // + if ( glFormat !== null ) { - currentRenderState = renderStates.get( scene, camera ); - currentRenderState.init(); + if ( useTexStorage ) { - scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget ); + state.compressedTexSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, mipmap.data, 0, 0 ); - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + } else { - _localClippingEnabled = this.localClippingEnabled; - _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); + state.compressedTexImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, mipmap.data, 0, 0 ); - currentRenderList = renderLists.get( scene, camera ); - currentRenderList.init(); + } - projectObject( scene, camera, 0, _this.sortObjects ); + } else { - if ( _this.sortObjects === true ) { + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - currentRenderList.sort(); + } - } + } else { - // + if ( useTexStorage ) { - if ( _clippingEnabled ) _clipping.beginShadows(); + state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, i, 0, 0, 0, mipmap.width, mipmap.height, image.depth, glFormat, glType, mipmap.data ); - var shadowsArray = currentRenderState.state.shadowsArray; + } else { - shadowMap.render( shadowsArray, scene, camera ); + state.texImage3D( _gl.TEXTURE_2D_ARRAY, i, glInternalFormat, mipmap.width, mipmap.height, image.depth, 0, glFormat, glType, mipmap.data ); - currentRenderState.setupLights( camera ); + } - if ( _clippingEnabled ) _clipping.endShadows(); + } - // + } - if ( this.info.autoReset ) this.info.reset(); + } else { - if ( renderTarget !== undefined ) { + if ( useTexStorage && allocateMemory ) { - this.setRenderTarget( renderTarget ); + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); - } + } - // + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - background.render( currentRenderList, scene, camera, forceClear ); + mipmap = mipmaps[ i ]; - // render scene + if ( texture.format !== RGBAFormat ) { - var opaqueObjects = currentRenderList.opaque; - var transparentObjects = currentRenderList.transparent; + if ( glFormat !== null ) { - if ( scene.overrideMaterial ) { + if ( useTexStorage ) { - var overrideMaterial = scene.overrideMaterial; + state.compressedTexSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial ); - if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial ); + } else { - } else { + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - // opaque pass (front-to-back order) + } - if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera ); + } else { - // transparent pass (back-to-front order) + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' ); - if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera ); + } - } + } else { - // + if ( useTexStorage ) { - scene.onAfterRender( _this, scene, camera ); + state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - // + } else { - if ( _currentRenderTarget !== null ) { + state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - // Generate mipmap if we're using any kind of mipmap filtering + } - textures.updateRenderTargetMipmap( _currentRenderTarget ); + } - // resolve multisample renderbuffers to a single-sample texture if necessary + } - textures.updateMultisampleRenderTarget( _currentRenderTarget ); + } - } + } else if ( texture.isDataArrayTexture ) { - // Ensure depth buffer writing is enabled so it can be cleared on next render + if ( useTexStorage ) { - state.buffers.depth.setTest( true ); - state.buffers.depth.setMask( true ); - state.buffers.color.setMask( true ); + if ( allocateMemory ) { - state.setPolygonOffset( false ); + state.texStorage3D( _gl.TEXTURE_2D_ARRAY, levels, glInternalFormat, image.width, image.height, image.depth ); - if ( vr.enabled ) { + } - vr.submitFrame(); + state.texSubImage3D( _gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - } + } else { - // _gl.finish(); + state.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - currentRenderList = null; - currentRenderState = null; + } - }; + } else if ( texture.isData3DTexture ) { - function projectObject( object, camera, groupOrder, sortObjects ) { + if ( useTexStorage ) { - if ( object.visible === false ) return; + if ( allocateMemory ) { - var visible = object.layers.test( camera.layers ); + state.texStorage3D( _gl.TEXTURE_3D, levels, glInternalFormat, image.width, image.height, image.depth ); - if ( visible ) { + } - if ( object.isGroup ) { + state.texSubImage3D( _gl.TEXTURE_3D, 0, 0, 0, 0, image.width, image.height, image.depth, glFormat, glType, image.data ); - groupOrder = object.renderOrder; + } else { - } else if ( object.isLOD ) { + state.texImage3D( _gl.TEXTURE_3D, 0, glInternalFormat, image.width, image.height, image.depth, 0, glFormat, glType, image.data ); - if ( object.autoUpdate === true ) object.update( camera ); + } - } else if ( object.isLight ) { + } else if ( texture.isFramebufferTexture ) { - currentRenderState.pushLight( object ); + if ( allocateMemory ) { - if ( object.castShadow ) { + if ( useTexStorage ) { - currentRenderState.pushShadow( object ); + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); - } + } else { - } else if ( object.isSprite ) { + let width = image.width, height = image.height; - if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { + for ( let i = 0; i < levels; i ++ ) { - if ( sortObjects ) { + state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, width, height, 0, glFormat, glType, null ); - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + width >>= 1; + height >>= 1; } - var geometry = objects.update( object ); - var material = object.material; + } - if ( material.visible ) { + } - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + } else { - } + // regular Texture (image, video, canvas) - } + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - } else if ( object.isImmediateRenderObject ) { + if ( mipmaps.length > 0 && supportsMips ) { - if ( sortObjects ) { + if ( useTexStorage && allocateMemory ) { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, mipmaps[ 0 ].width, mipmaps[ 0 ].height ); } - currentRenderList.push( object, null, object.material, groupOrder, _vector3.z, null ); - - } else if ( object.isMesh || object.isLine || object.isPoints ) { - - if ( object.isSkinnedMesh ) { + for ( let i = 0, il = mipmaps.length; i < il; i ++ ) { - object.skeleton.update(); + mipmap = mipmaps[ i ]; - } + if ( useTexStorage ) { - if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { + state.texSubImage2D( _gl.TEXTURE_2D, i, 0, 0, glFormat, glType, mipmap ); - if ( sortObjects ) { + } else { - _vector3.setFromMatrixPosition( object.matrixWorld ) - .applyMatrix4( _projScreenMatrix ); + state.texImage2D( _gl.TEXTURE_2D, i, glInternalFormat, glFormat, glType, mipmap ); } - var geometry = objects.update( object ); - var material = object.material; - - if ( Array.isArray( material ) ) { - - var groups = geometry.groups; + } - for ( var i = 0, l = groups.length; i < l; i ++ ) { + texture.generateMipmaps = false; - var group = groups[ i ]; - var groupMaterial = material[ group.materialIndex ]; + } else { - if ( groupMaterial && groupMaterial.visible ) { + if ( useTexStorage ) { - currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); + if ( allocateMemory ) { - } + state.texStorage2D( _gl.TEXTURE_2D, levels, glInternalFormat, image.width, image.height ); - } + } - } else if ( material.visible ) { + state.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, glFormat, glType, image ); - currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); + } else { - } + state.texImage2D( _gl.TEXTURE_2D, 0, glInternalFormat, glFormat, glType, image ); } @@ -24189,5519 +24719,5208 @@ } - var children = object.children; - - for ( var i = 0, l = children.length; i < l; i ++ ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - projectObject( children[ i ], camera, groupOrder, sortObjects ); + generateMipmap( textureType ); } + sourceProperties.__version = source.version; + + if ( texture.onUpdate ) texture.onUpdate( texture ); + } - function renderObjects( renderList, scene, camera, overrideMaterial ) { + textureProperties.__version = texture.version; - for ( var i = 0, l = renderList.length; i < l; i ++ ) { + } - var renderItem = renderList[ i ]; + function uploadCubeTexture( textureProperties, texture, slot ) { - var object = renderItem.object; - var geometry = renderItem.geometry; - var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; - var group = renderItem.group; + if ( texture.image.length !== 6 ) return; - if ( camera.isArrayCamera ) { + const forceUpload = initTexture( textureProperties, texture ); + const source = texture.source; - _currentArrayCamera = camera; + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture, _gl.TEXTURE0 + slot ); - var cameras = camera.cameras; + const sourceProperties = properties.get( source ); - for ( var j = 0, jl = cameras.length; j < jl; j ++ ) { + if ( source.version !== sourceProperties.__version || forceUpload === true ) { - var camera2 = cameras[ j ]; + state.activeTexture( _gl.TEXTURE0 + slot ); - if ( object.layers.test( camera2.layers ) ) { + const workingPrimaries = ColorManagement.getPrimaries( ColorManagement.workingColorSpace ); + const texturePrimaries = texture.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries( texture.colorSpace ); + const unpackConversion = texture.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? _gl.NONE : _gl.BROWSER_DEFAULT_WEBGL; - state.viewport( _currentViewport.copy( camera2.viewport ) ); + _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, unpackConversion ); - currentRenderState.setupLights( camera2 ); + const isCompressed = ( texture.isCompressedTexture || texture.image[ 0 ].isCompressedTexture ); + const isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture ); - renderObject( object, scene, camera2, geometry, material, group ); + const cubeImage = []; - } + for ( let i = 0; i < 6; i ++ ) { - } + if ( ! isCompressed && ! isDataTexture ) { - } else { + cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, maxCubemapSize ); - _currentArrayCamera = null; + } else { - renderObject( object, scene, camera, geometry, material, group ); + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } - } - - } + cubeImage[ i ] = verifyColorSpace( texture, cubeImage[ i ] ); - function renderObject( object, scene, camera, geometry, material, group ) { + } - object.onBeforeRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); + const image = cubeImage[ 0 ], + supportsMips = isPowerOfTwo$1( image ) || isWebGL2, + glFormat = utils.convert( texture.format, texture.colorSpace ), + glType = utils.convert( texture.type ), + glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + const useTexStorage = ( isWebGL2 && texture.isVideoTexture !== true ); + const allocateMemory = ( sourceProperties.__version === undefined ) || ( forceUpload === true ); + let levels = getMipLevels( texture, image, supportsMips ); - if ( object.isImmediateRenderObject ) { + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); - state.setMaterial( material ); + let mipmaps; - var program = setProgram( camera, scene.fog, material, object ); + if ( isCompressed ) { - _currentGeometryProgram.geometry = null; - _currentGeometryProgram.program = null; - _currentGeometryProgram.wireframe = false; + if ( useTexStorage && allocateMemory ) { - renderObjectImmediate( object, program ); + state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, image.width, image.height ); - } else { + } - _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group ); + for ( let i = 0; i < 6; i ++ ) { - } + mipmaps = cubeImage[ i ].mipmaps; - object.onAfterRender( _this, scene, camera, geometry, material, group ); - currentRenderState = renderStates.get( scene, _currentArrayCamera || camera ); + for ( let j = 0; j < mipmaps.length; j ++ ) { - } + const mipmap = mipmaps[ j ]; - function initMaterial( material, fog, object ) { + if ( texture.format !== RGBAFormat ) { - var materialProperties = properties.get( material ); + if ( glFormat !== null ) { - var lights = currentRenderState.state.lights; - var shadowsArray = currentRenderState.state.shadowsArray; + if ( useTexStorage ) { - var lightsStateVersion = lights.state.version; + state.compressedTexSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, mipmap.data ); - var parameters = programCache.getParameters( - material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object ); + } else { - var code = programCache.getProgramCode( material, parameters ); + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - var program = materialProperties.program; - var programChange = true; + } - if ( program === undefined ) { + } else { - // new material - material.addEventListener( 'dispose', onMaterialDispose ); + console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' ); - } else if ( program.code !== code ) { + } - // changed glsl or parameters - releaseMaterialProgramReference( material ); + } else { - } else if ( materialProperties.lightsStateVersion !== lightsStateVersion ) { + if ( useTexStorage ) { - materialProperties.lightsStateVersion = lightsStateVersion; + state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, 0, 0, mipmap.width, mipmap.height, glFormat, glType, mipmap.data ); - programChange = false; + } else { - } else if ( parameters.shaderID !== undefined ) { + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - // same glsl and uniform list - return; + } - } else { + } - // only rebuild uniform list - programChange = false; + } - } + } - if ( programChange ) { + } else { - if ( parameters.shaderID ) { + mipmaps = texture.mipmaps; - var shader = ShaderLib[ parameters.shaderID ]; + if ( useTexStorage && allocateMemory ) { - materialProperties.shader = { - name: material.type, - uniforms: cloneUniforms( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - }; + // TODO: Uniformly handle mipmap definitions + // Normal textures and compressed cube textures define base level + mips with their mipmap array + // Uncompressed cube textures use their mipmap array only for mips (no base level) - } else { + if ( mipmaps.length > 0 ) levels ++; - materialProperties.shader = { - name: material.type, - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - }; + state.texStorage2D( _gl.TEXTURE_CUBE_MAP, levels, glInternalFormat, cubeImage[ 0 ].width, cubeImage[ 0 ].height ); } - material.onBeforeCompile( materialProperties.shader, _this ); + for ( let i = 0; i < 6; i ++ ) { - // Computing code again as onBeforeCompile may have changed the shaders - code = programCache.getProgramCode( material, parameters ); + if ( isDataTexture ) { - program = programCache.acquireProgram( material, materialProperties.shader, parameters, code ); + if ( useTexStorage ) { - materialProperties.program = program; - material.program = program; + state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, cubeImage[ i ].width, cubeImage[ i ].height, glFormat, glType, cubeImage[ i ].data ); - } + } else { - var programAttributes = program.getAttributes(); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - if ( material.morphTargets ) { + } - material.numSupportedMorphTargets = 0; + for ( let j = 0; j < mipmaps.length; j ++ ) { - for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { + const mipmap = mipmaps[ j ]; + const mipmapImage = mipmap.image[ i ].image; - if ( programAttributes[ 'morphTarget' + i ] >= 0 ) { + if ( useTexStorage ) { - material.numSupportedMorphTargets ++; + state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, mipmapImage.width, mipmapImage.height, glFormat, glType, mipmapImage.data ); - } + } else { - } + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data ); - } + } - if ( material.morphNormals ) { + } - material.numSupportedMorphNormals = 0; + } else { - for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { + if ( useTexStorage ) { - if ( programAttributes[ 'morphNormal' + i ] >= 0 ) { + state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, glFormat, glType, cubeImage[ i ] ); - material.numSupportedMorphNormals ++; + } else { - } + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] ); - } + } - } + for ( let j = 0; j < mipmaps.length; j ++ ) { - var uniforms = materialProperties.shader.uniforms; + const mipmap = mipmaps[ j ]; - if ( ! material.isShaderMaterial && - ! material.isRawShaderMaterial || - material.clipping === true ) { + if ( useTexStorage ) { - materialProperties.numClippingPlanes = _clipping.numPlanes; - materialProperties.numIntersection = _clipping.numIntersection; - uniforms.clippingPlanes = _clipping.uniform; + state.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, 0, 0, glFormat, glType, mipmap.image[ i ] ); - } + } else { - materialProperties.fog = fog; + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] ); - // store the light setup it was created for + } - materialProperties.lightsStateVersion = lightsStateVersion; + } - if ( material.lights ) { + } - // wire up the material to this renderer's lighting state + } - uniforms.ambientLightColor.value = lights.state.ambient; - uniforms.lightProbe.value = lights.state.probe; - uniforms.directionalLights.value = lights.state.directional; - uniforms.spotLights.value = lights.state.spot; - uniforms.rectAreaLights.value = lights.state.rectArea; - uniforms.pointLights.value = lights.state.point; - uniforms.hemisphereLights.value = lights.state.hemi; + } - uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; - uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; - uniforms.spotShadowMap.value = lights.state.spotShadowMap; - uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix; - uniforms.pointShadowMap.value = lights.state.pointShadowMap; - uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; - // TODO (abelnation): add area lights shadow info to uniforms + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { + + // We assume images for cube map have the same size. + generateMipmap( _gl.TEXTURE_CUBE_MAP ); } - var progUniforms = materialProperties.program.getUniforms(), - uniformsList = - WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); + sourceProperties.__version = source.version; - materialProperties.uniformsList = uniformsList; + if ( texture.onUpdate ) texture.onUpdate( texture ); } - function setProgram( camera, fog, material, object ) { + textureProperties.__version = texture.version; - textures.resetTextureUnits(); + } - var materialProperties = properties.get( material ); - var lights = currentRenderState.state.lights; + // Render targets - if ( _clippingEnabled ) { + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget, level ) { - if ( _localClippingEnabled || camera !== _currentCamera ) { + const glFormat = utils.convert( texture.format, texture.colorSpace ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); + const renderTargetProperties = properties.get( renderTarget ); - var useCache = - camera === _currentCamera && - material.id === _currentMaterialId; + if ( ! renderTargetProperties.__hasExternalTextures ) { - // we might want to call this function with some ClippingGroup - // object instead of the material, once it becomes feasible - // (#8465, #8379) - _clipping.setState( - material.clippingPlanes, material.clipIntersection, material.clipShadows, - camera, materialProperties, useCache ); + const width = Math.max( 1, renderTarget.width >> level ); + const height = Math.max( 1, renderTarget.height >> level ); - } + if ( textureTarget === _gl.TEXTURE_3D || textureTarget === _gl.TEXTURE_2D_ARRAY ) { - } + state.texImage3D( textureTarget, level, glInternalFormat, width, height, renderTarget.depth, 0, glFormat, glType, null ); - if ( material.needsUpdate === false ) { + } else { - if ( materialProperties.program === undefined ) { + state.texImage2D( textureTarget, level, glInternalFormat, width, height, 0, glFormat, glType, null ); - material.needsUpdate = true; + } - } else if ( material.fog && materialProperties.fog !== fog ) { + } - material.needsUpdate = true; + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - } else if ( material.lights && materialProperties.lightsStateVersion !== lights.state.version ) { + if ( useMultisampledRTT( renderTarget ) ) { - material.needsUpdate = true; + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, 0, getRenderTargetSamples( renderTarget ) ); - } else if ( materialProperties.numClippingPlanes !== undefined && - ( materialProperties.numClippingPlanes !== _clipping.numPlanes || - materialProperties.numIntersection !== _clipping.numIntersection ) ) { + } else if ( textureTarget === _gl.TEXTURE_2D || ( textureTarget >= _gl.TEXTURE_CUBE_MAP_POSITIVE_X && textureTarget <= _gl.TEXTURE_CUBE_MAP_NEGATIVE_Z ) ) { // see #24753 - material.needsUpdate = true; + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( texture ).__webglTexture, level ); - } - - } - - if ( material.needsUpdate ) { + } - initMaterial( material, fog, object ); - material.needsUpdate = false; + state.bindFramebuffer( _gl.FRAMEBUFFER, null ); - } + } - var refreshProgram = false; - var refreshMaterial = false; - var refreshLights = false; - var program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.shader.uniforms; + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { - if ( state.useProgram( program.program ) ) { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - } + let glInternalFormat = ( isWebGL2 === true ) ? _gl.DEPTH_COMPONENT24 : _gl.DEPTH_COMPONENT16; - if ( material.id !== _currentMaterialId ) { + if ( isMultisample || useMultisampledRTT( renderTarget ) ) { - _currentMaterialId = material.id; + const depthTexture = renderTarget.depthTexture; - refreshMaterial = true; + if ( depthTexture && depthTexture.isDepthTexture ) { - } + if ( depthTexture.type === FloatType ) { - if ( refreshProgram || _currentCamera !== camera ) { + glInternalFormat = _gl.DEPTH_COMPONENT32F; - p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + } else if ( depthTexture.type === UnsignedIntType ) { - if ( capabilities.logarithmicDepthBuffer ) { + glInternalFormat = _gl.DEPTH_COMPONENT24; - p_uniforms.setValue( _gl, 'logDepthBufFC', - 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + } } - if ( _currentCamera !== camera ) { + const samples = getRenderTargetSamples( renderTarget ); - _currentCamera = camera; + if ( useMultisampledRTT( renderTarget ) ) { - // lighting uniforms depend on the camera so enforce an update - // now, in case this material supports lights - or later, when - // the next material that does gets activated: + multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - refreshMaterial = true; // set to true on material change - refreshLights = true; // remains set until update done + } else { + + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); } - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); - if ( material.isShaderMaterial || - material.isMeshPhongMaterial || - material.isMeshStandardMaterial || - material.envMap ) { + } - var uCamPos = p_uniforms.map.cameraPosition; + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - if ( uCamPos !== undefined ) { + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - uCamPos.setValue( _gl, - _vector3.setFromMatrixPosition( camera.matrixWorld ) ); + const samples = getRenderTargetSamples( renderTarget ); - } + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - } + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); - if ( material.isMeshPhongMaterial || - material.isMeshLambertMaterial || - material.isMeshBasicMaterial || - material.isMeshStandardMaterial || - material.isShaderMaterial || - material.skinning ) { + } else if ( useMultisampledRTT( renderTarget ) ) { - p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); + multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, _gl.DEPTH24_STENCIL8, renderTarget.width, renderTarget.height ); - } + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); } - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // not sure why, but otherwise weird things happen - if ( material.skinning ) { + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - p_uniforms.setOptional( _gl, object, 'bindMatrix' ); - p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); + } else { - var skeleton = object.skeleton; + const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - if ( skeleton ) { + for ( let i = 0; i < textures.length; i ++ ) { - var bones = skeleton.bones; + const texture = textures[ i ]; - if ( capabilities.floatVertexTextures ) { + const glFormat = utils.convert( texture.format, texture.colorSpace ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace ); + const samples = getRenderTargetSamples( renderTarget ); - if ( skeleton.boneTexture === undefined ) { + if ( isMultisample && useMultisampledRTT( renderTarget ) === false ) { - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + } else if ( useMultisampledRTT( renderTarget ) ) { - var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix - size = _Math.ceilPowerOfTwo( size ); - size = Math.max( size, 4 ); + multisampledRTTExt.renderbufferStorageMultisampleEXT( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel - boneMatrices.set( skeleton.boneMatrices ); // copy current values + } else { - var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); - boneTexture.needsUpdate = true; + _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); - skeleton.boneMatrices = boneMatrices; - skeleton.boneTexture = boneTexture; - skeleton.boneTextureSize = size; + } - } + } - p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); - p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); + } - } else { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); + } - } + // Setup resources for a Depth Texture for a FBO (needs an extension) + function setupDepthTexture( framebuffer, renderTarget ) { - } + const isCube = ( renderTarget && renderTarget.isWebGLCubeRenderTarget ); + if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' ); - } + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - if ( refreshMaterial ) { + if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) { - p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint ); + throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' ); + + } - if ( material.lights ) { + // upload an empty depth texture with framebuffer size + if ( ! properties.get( renderTarget.depthTexture ).__webglTexture || + renderTarget.depthTexture.image.width !== renderTarget.width || + renderTarget.depthTexture.image.height !== renderTarget.height ) { - // the current material requires lighting info + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; - // note: all lighting uniforms are always set correctly - // they simply reference the renderer's state for their - // values - // - // use the current material's .needsUpdate flags to set - // the GL state when required + } - markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); + setTexture2D( renderTarget.depthTexture, 0 ); - } + const webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + const samples = getRenderTargetSamples( renderTarget ); - // refresh uniforms common to several materials + if ( renderTarget.depthTexture.format === DepthFormat ) { - if ( fog && material.fog ) { + if ( useMultisampledRTT( renderTarget ) ) { - refreshUniformsFog( m_uniforms, fog ); + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); - } + } else { - if ( material.isMeshBasicMaterial ) { + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); - refreshUniformsCommon( m_uniforms, material ); + } - } else if ( material.isMeshLambertMaterial ) { + } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) { - refreshUniformsCommon( m_uniforms, material ); - refreshUniformsLambert( m_uniforms, material ); + if ( useMultisampledRTT( renderTarget ) ) { - } else if ( material.isMeshPhongMaterial ) { + multisampledRTTExt.framebufferTexture2DMultisampleEXT( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0, samples ); - refreshUniformsCommon( m_uniforms, material ); + } else { - if ( material.isMeshToonMaterial ) { + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); - refreshUniformsToon( m_uniforms, material ); + } - } else { + } else { - refreshUniformsPhong( m_uniforms, material ); + throw new Error( 'Unknown depthTexture format' ); - } + } - } else if ( material.isMeshStandardMaterial ) { + } - refreshUniformsCommon( m_uniforms, material ); + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { - if ( material.isMeshPhysicalMaterial ) { + const renderTargetProperties = properties.get( renderTarget ); + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); - refreshUniformsPhysical( m_uniforms, material ); + if ( renderTarget.depthTexture && ! renderTargetProperties.__autoAllocateDepthBuffer ) { - } else { + if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' ); - refreshUniformsStandard( m_uniforms, material ); + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - } + } else { - } else if ( material.isMeshMatcapMaterial ) { + if ( isCube ) { - refreshUniformsCommon( m_uniforms, material ); + renderTargetProperties.__webglDepthbuffer = []; - refreshUniformsMatcap( m_uniforms, material ); + for ( let i = 0; i < 6; i ++ ) { - } else if ( material.isMeshDepthMaterial ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget, false ); - refreshUniformsCommon( m_uniforms, material ); - refreshUniformsDepth( m_uniforms, material ); + } - } else if ( material.isMeshDistanceMaterial ) { + } else { - refreshUniformsCommon( m_uniforms, material ); - refreshUniformsDistance( m_uniforms, material ); + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget, false ); - } else if ( material.isMeshNormalMaterial ) { + } - refreshUniformsCommon( m_uniforms, material ); - refreshUniformsNormal( m_uniforms, material ); + } - } else if ( material.isLineBasicMaterial ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, null ); - refreshUniformsLine( m_uniforms, material ); + } - if ( material.isLineDashedMaterial ) { + // rebind framebuffer with external textures + function rebindTextures( renderTarget, colorTexture, depthTexture ) { - refreshUniformsDash( m_uniforms, material ); + const renderTargetProperties = properties.get( renderTarget ); - } + if ( colorTexture !== undefined ) { - } else if ( material.isPointsMaterial ) { + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, renderTarget.texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, 0 ); - refreshUniformsPoints( m_uniforms, material ); + } - } else if ( material.isSpriteMaterial ) { + if ( depthTexture !== undefined ) { - refreshUniformsSprites( m_uniforms, material ); + setupDepthRenderbuffer( renderTarget ); - } else if ( material.isShadowMaterial ) { + } - m_uniforms.color.value.copy( material.color ); - m_uniforms.opacity.value = material.opacity; + } - } + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { - // RectAreaLight Texture - // TODO (mrdoob): Find a nicer implementation + const texture = renderTarget.texture; - if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1; - if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2; + const renderTargetProperties = properties.get( renderTarget ); + const textureProperties = properties.get( texture ); - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - } + if ( renderTarget.isWebGLMultipleRenderTargets !== true ) { - if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { + if ( textureProperties.__webglTexture === undefined ) { - WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - material.uniformsNeedUpdate = false; + textureProperties.__webglTexture = _gl.createTexture(); } - if ( material.isSpriteMaterial ) { + textureProperties.__version = texture.version; + info.memory.textures ++; - p_uniforms.setValue( _gl, 'center', object.center ); + } - } + const isCube = ( renderTarget.isWebGLCubeRenderTarget === true ); + const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - // common matrices + // Setup framebuffer - p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); - p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); - p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); + if ( isCube ) { - return program; + renderTargetProperties.__webglFramebuffer = []; - } + for ( let i = 0; i < 6; i ++ ) { - // Uniforms (refresh uniforms objects) + if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { - function refreshUniformsCommon( uniforms, material ) { + renderTargetProperties.__webglFramebuffer[ i ] = []; - uniforms.opacity.value = material.opacity; + for ( let level = 0; level < texture.mipmaps.length; level ++ ) { - if ( material.color ) { + renderTargetProperties.__webglFramebuffer[ i ][ level ] = _gl.createFramebuffer(); - uniforms.diffuse.value.copy( material.color ); + } - } + } else { - if ( material.emissive ) { + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); + } } - if ( material.map ) { + } else { - uniforms.map.value = material.map; + if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { - } + renderTargetProperties.__webglFramebuffer = []; - if ( material.alphaMap ) { + for ( let level = 0; level < texture.mipmaps.length; level ++ ) { - uniforms.alphaMap.value = material.alphaMap; + renderTargetProperties.__webglFramebuffer[ level ] = _gl.createFramebuffer(); - } + } - if ( material.specularMap ) { + } else { - uniforms.specularMap.value = material.specularMap; + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); } - if ( material.envMap ) { + if ( isMultipleRenderTargets ) { - uniforms.envMap.value = material.envMap; + if ( capabilities.drawBuffers ) { - // don't flip CubeTexture envMaps, flip everything else: - // WebGLRenderTargetCube will be flipped for backwards compatibility - // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture - // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future - uniforms.flipEnvMap.value = material.envMap.isCubeTexture ? - 1 : 1; + const textures = renderTarget.texture; - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel; + const attachmentProperties = properties.get( textures[ i ] ); - } + if ( attachmentProperties.__webglTexture === undefined ) { - if ( material.lightMap ) { + attachmentProperties.__webglTexture = _gl.createTexture(); - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; + info.memory.textures ++; - } + } + + } - if ( material.aoMap ) { + } else { + + console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' ); - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; + } } - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map - // 6. emissive map + if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - var uvScaleMap; + const textures = isMultipleRenderTargets ? texture : [ texture ]; - if ( material.map ) { + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = []; - uvScaleMap = material.map; + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); - } else if ( material.specularMap ) { + for ( let i = 0; i < textures.length; i ++ ) { - uvScaleMap = material.specularMap; + const texture = textures[ i ]; + renderTargetProperties.__webglColorRenderbuffer[ i ] = _gl.createRenderbuffer(); - } else if ( material.displacementMap ) { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); - uvScaleMap = material.displacementMap; + const glFormat = utils.convert( texture.format, texture.colorSpace ); + const glType = utils.convert( texture.type ); + const glInternalFormat = getInternalFormat( texture.internalFormat, glFormat, glType, texture.colorSpace, renderTarget.isXRRenderTarget === true ); + const samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); - } else if ( material.normalMap ) { + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); - uvScaleMap = material.normalMap; + } - } else if ( material.bumpMap ) { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - uvScaleMap = material.bumpMap; + if ( renderTarget.depthBuffer ) { - } else if ( material.roughnessMap ) { + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); - uvScaleMap = material.roughnessMap; + } - } else if ( material.metalnessMap ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, null ); - uvScaleMap = material.metalnessMap; + } - } else if ( material.alphaMap ) { + } - uvScaleMap = material.alphaMap; + // Setup color buffer - } else if ( material.emissiveMap ) { + if ( isCube ) { - uvScaleMap = material.emissiveMap; + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips ); - } + for ( let i = 0; i < 6; i ++ ) { - if ( uvScaleMap !== undefined ) { + if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { - // backwards compatibility - if ( uvScaleMap.isWebGLRenderTarget ) { + for ( let level = 0; level < texture.mipmaps.length; level ++ ) { - uvScaleMap = uvScaleMap.texture; + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ][ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, level ); - } + } - if ( uvScaleMap.matrixAutoUpdate === true ) { + } else { - uvScaleMap.updateMatrix(); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0 ); } - uniforms.uvTransform.value.copy( uvScaleMap.matrix ); - } - } - - function refreshUniformsLine( uniforms, material ) { - - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - } + generateMipmap( _gl.TEXTURE_CUBE_MAP ); - function refreshUniformsDash( uniforms, material ) { + } - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + state.unbindTexture(); - } + } else if ( isMultipleRenderTargets ) { - function refreshUniformsPoints( uniforms, material ) { + const textures = renderTarget.texture; - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size * _pixelRatio; - uniforms.scale.value = _height * 0.5; + for ( let i = 0, il = textures.length; i < il; i ++ ) { - uniforms.map.value = material.map; + const attachment = textures[ i ]; + const attachmentProperties = properties.get( attachment ); - if ( material.map !== null ) { + state.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, 0 ); - if ( material.map.matrixAutoUpdate === true ) { + if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) { - material.map.updateMatrix(); + generateMipmap( _gl.TEXTURE_2D ); } - uniforms.uvTransform.value.copy( material.map.matrix ); - } - } + state.unbindTexture(); - function refreshUniformsSprites( uniforms, material ) { + } else { - uniforms.diffuse.value.copy( material.color ); - uniforms.opacity.value = material.opacity; - uniforms.rotation.value = material.rotation; - uniforms.map.value = material.map; + let glTextureType = _gl.TEXTURE_2D; - if ( material.map !== null ) { + if ( renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) { - if ( material.map.matrixAutoUpdate === true ) { + if ( isWebGL2 ) { - material.map.updateMatrix(); + glTextureType = renderTarget.isWebGL3DRenderTarget ? _gl.TEXTURE_3D : _gl.TEXTURE_2D_ARRAY; - } + } else { - uniforms.uvTransform.value.copy( material.map.matrix ); + console.error( 'THREE.WebGLTextures: THREE.Data3DTexture and THREE.DataArrayTexture only supported with WebGL2.' ); + + } } - } + state.bindTexture( glTextureType, textureProperties.__webglTexture ); + setTextureParameters( glTextureType, texture, supportsMips ); - function refreshUniformsFog( uniforms, fog ) { + if ( isWebGL2 && texture.mipmaps && texture.mipmaps.length > 0 ) { - uniforms.fogColor.value.copy( fog.color ); + for ( let level = 0; level < texture.mipmaps.length; level ++ ) { - if ( fog.isFog ) { + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ level ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, level ); - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } - } else if ( fog.isFogExp2 ) { + } else { - uniforms.fogDensity.value = fog.density; + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType, 0 ); } - } - - function refreshUniformsLambert( uniforms, material ) { - - if ( material.emissiveMap ) { + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - uniforms.emissiveMap.value = material.emissiveMap; + generateMipmap( glTextureType ); } + state.unbindTexture(); + } - function refreshUniformsPhong( uniforms, material ) { + // Setup depth and stencil buffers - uniforms.specular.value.copy( material.specular ); - uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) + if ( renderTarget.depthBuffer ) { - if ( material.emissiveMap ) { + setupDepthRenderbuffer( renderTarget ); - uniforms.emissiveMap.value = material.emissiveMap; + } - } + } - if ( material.bumpMap ) { + function updateRenderTargetMipmap( renderTarget ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const supportsMips = isPowerOfTwo$1( renderTarget ) || isWebGL2; - } + const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ]; - if ( material.normalMap ) { + for ( let i = 0, il = textures.length; i < il; i ++ ) { - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + const texture = textures[ i ]; - } + if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) { - if ( material.displacementMap ) { + const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + const webglTexture = properties.get( texture ).__webglTexture; - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + state.bindTexture( target, webglTexture ); + generateMipmap( target ); + state.unbindTexture(); } } - function refreshUniformsToon( uniforms, material ) { - - refreshUniformsPhong( uniforms, material ); + } - if ( material.gradientMap ) { + function updateMultisampleRenderTarget( renderTarget ) { - uniforms.gradientMap.value = material.gradientMap; + if ( ( isWebGL2 && renderTarget.samples > 0 ) && useMultisampledRTT( renderTarget ) === false ) { - } + const textures = renderTarget.isWebGLMultipleRenderTargets ? renderTarget.texture : [ renderTarget.texture ]; + const width = renderTarget.width; + const height = renderTarget.height; + let mask = _gl.COLOR_BUFFER_BIT; + const invalidationArray = []; + const depthStyle = renderTarget.stencilBuffer ? _gl.DEPTH_STENCIL_ATTACHMENT : _gl.DEPTH_ATTACHMENT; + const renderTargetProperties = properties.get( renderTarget ); + const isMultipleRenderTargets = ( renderTarget.isWebGLMultipleRenderTargets === true ); - } + // If MRT we need to remove FBO attachments + if ( isMultipleRenderTargets ) { - function refreshUniformsStandard( uniforms, material ) { + for ( let i = 0; i < textures.length; i ++ ) { - uniforms.roughness.value = material.roughness; - uniforms.metalness.value = material.metalness; + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, null ); - if ( material.roughnessMap ) { + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, null, 0 ); - uniforms.roughnessMap.value = material.roughnessMap; + } } - if ( material.metalnessMap ) { + state.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); + state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); - uniforms.metalnessMap.value = material.metalnessMap; + for ( let i = 0; i < textures.length; i ++ ) { - } + invalidationArray.push( _gl.COLOR_ATTACHMENT0 + i ); - if ( material.emissiveMap ) { + if ( renderTarget.depthBuffer ) { - uniforms.emissiveMap.value = material.emissiveMap; + invalidationArray.push( depthStyle ); - } + } - if ( material.bumpMap ) { + const ignoreDepthValues = ( renderTargetProperties.__ignoreDepthValues !== undefined ) ? renderTargetProperties.__ignoreDepthValues : false; - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + if ( ignoreDepthValues === false ) { - } + if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT; + if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT; - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + if ( isMultipleRenderTargets ) { - } + _gl.framebufferRenderbuffer( _gl.READ_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); - if ( material.displacementMap ) { + } - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + if ( ignoreDepthValues === true ) { - } + _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, [ depthStyle ] ); + _gl.invalidateFramebuffer( _gl.DRAW_FRAMEBUFFER, [ depthStyle ] ); - if ( material.envMap ) { + } - //uniforms.envMap.value = material.envMap; // part of uniforms common - uniforms.envMapIntensity.value = material.envMapIntensity; + if ( isMultipleRenderTargets ) { - } + const webglTexture = properties.get( textures[ i ] ).__webglTexture; + _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, webglTexture, 0 ); - } + } - function refreshUniformsPhysical( uniforms, material ) { + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST ); - refreshUniformsStandard( uniforms, material ); + if ( supportsInvalidateFramebuffer ) { - uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common + _gl.invalidateFramebuffer( _gl.READ_FRAMEBUFFER, invalidationArray ); - uniforms.clearcoat.value = material.clearcoat; - uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - if ( material.sheen ) uniforms.sheen.value.copy( material.sheen ); + } - if ( material.clearcoatNormalMap ) { - uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; + } - if ( material.side === BackSide ) { + state.bindFramebuffer( _gl.READ_FRAMEBUFFER, null ); + state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, null ); - uniforms.clearcoatNormalScale.value.negate(); + // If MRT since pre-blit we removed the FBO we need to reconstruct the attachments + if ( isMultipleRenderTargets ) { + + for ( let i = 0; i < textures.length; i ++ ) { + + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer[ i ] ); + + const webglTexture = properties.get( textures[ i ] ).__webglTexture; + + state.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + _gl.framebufferTexture2D( _gl.DRAW_FRAMEBUFFER, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D, webglTexture, 0 ); } } - uniforms.transparency.value = material.transparency; + state.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); } - function refreshUniformsMatcap( uniforms, material ) { + } - if ( material.matcap ) { + function getRenderTargetSamples( renderTarget ) { - uniforms.matcap.value = material.matcap; + return Math.min( maxSamples, renderTarget.samples ); - } + } - if ( material.bumpMap ) { + function useMultisampledRTT( renderTarget ) { - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + const renderTargetProperties = properties.get( renderTarget ); - } + return isWebGL2 && renderTarget.samples > 0 && extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true && renderTargetProperties.__useRenderToTexture !== false; - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + function updateVideoTexture( texture ) { - } + const frame = info.render.frame; - if ( material.displacementMap ) { + // Check the last frame we updated the VideoTexture - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + if ( _videoTextures.get( texture ) !== frame ) { - } + _videoTextures.set( texture, frame ); + texture.update(); } - function refreshUniformsDepth( uniforms, material ) { + } - if ( material.displacementMap ) { + function verifyColorSpace( texture, image ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + const colorSpace = texture.colorSpace; + const format = texture.format; + const type = texture.type; - } + if ( texture.isCompressedTexture === true || texture.isVideoTexture === true || texture.format === _SRGBAFormat ) return image; - } + if ( colorSpace !== LinearSRGBColorSpace && colorSpace !== NoColorSpace ) { - function refreshUniformsDistance( uniforms, material ) { + // sRGB - if ( material.displacementMap ) { + if ( ColorManagement.getTransfer( colorSpace ) === SRGBTransfer ) { - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + if ( isWebGL2 === false ) { - } + // in WebGL 1, try to use EXT_sRGB extension and unsized formats - uniforms.referencePosition.value.copy( material.referencePosition ); - uniforms.nearDistance.value = material.nearDistance; - uniforms.farDistance.value = material.farDistance; + if ( extensions.has( 'EXT_sRGB' ) === true && format === RGBAFormat ) { - } + texture.format = _SRGBAFormat; - function refreshUniformsNormal( uniforms, material ) { + // it's not possible to generate mips in WebGL 1 with this extension - if ( material.bumpMap ) { + texture.minFilter = LinearFilter; + texture.generateMipmaps = false; - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; - if ( material.side === BackSide ) uniforms.bumpScale.value *= - 1; + } else { - } + // slow fallback (CPU decode) - if ( material.normalMap ) { + image = ImageUtils.sRGBToLinear( image ); - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); - if ( material.side === BackSide ) uniforms.normalScale.value.negate(); + } - } + } else { - if ( material.displacementMap ) { + // in WebGL 2 uncompressed textures can only be sRGB encoded if they have the RGBA8 format - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + if ( format !== RGBAFormat || type !== UnsignedByteType ) { - } + console.warn( 'THREE.WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType.' ); - } + } - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + } - function markUniformsLightsNeedsUpdate( uniforms, value ) { + } else { - uniforms.ambientLightColor.needsUpdate = value; - uniforms.lightProbe.needsUpdate = value; + console.error( 'THREE.WebGLTextures: Unsupported texture color space:', colorSpace ); - uniforms.directionalLights.needsUpdate = value; - uniforms.pointLights.needsUpdate = value; - uniforms.spotLights.needsUpdate = value; - uniforms.rectAreaLights.needsUpdate = value; - uniforms.hemisphereLights.needsUpdate = value; + } } - // - this.setFramebuffer = function ( value ) { + return image; - if ( _framebuffer !== value ) _gl.bindFramebuffer( 36160, value ); + } - _framebuffer = value; + // - }; + this.allocateTextureUnit = allocateTextureUnit; + this.resetTextureUnits = resetTextureUnits; - this.getActiveCubeFace = function () { + this.setTexture2D = setTexture2D; + this.setTexture2DArray = setTexture2DArray; + this.setTexture3D = setTexture3D; + this.setTextureCube = setTextureCube; + this.rebindTextures = rebindTextures; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; + this.setupDepthRenderbuffer = setupDepthRenderbuffer; + this.setupFrameBufferTexture = setupFrameBufferTexture; + this.useMultisampledRTT = useMultisampledRTT; - return _currentActiveCubeFace; +} - }; - - this.getActiveMipmapLevel = function () { +function WebGLUtils( gl, extensions, capabilities ) { - return _currentActiveMipmapLevel; + const isWebGL2 = capabilities.isWebGL2; - }; + function convert( p, colorSpace = NoColorSpace ) { - this.getRenderTarget = function () { + let extension; - return _currentRenderTarget; + const transfer = ColorManagement.getTransfer( colorSpace ); - }; + 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; - this.setRenderTarget = function ( renderTarget, activeCubeFace, activeMipmapLevel ) { + 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; - _currentRenderTarget = renderTarget; - _currentActiveCubeFace = activeCubeFace; - _currentActiveMipmapLevel = activeMipmapLevel; + if ( p === HalfFloatType ) { - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + if ( isWebGL2 ) return gl.HALF_FLOAT; - textures.setupRenderTarget( renderTarget ); + extension = extensions.get( 'OES_texture_half_float' ); - } + if ( extension !== null ) { - var framebuffer = _framebuffer; - var isCube = false; + return extension.HALF_FLOAT_OES; - if ( renderTarget ) { + } else { - var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; + return null; - if ( renderTarget.isWebGLRenderTargetCube ) { + } - framebuffer = __webglFramebuffer[ activeCubeFace || 0 ]; - isCube = true; + } - } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + if ( p === AlphaFormat ) return gl.ALPHA; + 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; - framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + // WebGL 1 sRGB fallback - } else { + if ( p === _SRGBAFormat ) { - framebuffer = __webglFramebuffer; + extension = extensions.get( 'EXT_sRGB' ); - } + if ( extension !== null ) { - _currentViewport.copy( renderTarget.viewport ); - _currentScissor.copy( renderTarget.scissor ); - _currentScissorTest = renderTarget.scissorTest; + return extension.SRGB_ALPHA_EXT; } else { - _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); - _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); - _currentScissorTest = _scissorTest; + return null; } - if ( _currentFramebuffer !== framebuffer ) { + } - _gl.bindFramebuffer( 36160, framebuffer ); - _currentFramebuffer = framebuffer; + // 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; - state.viewport( _currentViewport ); - state.scissor( _currentScissor ); - state.setScissorTest( _currentScissorTest ); + // S3TC - if ( isCube ) { + if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format || p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) { - var textureProperties = properties.get( renderTarget.texture ); - _gl.framebufferTexture2D( 36160, 36064, 34069 + ( activeCubeFace || 0 ), textureProperties.__webglTexture, activeMipmapLevel || 0 ); + if ( transfer === SRGBTransfer ) { - } + extension = extensions.get( 'WEBGL_compressed_texture_s3tc_srgb' ); - }; + if ( extension !== null ) { - this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + 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; - if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { + } else { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + return null; - } + } - var framebuffer = properties.get( renderTarget ).__webglFramebuffer; + } else { - if ( renderTarget.isWebGLRenderTargetCube && activeCubeFaceIndex !== undefined ) { + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - framebuffer = framebuffer[ activeCubeFaceIndex ]; + 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; - if ( framebuffer ) { + } else { - var restore = false; + return null; - if ( framebuffer !== _currentFramebuffer ) { + } - _gl.bindFramebuffer( 36160, framebuffer ); + } - restore = true; + } - } + // PVRTC - try { + if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format || p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) { - var texture = renderTarget.texture; - var textureFormat = texture.format; - var textureType = texture.type; + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( 35739 ) ) { + if ( extension !== null ) { - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); - return; + 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 { - if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513) - ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox - ! ( textureType === HalfFloatType && ( capabilities.isWebGL2 ? extensions.get( 'EXT_color_buffer_float' ) : extensions.get( 'EXT_color_buffer_half_float' ) ) ) ) { + return null; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); - return; + } - } + } + + // ETC1 - if ( _gl.checkFramebufferStatus( 36160 ) === 36053 ) { + if ( p === RGB_ETC1_Format ) { - // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { + if ( extension !== null ) { - _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + return extension.COMPRESSED_RGB_ETC1_WEBGL; - } + } else { - } else { + return null; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + } - } + } - } finally { + // ETC2 - if ( restore ) { + if ( p === RGB_ETC2_Format || p === RGBA_ETC2_EAC_Format ) { - _gl.bindFramebuffer( 36160, _currentFramebuffer ); + extension = extensions.get( 'WEBGL_compressed_texture_etc' ); - } + if ( extension !== null ) { - } + if ( p === RGB_ETC2_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ETC2 : extension.COMPRESSED_RGB8_ETC2; + if ( p === RGBA_ETC2_EAC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : extension.COMPRESSED_RGBA8_ETC2_EAC; + + } else { + + return null; } - }; + } - this.copyFramebufferToTexture = function ( position, texture, level ) { + // ASTC - var width = texture.image.width; - var height = texture.image.height; - var glFormat = utils.convert( texture.format ); + 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 ) { - textures.setTexture2D( texture, 0 ); + extension = extensions.get( 'WEBGL_compressed_texture_astc' ); + + if ( extension !== null ) { - _gl.copyTexImage2D( 3553, level || 0, glFormat, position.x, position.y, width, height, 0 ); + if ( p === RGBA_ASTC_4x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : extension.COMPRESSED_RGBA_ASTC_4x4_KHR; + if ( p === RGBA_ASTC_5x4_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : extension.COMPRESSED_RGBA_ASTC_5x4_KHR; + if ( p === RGBA_ASTC_5x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : extension.COMPRESSED_RGBA_ASTC_5x5_KHR; + if ( p === RGBA_ASTC_6x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : extension.COMPRESSED_RGBA_ASTC_6x5_KHR; + if ( p === RGBA_ASTC_6x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : extension.COMPRESSED_RGBA_ASTC_6x6_KHR; + if ( p === RGBA_ASTC_8x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : extension.COMPRESSED_RGBA_ASTC_8x5_KHR; + if ( p === RGBA_ASTC_8x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : extension.COMPRESSED_RGBA_ASTC_8x6_KHR; + if ( p === RGBA_ASTC_8x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : extension.COMPRESSED_RGBA_ASTC_8x8_KHR; + if ( p === RGBA_ASTC_10x5_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : extension.COMPRESSED_RGBA_ASTC_10x5_KHR; + if ( p === RGBA_ASTC_10x6_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : extension.COMPRESSED_RGBA_ASTC_10x6_KHR; + if ( p === RGBA_ASTC_10x8_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : extension.COMPRESSED_RGBA_ASTC_10x8_KHR; + if ( p === RGBA_ASTC_10x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : extension.COMPRESSED_RGBA_ASTC_10x10_KHR; + if ( p === RGBA_ASTC_12x10_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : extension.COMPRESSED_RGBA_ASTC_12x10_KHR; + if ( p === RGBA_ASTC_12x12_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : extension.COMPRESSED_RGBA_ASTC_12x12_KHR; - }; + } else { - this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) { + return null; - var width = srcTexture.image.width; - var height = srcTexture.image.height; - var glFormat = utils.convert( dstTexture.format ); - var glType = utils.convert( dstTexture.type ); + } - textures.setTexture2D( dstTexture, 0 ); + } - if ( srcTexture.isDataTexture ) { + // BPTC - _gl.texSubImage2D( 3553, level || 0, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); + if ( p === RGBA_BPTC_Format || p === RGB_BPTC_SIGNED_Format || p === RGB_BPTC_UNSIGNED_Format ) { - } else { + extension = extensions.get( 'EXT_texture_compression_bptc' ); - _gl.texSubImage2D( 3553, level || 0, position.x, position.y, glFormat, glType, srcTexture.image ); + if ( extension !== null ) { - } + if ( p === RGBA_BPTC_Format ) return ( transfer === SRGBTransfer ) ? extension.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT : extension.COMPRESSED_RGBA_BPTC_UNORM_EXT; + if ( p === RGB_BPTC_SIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT; + if ( p === RGB_BPTC_UNSIGNED_Format ) return extension.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT; - }; + } else { - if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + return null; - __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef + } } - } + // RGTC - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( p === RED_RGTC1_Format || p === SIGNED_RED_RGTC1_Format || p === RED_GREEN_RGTC2_Format || p === SIGNED_RED_GREEN_RGTC2_Format ) { - function FogExp2( color, density ) { + extension = extensions.get( 'EXT_texture_compression_rgtc' ); - this.name = ''; + if ( extension !== null ) { - this.color = new Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; + 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 { - Object.assign( FogExp2.prototype, { + return null; - isFogExp2: true, + } - clone: function () { + } - return new FogExp2( this.color, this.density ); + // - }, + if ( p === UnsignedInt248Type ) { - toJSON: function ( /* meta */ ) { + if ( isWebGL2 ) return gl.UNSIGNED_INT_24_8; - return { - type: 'FogExp2', - color: this.color.getHex(), - density: this.density - }; + extension = extensions.get( 'WEBGL_depth_texture' ); - } + if ( extension !== null ) { - } ); + return extension.UNSIGNED_INT_24_8_WEBGL; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } else { - function Fog( color, near, far ) { + return null; - this.name = ''; + } - this.color = new Color( color ); + } - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; + // 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; } - Object.assign( Fog.prototype, { + return { convert: convert }; - isFog: true, +} - clone: function () { +class ArrayCamera extends PerspectiveCamera { - return new Fog( this.color, this.near, this.far ); + constructor( array = [] ) { - }, + super(); - toJSON: function ( /* meta */ ) { + this.isArrayCamera = true; - return { - type: 'Fog', - color: this.color.getHex(), - near: this.near, - far: this.far - }; + this.cameras = array; - } + } - } ); +} - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ +class Group extends Object3D { - function InterleavedBuffer( array, stride ) { + constructor() { - this.array = array; - this.stride = stride; - this.count = array !== undefined ? array.length / stride : 0; + super(); - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; + this.isGroup = true; - this.version = 0; + this.type = 'Group'; } - Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', { +} - set: function ( value ) { +const _moveEvent = { type: 'move' }; - if ( value === true ) this.version ++; +class WebXRController { - } + constructor() { - } ); + this._targetRay = null; + this._grip = null; + this._hand = null; - Object.assign( InterleavedBuffer.prototype, { + } - isInterleavedBuffer: true, + getHandSpace() { - onUploadCallback: function () {}, + if ( this._hand === null ) { - setArray: function ( array ) { + this._hand = new Group(); + this._hand.matrixAutoUpdate = false; + this._hand.visible = false; - if ( Array.isArray( array ) ) { + this._hand.joints = {}; + this._hand.inputState = { pinching: false }; - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); + } - } + return this._hand; - this.count = array !== undefined ? array.length / this.stride : 0; - this.array = array; + } - return this; + getTargetRaySpace() { - }, + if ( this._targetRay === null ) { - setDynamic: function ( value ) { + this._targetRay = new Group(); + this._targetRay.matrixAutoUpdate = false; + this._targetRay.visible = false; + this._targetRay.hasLinearVelocity = false; + this._targetRay.linearVelocity = new Vector3(); + this._targetRay.hasAngularVelocity = false; + this._targetRay.angularVelocity = new Vector3(); - this.dynamic = value; + } - return this; + return this._targetRay; - }, + } - copy: function ( source ) { + getGripSpace() { - this.array = new source.array.constructor( source.array ); - this.count = source.count; - this.stride = source.stride; - this.dynamic = source.dynamic; + if ( this._grip === null ) { - return this; + this._grip = new Group(); + this._grip.matrixAutoUpdate = false; + this._grip.visible = false; + this._grip.hasLinearVelocity = false; + this._grip.linearVelocity = new Vector3(); + this._grip.hasAngularVelocity = false; + this._grip.angularVelocity = new Vector3(); - }, + } - copyAt: function ( index1, attribute, index2 ) { + return this._grip; - index1 *= this.stride; - index2 *= attribute.stride; + } - for ( var i = 0, l = this.stride; i < l; i ++ ) { + dispatchEvent( event ) { - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + if ( this._targetRay !== null ) { - } + this._targetRay.dispatchEvent( event ); - return this; + } - }, + if ( this._grip !== null ) { - set: function ( value, offset ) { + this._grip.dispatchEvent( event ); - if ( offset === undefined ) offset = 0; + } - this.array.set( value, offset ); + if ( this._hand !== null ) { - return this; + this._hand.dispatchEvent( event ); - }, + } - clone: function () { + return this; - return new this.constructor().copy( this ); + } - }, + connect( inputSource ) { - onUpload: function ( callback ) { + if ( inputSource && inputSource.hand ) { - this.onUploadCallback = callback; + const hand = this._hand; - return this; + if ( hand ) { - } + for ( const inputjoint of inputSource.hand.values() ) { - } ); + // Initialize hand with joints when connected + this._getHandJoint( hand, inputjoint ); - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ + } - function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) { + } - this.data = interleavedBuffer; - this.itemSize = itemSize; - this.offset = offset; + } + + this.dispatchEvent( { type: 'connected', data: inputSource } ); - this.normalized = normalized === true; + return this; } - Object.defineProperties( InterleavedBufferAttribute.prototype, { + disconnect( inputSource ) { - count: { + this.dispatchEvent( { type: 'disconnected', data: inputSource } ); - get: function () { + if ( this._targetRay !== null ) { - return this.data.count; + this._targetRay.visible = false; - } + } - }, + if ( this._grip !== null ) { - array: { + this._grip.visible = false; - get: function () { + } - return this.data.array; + if ( this._hand !== null ) { - } + this._hand.visible = false; } - } ); + return this; - Object.assign( InterleavedBufferAttribute.prototype, { + } - isInterleavedBufferAttribute: true, + update( inputSource, frame, referenceSpace ) { - setX: function ( index, x ) { + let inputPose = null; + let gripPose = null; + let handPose = null; - this.data.array[ index * this.data.stride + this.offset ] = x; + const targetRay = this._targetRay; + const grip = this._grip; + const hand = this._hand; - return this; + if ( inputSource && frame.session.visibilityState !== 'visible-blurred' ) { - }, + if ( hand && inputSource.hand ) { - setY: function ( index, y ) { + handPose = true; - this.data.array[ index * this.data.stride + this.offset + 1 ] = y; + for ( const inputjoint of inputSource.hand.values() ) { - return this; + // Update the joints groups with the XRJoint poses + const jointPose = frame.getJointPose( inputjoint, referenceSpace ); - }, + // The transform of this joint will be updated with the joint pose on each frame + const joint = this._getHandJoint( hand, inputjoint ); - setZ: function ( index, z ) { + if ( jointPose !== null ) { - this.data.array[ index * this.data.stride + this.offset + 2 ] = z; + joint.matrix.fromArray( jointPose.transform.matrix ); + joint.matrix.decompose( joint.position, joint.rotation, joint.scale ); + joint.matrixWorldNeedsUpdate = true; + joint.jointRadius = jointPose.radius; - return this; + } - }, + joint.visible = jointPose !== null; - setW: function ( index, w ) { + } - this.data.array[ index * this.data.stride + this.offset + 3 ] = w; + // Custom events - return this; + // Check pinchz + const indexTip = hand.joints[ 'index-finger-tip' ]; + const thumbTip = hand.joints[ 'thumb-tip' ]; + const distance = indexTip.position.distanceTo( thumbTip.position ); - }, + const distanceToPinch = 0.02; + const threshold = 0.005; - getX: function ( index ) { + if ( hand.inputState.pinching && distance > distanceToPinch + threshold ) { - return this.data.array[ index * this.data.stride + this.offset ]; + hand.inputState.pinching = false; + this.dispatchEvent( { + type: 'pinchend', + handedness: inputSource.handedness, + target: this + } ); - }, + } else if ( ! hand.inputState.pinching && distance <= distanceToPinch - threshold ) { - getY: function ( index ) { + hand.inputState.pinching = true; + this.dispatchEvent( { + type: 'pinchstart', + handedness: inputSource.handedness, + target: this + } ); - return this.data.array[ index * this.data.stride + this.offset + 1 ]; + } - }, + } else { - getZ: function ( index ) { + if ( grip !== null && inputSource.gripSpace ) { - return this.data.array[ index * this.data.stride + this.offset + 2 ]; + gripPose = frame.getPose( inputSource.gripSpace, referenceSpace ); - }, + if ( gripPose !== null ) { - getW: function ( index ) { + grip.matrix.fromArray( gripPose.transform.matrix ); + grip.matrix.decompose( grip.position, grip.rotation, grip.scale ); + grip.matrixWorldNeedsUpdate = true; - return this.data.array[ index * this.data.stride + this.offset + 3 ]; + if ( gripPose.linearVelocity ) { - }, + grip.hasLinearVelocity = true; + grip.linearVelocity.copy( gripPose.linearVelocity ); - setXY: function ( index, x, y ) { + } else { - index = index * this.data.stride + this.offset; + grip.hasLinearVelocity = false; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; + } - return this; + if ( gripPose.angularVelocity ) { - }, + grip.hasAngularVelocity = true; + grip.angularVelocity.copy( gripPose.angularVelocity ); - setXYZ: function ( index, x, y, z ) { + } else { - index = index * this.data.stride + this.offset; + grip.hasAngularVelocity = false; - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; + } - return this; + } - }, + } - setXYZW: function ( index, x, y, z, w ) { + } - index = index * this.data.stride + this.offset; + if ( targetRay !== null ) { - this.data.array[ index + 0 ] = x; - this.data.array[ index + 1 ] = y; - this.data.array[ index + 2 ] = z; - this.data.array[ index + 3 ] = w; + inputPose = frame.getPose( inputSource.targetRaySpace, referenceSpace ); - return this; + // Some runtimes (namely Vive Cosmos with Vive OpenXR Runtime) have only grip space and ray space is equal to it + if ( inputPose === null && gripPose !== null ) { - } + inputPose = gripPose; - } ); + } - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * map: new THREE.Texture( ), - * rotation: , - * sizeAttenuation: - * } - */ + if ( inputPose !== null ) { - function SpriteMaterial( parameters ) { + targetRay.matrix.fromArray( inputPose.transform.matrix ); + targetRay.matrix.decompose( targetRay.position, targetRay.rotation, targetRay.scale ); + targetRay.matrixWorldNeedsUpdate = true; - Material.call( this ); + if ( inputPose.linearVelocity ) { - this.type = 'SpriteMaterial'; + targetRay.hasLinearVelocity = true; + targetRay.linearVelocity.copy( inputPose.linearVelocity ); - this.color = new Color( 0xffffff ); - this.map = null; + } else { - this.rotation = 0; + targetRay.hasLinearVelocity = false; - this.sizeAttenuation = true; + } - this.lights = false; - this.transparent = true; + if ( inputPose.angularVelocity ) { - this.setValues( parameters ); + targetRay.hasAngularVelocity = true; + targetRay.angularVelocity.copy( inputPose.angularVelocity ); - } + } else { - SpriteMaterial.prototype = Object.create( Material.prototype ); - SpriteMaterial.prototype.constructor = SpriteMaterial; - SpriteMaterial.prototype.isSpriteMaterial = true; + targetRay.hasAngularVelocity = false; - SpriteMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + this.dispatchEvent( _moveEvent ); - this.color.copy( source.color ); - this.map = source.map; + } - this.rotation = source.rotation; + } - this.sizeAttenuation = source.sizeAttenuation; - return this; + } - }; + if ( targetRay !== null ) { - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + targetRay.visible = ( inputPose !== null ); - var _geometry; + } - var _intersectPoint = new Vector3(); - var _worldScale = new Vector3(); - var _mvPosition = new Vector3(); + if ( grip !== null ) { - var _alignedPosition = new Vector2(); - var _rotatedPosition = new Vector2(); - var _viewWorldMatrix = new Matrix4(); + grip.visible = ( gripPose !== null ); - var _vA$1 = new Vector3(); - var _vB$1 = new Vector3(); - var _vC$1 = new Vector3(); + } - var _uvA$1 = new Vector2(); - var _uvB$1 = new Vector2(); - var _uvC$1 = new Vector2(); + if ( hand !== null ) { - function Sprite( material ) { + hand.visible = ( handPose !== null ); - Object3D.call( this ); + } - this.type = 'Sprite'; + return this; - if ( _geometry === undefined ) { + } - _geometry = new BufferGeometry(); + // private method - var float32Array = new Float32Array( [ - - 0.5, - 0.5, 0, 0, 0, - 0.5, - 0.5, 0, 1, 0, - 0.5, 0.5, 0, 1, 1, - - 0.5, 0.5, 0, 0, 1 - ] ); + _getHandJoint( hand, inputjoint ) { - var interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); + if ( hand.joints[ inputjoint.jointName ] === undefined ) { - _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); - _geometry.addAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); - _geometry.addAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); + const joint = new Group(); + joint.matrixAutoUpdate = false; + joint.visible = false; + hand.joints[ inputjoint.jointName ] = joint; - } + hand.add( joint ); - this.geometry = _geometry; - this.material = ( material !== undefined ) ? material : new SpriteMaterial(); + } - this.center = new Vector2( 0.5, 0.5 ); + return hand.joints[ inputjoint.jointName ]; } - Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), { +} - constructor: Sprite, +class DepthTexture extends Texture { - isSprite: true, + constructor( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { - raycast: function ( raycaster, intersects ) { + format = format !== undefined ? format : DepthFormat; - if ( raycaster.camera === null ) { + if ( format !== DepthFormat && format !== DepthStencilFormat ) { - console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); + throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); - } + } + + if ( type === undefined && format === DepthFormat ) type = UnsignedIntType; + if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; - _worldScale.setFromMatrixScale( this.matrixWorld ); + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); - this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); + this.isDepthTexture = true; - _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); + this.image = { width: width, height: height }; - if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { + this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; - _worldScale.multiplyScalar( - _mvPosition.z ); + this.flipY = false; + this.generateMipmaps = false; - } + this.compareFunction = null; - var rotation = this.material.rotation; - var sin, cos; - if ( rotation !== 0 ) { + } - cos = Math.cos( rotation ); - sin = Math.sin( rotation ); - } + copy( source ) { - var center = this.center; + super.copy( source ); - transformVertex( _vA$1.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vB$1.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - transformVertex( _vC$1.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + this.compareFunction = source.compareFunction; - _uvA$1.set( 0, 0 ); - _uvB$1.set( 1, 0 ); - _uvC$1.set( 1, 1 ); + return this; - // check first triangle - var intersect = raycaster.ray.intersectTriangle( _vA$1, _vB$1, _vC$1, false, _intersectPoint ); + } - if ( intersect === null ) { + toJSON( meta ) { - // check second triangle - transformVertex( _vB$1.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - _uvB$1.set( 0, 1 ); + const data = super.toJSON( meta ); - intersect = raycaster.ray.intersectTriangle( _vA$1, _vC$1, _vB$1, false, _intersectPoint ); - if ( intersect === null ) { + if ( this.compareFunction !== null ) data.compareFunction = this.compareFunction; - return; + return data; - } + } - } +} - var distance = raycaster.ray.origin.distanceTo( _intersectPoint ); +class WebXRManager extends EventDispatcher { - if ( distance < raycaster.near || distance > raycaster.far ) return; + constructor( renderer, gl ) { - intersects.push( { + super(); - distance: distance, - point: _intersectPoint.clone(), - uv: Triangle.getUV( _intersectPoint, _vA$1, _vB$1, _vC$1, _uvA$1, _uvB$1, _uvC$1, new Vector2() ), - face: null, - object: this + const scope = this; - } ); + let session = null; - }, + let framebufferScaleFactor = 1.0; - clone: function () { + let referenceSpace = null; + let referenceSpaceType = 'local-floor'; + // Set default foveation to maximum. + let foveation = 1.0; + let customReferenceSpace = null; - return new this.constructor( this.material ).copy( this ); + let pose = null; + let glBinding = null; + let glProjLayer = null; + let glBaseLayer = null; + let xrFrame = null; + const attributes = gl.getContextAttributes(); + let initialRenderTarget = null; + let newRenderTarget = null; - }, + const controllers = []; + const controllerInputSources = []; - copy: function ( source ) { + // - Object3D.prototype.copy.call( this, source ); + const cameraL = new PerspectiveCamera(); + cameraL.layers.enable( 1 ); + cameraL.viewport = new Vector4(); - if ( source.center !== undefined ) this.center.copy( source.center ); + const cameraR = new PerspectiveCamera(); + cameraR.layers.enable( 2 ); + cameraR.viewport = new Vector4(); - return this; + const cameras = [ cameraL, cameraR ]; - } + const cameraXR = new ArrayCamera(); + cameraXR.layers.enable( 1 ); + cameraXR.layers.enable( 2 ); + let _currentDepthNear = null; + let _currentDepthFar = null; - } ); + // - function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { + this.cameraAutoUpdate = true; + this.enabled = false; - // compute position in camera space - _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); + this.isPresenting = false; - // to check if rotation is not zero - if ( sin !== undefined ) { + this.getController = function ( index ) { - _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); - _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); + let controller = controllers[ index ]; - } else { + if ( controller === undefined ) { - _rotatedPosition.copy( _alignedPosition ); + controller = new WebXRController(); + controllers[ index ] = controller; - } + } + return controller.getTargetRaySpace(); - vertexPosition.copy( mvPosition ); - vertexPosition.x += _rotatedPosition.x; - vertexPosition.y += _rotatedPosition.y; + }; - // transform to world space - vertexPosition.applyMatrix4( _viewWorldMatrix ); + this.getControllerGrip = function ( index ) { - } + let controller = controllers[ index ]; - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + if ( controller === undefined ) { - var _v1$4 = new Vector3(); - var _v2$2 = new Vector3(); + controller = new WebXRController(); + controllers[ index ] = controller; - function LOD() { + } - Object3D.call( this ); + return controller.getGripSpace(); - this.type = 'LOD'; + }; - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - } - } ); + this.getHand = function ( index ) { - this.autoUpdate = true; + let controller = controllers[ index ]; - } + if ( controller === undefined ) { - LOD.prototype = Object.assign( Object.create( Object3D.prototype ), { + controller = new WebXRController(); + controllers[ index ] = controller; - constructor: LOD, + } - isLOD: true, + return controller.getHandSpace(); - copy: function ( source ) { + }; - Object3D.prototype.copy.call( this, source, false ); + // - var levels = source.levels; + function onSessionEvent( event ) { - for ( var i = 0, l = levels.length; i < l; i ++ ) { + const controllerIndex = controllerInputSources.indexOf( event.inputSource ); - var level = levels[ i ]; + if ( controllerIndex === - 1 ) { - this.addLevel( level.object.clone(), level.distance ); + return; } - return this; + const controller = controllers[ controllerIndex ]; - }, + if ( controller !== undefined ) { - addLevel: function ( object, distance ) { + controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace ); + controller.dispatchEvent( { type: event.type, data: event.inputSource } ); - if ( distance === undefined ) distance = 0; + } - distance = Math.abs( distance ); + } - var levels = this.levels; + function onSessionEnd() { - for ( var l = 0; l < levels.length; l ++ ) { + session.removeEventListener( 'select', onSessionEvent ); + session.removeEventListener( 'selectstart', onSessionEvent ); + session.removeEventListener( 'selectend', onSessionEvent ); + session.removeEventListener( 'squeeze', onSessionEvent ); + session.removeEventListener( 'squeezestart', onSessionEvent ); + session.removeEventListener( 'squeezeend', onSessionEvent ); + session.removeEventListener( 'end', onSessionEnd ); + session.removeEventListener( 'inputsourceschange', onInputSourcesChange ); - if ( distance < levels[ l ].distance ) { + for ( let i = 0; i < controllers.length; i ++ ) { - break; + const inputSource = controllerInputSources[ i ]; - } + if ( inputSource === null ) continue; + + controllerInputSources[ i ] = null; + + controllers[ i ].disconnect( inputSource ); } - levels.splice( l, 0, { distance: distance, object: object } ); + _currentDepthNear = null; + _currentDepthFar = null; - this.add( object ); + // restore framebuffer/rendering state - return this; + renderer.setRenderTarget( initialRenderTarget ); - }, + glBaseLayer = null; + glProjLayer = null; + glBinding = null; + session = null; + newRenderTarget = null; - getObjectForDistance: function ( distance ) { + // - var levels = this.levels; + animation.stop(); - for ( var i = 1, l = levels.length; i < l; i ++ ) { + scope.isPresenting = false; - if ( distance < levels[ i ].distance ) { + scope.dispatchEvent( { type: 'sessionend' } ); - break; + } - } + this.setFramebufferScaleFactor = function ( value ) { - } + framebufferScaleFactor = value; - return levels[ i - 1 ].object; + if ( scope.isPresenting === true ) { - }, + console.warn( 'THREE.WebXRManager: Cannot change framebuffer scale while presenting.' ); - raycast: function ( raycaster, intersects ) { + } - _v1$4.setFromMatrixPosition( this.matrixWorld ); + }; - var distance = raycaster.ray.origin.distanceTo( _v1$4 ); + this.setReferenceSpaceType = function ( value ) { - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + referenceSpaceType = value; - }, + if ( scope.isPresenting === true ) { - update: function ( camera ) { + console.warn( 'THREE.WebXRManager: Cannot change reference space type while presenting.' ); - var levels = this.levels; + } - if ( levels.length > 1 ) { + }; - _v1$4.setFromMatrixPosition( camera.matrixWorld ); - _v2$2.setFromMatrixPosition( this.matrixWorld ); + this.getReferenceSpace = function () { - var distance = _v1$4.distanceTo( _v2$2 ); + return customReferenceSpace || referenceSpace; - levels[ 0 ].object.visible = true; + }; - for ( var i = 1, l = levels.length; i < l; i ++ ) { + this.setReferenceSpace = function ( space ) { - if ( distance >= levels[ i ].distance ) { + customReferenceSpace = space; - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; + }; - } else { + this.getBaseLayer = function () { - break; + return glProjLayer !== null ? glProjLayer : glBaseLayer; - } + }; - } + this.getBinding = function () { - for ( ; i < l; i ++ ) { + return glBinding; - levels[ i ].object.visible = false; + }; - } + this.getFrame = function () { - } + return xrFrame; - }, + }; - toJSON: function ( meta ) { + this.getSession = function () { - var data = Object3D.prototype.toJSON.call( this, meta ); + return session; - data.object.levels = []; + }; - var levels = this.levels; + this.setSession = async function ( value ) { - for ( var i = 0, l = levels.length; i < l; i ++ ) { + session = value; - var level = levels[ i ]; + if ( session !== null ) { - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); + initialRenderTarget = renderer.getRenderTarget(); - } + session.addEventListener( 'select', onSessionEvent ); + session.addEventListener( 'selectstart', onSessionEvent ); + session.addEventListener( 'selectend', onSessionEvent ); + session.addEventListener( 'squeeze', onSessionEvent ); + session.addEventListener( 'squeezestart', onSessionEvent ); + session.addEventListener( 'squeezeend', onSessionEvent ); + session.addEventListener( 'end', onSessionEnd ); + session.addEventListener( 'inputsourceschange', onInputSourcesChange ); - return data; + if ( attributes.xrCompatible !== true ) { - } + await gl.makeXRCompatible(); - } ); + } - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ + if ( ( session.renderState.layers === undefined ) || ( renderer.capabilities.isWebGL2 === false ) ) { - function SkinnedMesh( geometry, material ) { + const layerInit = { + antialias: ( session.renderState.layers === undefined ) ? attributes.antialias : true, + alpha: true, + depth: attributes.depth, + stencil: attributes.stencil, + framebufferScaleFactor: framebufferScaleFactor + }; - if ( geometry && geometry.isGeometry ) { + glBaseLayer = new XRWebGLLayer( session, gl, layerInit ); - console.error( 'THREE.SkinnedMesh no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.' ); + session.updateRenderState( { baseLayer: glBaseLayer } ); - } + newRenderTarget = new WebGLRenderTarget( + glBaseLayer.framebufferWidth, + glBaseLayer.framebufferHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + colorSpace: renderer.outputColorSpace, + stencilBuffer: attributes.stencil + } + ); - Mesh.call( this, geometry, material ); + } else { - this.type = 'SkinnedMesh'; + let depthFormat = null; + let depthType = null; + let glDepthFormat = null; - this.bindMode = 'attached'; - this.bindMatrix = new Matrix4(); - this.bindMatrixInverse = new Matrix4(); + if ( attributes.depth ) { - } + glDepthFormat = attributes.stencil ? gl.DEPTH24_STENCIL8 : gl.DEPTH_COMPONENT24; + depthFormat = attributes.stencil ? DepthStencilFormat : DepthFormat; + depthType = attributes.stencil ? UnsignedInt248Type : UnsignedIntType; - SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), { + } - constructor: SkinnedMesh, + const projectionlayerInit = { + colorFormat: gl.RGBA8, + depthFormat: glDepthFormat, + scaleFactor: framebufferScaleFactor + }; - isSkinnedMesh: true, + glBinding = new XRWebGLBinding( session, gl ); - bind: function ( skeleton, bindMatrix ) { + glProjLayer = glBinding.createProjectionLayer( projectionlayerInit ); - this.skeleton = skeleton; + session.updateRenderState( { layers: [ glProjLayer ] } ); - if ( bindMatrix === undefined ) { + newRenderTarget = new WebGLRenderTarget( + glProjLayer.textureWidth, + glProjLayer.textureHeight, + { + format: RGBAFormat, + type: UnsignedByteType, + depthTexture: new DepthTexture( glProjLayer.textureWidth, glProjLayer.textureHeight, depthType, undefined, undefined, undefined, undefined, undefined, undefined, depthFormat ), + stencilBuffer: attributes.stencil, + colorSpace: renderer.outputColorSpace, + samples: attributes.antialias ? 4 : 0 + } ); - this.updateMatrixWorld( true ); + const renderTargetProperties = renderer.properties.get( newRenderTarget ); + renderTargetProperties.__ignoreDepthValues = glProjLayer.ignoreDepthValues; - this.skeleton.calculateInverses(); + } - bindMatrix = this.matrixWorld; + newRenderTarget.isXRRenderTarget = true; // TODO Remove this when possible, see #23278 - } + this.setFoveation( foveation ); - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); + customReferenceSpace = null; + referenceSpace = await session.requestReferenceSpace( referenceSpaceType ); - }, + animation.setContext( session ); + animation.start(); - pose: function () { + scope.isPresenting = true; - this.skeleton.pose(); + scope.dispatchEvent( { type: 'sessionstart' } ); - }, + } + + }; + + this.getEnvironmentBlendMode = function () { - normalizeSkinWeights: function () { + if ( session !== null ) { - var vector = new Vector4(); + return session.environmentBlendMode; - var skinWeight = this.geometry.attributes.skinWeight; + } - for ( var i = 0, l = skinWeight.count; i < l; i ++ ) { + }; - vector.x = skinWeight.getX( i ); - vector.y = skinWeight.getY( i ); - vector.z = skinWeight.getZ( i ); - vector.w = skinWeight.getW( i ); + function onInputSourcesChange( event ) { - var scale = 1.0 / vector.manhattanLength(); + // Notify disconnected - if ( scale !== Infinity ) { + for ( let i = 0; i < event.removed.length; i ++ ) { - vector.multiplyScalar( scale ); + const inputSource = event.removed[ i ]; + const index = controllerInputSources.indexOf( inputSource ); - } else { + if ( index >= 0 ) { - vector.set( 1, 0, 0, 0 ); // do something reasonable + controllerInputSources[ index ] = null; + controllers[ index ].disconnect( inputSource ); } - skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); - } - }, + // Notify connected - updateMatrixWorld: function ( force ) { + for ( let i = 0; i < event.added.length; i ++ ) { - Mesh.prototype.updateMatrixWorld.call( this, force ); + const inputSource = event.added[ i ]; - if ( this.bindMode === 'attached' ) { + let controllerIndex = controllerInputSources.indexOf( inputSource ); - this.bindMatrixInverse.getInverse( this.matrixWorld ); + if ( controllerIndex === - 1 ) { - } else if ( this.bindMode === 'detached' ) { + // Assign input source a controller that currently has no input source - this.bindMatrixInverse.getInverse( this.bindMatrix ); + for ( let i = 0; i < controllers.length; i ++ ) { - } else { + if ( i >= controllerInputSources.length ) { - console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); + controllerInputSources.push( inputSource ); + controllerIndex = i; + break; - } + } else if ( controllerInputSources[ i ] === null ) { - }, + controllerInputSources[ i ] = inputSource; + controllerIndex = i; + break; - clone: function () { + } - return new this.constructor( this.geometry, this.material ).copy( this ); + } - } + // If all controllers do currently receive input we ignore new ones - } ); + if ( controllerIndex === - 1 ) break; - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author michael guerrero / http://realitymeltdown.com - * @author ikerr / http://verold.com - */ + } - var _offsetMatrix = new Matrix4(); - var _identityMatrix = new Matrix4(); + const controller = controllers[ controllerIndex ]; - function Skeleton( bones, boneInverses ) { + if ( controller ) { - // copy the bone array + controller.connect( inputSource ); - bones = bones || []; + } - this.bones = bones.slice( 0 ); - this.boneMatrices = new Float32Array( this.bones.length * 16 ); + } - // use the supplied bone inverses or calculate the inverses + } - if ( boneInverses === undefined ) { + // - this.calculateInverses(); + const cameraLPos = new Vector3(); + const cameraRPos = new Vector3(); - } else { + /** + * Assumes 2 cameras that are parallel and share an X-axis, and that + * the cameras' projection and world matrices have already been set. + * And that near and far planes are identical for both cameras. + * Visualization of this technique: https://computergraphics.stackexchange.com/a/4765 + */ + function setProjectionFromUnion( camera, cameraL, cameraR ) { - if ( this.bones.length === boneInverses.length ) { + cameraLPos.setFromMatrixPosition( cameraL.matrixWorld ); + cameraRPos.setFromMatrixPosition( cameraR.matrixWorld ); - this.boneInverses = boneInverses.slice( 0 ); + const ipd = cameraLPos.distanceTo( cameraRPos ); - } else { + const projL = cameraL.projectionMatrix.elements; + const projR = cameraR.projectionMatrix.elements; - console.warn( 'THREE.Skeleton boneInverses is the wrong length.' ); + // VR systems will have identical far and near planes, and + // most likely identical top and bottom frustum extents. + // Use the left camera for these values. + const near = projL[ 14 ] / ( projL[ 10 ] - 1 ); + const far = projL[ 14 ] / ( projL[ 10 ] + 1 ); + const topFov = ( projL[ 9 ] + 1 ) / projL[ 5 ]; + const bottomFov = ( projL[ 9 ] - 1 ) / projL[ 5 ]; - this.boneInverses = []; + const leftFov = ( projL[ 8 ] - 1 ) / projL[ 0 ]; + const rightFov = ( projR[ 8 ] + 1 ) / projR[ 0 ]; + const left = near * leftFov; + const right = near * rightFov; - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { + // Calculate the new camera's position offset from the + // left camera. xOffset should be roughly half `ipd`. + const zOffset = ipd / ( - leftFov + rightFov ); + const xOffset = zOffset * - leftFov; - this.boneInverses.push( new Matrix4() ); + // TODO: Better way to apply this offset? + cameraL.matrixWorld.decompose( camera.position, camera.quaternion, camera.scale ); + camera.translateX( xOffset ); + camera.translateZ( zOffset ); + camera.matrixWorld.compose( camera.position, camera.quaternion, camera.scale ); + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - } + // Find the union of the frustum values of the cameras and scale + // the values so that the near plane's position does not change in world space, + // although must now be relative to the new union camera. + const near2 = near + zOffset; + const far2 = far + zOffset; + const left2 = left - xOffset; + const right2 = right + ( ipd - xOffset ); + const top2 = topFov * far / far2 * near2; + const bottom2 = bottomFov * far / far2 * near2; - } + camera.projectionMatrix.makePerspective( left2, right2, top2, bottom2, near2, far2 ); + camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); } - } + function updateCamera( camera, parent ) { - Object.assign( Skeleton.prototype, { + if ( parent === null ) { - calculateInverses: function () { + camera.matrixWorld.copy( camera.matrix ); - this.boneInverses = []; + } else { - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { + camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix ); - var inverse = new Matrix4(); + } - if ( this.bones[ i ] ) { + camera.matrixWorldInverse.copy( camera.matrixWorld ).invert(); - inverse.getInverse( this.bones[ i ].matrixWorld ); + } - } + this.updateCamera = function ( camera ) { - this.boneInverses.push( inverse ); + if ( session === null ) return; - } + cameraXR.near = cameraR.near = cameraL.near = camera.near; + cameraXR.far = cameraR.far = cameraL.far = camera.far; - }, + if ( _currentDepthNear !== cameraXR.near || _currentDepthFar !== cameraXR.far ) { - pose: function () { + // Note that the new renderState won't apply until the next frame. See #18320 - var bone, i, il; + session.updateRenderState( { + depthNear: cameraXR.near, + depthFar: cameraXR.far + } ); - // recover the bind-time world matrices + _currentDepthNear = cameraXR.near; + _currentDepthFar = cameraXR.far; - for ( i = 0, il = this.bones.length; i < il; i ++ ) { + } - bone = this.bones[ i ]; + const parent = camera.parent; + const cameras = cameraXR.cameras; - if ( bone ) { + updateCamera( cameraXR, parent ); - bone.matrixWorld.getInverse( this.boneInverses[ i ] ); + for ( let i = 0; i < cameras.length; i ++ ) { - } + updateCamera( cameras[ i ], parent ); } - // compute the local matrices, positions, rotations and scales + // update projection matrix for proper view frustum culling - for ( i = 0, il = this.bones.length; i < il; i ++ ) { + if ( cameras.length === 2 ) { - bone = this.bones[ i ]; + setProjectionFromUnion( cameraXR, cameraL, cameraR ); - if ( bone ) { + } else { - if ( bone.parent && bone.parent.isBone ) { + // assume single camera setup (AR) - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); + cameraXR.projectionMatrix.copy( cameraL.projectionMatrix ); - } else { + } - bone.matrix.copy( bone.matrixWorld ); + // update user camera and its children - } + updateUserCamera( camera, cameraXR, parent ); - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + }; - } + function updateUserCamera( camera, cameraXR, parent ) { - } + if ( parent === null ) { - }, + camera.matrix.copy( cameraXR.matrixWorld ); - update: function () { + } else { - var bones = this.bones; - var boneInverses = this.boneInverses; - var boneMatrices = this.boneMatrices; - var boneTexture = this.boneTexture; + camera.matrix.copy( parent.matrixWorld ); + camera.matrix.invert(); + camera.matrix.multiply( cameraXR.matrixWorld ); - // flatten bone matrices to array + } - for ( var i = 0, il = bones.length; i < il; i ++ ) { + camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + camera.updateMatrixWorld( true ); - // compute the offset between the current and the original transform + camera.projectionMatrix.copy( cameraXR.projectionMatrix ); + camera.projectionMatrixInverse.copy( cameraXR.projectionMatrixInverse ); - var matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; + if ( camera.isPerspectiveCamera ) { - _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); - _offsetMatrix.toArray( boneMatrices, i * 16 ); + camera.fov = RAD2DEG * 2 * Math.atan( 1 / camera.projectionMatrix.elements[ 5 ] ); + camera.zoom = 1; } - if ( boneTexture !== undefined ) { + } - boneTexture.needsUpdate = true; + this.getCamera = function () { - } + return cameraXR; - }, + }; - clone: function () { + this.getFoveation = function () { - return new Skeleton( this.bones, this.boneInverses ); + if ( glProjLayer === null && glBaseLayer === null ) { - }, + return undefined; + + } + + return foveation; - getBoneByName: function ( name ) { + }; - for ( var i = 0, il = this.bones.length; i < il; i ++ ) { + this.setFoveation = function ( value ) { - var bone = this.bones[ i ]; + // 0 = no foveation = full resolution + // 1 = maximum foveation = the edges render at lower resolution - if ( bone.name === name ) { + foveation = value; - return bone; + if ( glProjLayer !== null ) { - } + glProjLayer.fixedFoveation = value; } - return undefined; + if ( glBaseLayer !== null && glBaseLayer.fixedFoveation !== undefined ) { - } + glBaseLayer.fixedFoveation = value; - } ); + } - /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ + }; - function Bone() { + // Animation Loop - Object3D.call( this ); + let onAnimationFrameCallback = null; - this.type = 'Bone'; + function onAnimationFrame( time, frame ) { - } + pose = frame.getViewerPose( customReferenceSpace || referenceSpace ); + xrFrame = frame; - Bone.prototype = Object.assign( Object.create( Object3D.prototype ), { + if ( pose !== null ) { - constructor: Bone, + const views = pose.views; - isBone: true + if ( glBaseLayer !== null ) { - } ); + renderer.setRenderTargetFramebuffer( newRenderTarget, glBaseLayer.framebuffer ); + renderer.setRenderTarget( newRenderTarget ); - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round" - * } - */ + } - function LineBasicMaterial( parameters ) { + let cameraXRNeedsUpdate = false; - Material.call( this ); + // check if it's necessary to rebuild cameraXR's camera list - this.type = 'LineBasicMaterial'; + if ( views.length !== cameraXR.cameras.length ) { - this.color = new Color( 0xffffff ); + cameraXR.cameras.length = 0; + cameraXRNeedsUpdate = true; - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; + } - this.lights = false; + for ( let i = 0; i < views.length; i ++ ) { - this.setValues( parameters ); + const view = views[ i ]; - } + let viewport = null; - LineBasicMaterial.prototype = Object.create( Material.prototype ); - LineBasicMaterial.prototype.constructor = LineBasicMaterial; + if ( glBaseLayer !== null ) { - LineBasicMaterial.prototype.isLineBasicMaterial = true; + viewport = glBaseLayer.getViewport( view ); - LineBasicMaterial.prototype.copy = function ( source ) { + } else { - Material.prototype.copy.call( this, source ); + const glSubImage = glBinding.getViewSubImage( glProjLayer, view ); + viewport = glSubImage.viewport; - this.color.copy( source.color ); + // For side-by-side projection, we only produce a single texture for both eyes. + if ( i === 0 ) { - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; + renderer.setRenderTargetTextures( + newRenderTarget, + glSubImage.colorTexture, + glProjLayer.ignoreDepthValues ? undefined : glSubImage.depthStencilTexture ); - return this; + renderer.setRenderTarget( newRenderTarget ); - }; + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - var _start = new Vector3(); - var _end = new Vector3(); - var _inverseMatrix$1 = new Matrix4(); - var _ray$1 = new Ray(); - var _sphere$2 = new Sphere(); + let camera = cameras[ i ]; - function Line( geometry, material, mode ) { + if ( camera === undefined ) { - if ( mode === 1 ) { + camera = new PerspectiveCamera(); + camera.layers.enable( i ); + camera.viewport = new Vector4(); + cameras[ i ] = camera; - console.error( 'THREE.Line: parameter THREE.LinePieces no longer supported. Use THREE.LineSegments instead.' ); + } - } + camera.matrix.fromArray( view.transform.matrix ); + camera.matrix.decompose( camera.position, camera.quaternion, camera.scale ); + camera.projectionMatrix.fromArray( view.projectionMatrix ); + camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); + camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height ); - Object3D.call( this ); + if ( i === 0 ) { - this.type = 'Line'; + cameraXR.matrix.copy( camera.matrix ); + cameraXR.matrix.decompose( cameraXR.position, cameraXR.quaternion, cameraXR.scale ); - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } ); + } - } + if ( cameraXRNeedsUpdate === true ) { - Line.prototype = Object.assign( Object.create( Object3D.prototype ), { + cameraXR.cameras.push( camera ); - constructor: Line, + } - isLine: true, + } - computeLineDistances: function () { + } - var geometry = this.geometry; + // - if ( geometry.isBufferGeometry ) { + for ( let i = 0; i < controllers.length; i ++ ) { - // we assume non-indexed geometry + const inputSource = controllerInputSources[ i ]; + const controller = controllers[ i ]; - if ( geometry.index === null ) { + if ( inputSource !== null && controller !== undefined ) { - var positionAttribute = geometry.attributes.position; - var lineDistances = [ 0 ]; + controller.update( inputSource, frame, customReferenceSpace || referenceSpace ); - for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) { + } - _start.fromBufferAttribute( positionAttribute, i - 1 ); - _end.fromBufferAttribute( positionAttribute, i ); + } - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += _start.distanceTo( _end ); + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time, frame ); - } + if ( frame.detectedPlanes ) { - geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + scope.dispatchEvent( { type: 'planesdetected', data: frame } ); - } else { + } - console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + xrFrame = null; - } + } - } else if ( geometry.isGeometry ) { + const animation = new WebGLAnimation(); - var vertices = geometry.vertices; - var lineDistances = geometry.lineDistances; + animation.setAnimationLoop( onAnimationFrame ); - lineDistances[ 0 ] = 0; + this.setAnimationLoop = function ( callback ) { - for ( var i = 1, l = vertices.length; i < l; i ++ ) { + onAnimationFrameCallback = callback; - lineDistances[ i ] = lineDistances[ i - 1 ]; - lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] ); + }; - } + this.dispose = function () {}; - } + } - return this; +} - }, +function WebGLMaterials( renderer, properties ) { - raycast: function ( raycaster, intersects ) { + function refreshTransformUniform( map, uniform ) { - var precision = raycaster.linePrecision; + if ( map.matrixAutoUpdate === true ) { - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; + map.updateMatrix(); - // Checking boundingSphere distance to ray + } - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + uniform.value.copy( map.matrix ); - _sphere$2.copy( geometry.boundingSphere ); - _sphere$2.applyMatrix4( matrixWorld ); - _sphere$2.radius += precision; + } - if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; + function refreshFogUniforms( uniforms, fog ) { - // + fog.color.getRGB( uniforms.fogColor.value, getUnlitUniformColorSpace( renderer ) ); - _inverseMatrix$1.getInverse( matrixWorld ); - _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); + if ( fog.isFog ) { - var localPrecision = precision / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localPrecisionSq = localPrecision * localPrecision; + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - var vStart = new Vector3(); - var vEnd = new Vector3(); - var interSegment = new Vector3(); - var interRay = new Vector3(); - var step = ( this && this.isLineSegments ) ? 2 : 1; + } else if ( fog.isFogExp2 ) { - if ( geometry.isBufferGeometry ) { + uniforms.fogDensity.value = fog.density; - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; + } - if ( index !== null ) { + } - var indices = index.array; + function refreshMaterialUniforms( uniforms, material, pixelRatio, height, transmissionRenderTarget ) { - for ( var i = 0, l = indices.length - 1; i < l; i += step ) { + if ( material.isMeshBasicMaterial ) { - var a = indices[ i ]; - var b = indices[ i + 1 ]; + refreshUniformsCommon( uniforms, material ); - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); + } else if ( material.isMeshLambertMaterial ) { - var distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + refreshUniformsCommon( uniforms, material ); - if ( distSq > localPrecisionSq ) continue; + } else if ( material.isMeshToonMaterial ) { - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + refreshUniformsCommon( uniforms, material ); + refreshUniformsToon( uniforms, material ); - var distance = raycaster.ray.origin.distanceTo( interRay ); + } else if ( material.isMeshPhongMaterial ) { - if ( distance < raycaster.near || distance > raycaster.far ) continue; + refreshUniformsCommon( uniforms, material ); + refreshUniformsPhong( uniforms, material ); - intersects.push( { + } else if ( material.isMeshStandardMaterial ) { - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + refreshUniformsCommon( uniforms, material ); + refreshUniformsStandard( uniforms, material ); - } ); + if ( material.isMeshPhysicalMaterial ) { - } + refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ); - } else { + } - for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { + } else if ( material.isMeshMatcapMaterial ) { - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsMatcap( uniforms, material ); - var distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + } else if ( material.isMeshDepthMaterial ) { - if ( distSq > localPrecisionSq ) continue; + refreshUniformsCommon( uniforms, material ); - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } else if ( material.isMeshDistanceMaterial ) { - var distance = raycaster.ray.origin.distanceTo( interRay ); + refreshUniformsCommon( uniforms, material ); + refreshUniformsDistance( uniforms, material ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } else if ( material.isMeshNormalMaterial ) { - intersects.push( { + refreshUniformsCommon( uniforms, material ); - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } else if ( material.isLineBasicMaterial ) { - } ); + refreshUniformsLine( uniforms, material ); - } + if ( material.isLineDashedMaterial ) { - } + refreshUniformsDash( uniforms, material ); - } else if ( geometry.isGeometry ) { + } - var vertices = geometry.vertices; - var nbVertices = vertices.length; + } else if ( material.isPointsMaterial ) { - for ( var i = 0; i < nbVertices - 1; i += step ) { + refreshUniformsPoints( uniforms, material, pixelRatio, height ); - var distSq = _ray$1.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + } else if ( material.isSpriteMaterial ) { - if ( distSq > localPrecisionSq ) continue; + refreshUniformsSprites( uniforms, material ); - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } else if ( material.isShadowMaterial ) { - var distance = raycaster.ray.origin.distanceTo( interRay ); + uniforms.color.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } else if ( material.isShaderMaterial ) { - intersects.push( { + material.uniformsNeedUpdate = false; // #15581 - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + } - } ); + } - } + function refreshUniformsCommon( uniforms, material ) { - } + uniforms.opacity.value = material.opacity; - }, + if ( material.color ) { + + uniforms.diffuse.value.copy( material.color ); + + } - clone: function () { + if ( material.emissive ) { - return new this.constructor( this.geometry, this.material ).copy( this ); + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } - } ); + if ( material.map ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + uniforms.map.value = material.map; - var _start$1 = new Vector3(); - var _end$1 = new Vector3(); + refreshTransformUniform( material.map, uniforms.mapTransform ); - function LineSegments( geometry, material ) { + } - Line.call( this, geometry, material ); + if ( material.alphaMap ) { - this.type = 'LineSegments'; + uniforms.alphaMap.value = material.alphaMap; - } + refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); - LineSegments.prototype = Object.assign( Object.create( Line.prototype ), { + } - constructor: LineSegments, + if ( material.bumpMap ) { - isLineSegments: true, + uniforms.bumpMap.value = material.bumpMap; - computeLineDistances: function () { + refreshTransformUniform( material.bumpMap, uniforms.bumpMapTransform ); - var geometry = this.geometry; + uniforms.bumpScale.value = material.bumpScale; - if ( geometry.isBufferGeometry ) { + if ( material.side === BackSide ) { - // we assume non-indexed geometry + uniforms.bumpScale.value *= - 1; - if ( geometry.index === null ) { + } - var positionAttribute = geometry.attributes.position; - var lineDistances = []; + } - for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) { + if ( material.normalMap ) { - _start$1.fromBufferAttribute( positionAttribute, i ); - _end$1.fromBufferAttribute( positionAttribute, i + 1 ); + uniforms.normalMap.value = material.normalMap; - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); + refreshTransformUniform( material.normalMap, uniforms.normalMapTransform ); - } + uniforms.normalScale.value.copy( material.normalScale ); - geometry.addAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); + if ( material.side === BackSide ) { - } else { + uniforms.normalScale.value.negate(); - console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); + } - } + } - } else if ( geometry.isGeometry ) { + if ( material.displacementMap ) { - var vertices = geometry.vertices; - var lineDistances = geometry.lineDistances; + uniforms.displacementMap.value = material.displacementMap; - for ( var i = 0, l = vertices.length; i < l; i += 2 ) { + refreshTransformUniform( material.displacementMap, uniforms.displacementMapTransform ); - _start$1.copy( vertices[ i ] ); - _end$1.copy( vertices[ i + 1 ] ); + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; - lineDistances[ i + 1 ] = lineDistances[ i ] + _start$1.distanceTo( _end$1 ); + } - } + if ( material.emissiveMap ) { - } + uniforms.emissiveMap.value = material.emissiveMap; - return this; + refreshTransformUniform( material.emissiveMap, uniforms.emissiveMapTransform ); } - } ); + if ( material.specularMap ) { - /** - * @author mgreter / http://github.com/mgreter - */ + uniforms.specularMap.value = material.specularMap; - function LineLoop( geometry, material ) { + refreshTransformUniform( material.specularMap, uniforms.specularMapTransform ); - Line.call( this, geometry, material ); + } - this.type = 'LineLoop'; + if ( material.alphaTest > 0 ) { - } + uniforms.alphaTest.value = material.alphaTest; - LineLoop.prototype = Object.assign( Object.create( Line.prototype ), { + } - constructor: LineLoop, + const envMap = properties.get( material ).envMap; - isLineLoop: true, + if ( envMap ) { - } ); + uniforms.envMap.value = envMap; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: - * - * morphTargets: - * } - */ + uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; - function PointsMaterial( parameters ) { + uniforms.reflectivity.value = material.reflectivity; + uniforms.ior.value = material.ior; + uniforms.refractionRatio.value = material.refractionRatio; - Material.call( this ); + } - this.type = 'PointsMaterial'; + if ( material.lightMap ) { - this.color = new Color( 0xffffff ); + uniforms.lightMap.value = material.lightMap; - this.map = null; + // artist-friendly light intensity scaling factor + const scaleFactor = ( renderer._useLegacyLights === true ) ? Math.PI : 1; - this.size = 1; - this.sizeAttenuation = true; + uniforms.lightMapIntensity.value = material.lightMapIntensity * scaleFactor; - this.morphTargets = false; + refreshTransformUniform( material.lightMap, uniforms.lightMapTransform ); - this.lights = false; + } - this.setValues( parameters ); + if ( material.aoMap ) { - } + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - PointsMaterial.prototype = Object.create( Material.prototype ); - PointsMaterial.prototype.constructor = PointsMaterial; + refreshTransformUniform( material.aoMap, uniforms.aoMapTransform ); - PointsMaterial.prototype.isPointsMaterial = true; + } - PointsMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + function refreshUniformsLine( uniforms, material ) { - this.color.copy( source.color ); + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; - this.map = source.map; + if ( material.map ) { - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; + uniforms.map.value = material.map; - this.morphTargets = source.morphTargets; + refreshTransformUniform( material.map, uniforms.mapTransform ); - return this; + } - }; + } - /** - * @author alteredq / http://alteredqualia.com/ - */ + function refreshUniformsDash( uniforms, material ) { - var _inverseMatrix$2 = new Matrix4(); - var _ray$2 = new Ray(); - var _sphere$3 = new Sphere(); - var _position$1 = new Vector3(); + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; - function Points( geometry, material ) { + } - Object3D.call( this ); + function refreshUniformsPoints( uniforms, material, pixelRatio, height ) { - this.type = 'Points'; + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * pixelRatio; + uniforms.scale.value = height * 0.5; - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } ); + if ( material.map ) { - this.updateMorphTargets(); + uniforms.map.value = material.map; - } + refreshTransformUniform( material.map, uniforms.uvTransform ); - Points.prototype = Object.assign( Object.create( Object3D.prototype ), { + } - constructor: Points, + if ( material.alphaMap ) { - isPoints: true, + uniforms.alphaMap.value = material.alphaMap; - raycast: function ( raycaster, intersects ) { + refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); - var geometry = this.geometry; - var matrixWorld = this.matrixWorld; - var threshold = raycaster.params.Points.threshold; + } - // Checking boundingSphere distance to ray + if ( material.alphaTest > 0 ) { - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + uniforms.alphaTest.value = material.alphaTest; - _sphere$3.copy( geometry.boundingSphere ); - _sphere$3.applyMatrix4( matrixWorld ); - _sphere$3.radius += threshold; + } - if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; + } - // + function refreshUniformsSprites( uniforms, material ) { - _inverseMatrix$2.getInverse( matrixWorld ); - _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); + uniforms.diffuse.value.copy( material.color ); + uniforms.opacity.value = material.opacity; + uniforms.rotation.value = material.rotation; - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localThresholdSq = localThreshold * localThreshold; + if ( material.map ) { - if ( geometry.isBufferGeometry ) { + uniforms.map.value = material.map; - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; + refreshTransformUniform( material.map, uniforms.mapTransform ); - if ( index !== null ) { + } - var indices = index.array; + if ( material.alphaMap ) { - for ( var i = 0, il = indices.length; i < il; i ++ ) { + uniforms.alphaMap.value = material.alphaMap; - var a = indices[ i ]; + refreshTransformUniform( material.alphaMap, uniforms.alphaMapTransform ); - _position$1.fromArray( positions, a * 3 ); + } - testPoint( _position$1, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); + if ( material.alphaTest > 0 ) { - } + uniforms.alphaTest.value = material.alphaTest; - } else { + } - for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { + } - _position$1.fromArray( positions, i * 3 ); + function refreshUniformsPhong( uniforms, material ) { - testPoint( _position$1, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); + uniforms.specular.value.copy( material.specular ); + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - } + } - } + function refreshUniformsToon( uniforms, material ) { - } else { + if ( material.gradientMap ) { - var vertices = geometry.vertices; + uniforms.gradientMap.value = material.gradientMap; - for ( var i = 0, l = vertices.length; i < l; i ++ ) { + } - testPoint( vertices[ i ], i, localThresholdSq, matrixWorld, raycaster, intersects, this ); + } - } + function refreshUniformsStandard( uniforms, material ) { - } + uniforms.metalness.value = material.metalness; - }, + if ( material.metalnessMap ) { - updateMorphTargets: function () { + uniforms.metalnessMap.value = material.metalnessMap; - var geometry = this.geometry; - var m, ml, name; + refreshTransformUniform( material.metalnessMap, uniforms.metalnessMapTransform ); - if ( geometry.isBufferGeometry ) { + } - var morphAttributes = geometry.morphAttributes; - var keys = Object.keys( morphAttributes ); + uniforms.roughness.value = material.roughness; - if ( keys.length > 0 ) { + if ( material.roughnessMap ) { - var morphAttribute = morphAttributes[ keys[ 0 ] ]; + uniforms.roughnessMap.value = material.roughnessMap; - if ( morphAttribute !== undefined ) { + refreshTransformUniform( material.roughnessMap, uniforms.roughnessMapTransform ); - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + } - for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) { + const envMap = properties.get( material ).envMap; - name = morphAttribute[ m ].name || String( m ); + if ( envMap ) { - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ name ] = m; + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; - } + } - } + } - } + function refreshUniformsPhysical( uniforms, material, transmissionRenderTarget ) { - } else { + uniforms.ior.value = material.ior; // also part of uniforms common - var morphTargets = geometry.morphTargets; + if ( material.sheen > 0 ) { - if ( morphTargets !== undefined && morphTargets.length > 0 ) { + uniforms.sheenColor.value.copy( material.sheenColor ).multiplyScalar( material.sheen ); - console.error( 'THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.' ); + uniforms.sheenRoughness.value = material.sheenRoughness; - } + if ( material.sheenColorMap ) { + + uniforms.sheenColorMap.value = material.sheenColorMap; + + refreshTransformUniform( material.sheenColorMap, uniforms.sheenColorMapTransform ); } - }, + if ( material.sheenRoughnessMap ) { + + uniforms.sheenRoughnessMap.value = material.sheenRoughnessMap; - clone: function () { + refreshTransformUniform( material.sheenRoughnessMap, uniforms.sheenRoughnessMapTransform ); - return new this.constructor( this.geometry, this.material ).copy( this ); + } } - } ); + if ( material.clearcoat > 0 ) { - function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { + uniforms.clearcoat.value = material.clearcoat; + uniforms.clearcoatRoughness.value = material.clearcoatRoughness; - var rayPointDistanceSq = _ray$2.distanceSqToPoint( point ); + if ( material.clearcoatMap ) { - if ( rayPointDistanceSq < localThresholdSq ) { + uniforms.clearcoatMap.value = material.clearcoatMap; - var intersectPoint = new Vector3(); + refreshTransformUniform( material.clearcoatMap, uniforms.clearcoatMapTransform ); - _ray$2.closestPointToPoint( point, intersectPoint ); - intersectPoint.applyMatrix4( matrixWorld ); + } - var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + if ( material.clearcoatRoughnessMap ) { - if ( distance < raycaster.near || distance > raycaster.far ) return; + uniforms.clearcoatRoughnessMap.value = material.clearcoatRoughnessMap; - intersects.push( { + refreshTransformUniform( material.clearcoatRoughnessMap, uniforms.clearcoatRoughnessMapTransform ); - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint, - index: index, - face: null, - object: object + } - } ); + if ( material.clearcoatNormalMap ) { - } + uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap; - } + refreshTransformUniform( material.clearcoatNormalMap, uniforms.clearcoatNormalMapTransform ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale ); - function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + if ( material.side === BackSide ) { - Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + uniforms.clearcoatNormalScale.value.negate(); - this.format = format !== undefined ? format : RGBFormat; + } - this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; - this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; + } - this.generateMipmaps = false; + } - } + if ( material.iridescence > 0 ) { + + uniforms.iridescence.value = material.iridescence; + uniforms.iridescenceIOR.value = material.iridescenceIOR; + uniforms.iridescenceThicknessMinimum.value = material.iridescenceThicknessRange[ 0 ]; + uniforms.iridescenceThicknessMaximum.value = material.iridescenceThicknessRange[ 1 ]; - VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), { + if ( material.iridescenceMap ) { - constructor: VideoTexture, + uniforms.iridescenceMap.value = material.iridescenceMap; - isVideoTexture: true, + refreshTransformUniform( material.iridescenceMap, uniforms.iridescenceMapTransform ); - update: function () { + } - var video = this.image; + if ( material.iridescenceThicknessMap ) { - if ( video.readyState >= video.HAVE_CURRENT_DATA ) { + uniforms.iridescenceThicknessMap.value = material.iridescenceThicknessMap; - this.needsUpdate = true; + refreshTransformUniform( material.iridescenceThicknessMap, uniforms.iridescenceThicknessMapTransform ); } } - } ); + if ( material.transmission > 0 ) { - /** - * @author alteredq / http://alteredqualia.com/ - */ + uniforms.transmission.value = material.transmission; + uniforms.transmissionSamplerMap.value = transmissionRenderTarget.texture; + uniforms.transmissionSamplerSize.value.set( transmissionRenderTarget.width, transmissionRenderTarget.height ); - function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { + if ( material.transmissionMap ) { - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); + uniforms.transmissionMap.value = material.transmissionMap; - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + refreshTransformUniform( material.transmissionMap, uniforms.transmissionMapTransform ); - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) + } - this.flipY = false; + uniforms.thickness.value = material.thickness; - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + if ( material.thicknessMap ) { - this.generateMipmaps = false; + uniforms.thicknessMap.value = material.thicknessMap; - } + refreshTransformUniform( material.thicknessMap, uniforms.thicknessMapTransform ); - CompressedTexture.prototype = Object.create( Texture.prototype ); - CompressedTexture.prototype.constructor = CompressedTexture; + } - CompressedTexture.prototype.isCompressedTexture = true; + uniforms.attenuationDistance.value = material.attenuationDistance; + uniforms.attenuationColor.value.copy( material.attenuationColor ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + if ( material.anisotropy > 0 ) { - Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + uniforms.anisotropyVector.value.set( material.anisotropy * Math.cos( material.anisotropyRotation ), material.anisotropy * Math.sin( material.anisotropyRotation ) ); - this.needsUpdate = true; + if ( material.anisotropyMap ) { - } + uniforms.anisotropyMap.value = material.anisotropyMap; - CanvasTexture.prototype = Object.create( Texture.prototype ); - CanvasTexture.prototype.constructor = CanvasTexture; - CanvasTexture.prototype.isCanvasTexture = true; + refreshTransformUniform( material.anisotropyMap, uniforms.anisotropyMapTransform ); - /** - * @author Matt DesLauriers / @mattdesl - * @author atix / arthursilber.de - */ + } - function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) { + } - format = format !== undefined ? format : DepthFormat; + uniforms.specularIntensity.value = material.specularIntensity; + uniforms.specularColor.value.copy( material.specularColor ); - if ( format !== DepthFormat && format !== DepthStencilFormat ) { + if ( material.specularColorMap ) { - throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' ); + uniforms.specularColorMap.value = material.specularColorMap; - } + refreshTransformUniform( material.specularColorMap, uniforms.specularColorMapTransform ); - if ( type === undefined && format === DepthFormat ) type = UnsignedShortType; - if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type; + } - Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + if ( material.specularIntensityMap ) { - this.image = { width: width, height: height }; + uniforms.specularIntensityMap.value = material.specularIntensityMap; - this.magFilter = magFilter !== undefined ? magFilter : NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : NearestFilter; + refreshTransformUniform( material.specularIntensityMap, uniforms.specularIntensityMapTransform ); - this.flipY = false; - this.generateMipmaps = false; + } } - DepthTexture.prototype = Object.create( Texture.prototype ); - DepthTexture.prototype.constructor = DepthTexture; - DepthTexture.prototype.isDepthTexture = true; + function refreshUniformsMatcap( uniforms, material ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ + if ( material.matcap ) { - function WireframeGeometry( geometry ) { + uniforms.matcap.value = material.matcap; - BufferGeometry.call( this ); + } - this.type = 'WireframeGeometry'; + } - // buffer + function refreshUniformsDistance( uniforms, material ) { - var vertices = []; + const light = properties.get( material ).light; - // helper variables + uniforms.referencePosition.value.setFromMatrixPosition( light.matrixWorld ); + uniforms.nearDistance.value = light.shadow.camera.near; + uniforms.farDistance.value = light.shadow.camera.far; - var i, j, l, o, ol; - var edge = [ 0, 0 ], edges = {}, e, edge1, edge2; - var key, keys = [ 'a', 'b', 'c' ]; - var vertex; + } - // different logic for Geometry and BufferGeometry + return { + refreshFogUniforms: refreshFogUniforms, + refreshMaterialUniforms: refreshMaterialUniforms + }; - if ( geometry && geometry.isGeometry ) { +} - // create a data structure that contains all edges without duplicates +function WebGLUniformsGroups( gl, info, capabilities, state ) { - var faces = geometry.faces; + let buffers = {}; + let updateList = {}; + let allocatedBindingPoints = []; - for ( i = 0, l = faces.length; i < l; i ++ ) { + const maxBindingPoints = ( capabilities.isWebGL2 ) ? gl.getParameter( gl.MAX_UNIFORM_BUFFER_BINDINGS ) : 0; // binding points are global whereas block indices are per shader program - var face = faces[ i ]; + function bind( uniformsGroup, program ) { - for ( j = 0; j < 3; j ++ ) { + const webglProgram = program.program; + state.uniformBlockBinding( uniformsGroup, webglProgram ); - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); + } - key = edge[ 0 ] + ',' + edge[ 1 ]; + function update( uniformsGroup, program ) { - if ( edges[ key ] === undefined ) { + let buffer = buffers[ uniformsGroup.id ]; - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; + if ( buffer === undefined ) { - } + prepareUniformsGroup( uniformsGroup ); - } + buffer = createBuffer( uniformsGroup ); + buffers[ uniformsGroup.id ] = buffer; - } + uniformsGroup.addEventListener( 'dispose', onUniformsGroupsDispose ); - // generate vertices + } - for ( key in edges ) { + // ensure to update the binding points/block indices mapping for this program - e = edges[ key ]; + const webglProgram = program.program; + state.updateUBOMapping( uniformsGroup, webglProgram ); - vertex = geometry.vertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + // update UBO once per frame - vertex = geometry.vertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + const frame = info.render.frame; - } + if ( updateList[ uniformsGroup.id ] !== frame ) { - } else if ( geometry && geometry.isBufferGeometry ) { + updateBufferData( uniformsGroup ); - var position, indices, groups; - var group, start, count; - var index1, index2; + updateList[ uniformsGroup.id ] = frame; - vertex = new Vector3(); + } - if ( geometry.index !== null ) { + } - // indexed BufferGeometry + function createBuffer( uniformsGroup ) { - position = geometry.attributes.position; - indices = geometry.index; - groups = geometry.groups; + // the setup of an UBO is independent of a particular shader program but global - if ( groups.length === 0 ) { + const bindingPointIndex = allocateBindingPointIndex(); + uniformsGroup.__bindingPointIndex = bindingPointIndex; - groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; + const buffer = gl.createBuffer(); + const size = uniformsGroup.__size; + const usage = uniformsGroup.usage; - } + gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); + gl.bufferData( gl.UNIFORM_BUFFER, size, usage ); + gl.bindBuffer( gl.UNIFORM_BUFFER, null ); + gl.bindBufferBase( gl.UNIFORM_BUFFER, bindingPointIndex, buffer ); - // create a data structure that contains all eges without duplicates + return buffer; - for ( o = 0, ol = groups.length; o < ol; ++ o ) { + } - group = groups[ o ]; + function allocateBindingPointIndex() { - start = group.start; - count = group.count; + for ( let i = 0; i < maxBindingPoints; i ++ ) { - for ( i = start, l = ( start + count ); i < l; i += 3 ) { + if ( allocatedBindingPoints.indexOf( i ) === - 1 ) { - for ( j = 0; j < 3; j ++ ) { + allocatedBindingPoints.push( i ); + return i; - edge1 = indices.getX( i + j ); - edge2 = indices.getX( i + ( j + 1 ) % 3 ); - edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates - edge[ 1 ] = Math.max( edge1, edge2 ); + } - key = edge[ 0 ] + ',' + edge[ 1 ]; + } - if ( edges[ key ] === undefined ) { + console.error( 'THREE.WebGLRenderer: Maximum number of simultaneously usable uniforms groups reached.' ); - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] }; + return 0; - } + } - } + function updateBufferData( uniformsGroup ) { - } + const buffer = buffers[ uniformsGroup.id ]; + const uniforms = uniformsGroup.uniforms; + const cache = uniformsGroup.__cache; - } + gl.bindBuffer( gl.UNIFORM_BUFFER, buffer ); - // generate vertices + for ( let i = 0, il = uniforms.length; i < il; i ++ ) { - for ( key in edges ) { + const uniform = uniforms[ i ]; - e = edges[ key ]; + // partly update the buffer if necessary - vertex.fromBufferAttribute( position, e.index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); + if ( hasUniformChanged( uniform, i, cache ) === true ) { - vertex.fromBufferAttribute( position, e.index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); + const offset = uniform.__offset; - } + const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; - } else { + let arrayOffset = 0; - // non-indexed BufferGeometry + for ( let i = 0; i < values.length; i ++ ) { - position = geometry.attributes.position; + const value = values[ i ]; - for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) { + const info = getUniformSize( value ); - for ( j = 0; j < 3; j ++ ) { + if ( typeof value === 'number' ) { - // three edges per triangle, an edge is represented as (index1, index2) - // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) + uniform.__data[ 0 ] = value; + gl.bufferSubData( gl.UNIFORM_BUFFER, offset + arrayOffset, uniform.__data ); - index1 = 3 * i + j; - vertex.fromBufferAttribute( position, index1 ); - vertices.push( vertex.x, vertex.y, vertex.z ); + } else if ( value.isMatrix3 ) { - index2 = 3 * i + ( ( j + 1 ) % 3 ); - vertex.fromBufferAttribute( position, index2 ); - vertices.push( vertex.x, vertex.y, vertex.z ); + // manually converting 3x3 to 3x4 + + uniform.__data[ 0 ] = value.elements[ 0 ]; + uniform.__data[ 1 ] = value.elements[ 1 ]; + uniform.__data[ 2 ] = value.elements[ 2 ]; + uniform.__data[ 3 ] = value.elements[ 0 ]; + uniform.__data[ 4 ] = value.elements[ 3 ]; + uniform.__data[ 5 ] = value.elements[ 4 ]; + uniform.__data[ 6 ] = value.elements[ 5 ]; + uniform.__data[ 7 ] = value.elements[ 0 ]; + uniform.__data[ 8 ] = value.elements[ 6 ]; + uniform.__data[ 9 ] = value.elements[ 7 ]; + uniform.__data[ 10 ] = value.elements[ 8 ]; + uniform.__data[ 11 ] = value.elements[ 0 ]; + + } else { + + value.toArray( uniform.__data, arrayOffset ); + + arrayOffset += info.storage / Float32Array.BYTES_PER_ELEMENT; } } + gl.bufferSubData( gl.UNIFORM_BUFFER, offset, uniform.__data ); + } } - // build geometry - - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + gl.bindBuffer( gl.UNIFORM_BUFFER, null ); } - WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); - WireframeGeometry.prototype.constructor = WireframeGeometry; + function hasUniformChanged( uniform, index, cache ) { - /** - * @author zz85 / https://github.com/zz85 - * @author Mugen87 / https://github.com/Mugen87 - * - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 - */ + const value = uniform.value; - // ParametricGeometry + if ( cache[ index ] === undefined ) { - function ParametricGeometry( func, slices, stacks ) { + // cache entry does not exist so far - Geometry.call( this ); + if ( typeof value === 'number' ) { - this.type = 'ParametricGeometry'; + cache[ index ] = value; - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; + } else { - this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) ); - this.mergeVertices(); + const values = Array.isArray( value ) ? value : [ value ]; - } + const tempValues = []; + + for ( let i = 0; i < values.length; i ++ ) { - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; + tempValues.push( values[ i ].clone() ); - // ParametricBufferGeometry + } - function ParametricBufferGeometry( func, slices, stacks ) { + cache[ index ] = tempValues; - BufferGeometry.call( this ); + } - this.type = 'ParametricBufferGeometry'; + return true; - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; + } else { - // buffers + // compare current value with cached entry - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + if ( typeof value === 'number' ) { - var EPS = 0.00001; + if ( cache[ index ] !== value ) { - var normal = new Vector3(); + cache[ index ] = value; + return true; - var p0 = new Vector3(), p1 = new Vector3(); - var pu = new Vector3(), pv = new Vector3(); + } - var i, j; + } else { - if ( func.length < 3 ) { + const cachedObjects = Array.isArray( cache[ index ] ) ? cache[ index ] : [ cache[ index ] ]; + const values = Array.isArray( value ) ? value : [ value ]; - console.error( 'THREE.ParametricGeometry: Function must now modify a Vector3 as third parameter.' ); + for ( let i = 0; i < cachedObjects.length; i ++ ) { - } + const cachedObject = cachedObjects[ i ]; - // generate vertices, normals and uvs + if ( cachedObject.equals( values[ i ] ) === false ) { - var sliceCount = slices + 1; + cachedObject.copy( values[ i ] ); + return true; - for ( i = 0; i <= stacks; i ++ ) { + } - var v = i / stacks; + } - for ( j = 0; j <= slices; j ++ ) { + } - var u = j / slices; + } - // vertex + return false; - func( u, v, p0 ); - vertices.push( p0.x, p0.y, p0.z ); + } - // normal + function prepareUniformsGroup( uniformsGroup ) { - // approximate tangent vectors via finite differences + // determine total buffer size according to the STD140 layout + // Hint: STD140 is the only supported layout in WebGL 2 - if ( u - EPS >= 0 ) { + const uniforms = uniformsGroup.uniforms; - func( u - EPS, v, p1 ); - pu.subVectors( p0, p1 ); + let offset = 0; // global buffer offset in bytes + const chunkSize = 16; // size of a chunk in bytes + let chunkOffset = 0; // offset within a single chunk in bytes - } else { + for ( let i = 0, l = uniforms.length; i < l; i ++ ) { - func( u + EPS, v, p1 ); - pu.subVectors( p1, p0 ); + const uniform = uniforms[ i ]; - } + const infos = { + boundary: 0, // bytes + storage: 0 // bytes + }; - if ( v - EPS >= 0 ) { + const values = Array.isArray( uniform.value ) ? uniform.value : [ uniform.value ]; - func( u, v - EPS, p1 ); - pv.subVectors( p0, p1 ); + for ( let j = 0, jl = values.length; j < jl; j ++ ) { - } else { + const value = values[ j ]; - func( u, v + EPS, p1 ); - pv.subVectors( p1, p0 ); + const info = getUniformSize( value ); - } + infos.boundary += info.boundary; + infos.storage += info.storage; - // cross product of tangent vectors returns surface normal + } - normal.crossVectors( pu, pv ).normalize(); - normals.push( normal.x, normal.y, normal.z ); + // the following two properties will be used for partial buffer updates - // uv + uniform.__data = new Float32Array( infos.storage / Float32Array.BYTES_PER_ELEMENT ); + uniform.__offset = offset; - uvs.push( u, v ); + // - } + if ( i > 0 ) { - } + chunkOffset = offset % chunkSize; - // generate indices + const remainingSizeInChunk = chunkSize - chunkOffset; - for ( i = 0; i < stacks; i ++ ) { + // check for chunk overflow - for ( j = 0; j < slices; j ++ ) { + if ( chunkOffset !== 0 && ( remainingSizeInChunk - infos.boundary ) < 0 ) { - var a = i * sliceCount + j; - var b = i * sliceCount + j + 1; - var c = ( i + 1 ) * sliceCount + j + 1; - var d = ( i + 1 ) * sliceCount + j; + // add padding and adjust offset - // faces one and two + offset += ( chunkSize - chunkOffset ); + uniform.__offset = offset; - indices.push( a, b, d ); - indices.push( b, c, d ); + } } - } + offset += infos.storage; - // build geometry + } - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + // ensure correct final padding - } + chunkOffset = offset % chunkSize; - ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry; + if ( chunkOffset > 0 ) offset += ( chunkSize - chunkOffset ); - /** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley - * @author Mugen87 / https://github.com/Mugen87 - */ + // - // PolyhedronGeometry + uniformsGroup.__size = offset; + uniformsGroup.__cache = {}; - function PolyhedronGeometry( vertices, indices, radius, detail ) { + return this; - Geometry.call( this ); + } - this.type = 'PolyhedronGeometry'; + function getUniformSize( value ) { - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail + const info = { + boundary: 0, // bytes + storage: 0 // bytes }; - this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) ); - this.mergeVertices(); + // determine sizes according to STD140 - } + if ( typeof value === 'number' ) { - PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); - PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; + // float/int - // PolyhedronBufferGeometry + info.boundary = 4; + info.storage = 4; - function PolyhedronBufferGeometry( vertices, indices, radius, detail ) { + } else if ( value.isVector2 ) { - BufferGeometry.call( this ); + // vec2 - this.type = 'PolyhedronBufferGeometry'; + info.boundary = 8; + info.storage = 8; - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; + } else if ( value.isVector3 || value.isColor ) { - radius = radius || 1; - detail = detail || 0; + // vec3 - // default buffer data + info.boundary = 16; + info.storage = 12; // evil: vec3 must start on a 16-byte boundary but it only consumes 12 bytes - var vertexBuffer = []; - var uvBuffer = []; + } else if ( value.isVector4 ) { - // the subdivision creates the vertex buffer data + // vec4 - subdivide( detail ); + info.boundary = 16; + info.storage = 16; - // all vertices should lie on a conceptual sphere with a given radius + } else if ( value.isMatrix3 ) { - applyRadius( radius ); + // mat3 (in STD140 a 3x3 matrix is represented as 3x4) - // finally, create the uv data + info.boundary = 48; + info.storage = 48; - generateUVs(); + } else if ( value.isMatrix4 ) { - // build non-indexed geometry + // mat4 - this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); + info.boundary = 64; + info.storage = 64; - if ( detail === 0 ) { + } else if ( value.isTexture ) { - this.computeVertexNormals(); // flat normals + console.warn( 'THREE.WebGLRenderer: Texture samplers can not be part of an uniforms group.' ); } else { - this.normalizeNormals(); // smooth normals + console.warn( 'THREE.WebGLRenderer: Unsupported uniform value type.', value ); } - // helper functions + return info; - function subdivide( detail ) { + } - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); + function onUniformsGroupsDispose( event ) { - // iterate over all faces and apply a subdivison with the given detail value + const uniformsGroup = event.target; - for ( var i = 0; i < indices.length; i += 3 ) { + uniformsGroup.removeEventListener( 'dispose', onUniformsGroupsDispose ); - // get the vertices of the face + const index = allocatedBindingPoints.indexOf( uniformsGroup.__bindingPointIndex ); + allocatedBindingPoints.splice( index, 1 ); - getVertexByIndex( indices[ i + 0 ], a ); - getVertexByIndex( indices[ i + 1 ], b ); - getVertexByIndex( indices[ i + 2 ], c ); + gl.deleteBuffer( buffers[ uniformsGroup.id ] ); - // perform subdivision + delete buffers[ uniformsGroup.id ]; + delete updateList[ uniformsGroup.id ]; - subdivideFace( a, b, c, detail ); + } - } + function dispose() { + + for ( const id in buffers ) { + + gl.deleteBuffer( buffers[ id ] ); } - function subdivideFace( a, b, c, detail ) { + allocatedBindingPoints = []; + buffers = {}; + updateList = {}; - var cols = Math.pow( 2, detail ); + } - // we use this multidimensional array as a data structure for creating the subdivision + return { - var v = []; + bind: bind, + update: update, - var i, j; + dispose: dispose - // construct all of the vertices for this subdivision + }; - for ( i = 0; i <= cols; i ++ ) { +} - v[ i ] = []; +class WebGLRenderer { - var aj = a.clone().lerp( c, i / cols ); - var bj = b.clone().lerp( c, i / cols ); + constructor( parameters = {} ) { - var rows = cols - i; + const { + canvas = createCanvasElement(), + context = null, + depth = true, + stencil = true, + alpha = false, + antialias = false, + premultipliedAlpha = true, + preserveDrawingBuffer = false, + powerPreference = 'default', + failIfMajorPerformanceCaveat = false, + } = parameters; - for ( j = 0; j <= rows; j ++ ) { + this.isWebGLRenderer = true; - if ( j === 0 && i === cols ) { + let _alpha; - v[ i ][ j ] = aj; + if ( context !== null ) { - } else { + _alpha = context.getContextAttributes().alpha; - v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); + } else { - } + _alpha = alpha; - } + } - } + const uintClearColor = new Uint32Array( 4 ); + const intClearColor = new Int32Array( 4 ); - // construct all of the faces + let currentRenderList = null; + let currentRenderState = null; - for ( i = 0; i < cols; i ++ ) { + // render() can be called from within a callback triggered by another render. + // We track this so that the nested render call gets its list and state isolated from the parent render call. - for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { + const renderListStack = []; + const renderStateStack = []; - var k = Math.floor( j / 2 ); + // public properties - if ( j % 2 === 0 ) { + this.domElement = canvas; - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); - pushVertex( v[ i ][ k ] ); + // Debug configuration container + this.debug = { - } else { + /** + * Enables error checking and reporting when shader programs are being compiled + * @type {boolean} + */ + checkShaderErrors: true, + /** + * Callback for custom error reporting. + * @type {?Function} + */ + onShaderError: null + }; - pushVertex( v[ i ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k + 1 ] ); - pushVertex( v[ i + 1 ][ k ] ); + // clearing - } + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - } + // scene graph - } + this.sortObjects = true; - } + // user-defined clipping - function applyRadius( radius ) { + this.clippingPlanes = []; + this.localClippingEnabled = false; - var vertex = new Vector3(); + // physically based shading - // iterate over the entire buffer and apply the radius to each vertex + this._outputColorSpace = SRGBColorSpace; - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { + // physical lights - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; + this._useLegacyLights = false; - vertex.normalize().multiplyScalar( radius ); + // tone mapping - vertexBuffer[ i + 0 ] = vertex.x; - vertexBuffer[ i + 1 ] = vertex.y; - vertexBuffer[ i + 2 ] = vertex.z; + this.toneMapping = NoToneMapping; + this.toneMappingExposure = 1.0; - } + // internal properties - } + const _this = this; - function generateUVs() { + let _isContextLost = false; - var vertex = new Vector3(); + // internal state cache - for ( var i = 0; i < vertexBuffer.length; i += 3 ) { + let _currentActiveCubeFace = 0; + let _currentActiveMipmapLevel = 0; + let _currentRenderTarget = null; + let _currentMaterialId = - 1; - vertex.x = vertexBuffer[ i + 0 ]; - vertex.y = vertexBuffer[ i + 1 ]; - vertex.z = vertexBuffer[ i + 2 ]; + let _currentCamera = null; - var u = azimuth( vertex ) / 2 / Math.PI + 0.5; - var v = inclination( vertex ) / Math.PI + 0.5; - uvBuffer.push( u, 1 - v ); + const _currentViewport = new Vector4(); + const _currentScissor = new Vector4(); + let _currentScissorTest = null; - } + const _currentClearColor = new Color( 0x000000 ); + let _currentClearAlpha = 0; - correctUVs(); + // - correctSeam(); + let _width = canvas.width; + let _height = canvas.height; - } + let _pixelRatio = 1; + let _opaqueSort = null; + let _transparentSort = null; - function correctSeam() { + const _viewport = new Vector4( 0, 0, _width, _height ); + const _scissor = new Vector4( 0, 0, _width, _height ); + let _scissorTest = false; - // handle case when face straddles the seam, see #3269 + // frustum - for ( var i = 0; i < uvBuffer.length; i += 6 ) { + const _frustum = new Frustum(); - // uv data of a single face + // clipping - var x0 = uvBuffer[ i + 0 ]; - var x1 = uvBuffer[ i + 2 ]; - var x2 = uvBuffer[ i + 4 ]; + let _clippingEnabled = false; + let _localClippingEnabled = false; - var max = Math.max( x0, x1, x2 ); - var min = Math.min( x0, x1, x2 ); + // transmission - // 0.9 is somewhat arbitrary + let _transmissionRenderTarget = null; - if ( max > 0.9 && min < 0.1 ) { + // camera matrices cache - if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; - if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; - if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; + const _projScreenMatrix = new Matrix4(); - } + const _vector2 = new Vector2(); + const _vector3 = new Vector3(); - } + const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true }; + + function getTargetPixelRatio() { + + return _currentRenderTarget === null ? _pixelRatio : 1; } - function pushVertex( vertex ) { + // initialize - vertexBuffer.push( vertex.x, vertex.y, vertex.z ); + let _gl = context; - } + function getContext( contextNames, contextAttributes ) { - function getVertexByIndex( index, vertex ) { + for ( let i = 0; i < contextNames.length; i ++ ) { - var stride = index * 3; + const contextName = contextNames[ i ]; + const context = canvas.getContext( contextName, contextAttributes ); + if ( context !== null ) return context; - vertex.x = vertices[ stride + 0 ]; - vertex.y = vertices[ stride + 1 ]; - vertex.z = vertices[ stride + 2 ]; + } + + return null; } - function correctUVs() { + try { - var a = new Vector3(); - var b = new Vector3(); - var c = new Vector3(); + const contextAttributes = { + alpha: true, + depth, + stencil, + antialias, + premultipliedAlpha, + preserveDrawingBuffer, + powerPreference, + failIfMajorPerformanceCaveat, + }; - var centroid = new Vector3(); + // OffscreenCanvas does not have setAttribute, see #22811 + if ( 'setAttribute' in canvas ) canvas.setAttribute( 'data-engine', `three.js r${REVISION}` ); - var uvA = new Vector2(); - var uvB = new Vector2(); - var uvC = new Vector2(); + // event listeners must be registered before WebGL context is created, see #12753 + canvas.addEventListener( 'webglcontextlost', onContextLost, false ); + canvas.addEventListener( 'webglcontextrestored', onContextRestore, false ); + canvas.addEventListener( 'webglcontextcreationerror', onContextCreationError, false ); - for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { + if ( _gl === null ) { - a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); - b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); - c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); + const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ]; - uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); - uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); - uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); + if ( _this.isWebGL1Renderer === true ) { - centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); + contextNames.shift(); - var azi = azimuth( centroid ); + } - correctUV( uvA, j + 0, a, azi ); - correctUV( uvB, j + 2, b, azi ); - correctUV( uvC, j + 4, c, azi ); + _gl = getContext( contextNames, contextAttributes ); - } + if ( _gl === null ) { - } + if ( getContext( contextNames ) ) { - function correctUV( uv, stride, vector, azimuth ) { + throw new Error( 'Error creating WebGL context with your selected attributes.' ); - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { + } else { - uvBuffer[ stride ] = uv.x - 1; + throw new Error( 'Error creating WebGL context.' ); + + } + + } } - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { + if ( typeof WebGLRenderingContext !== 'undefined' && _gl instanceof WebGLRenderingContext ) { // @deprecated, r153 - uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; + console.warn( 'THREE.WebGLRenderer: WebGL 1 support was deprecated in r153 and will be removed in r163.' ); } - } - - // Angle around the Y axis, counter-clockwise when looking from above. + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - function azimuth( vector ) { + if ( _gl.getShaderPrecisionFormat === undefined ) { - return Math.atan2( vector.z, - vector.x ); + _gl.getShaderPrecisionFormat = function () { - } + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; + }; - // Angle above the XZ plane. + } - function inclination( vector ) { + } catch ( error ) { - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + console.error( 'THREE.WebGLRenderer: ' + error.message ); + throw error; } - } + let extensions, capabilities, state, info; + let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects; + let programCache, materials, renderLists, renderStates, clipping, shadowMap; - PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry; + let background, morphtargets, bufferRenderer, indexedBufferRenderer; - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ + let utils, bindingStates, uniformsGroups; - // TetrahedronGeometry + function initGLContext() { - function TetrahedronGeometry( radius, detail ) { + extensions = new WebGLExtensions( _gl ); - Geometry.call( this ); + capabilities = new WebGLCapabilities( _gl, extensions, parameters ); - this.type = 'TetrahedronGeometry'; - - this.parameters = { - radius: radius, - detail: detail - }; + extensions.init( capabilities ); - this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); + utils = new WebGLUtils( _gl, extensions, capabilities ); - } + state = new WebGLState( _gl, extensions, capabilities ); - TetrahedronGeometry.prototype = Object.create( Geometry.prototype ); - TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; + info = new WebGLInfo( _gl ); + properties = new WebGLProperties(); + textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ); + cubemaps = new WebGLCubeMaps( _this ); + cubeuvmaps = new WebGLCubeUVMaps( _this ); + attributes = new WebGLAttributes( _gl, capabilities ); + bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities ); + geometries = new WebGLGeometries( _gl, attributes, info, bindingStates ); + objects = new WebGLObjects( _gl, geometries, attributes, info ); + morphtargets = new WebGLMorphtargets( _gl, capabilities, textures ); + clipping = new WebGLClipping( properties ); + programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping ); + materials = new WebGLMaterials( _this, properties ); + renderLists = new WebGLRenderLists(); + renderStates = new WebGLRenderStates( extensions, capabilities ); + background = new WebGLBackground( _this, cubemaps, cubeuvmaps, state, objects, _alpha, premultipliedAlpha ); + shadowMap = new WebGLShadowMap( _this, objects, capabilities ); + uniformsGroups = new WebGLUniformsGroups( _gl, info, capabilities, state ); - // TetrahedronBufferGeometry + bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities ); + indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities ); - function TetrahedronBufferGeometry( radius, detail ) { + info.programs = programCache.programs; - var vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; + _this.capabilities = capabilities; + _this.extensions = extensions; + _this.properties = properties; + _this.renderLists = renderLists; + _this.shadowMap = shadowMap; + _this.state = state; + _this.info = info; - var indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; + } - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); + initGLContext(); - this.type = 'TetrahedronBufferGeometry'; + // xr - this.parameters = { - radius: radius, - detail: detail - }; + const xr = new WebXRManager( _this, _gl ); - } + this.xr = xr; - TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry; + // API - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ + this.getContext = function () { - // OctahedronGeometry + return _gl; - function OctahedronGeometry( radius, detail ) { + }; - Geometry.call( this ); + this.getContextAttributes = function () { - this.type = 'OctahedronGeometry'; + return _gl.getContextAttributes(); - this.parameters = { - radius: radius, - detail: detail }; - this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); - - } + this.forceContextLoss = function () { - OctahedronGeometry.prototype = Object.create( Geometry.prototype ); - OctahedronGeometry.prototype.constructor = OctahedronGeometry; + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.loseContext(); - // OctahedronBufferGeometry + }; - function OctahedronBufferGeometry( radius, detail ) { + this.forceContextRestore = function () { - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; + const extension = extensions.get( 'WEBGL_lose_context' ); + if ( extension ) extension.restoreContext(); - var indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, - 0, 5, 2, 1, 2, 5, 1, 5, 3, - 1, 3, 4, 1, 4, 2 - ]; + }; - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); + this.getPixelRatio = function () { - this.type = 'OctahedronBufferGeometry'; + return _pixelRatio; - this.parameters = { - radius: radius, - detail: detail }; - } + this.setPixelRatio = function ( value ) { - OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry; + if ( value === undefined ) return; - /** - * @author timothypratley / https://github.com/timothypratley - * @author Mugen87 / https://github.com/Mugen87 - */ + _pixelRatio = value; - // IcosahedronGeometry + this.setSize( _width, _height, false ); - function IcosahedronGeometry( radius, detail ) { + }; - Geometry.call( this ); + this.getSize = function ( target ) { - this.type = 'IcosahedronGeometry'; + return target.set( _width, _height ); - this.parameters = { - radius: radius, - detail: detail }; - this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); + this.setSize = function ( width, height, updateStyle = true ) { - } + if ( xr.isPresenting ) { - IcosahedronGeometry.prototype = Object.create( Geometry.prototype ); - IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; + console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' ); + return; - // IcosahedronBufferGeometry + } - function IcosahedronBufferGeometry( radius, detail ) { + _width = width; + _height = height; - var t = ( 1 + Math.sqrt( 5 ) ) / 2; + canvas.width = Math.floor( width * _pixelRatio ); + canvas.height = Math.floor( height * _pixelRatio ); - var vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; + if ( updateStyle === true ) { - var indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); + } - this.type = 'IcosahedronBufferGeometry'; + this.setViewport( 0, 0, width, height ); - this.parameters = { - radius: radius, - detail: detail }; - } + this.getDrawingBufferSize = function ( target ) { - IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry; + return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor(); - /** - * @author Abe Pazos / https://hamoid.com - * @author Mugen87 / https://github.com/Mugen87 - */ + }; - // DodecahedronGeometry + this.setDrawingBufferSize = function ( width, height, pixelRatio ) { - function DodecahedronGeometry( radius, detail ) { + _width = width; + _height = height; - Geometry.call( this ); + _pixelRatio = pixelRatio; - this.type = 'DodecahedronGeometry'; + canvas.width = Math.floor( width * pixelRatio ); + canvas.height = Math.floor( height * pixelRatio ); + + this.setViewport( 0, 0, width, height ); - this.parameters = { - radius: radius, - detail: detail }; - this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) ); - this.mergeVertices(); + this.getCurrentViewport = function ( target ) { - } + return target.copy( _currentViewport ); - DodecahedronGeometry.prototype = Object.create( Geometry.prototype ); - DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; + }; - // DodecahedronBufferGeometry + this.getViewport = function ( target ) { - function DodecahedronBufferGeometry( radius, detail ) { + return target.copy( _viewport ); - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - var r = 1 / t; + }; - var vertices = [ + this.setViewport = function ( x, y, width, height ) { - // (±1, ±1, ±1) - - 1, - 1, - 1, - 1, - 1, 1, - - 1, 1, - 1, - 1, 1, 1, - 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, 1, 1, 1, + if ( x.isVector4 ) { - // (0, ±1/φ, ±φ) - 0, - r, - t, 0, - r, t, - 0, r, - t, 0, r, t, + _viewport.set( x.x, x.y, x.z, x.w ); - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, + } else { - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; + _viewport.set( x, y, width, height ); - var indices = [ - 3, 11, 7, 3, 7, 15, 3, 15, 13, - 7, 19, 17, 7, 17, 6, 7, 6, 15, - 17, 4, 8, 17, 8, 10, 17, 10, 6, - 8, 0, 16, 8, 16, 2, 8, 2, 10, - 0, 12, 1, 0, 1, 18, 0, 18, 16, - 6, 10, 2, 6, 2, 13, 6, 13, 15, - 2, 16, 18, 2, 18, 3, 2, 3, 13, - 18, 1, 9, 18, 9, 11, 18, 11, 3, - 4, 14, 12, 4, 12, 0, 4, 0, 8, - 11, 9, 5, 11, 5, 19, 11, 19, 7, - 19, 5, 14, 19, 14, 4, 19, 4, 17, - 1, 12, 14, 1, 14, 5, 1, 5, 9 - ]; + } - PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail ); + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() ); - this.type = 'DodecahedronBufferGeometry'; + }; + + this.getScissor = function ( target ) { + + return target.copy( _scissor ); - this.parameters = { - radius: radius, - detail: detail }; - } + this.setScissor = function ( x, y, width, height ) { - DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype ); - DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry; + if ( x.isVector4 ) { - /** - * @author oosmoxiecode / https://github.com/oosmoxiecode - * @author WestLangley / https://github.com/WestLangley - * @author zz85 / https://github.com/zz85 - * @author miningold / https://github.com/miningold - * @author jonobr1 / https://github.com/jonobr1 - * @author Mugen87 / https://github.com/Mugen87 - * - */ + _scissor.set( x.x, x.y, x.z, x.w ); - // TubeGeometry + } else { - function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) { + _scissor.set( x, y, width, height ); - Geometry.call( this ); + } - this.type = 'TubeGeometry'; + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() ); - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed }; - if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' ); - - var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ); + this.getScissorTest = function () { - // expose internals + return _scissorTest; - this.tangents = bufferGeometry.tangents; - this.normals = bufferGeometry.normals; - this.binormals = bufferGeometry.binormals; + }; - // create geometry + this.setScissorTest = function ( boolean ) { - this.fromBufferGeometry( bufferGeometry ); - this.mergeVertices(); + state.setScissorTest( _scissorTest = boolean ); - } + }; - TubeGeometry.prototype = Object.create( Geometry.prototype ); - TubeGeometry.prototype.constructor = TubeGeometry; + this.setOpaqueSort = function ( method ) { - // TubeBufferGeometry + _opaqueSort = method; - function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) { + }; - BufferGeometry.call( this ); + this.setTransparentSort = function ( method ) { - this.type = 'TubeBufferGeometry'; + _transparentSort = method; - this.parameters = { - path: path, - tubularSegments: tubularSegments, - radius: radius, - radialSegments: radialSegments, - closed: closed }; - tubularSegments = tubularSegments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; + // Clearing - var frames = path.computeFrenetFrames( tubularSegments, closed ); + this.getClearColor = function ( target ) { - // expose internals + return target.copy( background.getClearColor() ); - this.tangents = frames.tangents; - this.normals = frames.normals; - this.binormals = frames.binormals; + }; - // helper variables + this.setClearColor = function () { - var vertex = new Vector3(); - var normal = new Vector3(); - var uv = new Vector2(); - var P = new Vector3(); + background.setClearColor.apply( background, arguments ); - var i, j; + }; - // buffer + this.getClearAlpha = function () { - var vertices = []; - var normals = []; - var uvs = []; - var indices = []; + return background.getClearAlpha(); - // create buffer data + }; - generateBufferData(); + this.setClearAlpha = function () { - // build geometry + background.setClearAlpha.apply( background, arguments ); - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + }; - // functions + this.clear = function ( color = true, depth = true, stencil = true ) { - function generateBufferData() { + let bits = 0; - for ( i = 0; i < tubularSegments; i ++ ) { + if ( color ) { - generateSegment( i ); + // check if we're trying to clear an integer target + let isIntegerFormat = false; + if ( _currentRenderTarget !== null ) { - } + const targetFormat = _currentRenderTarget.texture.format; + isIntegerFormat = targetFormat === RGBAIntegerFormat || + targetFormat === RGIntegerFormat || + targetFormat === RedIntegerFormat; - // if the geometry is not closed, generate the last row of vertices and normals - // at the regular position on the given path - // - // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) + } - generateSegment( ( closed === false ) ? tubularSegments : 0 ); + // use the appropriate clear functions to clear the target if it's a signed + // or unsigned integer target + if ( isIntegerFormat ) { - // uvs are generated in a separate function. - // this makes it easy compute correct values for closed geometries + const targetType = _currentRenderTarget.texture.type; + const isUnsignedType = targetType === UnsignedByteType || + targetType === UnsignedIntType || + targetType === UnsignedShortType || + targetType === UnsignedInt248Type || + targetType === UnsignedShort4444Type || + targetType === UnsignedShort5551Type; - generateUVs(); + const clearColor = background.getClearColor(); + const a = background.getClearAlpha(); + const r = clearColor.r; + const g = clearColor.g; + const b = clearColor.b; - // finally create faces + if ( isUnsignedType ) { - generateIndices(); + uintClearColor[ 0 ] = r; + uintClearColor[ 1 ] = g; + uintClearColor[ 2 ] = b; + uintClearColor[ 3 ] = a; + _gl.clearBufferuiv( _gl.COLOR, 0, uintClearColor ); - } + } else { - function generateSegment( i ) { + intClearColor[ 0 ] = r; + intClearColor[ 1 ] = g; + intClearColor[ 2 ] = b; + intClearColor[ 3 ] = a; + _gl.clearBufferiv( _gl.COLOR, 0, intClearColor ); - // we use getPointAt to sample evenly distributed points from the given path + } - P = path.getPointAt( i / tubularSegments, P ); + } else { - // retrieve corresponding normal and binormal + bits |= _gl.COLOR_BUFFER_BIT; - var N = frames.normals[ i ]; - var B = frames.binormals[ i ]; + } - // generate normals and vertices for the current segment + } - for ( j = 0; j <= radialSegments; j ++ ) { + if ( depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil ) bits |= _gl.STENCIL_BUFFER_BIT; - var v = j / radialSegments * Math.PI * 2; + _gl.clear( bits ); - var sin = Math.sin( v ); - var cos = - Math.cos( v ); + }; - // normal + this.clearColor = function () { - normal.x = ( cos * N.x + sin * B.x ); - normal.y = ( cos * N.y + sin * B.y ); - normal.z = ( cos * N.z + sin * B.z ); - normal.normalize(); + this.clear( true, false, false ); - normals.push( normal.x, normal.y, normal.z ); + }; - // vertex + this.clearDepth = function () { - vertex.x = P.x + radius * normal.x; - vertex.y = P.y + radius * normal.y; - vertex.z = P.z + radius * normal.z; + this.clear( false, true, false ); - vertices.push( vertex.x, vertex.y, vertex.z ); + }; - } + this.clearStencil = function () { - } + this.clear( false, false, true ); - function generateIndices() { + }; - for ( j = 1; j <= tubularSegments; j ++ ) { + // - for ( i = 1; i <= radialSegments; i ++ ) { + this.dispose = function () { - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; + canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false ); + canvas.removeEventListener( 'webglcontextcreationerror', onContextCreationError, false ); - // faces + renderLists.dispose(); + renderStates.dispose(); + properties.dispose(); + cubemaps.dispose(); + cubeuvmaps.dispose(); + objects.dispose(); + bindingStates.dispose(); + uniformsGroups.dispose(); + programCache.dispose(); - indices.push( a, b, d ); - indices.push( b, c, d ); + xr.dispose(); - } + xr.removeEventListener( 'sessionstart', onXRSessionStart ); + xr.removeEventListener( 'sessionend', onXRSessionEnd ); - } + if ( _transmissionRenderTarget ) { - } + _transmissionRenderTarget.dispose(); + _transmissionRenderTarget = null; - function generateUVs() { + } - for ( i = 0; i <= tubularSegments; i ++ ) { + animation.stop(); - for ( j = 0; j <= radialSegments; j ++ ) { + }; - uv.x = i / tubularSegments; - uv.y = j / radialSegments; + // Events - uvs.push( uv.x, uv.y ); + function onContextLost( event ) { - } + event.preventDefault(); - } + console.log( 'THREE.WebGLRenderer: Context Lost.' ); - } + _isContextLost = true; - } + } - TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TubeBufferGeometry.prototype.constructor = TubeBufferGeometry; + function onContextRestore( /* event */ ) { - TubeBufferGeometry.prototype.toJSON = function () { + console.log( 'THREE.WebGLRenderer: Context Restored.' ); - var data = BufferGeometry.prototype.toJSON.call( this ); + _isContextLost = false; - data.path = this.parameters.path.toJSON(); + const infoAutoReset = info.autoReset; + const shadowMapEnabled = shadowMap.enabled; + const shadowMapAutoUpdate = shadowMap.autoUpdate; + const shadowMapNeedsUpdate = shadowMap.needsUpdate; + const shadowMapType = shadowMap.type; - return data; + initGLContext(); - }; + info.autoReset = infoAutoReset; + shadowMap.enabled = shadowMapEnabled; + shadowMap.autoUpdate = shadowMapAutoUpdate; + shadowMap.needsUpdate = shadowMapNeedsUpdate; + shadowMap.type = shadowMapType; - /** - * @author oosmoxiecode - * @author Mugen87 / https://github.com/Mugen87 - * - * based on http://www.blackpawn.com/texts/pqtorus/ - */ + } - // TorusKnotGeometry + function onContextCreationError( event ) { - function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { + console.error( 'THREE.WebGLRenderer: A WebGL context could not be created. Reason: ', event.statusMessage ); - Geometry.call( this ); + } - this.type = 'TorusKnotGeometry'; + function onMaterialDispose( event ) { - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; + const material = event.target; - if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); + material.removeEventListener( 'dispose', onMaterialDispose ); - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); + deallocateMaterial( material ); - } + } - TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); - TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; + // Buffer deallocation - // TorusKnotBufferGeometry + function deallocateMaterial( material ) { - function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { + releaseMaterialProgramReferences( material ); - BufferGeometry.call( this ); + properties.remove( material ); - this.type = 'TorusKnotBufferGeometry'; + } - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; - radius = radius || 1; - tube = tube || 0.4; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; + function releaseMaterialProgramReferences( material ) { - // buffers + const programs = properties.get( material ).programs; - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + if ( programs !== undefined ) { - // helper variables + programs.forEach( function ( program ) { - var i, j; + programCache.releaseProgram( program ); - var vertex = new Vector3(); - var normal = new Vector3(); + } ); - var P1 = new Vector3(); - var P2 = new Vector3(); + if ( material.isShaderMaterial ) { - var B = new Vector3(); - var T = new Vector3(); - var N = new Vector3(); + programCache.releaseShaderCache( material ); - // generate vertices, normals and uvs + } - for ( i = 0; i <= tubularSegments; ++ i ) { + } - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement + } - var u = i / tubularSegments * p * Math.PI * 2; + // Buffer rendering - // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. - // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions + this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) { - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); + if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null) - // calculate orthonormal basis + const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 ); - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); + const program = setProgram( camera, scene, geometry, material, object ); - // normalize B, N. T can be ignored, we don't use it + state.setMaterial( material, frontFaceCW ); - B.normalize(); - N.normalize(); + // - for ( j = 0; j <= radialSegments; ++ j ) { + let index = geometry.index; + let rangeFactor = 1; - // now calculate the vertices. they are nothing more than an extrusion of the torus curve. - // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. + if ( material.wireframe === true ) { - var v = j / radialSegments * Math.PI * 2; - var cx = - tube * Math.cos( v ); - var cy = tube * Math.sin( v ); + index = geometries.getWireframeAttribute( geometry ); - // now calculate the final vertex position. - // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve + if ( index === undefined ) return; - vertex.x = P1.x + ( cx * N.x + cy * B.x ); - vertex.y = P1.y + ( cx * N.y + cy * B.y ); - vertex.z = P1.z + ( cx * N.z + cy * B.z ); + rangeFactor = 2; - vertices.push( vertex.x, vertex.y, vertex.z ); + } - // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) + // - normal.subVectors( vertex, P1 ).normalize(); + const drawRange = geometry.drawRange; + const position = geometry.attributes.position; - normals.push( normal.x, normal.y, normal.z ); + let drawStart = drawRange.start * rangeFactor; + let drawEnd = ( drawRange.start + drawRange.count ) * rangeFactor; - // uv + if ( group !== null ) { - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); + drawStart = Math.max( drawStart, group.start * rangeFactor ); + drawEnd = Math.min( drawEnd, ( group.start + group.count ) * rangeFactor ); } - } - - // generate indices - - for ( j = 1; j <= tubularSegments; j ++ ) { - - for ( i = 1; i <= radialSegments; i ++ ) { - - // indices + if ( index !== null ) { - var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); - var b = ( radialSegments + 1 ) * j + ( i - 1 ); - var c = ( radialSegments + 1 ) * j + i; - var d = ( radialSegments + 1 ) * ( j - 1 ) + i; + drawStart = Math.max( drawStart, 0 ); + drawEnd = Math.min( drawEnd, index.count ); - // faces + } else if ( position !== undefined && position !== null ) { - indices.push( a, b, d ); - indices.push( b, c, d ); + drawStart = Math.max( drawStart, 0 ); + drawEnd = Math.min( drawEnd, position.count ); } - } + const drawCount = drawEnd - drawStart; - // build geometry + if ( drawCount < 0 || drawCount === Infinity ) return; - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + // - // this function calculates the current position on the torus curve + bindingStates.setup( object, material, program, geometry, index ); - function calculatePositionOnCurve( u, p, q, radius, position ) { + let attribute; + let renderer = bufferRenderer; - var cu = Math.cos( u ); - var su = Math.sin( u ); - var quOverP = q / p * u; - var cs = Math.cos( quOverP ); + if ( index !== null ) { - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; + attribute = attributes.get( index ); - } + renderer = indexedBufferRenderer; + renderer.setIndex( attribute ); - } + } - TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; + // - /** - * @author oosmoxiecode - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ + if ( object.isMesh ) { - // TorusGeometry + if ( material.wireframe === true ) { - function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( _gl.LINES ); - Geometry.call( this ); + } else { - this.type = 'TorusGeometry'; + renderer.setMode( _gl.TRIANGLES ); - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; + } - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - this.mergeVertices(); + } else if ( object.isLine ) { - } + let lineWidth = material.linewidth; - TorusGeometry.prototype = Object.create( Geometry.prototype ); - TorusGeometry.prototype.constructor = TorusGeometry; + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - // TorusBufferGeometry + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { + if ( object.isLineSegments ) { - BufferGeometry.call( this ); + renderer.setMode( _gl.LINES ); - this.type = 'TorusBufferGeometry'; + } else if ( object.isLineLoop ) { - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; + renderer.setMode( _gl.LINE_LOOP ); - radius = radius || 1; - tube = tube || 0.4; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; + } else { - // buffers + renderer.setMode( _gl.LINE_STRIP ); - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + } - // helper variables + } else if ( object.isPoints ) { - var center = new Vector3(); - var vertex = new Vector3(); - var normal = new Vector3(); + renderer.setMode( _gl.POINTS ); - var j, i; + } else if ( object.isSprite ) { - // generate vertices, normals and uvs + renderer.setMode( _gl.TRIANGLES ); - for ( j = 0; j <= radialSegments; j ++ ) { + } - for ( i = 0; i <= tubularSegments; i ++ ) { + if ( object.isInstancedMesh ) { - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; + renderer.renderInstances( drawStart, drawCount, object.count ); - // vertex + } else if ( geometry.isInstancedBufferGeometry ) { - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); + const maxInstanceCount = geometry._maxInstanceCount !== undefined ? geometry._maxInstanceCount : Infinity; + const instanceCount = Math.min( geometry.instanceCount, maxInstanceCount ); - vertices.push( vertex.x, vertex.y, vertex.z ); + renderer.renderInstances( drawStart, drawCount, instanceCount ); - // normal + } else { - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - normal.subVectors( vertex, center ).normalize(); + renderer.render( drawStart, drawCount ); - normals.push( normal.x, normal.y, normal.z ); + } - // uv + }; - uvs.push( i / tubularSegments ); - uvs.push( j / radialSegments ); + // Compile - } + this.compile = function ( scene, camera ) { - } + function prepare( material, scene, object ) { - // generate indices + if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { - for ( j = 1; j <= radialSegments; j ++ ) { + material.side = BackSide; + material.needsUpdate = true; + getProgram( material, scene, object ); - for ( i = 1; i <= tubularSegments; i ++ ) { + material.side = FrontSide; + material.needsUpdate = true; + getProgram( material, scene, object ); - // indices + material.side = DoubleSide; - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; + } else { - // faces + getProgram( material, scene, object ); - indices.push( a, b, d ); - indices.push( b, c, d ); + } } - } + currentRenderState = renderStates.get( scene ); + currentRenderState.init(); - // build geometry + renderStateStack.push( currentRenderState ); - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + scene.traverseVisible( function ( object ) { - } + if ( object.isLight && object.layers.test( camera.layers ) ) { - TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; + currentRenderState.pushLight( object ); - /** - * @author Mugen87 / https://github.com/Mugen87 - * Port from https://github.com/mapbox/earcut (v2.1.5) - */ + if ( object.castShadow ) { - var Earcut = { + currentRenderState.pushShadow( object ); - triangulate: function ( data, holeIndices, dim ) { + } - dim = dim || 2; + } - var hasHoles = holeIndices && holeIndices.length, - outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length, - outerNode = linkedList( data, 0, outerLen, dim, true ), - triangles = []; + } ); - if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; + currentRenderState.setupLights( _this._useLegacyLights ); - var minX, minY, maxX, maxY, x, y, invSize; + scene.traverse( function ( object ) { - if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); + const material = object.material; - // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox - if ( data.length > 80 * dim ) { + if ( material ) { - minX = maxX = data[ 0 ]; - minY = maxY = data[ 1 ]; + if ( Array.isArray( material ) ) { - for ( var i = dim; i < outerLen; i += dim ) { + for ( let i = 0; i < material.length; i ++ ) { - x = data[ i ]; - y = data[ i + 1 ]; - if ( x < minX ) minX = x; - if ( y < minY ) minY = y; - if ( x > maxX ) maxX = x; - if ( y > maxY ) maxY = y; + const material2 = material[ i ]; - } + prepare( material2, scene, object ); - // minX, minY and invSize are later used to transform coords into integers for z-order calculation - invSize = Math.max( maxX - minX, maxY - minY ); - invSize = invSize !== 0 ? 1 / invSize : 0; + } - } + } else { - earcutLinked( outerNode, triangles, dim, minX, minY, invSize ); + prepare( material, scene, object ); - return triangles; + } - } + } - }; + } ); - // create a circular doubly linked list from polygon points in the specified winding order - function linkedList( data, start, end, dim, clockwise ) { + renderStateStack.pop(); + currentRenderState = null; - var i, last; + }; - if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { + // Animation Loop - for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + let onAnimationFrameCallback = null; - } else { + function onAnimationFrame( time ) { - for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + if ( onAnimationFrameCallback ) onAnimationFrameCallback( time ); } - if ( last && equals( last, last.next ) ) { + function onXRSessionStart() { - removeNode( last ); - last = last.next; + animation.stop(); } - return last; + function onXRSessionEnd() { - } + animation.start(); - // eliminate colinear or duplicate points - function filterPoints( start, end ) { + } - if ( ! start ) return start; - if ( ! end ) end = start; + const animation = new WebGLAnimation(); + animation.setAnimationLoop( onAnimationFrame ); - var p = start, - again; - do { + if ( typeof self !== 'undefined' ) animation.setContext( self ); - again = false; + this.setAnimationLoop = function ( callback ) { - if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { + onAnimationFrameCallback = callback; + xr.setAnimationLoop( callback ); - removeNode( p ); - p = end = p.prev; - if ( p === p.next ) break; - again = true; + ( callback === null ) ? animation.stop() : animation.start(); - } else { + }; - p = p.next; + xr.addEventListener( 'sessionstart', onXRSessionStart ); + xr.addEventListener( 'sessionend', onXRSessionEnd ); - } + // Rendering - } while ( again || p !== end ); + this.render = function ( scene, camera ) { - return end; + if ( camera !== undefined && camera.isCamera !== true ) { - } + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - // main ear slicing loop which triangulates a polygon (given as a linked list) - function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { + } - if ( ! ear ) return; + if ( _isContextLost === true ) return; - // interlink polygon nodes in z-order - if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); + // update scene graph - var stop = ear, - prev, next; + if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); - // iterate through ears, slicing them one by one - while ( ear.prev !== ear.next ) { + // update camera matrices and frustum - prev = ear.prev; - next = ear.next; + if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); - if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { + if ( xr.enabled === true && xr.isPresenting === true ) { - // cut off the triangle - triangles.push( prev.i / dim ); - triangles.push( ear.i / dim ); - triangles.push( next.i / dim ); + if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera ); - removeNode( ear ); + camera = xr.getCamera(); // use XR camera for rendering - // skipping the next vertex leads to less sliver triangles - ear = next.next; - stop = next.next; + } - continue; + // + if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget ); - } + currentRenderState = renderStates.get( scene, renderStateStack.length ); + currentRenderState.init(); - ear = next; + renderStateStack.push( currentRenderState ); - // if we looped through the whole remaining polygon and can't find any more ears - if ( ear === stop ) { + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromProjectionMatrix( _projScreenMatrix ); - // try filtering points and slicing again - if ( ! pass ) { + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled ); - earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); + currentRenderList = renderLists.get( scene, renderListStack.length ); + currentRenderList.init(); - // if this didn't work, try curing all small self-intersections locally + renderListStack.push( currentRenderList ); - } else if ( pass === 1 ) { + projectObject( scene, camera, 0, _this.sortObjects ); - ear = cureLocalIntersections( ear, triangles, dim ); - earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); + currentRenderList.finish(); - // as a last resort, try splitting the remaining polygon into two + if ( _this.sortObjects === true ) { - } else if ( pass === 2 ) { + currentRenderList.sort( _opaqueSort, _transparentSort ); - splitEarcut( ear, triangles, dim, minX, minY, invSize ); + } - } + // - break; + this.info.render.frame ++; - } + if ( _clippingEnabled === true ) clipping.beginShadows(); - } + const shadowsArray = currentRenderState.state.shadowsArray; - } + shadowMap.render( shadowsArray, scene, camera ); - // check whether a polygon node forms a valid ear with adjacent nodes - function isEar( ear ) { + if ( _clippingEnabled === true ) clipping.endShadows(); - var a = ear.prev, - b = ear, - c = ear.next; + // - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + if ( this.info.autoReset === true ) this.info.reset(); - // now make sure we don't have other points inside the potential ear - var p = ear.next.next; - while ( p !== ear.prev ) { + // - if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.next; + background.render( currentRenderList, scene ); - } + // render scene - return true; + currentRenderState.setupLights( _this._useLegacyLights ); - } + if ( camera.isArrayCamera ) { - function isEarHashed( ear, minX, minY, invSize ) { + const cameras = camera.cameras; - var a = ear.prev, - b = ear, - c = ear.next; + for ( let i = 0, l = cameras.length; i < l; i ++ ) { - if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + const camera2 = cameras[ i ]; - // triangle bbox; min & max are calculated like this for speed - var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ), - minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ), - maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ), - maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y ); + renderScene( currentRenderList, scene, camera2, camera2.viewport ); - // z-order range for the current triangle bbox; - var minZ = zOrder( minTX, minTY, minX, minY, invSize ), - maxZ = zOrder( maxTX, maxTY, minX, minY, invSize ); + } + + } else { - var p = ear.prevZ, - n = ear.nextZ; + renderScene( currentRenderList, scene, camera ); - // look for points inside the triangle in both directions - while ( p && p.z >= minZ && n && n.z <= maxZ ) { + } - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + // - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + if ( _currentRenderTarget !== null ) { - } + // resolve multisample renderbuffers to a single-sample texture if necessary - // look for remaining points in decreasing z-order - while ( p && p.z >= minZ ) { + textures.updateMultisampleRenderTarget( _currentRenderTarget ); - if ( p !== ear.prev && p !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && - area( p.prev, p, p.next ) >= 0 ) return false; - p = p.prevZ; + // Generate mipmap if we're using any kind of mipmap filtering - } + textures.updateRenderTargetMipmap( _currentRenderTarget ); - // look for remaining points in increasing z-order - while ( n && n.z <= maxZ ) { + } - if ( n !== ear.prev && n !== ear.next && - pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y ) && - area( n.prev, n, n.next ) >= 0 ) return false; - n = n.nextZ; + // - } + if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera ); - return true; + // _gl.finish(); - } + bindingStates.resetDefaultState(); + _currentMaterialId = - 1; + _currentCamera = null; - // go through all polygon nodes and cure small local self-intersections - function cureLocalIntersections( start, triangles, dim ) { + renderStateStack.pop(); - var p = start; - do { + if ( renderStateStack.length > 0 ) { + + currentRenderState = renderStateStack[ renderStateStack.length - 1 ]; + + } else { - var a = p.prev, - b = p.next.next; + currentRenderState = null; - if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { + } + + renderListStack.pop(); - triangles.push( a.i / dim ); - triangles.push( p.i / dim ); - triangles.push( b.i / dim ); + if ( renderListStack.length > 0 ) { - // remove two nodes involved - removeNode( p ); - removeNode( p.next ); + currentRenderList = renderListStack[ renderListStack.length - 1 ]; + + } else { - p = start = b; + currentRenderList = null; } - p = p.next; + }; + + function projectObject( object, camera, groupOrder, sortObjects ) { - } while ( p !== start ); + if ( object.visible === false ) return; - return p; + const visible = object.layers.test( camera.layers ); - } + if ( visible ) { + + if ( object.isGroup ) { - // try splitting polygon into two and triangulate them independently - function splitEarcut( start, triangles, dim, minX, minY, invSize ) { + groupOrder = object.renderOrder; - // look for a valid diagonal that divides the polygon into two - var a = start; - do { + } else if ( object.isLOD ) { - var b = a.next.next; - while ( b !== a.prev ) { + if ( object.autoUpdate === true ) object.update( camera ); - if ( a.i !== b.i && isValidDiagonal( a, b ) ) { + } else if ( object.isLight ) { - // split the polygon in two by the diagonal - var c = splitPolygon( a, b ); + currentRenderState.pushLight( object ); - // filter colinear points around the cuts - a = filterPoints( a, a.next ); - c = filterPoints( c, c.next ); + if ( object.castShadow ) { - // run earcut on each half - earcutLinked( a, triangles, dim, minX, minY, invSize ); - earcutLinked( c, triangles, dim, minX, minY, invSize ); - return; + currentRenderState.pushShadow( object ); - } + } - b = b.next; + } else if ( object.isSprite ) { - } + if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) { - a = a.next; + if ( sortObjects ) { - } while ( a !== start ); + _vector3.setFromMatrixPosition( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - } + } - // link every hole into the outer loop, producing a single-ring polygon without holes - function eliminateHoles( data, holeIndices, outerNode, dim ) { + const geometry = objects.update( object ); + const material = object.material; - var queue = [], - i, len, start, end, list; + if ( material.visible ) { - for ( i = 0, len = holeIndices.length; i < len; i ++ ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - start = holeIndices[ i ] * dim; - end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; - list = linkedList( data, start, end, dim, false ); - if ( list === list.next ) list.steiner = true; - queue.push( getLeftmost( list ) ); + } - } + } - queue.sort( compareX ); + } else if ( object.isMesh || object.isLine || object.isPoints ) { - // process holes from left to right - for ( i = 0; i < queue.length; i ++ ) { + if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) { - eliminateHole( queue[ i ], outerNode ); - outerNode = filterPoints( outerNode, outerNode.next ); + const geometry = objects.update( object ); + const material = object.material; - } + if ( sortObjects ) { - return outerNode; + if ( object.boundingSphere !== undefined ) { - } + if ( object.boundingSphere === null ) object.computeBoundingSphere(); + _vector3.copy( object.boundingSphere.center ); + + } else { - function compareX( a, b ) { + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + _vector3.copy( geometry.boundingSphere.center ); - return a.x - b.x; + } - } + _vector3 + .applyMatrix4( object.matrixWorld ) + .applyMatrix4( _projScreenMatrix ); - // find a bridge between vertices that connects hole with an outer ring and and link it - function eliminateHole( hole, outerNode ) { + } - outerNode = findHoleBridge( hole, outerNode ); - if ( outerNode ) { + if ( Array.isArray( material ) ) { - var b = splitPolygon( outerNode, hole ); - filterPoints( b, b.next ); + const groups = geometry.groups; - } + for ( let i = 0, l = groups.length; i < l; i ++ ) { - } + const group = groups[ i ]; + const groupMaterial = material[ group.materialIndex ]; - // David Eberly's algorithm for finding a bridge between hole and outer polygon - function findHoleBridge( hole, outerNode ) { + if ( groupMaterial && groupMaterial.visible ) { - var p = outerNode, - hx = hole.x, - hy = hole.y, - qx = - Infinity, - m; + currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group ); - // find a segment intersected by a ray from the hole's leftmost point to the left; - // segment's endpoint with lesser x will be potential connection point - do { + } - if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { + } - var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); - if ( x <= hx && x > qx ) { + } else if ( material.visible ) { - qx = x; - if ( x === hx ) { + currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null ); - if ( hy === p.y ) return p; - if ( hy === p.next.y ) return p.next; + } } - m = p.x < p.next.x ? p : p.next; - } } - p = p.next; + const children = object.children; - } while ( p !== outerNode ); + for ( let i = 0, l = children.length; i < l; i ++ ) { - if ( ! m ) return null; + projectObject( children[ i ], camera, groupOrder, sortObjects ); - if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint + } - // look for points inside the triangle of hole point, segment intersection and endpoint; - // if there are no points found, we have a valid connection; - // otherwise choose the point of the minimum angle with the ray as connection point + } - var stop = m, - mx = m.x, - my = m.y, - tanMin = Infinity, - tan; + function renderScene( currentRenderList, scene, camera, viewport ) { - p = m.next; + const opaqueObjects = currentRenderList.opaque; + const transmissiveObjects = currentRenderList.transmissive; + const transparentObjects = currentRenderList.transparent; - while ( p !== stop ) { + currentRenderState.setupLightsView( camera ); - if ( hx >= p.x && p.x >= mx && hx !== p.x && - pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { + if ( _clippingEnabled === true ) clipping.setGlobalState( _this.clippingPlanes, camera ); - tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential + if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ); - if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) { + if ( viewport ) state.viewport( _currentViewport.copy( viewport ) ); - m = p; - tanMin = tan; + if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera ); + if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera ); + if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera ); - } + // Ensure depth buffer writing is enabled so it can be cleared on next render - } + state.buffers.depth.setTest( true ); + state.buffers.depth.setMask( true ); + state.buffers.color.setMask( true ); - p = p.next; + state.setPolygonOffset( false ); } - return m; - - } + function renderTransmissionPass( opaqueObjects, transmissiveObjects, scene, camera ) { - // interlink polygon nodes in z-order - function indexCurve( start, minX, minY, invSize ) { + const isWebGL2 = capabilities.isWebGL2; - var p = start; - do { + if ( _transmissionRenderTarget === null ) { - if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); - p.prevZ = p.prev; - p.nextZ = p.next; - p = p.next; + _transmissionRenderTarget = new WebGLRenderTarget( 1, 1, { + generateMipmaps: true, + type: extensions.has( 'EXT_color_buffer_half_float' ) ? HalfFloatType : UnsignedByteType, + minFilter: LinearMipmapLinearFilter, + samples: ( isWebGL2 ) ? 4 : 0 + } ); - } while ( p !== start ); + // debug - p.prevZ.nextZ = null; - p.prevZ = null; + /* + const geometry = new PlaneGeometry(); + const material = new MeshBasicMaterial( { map: _transmissionRenderTarget.texture } ); - sortLinked( p ); + const mesh = new Mesh( geometry, material ); + scene.add( mesh ); + */ - } + } - // Simon Tatham's linked list merge sort algorithm - // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html - function sortLinked( list ) { + _this.getDrawingBufferSize( _vector2 ); - var i, p, q, e, tail, numMerges, pSize, qSize, - inSize = 1; + if ( isWebGL2 ) { - do { + _transmissionRenderTarget.setSize( _vector2.x, _vector2.y ); - p = list; - list = null; - tail = null; - numMerges = 0; + } else { - while ( p ) { + _transmissionRenderTarget.setSize( floorPowerOfTwo( _vector2.x ), floorPowerOfTwo( _vector2.y ) ); - numMerges ++; - q = p; - pSize = 0; - for ( i = 0; i < inSize; i ++ ) { + } - pSize ++; - q = q.nextZ; - if ( ! q ) break; + // - } + const currentRenderTarget = _this.getRenderTarget(); + _this.setRenderTarget( _transmissionRenderTarget ); - qSize = inSize; + _this.getClearColor( _currentClearColor ); + _currentClearAlpha = _this.getClearAlpha(); + if ( _currentClearAlpha < 1 ) _this.setClearColor( 0xffffff, 0.5 ); - while ( pSize > 0 || ( qSize > 0 && q ) ) { + _this.clear(); - if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { + // Turn off the features which can affect the frag color for opaque objects pass. + // Otherwise they are applied twice in opaque objects pass and transmission objects pass. + const currentToneMapping = _this.toneMapping; + _this.toneMapping = NoToneMapping; - e = p; - p = p.nextZ; - pSize --; + renderObjects( opaqueObjects, scene, camera ); - } else { + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - e = q; - q = q.nextZ; - qSize --; + let renderTargetNeedsUpdate = false; - } + for ( let i = 0, l = transmissiveObjects.length; i < l; i ++ ) { - if ( tail ) tail.nextZ = e; - else list = e; + const renderItem = transmissiveObjects[ i ]; - e.prevZ = tail; - tail = e; + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = renderItem.material; + const group = renderItem.group; - } + if ( material.side === DoubleSide && object.layers.test( camera.layers ) ) { - p = q; + const currentSide = material.side; - } + material.side = BackSide; + material.needsUpdate = true; - tail.nextZ = null; - inSize *= 2; + renderObject( object, scene, camera, geometry, material, group ); - } while ( numMerges > 1 ); + material.side = currentSide; + material.needsUpdate = true; - return list; + renderTargetNeedsUpdate = true; - } + } - // z-order of a point given coords and inverse of the longer side of data bbox - function zOrder( x, y, minX, minY, invSize ) { + } - // coords are transformed into non-negative 15-bit integer range - x = 32767 * ( x - minX ) * invSize; - y = 32767 * ( y - minY ) * invSize; + if ( renderTargetNeedsUpdate === true ) { - x = ( x | ( x << 8 ) ) & 0x00FF00FF; - x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; - x = ( x | ( x << 2 ) ) & 0x33333333; - x = ( x | ( x << 1 ) ) & 0x55555555; + textures.updateMultisampleRenderTarget( _transmissionRenderTarget ); + textures.updateRenderTargetMipmap( _transmissionRenderTarget ); - y = ( y | ( y << 8 ) ) & 0x00FF00FF; - y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; - y = ( y | ( y << 2 ) ) & 0x33333333; - y = ( y | ( y << 1 ) ) & 0x55555555; + } - return x | ( y << 1 ); + _this.setRenderTarget( currentRenderTarget ); - } + _this.setClearColor( _currentClearColor, _currentClearAlpha ); - // find the leftmost node of a polygon ring - function getLeftmost( start ) { + _this.toneMapping = currentToneMapping; - var p = start, - leftmost = start; - do { + } - if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; - p = p.next; + function renderObjects( renderList, scene, camera ) { - } while ( p !== start ); + const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null; - return leftmost; + for ( let i = 0, l = renderList.length; i < l; i ++ ) { - } + const renderItem = renderList[ i ]; - // check if a point lies within a convex triangle - function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { + const object = renderItem.object; + const geometry = renderItem.geometry; + const material = overrideMaterial === null ? renderItem.material : overrideMaterial; + const group = renderItem.group; - return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 && - ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 && - ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0; + if ( object.layers.test( camera.layers ) ) { - } + renderObject( object, scene, camera, geometry, material, group ); - // check if a diagonal between two polygon nodes is valid (lies in polygon interior) - function isValidDiagonal( a, b ) { + } - return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && - locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ); + } - } + } - // signed area of a triangle - function area( p, q, r ) { + function renderObject( object, scene, camera, geometry, material, group ) { - return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); + object.onBeforeRender( _this, scene, camera, geometry, material, group ); - } + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - // check if two points are equal - function equals( p1, p2 ) { + material.onBeforeRender( _this, scene, camera, geometry, object, group ); - return p1.x === p2.x && p1.y === p2.y; + if ( material.transparent === true && material.side === DoubleSide && material.forceSinglePass === false ) { - } + material.side = BackSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - // check if two segments intersect - function intersects( p1, q1, p2, q2 ) { + material.side = FrontSide; + material.needsUpdate = true; + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - if ( ( equals( p1, p2 ) && equals( q1, q2 ) ) || - ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true; - return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 && - area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0; + material.side = DoubleSide; - } + } else { - // check if a polygon diagonal intersects any polygon segments - function intersectsPolygon( a, b ) { + _this.renderBufferDirect( camera, scene, geometry, material, object, group ); - var p = a; - do { + } - if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && - intersects( p, p.next, a, b ) ) return true; - p = p.next; + object.onAfterRender( _this, scene, camera, geometry, material, group ); - } while ( p !== a ); + } - return false; + function getProgram( material, scene, object ) { - } + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - // check if a polygon diagonal is locally inside the polygon - function locallyInside( a, b ) { + const materialProperties = properties.get( material ); - return area( a.prev, a, a.next ) < 0 ? - area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : - area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; + const lights = currentRenderState.state.lights; + const shadowsArray = currentRenderState.state.shadowsArray; - } + const lightsStateVersion = lights.state.version; - // check if the middle point of a polygon diagonal is inside the polygon - function middleInside( a, b ) { + const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object ); + const programCacheKey = programCache.getProgramCacheKey( parameters ); - var p = a, - inside = false, - px = ( a.x + b.x ) / 2, - py = ( a.y + b.y ) / 2; - do { + let programs = materialProperties.programs; - if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && - ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) - inside = ! inside; - p = p.next; + // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change - } while ( p !== a ); + materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null; + materialProperties.fog = scene.fog; + materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment ); - return inside; + if ( programs === undefined ) { - } + // new material - // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; - // if one belongs to the outer ring and another to a hole, it merges it into a single ring - function splitPolygon( a, b ) { + material.addEventListener( 'dispose', onMaterialDispose ); - var a2 = new Node( a.i, a.x, a.y ), - b2 = new Node( b.i, b.x, b.y ), - an = a.next, - bp = b.prev; + programs = new Map(); + materialProperties.programs = programs; - a.next = b; - b.prev = a; + } - a2.next = an; - an.prev = a2; + let program = programs.get( programCacheKey ); - b2.next = a2; - a2.prev = b2; + if ( program !== undefined ) { - bp.next = b2; - b2.prev = bp; + // early out if program and light state is identical - return b2; + if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) { - } + updateCommonMaterialProperties( material, parameters ); - // create a node and optionally link it with previous one (in a circular doubly linked list) - function insertNode( i, x, y, last ) { + return program; - var p = new Node( i, x, y ); + } - if ( ! last ) { + } else { - p.prev = p; - p.next = p; + parameters.uniforms = programCache.getUniforms( material ); - } else { + material.onBuild( object, parameters, _this ); - p.next = last.next; - p.prev = last; - last.next.prev = p; - last.next = p; + material.onBeforeCompile( parameters, _this ); - } + program = programCache.acquireProgram( parameters, programCacheKey ); + programs.set( programCacheKey, program ); - return p; + materialProperties.uniforms = parameters.uniforms; - } + } - function removeNode( p ) { + const uniforms = materialProperties.uniforms; - p.next.prev = p.prev; - p.prev.next = p.next; + if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) { - if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; - if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; + uniforms.clippingPlanes = clipping.uniform; - } + } - function Node( i, x, y ) { + updateCommonMaterialProperties( material, parameters ); - // vertex index in coordinates array - this.i = i; + // store the light setup it was created for - // vertex coordinates - this.x = x; - this.y = y; + materialProperties.needsLights = materialNeedsLights( material ); + materialProperties.lightsStateVersion = lightsStateVersion; - // previous and next vertex nodes in a polygon ring - this.prev = null; - this.next = null; + if ( materialProperties.needsLights ) { - // z-order curve value - this.z = null; + // wire up the material to this renderer's lighting state - // previous and next nodes in z-order - this.prevZ = null; - this.nextZ = null; + uniforms.ambientLightColor.value = lights.state.ambient; + uniforms.lightProbe.value = lights.state.probe; + uniforms.directionalLights.value = lights.state.directional; + uniforms.directionalLightShadows.value = lights.state.directionalShadow; + uniforms.spotLights.value = lights.state.spot; + uniforms.spotLightShadows.value = lights.state.spotShadow; + uniforms.rectAreaLights.value = lights.state.rectArea; + uniforms.ltc_1.value = lights.state.rectAreaLTC1; + uniforms.ltc_2.value = lights.state.rectAreaLTC2; + uniforms.pointLights.value = lights.state.point; + uniforms.pointLightShadows.value = lights.state.pointShadow; + uniforms.hemisphereLights.value = lights.state.hemi; - // indicates whether this is a steiner point - this.steiner = false; + uniforms.directionalShadowMap.value = lights.state.directionalShadowMap; + uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix; + uniforms.spotShadowMap.value = lights.state.spotShadowMap; + uniforms.spotLightMatrix.value = lights.state.spotLightMatrix; + uniforms.spotLightMap.value = lights.state.spotLightMap; + uniforms.pointShadowMap.value = lights.state.pointShadowMap; + uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix; + // TODO (abelnation): add area lights shadow info to uniforms - } + } - function signedArea( data, start, end, dim ) { + const progUniforms = program.getUniforms(); + const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - var sum = 0; - for ( var i = start, j = end - dim; i < end; i += dim ) { + materialProperties.currentProgram = program; + materialProperties.uniformsList = uniformsList; - sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); - j = i; + return program; } - return sum; - - } - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - */ + function updateCommonMaterialProperties( material, parameters ) { - var ShapeUtils = { + const materialProperties = properties.get( material ); - // calculate area of the contour polygon + materialProperties.outputColorSpace = parameters.outputColorSpace; + materialProperties.instancing = parameters.instancing; + materialProperties.instancingColor = parameters.instancingColor; + materialProperties.skinning = parameters.skinning; + materialProperties.morphTargets = parameters.morphTargets; + materialProperties.morphNormals = parameters.morphNormals; + materialProperties.morphColors = parameters.morphColors; + materialProperties.morphTargetsCount = parameters.morphTargetsCount; + materialProperties.numClippingPlanes = parameters.numClippingPlanes; + materialProperties.numIntersection = parameters.numClipIntersection; + materialProperties.vertexAlphas = parameters.vertexAlphas; + materialProperties.vertexTangents = parameters.vertexTangents; + materialProperties.toneMapping = parameters.toneMapping; - area: function ( contour ) { + } - var n = contour.length; - var a = 0.0; + function setProgram( camera, scene, geometry, material, object ) { - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ... - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + textures.resetTextureUnits(); - } + const fog = scene.fog; + const environment = material.isMeshStandardMaterial ? scene.environment : null; + const colorSpace = ( _currentRenderTarget === null ) ? _this.outputColorSpace : ( _currentRenderTarget.isXRRenderTarget === true ? _currentRenderTarget.texture.colorSpace : LinearSRGBColorSpace ); + const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment ); + const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4; + const vertexTangents = !! geometry.attributes.tangent && ( !! material.normalMap || material.anisotropy > 0 ); + const morphTargets = !! geometry.morphAttributes.position; + const morphNormals = !! geometry.morphAttributes.normal; + const morphColors = !! geometry.morphAttributes.color; - return a * 0.5; + let toneMapping = NoToneMapping; - }, + if ( material.toneMapped ) { - isClockWise: function ( pts ) { + if ( _currentRenderTarget === null || _currentRenderTarget.isXRRenderTarget === true ) { - return ShapeUtils.area( pts ) < 0; + toneMapping = _this.toneMapping; - }, + } - triangulateShape: function ( contour, holes ) { + } - var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] - var holeIndices = []; // array of hole indices - var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + const morphAttribute = geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color; + const morphTargetsCount = ( morphAttribute !== undefined ) ? morphAttribute.length : 0; - removeDupEndPts( contour ); - addContour( vertices, contour ); + const materialProperties = properties.get( material ); + const lights = currentRenderState.state.lights; - // + if ( _clippingEnabled === true ) { - var holeIndex = contour.length; + if ( _localClippingEnabled === true || camera !== _currentCamera ) { - holes.forEach( removeDupEndPts ); + const useCache = + camera === _currentCamera && + material.id === _currentMaterialId; - for ( var i = 0; i < holes.length; i ++ ) { + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + clipping.setState( material, camera, useCache ); - holeIndices.push( holeIndex ); - holeIndex += holes[ i ].length; - addContour( vertices, holes[ i ] ); + } } // - var triangles = Earcut.triangulate( vertices, holeIndices ); + let needsProgramChange = false; - // + if ( material.version === materialProperties.__version ) { - for ( var i = 0; i < triangles.length; i += 3 ) { + if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) { - faces.push( triangles.slice( i, i + 3 ) ); + needsProgramChange = true; - } + } else if ( materialProperties.outputColorSpace !== colorSpace ) { - return faces; + needsProgramChange = true; - } + } else if ( object.isInstancedMesh && materialProperties.instancing === false ) { - }; + needsProgramChange = true; - function removeDupEndPts( points ) { + } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) { - var l = points.length; + needsProgramChange = true; - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) { - points.pop(); + needsProgramChange = true; - } + } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) { - } + needsProgramChange = true; - function addContour( vertices, contour ) { + } else if ( object.isInstancedMesh && materialProperties.instancingColor === true && object.instanceColor === null ) { - for ( var i = 0; i < contour.length; i ++ ) { + needsProgramChange = true; - vertices.push( contour[ i ].x ); - vertices.push( contour[ i ].y ); + } else if ( object.isInstancedMesh && materialProperties.instancingColor === false && object.instanceColor !== null ) { - } + needsProgramChange = true; - } + } else if ( materialProperties.envMap !== envMap ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too - * depth: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline (including bevelOffset) is bevel - * bevelOffset: , // how far from shape outline does bevel start - * bevelSegments: , // number of bevel layers - * - * extrudePath: // curve to extrude shape along - * - * UVGenerator: // object that provides UV generator functions - * - * } - */ + needsProgramChange = true; - // ExtrudeGeometry + } else if ( material.fog === true && materialProperties.fog !== fog ) { - function ExtrudeGeometry( shapes, options ) { + needsProgramChange = true; - Geometry.call( this ); + } else if ( materialProperties.numClippingPlanes !== undefined && + ( materialProperties.numClippingPlanes !== clipping.numPlanes || + materialProperties.numIntersection !== clipping.numIntersection ) ) { - this.type = 'ExtrudeGeometry'; + needsProgramChange = true; - this.parameters = { - shapes: shapes, - options: options - }; + } else if ( materialProperties.vertexAlphas !== vertexAlphas ) { - this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) ); - this.mergeVertices(); + needsProgramChange = true; - } + } else if ( materialProperties.vertexTangents !== vertexTangents ) { - ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); - ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; + needsProgramChange = true; - ExtrudeGeometry.prototype.toJSON = function () { + } else if ( materialProperties.morphTargets !== morphTargets ) { - var data = Geometry.prototype.toJSON.call( this ); + needsProgramChange = true; - var shapes = this.parameters.shapes; - var options = this.parameters.options; + } else if ( materialProperties.morphNormals !== morphNormals ) { - return toJSON( shapes, options, data ); + needsProgramChange = true; - }; + } else if ( materialProperties.morphColors !== morphColors ) { - // ExtrudeBufferGeometry + needsProgramChange = true; - function ExtrudeBufferGeometry( shapes, options ) { + } else if ( materialProperties.toneMapping !== toneMapping ) { - BufferGeometry.call( this ); + needsProgramChange = true; - this.type = 'ExtrudeBufferGeometry'; + } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) { - this.parameters = { - shapes: shapes, - options: options - }; + needsProgramChange = true; - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + } - var scope = this; + } else { - var verticesArray = []; - var uvArray = []; + needsProgramChange = true; + materialProperties.__version = material.version; - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + } - var shape = shapes[ i ]; - addShape( shape ); + // - } + let program = materialProperties.currentProgram; - // build geometry + if ( needsProgramChange === true ) { - this.addAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); + program = getProgram( material, scene, object ); - this.computeVertexNormals(); + } - // functions + let refreshProgram = false; + let refreshMaterial = false; + let refreshLights = false; - function addShape( shape ) { + const p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.uniforms; - var placeholder = []; + if ( state.useProgram( program.program ) ) { - // options + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - var steps = options.steps !== undefined ? options.steps : 1; - var depth = options.depth !== undefined ? options.depth : 100; + } - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; - var bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + if ( material.id !== _currentMaterialId ) { - var extrudePath = options.extrudePath; + _currentMaterialId = material.id; - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; + refreshMaterial = true; - // deprecated options + } - if ( options.amount !== undefined ) { + if ( refreshProgram || _currentCamera !== camera ) { - console.warn( 'THREE.ExtrudeBufferGeometry: amount has been renamed to depth.' ); - depth = options.amount; + // common camera uniforms - } + p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix ); + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - // + const uCamPos = p_uniforms.map.cameraPosition; - var extrudePts, extrudeByPath = false; - var splineTube, binormal, normal, position2; + if ( uCamPos !== undefined ) { - if ( extrudePath ) { + uCamPos.setValue( _gl, _vector3.setFromMatrixPosition( camera.matrixWorld ) ); - extrudePts = extrudePath.getSpacedPoints( steps ); + } - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + if ( capabilities.logarithmicDepthBuffer ) { - // SETUP TNB variables + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - // TODO1 - have a .isClosed in spline? + } - splineTube = extrudePath.computeFrenetFrames( steps, false ); + // consider moving isOrthographic to UniformLib and WebGLMaterials, see https://github.com/mrdoob/three.js/pull/26467#issuecomment-1645185067 - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + if ( material.isMeshPhongMaterial || + material.isMeshToonMaterial || + material.isMeshLambertMaterial || + material.isMeshBasicMaterial || + material.isMeshStandardMaterial || + material.isShaderMaterial ) { - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); + p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true ); - } + } - // Safeguards if bevels are not enabled + if ( _currentCamera !== camera ) { - if ( ! bevelEnabled ) { + _currentCamera = camera; - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; - bevelOffset = 0; + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: - } + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done - // Variables initialization + } - var ahole, h, hl; // looping of holes + } - var shapePoints = shape.extractPoints( curveSegments ); + // skinning and morph target uniforms must be set even if material didn't change + // auto-setting of texture unit for bone and morph texture must go before other textures + // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + if ( object.isSkinnedMesh ) { - var reverse = ! ShapeUtils.isClockWise( vertices ); + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - if ( reverse ) { + const skeleton = object.skeleton; - vertices = vertices.reverse(); + if ( skeleton ) { - // Maybe we should also check if holes are in the opposite direction, just to be safe ... + if ( capabilities.floatVertexTextures ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture(); - ahole = holes[ h ]; + p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures ); + p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize ); - if ( ShapeUtils.isClockWise( ahole ) ) { + } else { - holes[ h ] = ahole.reverse(); + console.warn( 'THREE.WebGLRenderer: SkinnedMesh can only be used with WebGL 2. With WebGL 1 OES_texture_float and vertex textures support is required.' ); } @@ -29709,19219 +29928,21562 @@ } + const morphAttributes = geometry.morphAttributes; - var faces = ShapeUtils.triangulateShape( vertices, holes ); + if ( morphAttributes.position !== undefined || morphAttributes.normal !== undefined || ( morphAttributes.color !== undefined && capabilities.isWebGL2 === true ) ) { - /* Vertices */ - - var contour = vertices; // vertices has all points but contour has only points of circumference + morphtargets.update( object, geometry, program ); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + } - ahole = holes[ h ]; + if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) { - vertices = vertices.concat( ahole ); + materialProperties.receiveShadow = object.receiveShadow; + p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow ); } + // https://github.com/mrdoob/three.js/pull/24467#issuecomment-1209031512 - function scalePt2( pt, vec, size ) { + if ( material.isMeshGouraudMaterial && material.envMap !== null ) { - if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); + m_uniforms.envMap.value = envMap; - return vec.clone().multiplyScalar( size ).add( pt ); + m_uniforms.flipEnvMap.value = ( envMap.isCubeTexture && envMap.isRenderTargetTexture === false ) ? - 1 : 1; } - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length; - - - // Find directions for point movement - - - function getBevelVec( inPt, inPrev, inNext ) { + if ( refreshMaterial ) { - // computes for inPt the corresponding point inPt' on a new contour - // shifted by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. + p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); - var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + if ( materialProperties.needsLights ) { - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + // the current material requires lighting info - var v_prev_x = inPt.x - inPrev.x, - v_prev_y = inPt.y - inPrev.y; - var v_next_x = inNext.x - inPt.x, - v_next_y = inNext.y - inPt.y; + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required - var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - // check for collinear edges - var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + } - if ( Math.abs( collinear0 ) > Number.EPSILON ) { + // refresh uniforms common to several materials - // not collinear + if ( fog && material.fog === true ) { - // length of vectors for normalizing + materials.refreshFogUniforms( m_uniforms, fog ); - var v_prev_len = Math.sqrt( v_prev_lensq ); - var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + } - // shift adjacent points by unit vectors to the left + materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget ); - var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); - var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + } - // scaling factor for v_prev to intersection point + if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) { - var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures ); + material.uniformsNeedUpdate = false; - // vector from inPt to intersection point + } - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + if ( material.isSpriteMaterial ) { - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); - if ( v_trans_lensq <= 2 ) { + p_uniforms.setValue( _gl, 'center', object.center ); - return new Vector2( v_trans_x, v_trans_y ); + } - } else { + // common matrices - shrink_by = Math.sqrt( v_trans_lensq / 2 ); + p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix ); + p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - } + // UBOs - } else { + if ( material.isShaderMaterial || material.isRawShaderMaterial ) { - // handle special case of collinear edges + const groups = material.uniformsGroups; - var direction_eq = false; // assumes: opposite - if ( v_prev_x > Number.EPSILON ) { + for ( let i = 0, l = groups.length; i < l; i ++ ) { - if ( v_next_x > Number.EPSILON ) { + if ( capabilities.isWebGL2 ) { - direction_eq = true; + const group = groups[ i ]; - } + uniformsGroups.update( group, program ); + uniformsGroups.bind( group, program ); } else { - if ( v_prev_x < - Number.EPSILON ) { + console.warn( 'THREE.WebGLRenderer: Uniform Buffer Objects can only be used with WebGL 2.' ); - if ( v_next_x < - Number.EPSILON ) { + } - direction_eq = true; + } - } + } - } else { + return program; - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + } - direction_eq = true; + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - } + function markUniformsLightsNeedsUpdate( uniforms, value ) { - } + uniforms.ambientLightColor.needsUpdate = value; + uniforms.lightProbe.needsUpdate = value; - } + uniforms.directionalLights.needsUpdate = value; + uniforms.directionalLightShadows.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.pointLightShadows.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.spotLightShadows.needsUpdate = value; + uniforms.rectAreaLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; - if ( direction_eq ) { + } - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); + function materialNeedsLights( material ) { - } else { + return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial || + material.isMeshStandardMaterial || material.isShadowMaterial || + ( material.isShaderMaterial && material.lights === true ); - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } - } + this.getActiveCubeFace = function () { - } + return _currentActiveCubeFace; - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + }; - } + this.getActiveMipmapLevel = function () { + return _currentActiveMipmapLevel; - var contourMovements = []; + }; - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + this.getRenderTarget = function () { - if ( j === il ) j = 0; - if ( k === il ) k = 0; + return _currentRenderTarget; - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + }; - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + this.setRenderTargetTextures = function ( renderTarget, colorTexture, depthTexture ) { - } + properties.get( renderTarget.texture ).__webglTexture = colorTexture; + properties.get( renderTarget.depthTexture ).__webglTexture = depthTexture; - var holesMovements = [], - oneHoleMovements, verticesMovements = contourMovements.concat(); + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__hasExternalTextures = true; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + if ( renderTargetProperties.__hasExternalTextures ) { - ahole = holes[ h ]; + renderTargetProperties.__autoAllocateDepthBuffer = depthTexture === undefined; - oneHoleMovements = []; + if ( ! renderTargetProperties.__autoAllocateDepthBuffer ) { - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + // The multisample_render_to_texture extension doesn't work properly if there + // are midframe flushes and an external depth buffer. Disable use of the extension. + if ( extensions.has( 'WEBGL_multisampled_render_to_texture' ) === true ) { - if ( j === il ) j = 0; - if ( k === il ) k = 0; + console.warn( 'THREE.WebGLRenderer: Render-to-texture extension was disabled because an external texture was provided' ); + renderTargetProperties.__useRenderToTexture = false; - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + } } - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); - } + }; - // Loop bevelSegments, 1 for the front, 1 for the back - - for ( b = 0; b < bevelSegments; b ++ ) { - - //for ( b = bevelSegments; b > 0; b -- ) { + this.setRenderTargetFramebuffer = function ( renderTarget, defaultFramebuffer ) { - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + const renderTargetProperties = properties.get( renderTarget ); + renderTargetProperties.__webglFramebuffer = defaultFramebuffer; + renderTargetProperties.__useDefaultFramebuffer = defaultFramebuffer === undefined; - // contract shape + }; - for ( i = 0, il = contour.length; i < il; i ++ ) { + this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) { - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + _currentRenderTarget = renderTarget; + _currentActiveCubeFace = activeCubeFace; + _currentActiveMipmapLevel = activeMipmapLevel; - v( vert.x, vert.y, - z ); + let useDefaultFramebuffer = true; + let framebuffer = null; + let isCube = false; + let isRenderTarget3D = false; - } + if ( renderTarget ) { - // expand holes + const renderTargetProperties = properties.get( renderTarget ); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + if ( renderTargetProperties.__useDefaultFramebuffer !== undefined ) { - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + // We need to make sure to rebind the framebuffer. + state.bindFramebuffer( _gl.FRAMEBUFFER, null ); + useDefaultFramebuffer = false; - for ( i = 0, il = ahole.length; i < il; i ++ ) { + } else if ( renderTargetProperties.__webglFramebuffer === undefined ) { - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + textures.setupRenderTarget( renderTarget ); - v( vert.x, vert.y, - z ); + } else if ( renderTargetProperties.__hasExternalTextures ) { - } + // Color and depth texture must be rebound in order for the swapchain to update. + textures.rebindTextures( renderTarget, properties.get( renderTarget.texture ).__webglTexture, properties.get( renderTarget.depthTexture ).__webglTexture ); } - } + const texture = renderTarget.texture; - bs = bevelSize + bevelOffset; + if ( texture.isData3DTexture || texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { - // Back facing vertices + isRenderTarget3D = true; - for ( i = 0; i < vlen; i ++ ) { + } - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer; - if ( ! extrudeByPath ) { + if ( renderTarget.isWebGLCubeRenderTarget ) { - v( vert.x, vert.y, 0 ); + if ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) { - } else { + framebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ]; - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + } else { - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + framebuffer = __webglFramebuffer[ activeCubeFace ]; - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + } - v( position2.x, position2.y, position2.z ); + isCube = true; - } + } else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) { - } + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; - // Add stepped vertices... - // Including front facing vertices + } else { - var s; + if ( Array.isArray( __webglFramebuffer ) ) { - for ( s = 1; s <= steps; s ++ ) { + framebuffer = __webglFramebuffer[ activeMipmapLevel ]; - for ( i = 0; i < vlen; i ++ ) { + } else { - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + framebuffer = __webglFramebuffer; - if ( ! extrudeByPath ) { + } - v( vert.x, vert.y, depth / steps * s ); + } - } else { + _currentViewport.copy( renderTarget.viewport ); + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + } else { - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor(); + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor(); + _currentScissorTest = _scissorTest; - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + } - v( position2.x, position2.y, position2.z ); + const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - } + if ( framebufferBound && capabilities.drawBuffers && useDefaultFramebuffer ) { - } + state.drawBuffers( renderTarget, framebuffer ); } + state.viewport( _currentViewport ); + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); - // Add bevel segments planes + if ( isCube ) { - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { + const textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel ); - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + } else if ( isRenderTarget3D ) { - // contract shape + const textureProperties = properties.get( renderTarget.texture ); + const layer = activeCubeFace || 0; + _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer ); - for ( i = 0, il = contour.length; i < il; i ++ ) { + } - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, depth + z ); + _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings - } + }; - // expand holes + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) { + + if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + } - for ( i = 0, il = ahole.length; i < il; i ++ ) { + let framebuffer = properties.get( renderTarget ).__webglFramebuffer; - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) { - if ( ! extrudeByPath ) { + framebuffer = framebuffer[ activeCubeFaceIndex ]; - v( vert.x, vert.y, depth + z ); + } - } else { + if ( framebuffer ) { - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - } + try { + + const texture = renderTarget.texture; + const textureFormat = texture.format; + const textureType = texture.type; + + if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { + + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; } - } + const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) ); - } + if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513) + ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox + ! halfFloatSupportedByExt ) { - /* Faces */ + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - // Top and bottom faces + } - buildLidFaces(); + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - // Sides faces + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - buildSideFaces(); + _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer ); + } - ///// Internal functions + } finally { - function buildLidFaces() { + // restore framebuffer of current render target if necessary - var start = verticesArray.length / 3; + const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null; + state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - if ( bevelEnabled ) { + } - var layer = 0; // steps + 1 - var offset = vlen * layer; + } - // Bottom faces + }; - for ( i = 0; i < flen; i ++ ) { + this.copyFramebufferToTexture = function ( position, texture, level = 0 ) { - face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + const levelScale = Math.pow( 2, - level ); + const width = Math.floor( texture.image.width * levelScale ); + const height = Math.floor( texture.image.height * levelScale ); - } + textures.setTexture2D( texture, 0 ); - layer = steps + bevelSegments * 2; - offset = vlen * layer; + _gl.copyTexSubImage2D( _gl.TEXTURE_2D, level, 0, 0, position.x, position.y, width, height ); - // Top faces + state.unbindTexture(); - for ( i = 0; i < flen; i ++ ) { + }; - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) { - } + const width = srcTexture.image.width; + const height = srcTexture.image.height; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); - } else { + textures.setTexture2D( dstTexture, 0 ); - // Bottom faces + // 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 ); - for ( i = 0; i < flen; i ++ ) { + if ( srcTexture.isDataTexture ) { - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data ); - } + } else { - // Top faces + if ( srcTexture.isCompressedTexture ) { - for ( i = 0; i < flen; i ++ ) { + _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data ); - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + } else { - } + _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image ); } - scope.addGroup( start, verticesArray.length / 3 - start, 0 ); - } - // Create faces for the z-sides of the shape + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D ); - function buildSideFaces() { + state.unbindTexture(); - var start = verticesArray.length / 3; - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + }; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) { - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + if ( _this.isWebGL1Renderer ) { - //, true - layeroffset += ahole.length; + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' ); + return; - } + } + const width = sourceBox.max.x - sourceBox.min.x + 1; + const height = sourceBox.max.y - sourceBox.min.y + 1; + const depth = sourceBox.max.z - sourceBox.min.z + 1; + const glFormat = utils.convert( dstTexture.format ); + const glType = utils.convert( dstTexture.type ); + let glTarget; - scope.addGroup( start, verticesArray.length / 3 - start, 1 ); + if ( dstTexture.isData3DTexture ) { + textures.setTexture3D( dstTexture, 0 ); + glTarget = _gl.TEXTURE_3D; - } + } else if ( dstTexture.isDataArrayTexture ) { - function sidewalls( contour, layeroffset ) { + textures.setTexture2DArray( dstTexture, 0 ); + glTarget = _gl.TEXTURE_2D_ARRAY; - var j, k; - i = contour.length; + } else { - while ( -- i >= 0 ) { + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' ); + return; - j = i; - k = i - 1; - if ( k < 0 ) k = contour.length - 1; + } - //console.log('b', i,j, i-1, k,vertices.length); + _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 ); - var s = 0, - sl = steps + bevelSegments * 2; + const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH ); + const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT ); + const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS ); + const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS ); + const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES ); - for ( s = 0; s < sl; s ++ ) { + const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image; - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); + _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width ); + _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height ); + _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x ); + _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y ); + _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z ); - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + if ( srcTexture.isDataTexture || srcTexture.isData3DTexture ) { - f4( a, b, c, d ); + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data ); - } + } else { - } + if ( srcTexture.isCompressedArrayTexture ) { - } + console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' ); + _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data ); - function v( x, y, z ) { + } else { - placeholder.push( x ); - placeholder.push( y ); - placeholder.push( z ); + _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image ); + + } } + _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen ); + _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight ); + _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels ); + _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows ); + _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages ); - function f3( a, b, c ) { + // Generate mipmaps only when copying level 0 + if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget ); - addVertex( a ); - addVertex( b ); - addVertex( c ); + state.unbindTexture(); - var nextIndex = verticesArray.length / 3; - var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + }; - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); + this.initTexture = function ( texture ) { - } + if ( texture.isCubeTexture ) { - function f4( a, b, c, d ) { + textures.setTextureCube( texture, 0 ); - addVertex( a ); - addVertex( b ); - addVertex( d ); + } else if ( texture.isData3DTexture ) { - addVertex( b ); - addVertex( c ); - addVertex( d ); + textures.setTexture3D( texture, 0 ); + } else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) { - var nextIndex = verticesArray.length / 3; - var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + textures.setTexture2DArray( texture, 0 ); - addUV( uvs[ 0 ] ); - addUV( uvs[ 1 ] ); - addUV( uvs[ 3 ] ); + } else { - addUV( uvs[ 1 ] ); - addUV( uvs[ 2 ] ); - addUV( uvs[ 3 ] ); + textures.setTexture2D( texture, 0 ); } - function addVertex( index ) { + state.unbindTexture(); - verticesArray.push( placeholder[ index * 3 + 0 ] ); - verticesArray.push( placeholder[ index * 3 + 1 ] ); - verticesArray.push( placeholder[ index * 3 + 2 ] ); + }; - } + this.resetState = function () { + _currentActiveCubeFace = 0; + _currentActiveMipmapLevel = 0; + _currentRenderTarget = null; - function addUV( vector2 ) { + state.reset(); + bindingStates.reset(); - uvArray.push( vector2.x ); - uvArray.push( vector2.y ); + }; - } + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { + + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); } } - ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry; + get coordinateSystem() { - ExtrudeBufferGeometry.prototype.toJSON = function () { + return WebGLCoordinateSystem; - var data = BufferGeometry.prototype.toJSON.call( this ); + } - var shapes = this.parameters.shapes; - var options = this.parameters.options; + get outputColorSpace() { - return toJSON( shapes, options, data ); + return this._outputColorSpace; - }; + } - // + set outputColorSpace( colorSpace ) { - var WorldUVGenerator = { + this._outputColorSpace = colorSpace; - generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { + const gl = this.getContext(); + gl.drawingBufferColorSpace = colorSpace === DisplayP3ColorSpace ? 'display-p3' : 'srgb'; + gl.unpackColorSpace = ColorManagement.workingColorSpace === LinearDisplayP3ColorSpace ? 'display-p3' : 'srgb'; - var a_x = vertices[ indexA * 3 ]; - var a_y = vertices[ indexA * 3 + 1 ]; - var b_x = vertices[ indexB * 3 ]; - var b_y = vertices[ indexB * 3 + 1 ]; - var c_x = vertices[ indexC * 3 ]; - var c_y = vertices[ indexC * 3 + 1 ]; + } - return [ - new Vector2( a_x, a_y ), - new Vector2( b_x, b_y ), - new Vector2( c_x, c_y ) - ]; + get physicallyCorrectLights() { // @deprecated, r150 - }, + console.warn( 'THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); + return ! this.useLegacyLights; - generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { - - var a_x = vertices[ indexA * 3 ]; - var a_y = vertices[ indexA * 3 + 1 ]; - var a_z = vertices[ indexA * 3 + 2 ]; - var b_x = vertices[ indexB * 3 ]; - var b_y = vertices[ indexB * 3 + 1 ]; - var b_z = vertices[ indexB * 3 + 2 ]; - var c_x = vertices[ indexC * 3 ]; - var c_y = vertices[ indexC * 3 + 1 ]; - var c_z = vertices[ indexC * 3 + 2 ]; - var d_x = vertices[ indexD * 3 ]; - var d_y = vertices[ indexD * 3 + 1 ]; - var d_z = vertices[ indexD * 3 + 2 ]; - - if ( Math.abs( a_y - b_y ) < 0.01 ) { - - return [ - new Vector2( a_x, 1 - a_z ), - new Vector2( b_x, 1 - b_z ), - new Vector2( c_x, 1 - c_z ), - new Vector2( d_x, 1 - d_z ) - ]; + } - } else { + set physicallyCorrectLights( value ) { // @deprecated, r150 - return [ - new Vector2( a_y, 1 - a_z ), - new Vector2( b_y, 1 - b_z ), - new Vector2( c_y, 1 - c_z ), - new Vector2( d_y, 1 - d_z ) - ]; + console.warn( 'THREE.WebGLRenderer: The property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead.' ); + this.useLegacyLights = ! value; - } + } - } - }; + get outputEncoding() { // @deprecated, r152 - function toJSON( shapes, options, data ) { + console.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' ); + return this.outputColorSpace === SRGBColorSpace ? sRGBEncoding : LinearEncoding; - // + } - data.shapes = []; + set outputEncoding( encoding ) { // @deprecated, r152 - if ( Array.isArray( shapes ) ) { + console.warn( 'THREE.WebGLRenderer: Property .outputEncoding has been removed. Use .outputColorSpace instead.' ); + this.outputColorSpace = encoding === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace; - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + } - var shape = shapes[ i ]; + get useLegacyLights() { // @deprecated, r155 - data.shapes.push( shape.uuid ); + console.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' ); + return this._useLegacyLights; - } + } - } else { + set useLegacyLights( value ) { // @deprecated, r155 - data.shapes.push( shapes.uuid ); + console.warn( 'THREE.WebGLRenderer: The property .useLegacyLights has been deprecated. Migrate your lighting according to the following guide: https://discourse.threejs.org/t/updates-to-lighting-in-three-js-r155/53733.' ); + this._useLegacyLights = value; - } + } - // +} - if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); +class WebGL1Renderer extends WebGLRenderer {} - return data; +WebGL1Renderer.prototype.isWebGL1Renderer = true; - } +class FogExp2 { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * Text = 3D Text - * - * parameters = { - * font: , // font - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline (including bevelOffset) is bevel - * bevelOffset: // how far from text outline does bevel start - * } - */ + constructor( color, density = 0.00025 ) { - // TextGeometry + this.isFogExp2 = true; - function TextGeometry( text, parameters ) { + this.name = ''; - Geometry.call( this ); + this.color = new Color( color ); + this.density = density; - this.type = 'TextGeometry'; + } - this.parameters = { - text: text, - parameters: parameters - }; + clone() { - this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) ); - this.mergeVertices(); + return new FogExp2( this.color, this.density ); } - TextGeometry.prototype = Object.create( Geometry.prototype ); - TextGeometry.prototype.constructor = TextGeometry; + toJSON( /* meta */ ) { + + return { + type: 'FogExp2', + name: this.name, + color: this.color.getHex(), + density: this.density + }; - // TextBufferGeometry + } - function TextBufferGeometry( text, parameters ) { +} - parameters = parameters || {}; +class Fog { - var font = parameters.font; + constructor( color, near = 1, far = 1000 ) { - if ( ! ( font && font.isFont ) ) { + this.isFog = true; - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); + this.name = ''; - } + this.color = new Color( color ); - var shapes = font.generateShapes( text, parameters.size ); + this.near = near; + this.far = far; - // translate parameters to ExtrudeGeometry API + } - parameters.depth = parameters.height !== undefined ? parameters.height : 50; + clone() { - // defaults + return new Fog( this.color, this.near, this.far ); - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + } - ExtrudeBufferGeometry.call( this, shapes, parameters ); + toJSON( /* meta */ ) { - this.type = 'TextBufferGeometry'; + return { + type: 'Fog', + name: this.name, + color: this.color.getHex(), + near: this.near, + far: this.far + }; } - TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype ); - TextBufferGeometry.prototype.constructor = TextBufferGeometry; +} - /** - * @author mrdoob / http://mrdoob.com/ - * @author benaadams / https://twitter.com/ben_a_adams - * @author Mugen87 / https://github.com/Mugen87 - */ +class Scene extends Object3D { - // SphereGeometry + constructor() { - function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + super(); - Geometry.call( this ); + this.isScene = true; - this.type = 'SphereGeometry'; + this.type = 'Scene'; - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + this.background = null; + this.environment = null; + this.fog = null; - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - this.mergeVertices(); + this.backgroundBlurriness = 0; + this.backgroundIntensity = 1; - } + this.overrideMaterial = null; - SphereGeometry.prototype = Object.create( Geometry.prototype ); - SphereGeometry.prototype.constructor = SphereGeometry; + if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - // SphereBufferGeometry + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); - function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + } - BufferGeometry.call( this ); + } - this.type = 'SphereBufferGeometry'; + copy( source, recursive ) { - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + super.copy( source, recursive ); - radius = radius || 1; + if ( source.background !== null ) this.background = source.background.clone(); + if ( source.environment !== null ) this.environment = source.environment.clone(); + if ( source.fog !== null ) this.fog = source.fog.clone(); - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + this.backgroundBlurriness = source.backgroundBlurriness; + this.backgroundIntensity = source.backgroundIntensity; - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + this.matrixAutoUpdate = source.matrixAutoUpdate; - var thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); + return this; - var ix, iy; + } - var index = 0; - var grid = []; + toJSON( meta ) { - var vertex = new Vector3(); - var normal = new Vector3(); + const data = super.toJSON( meta ); - // buffers + if ( this.fog !== null ) data.object.fog = this.fog.toJSON(); + if ( this.backgroundBlurriness > 0 ) data.object.backgroundBlurriness = this.backgroundBlurriness; + if ( this.backgroundIntensity !== 1 ) data.object.backgroundIntensity = this.backgroundIntensity; - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + return data; - // generate vertices, normals and uvs + } - for ( iy = 0; iy <= heightSegments; iy ++ ) { +} - var verticesRow = []; +class InterleavedBuffer { - var v = iy / heightSegments; + constructor( array, stride ) { - // special case for the poles + this.isInterleavedBuffer = true; - var uOffset = 0; + this.array = array; + this.stride = stride; + this.count = array !== undefined ? array.length / stride : 0; - if ( iy == 0 && thetaStart == 0 ) { + this.usage = StaticDrawUsage; + this.updateRange = { offset: 0, count: - 1 }; - uOffset = 0.5 / widthSegments; + this.version = 0; - } else if ( iy == heightSegments && thetaEnd == Math.PI ) { + this.uuid = generateUUID(); - uOffset = - 0.5 / widthSegments; + } - } + onUploadCallback() {} - for ( ix = 0; ix <= widthSegments; ix ++ ) { + set needsUpdate( value ) { - var u = ix / widthSegments; + if ( value === true ) this.version ++; - // vertex - - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + } - vertices.push( vertex.x, vertex.y, vertex.z ); + setUsage( value ) { - // normal + this.usage = value; - normal.copy( vertex ).normalize(); - normals.push( normal.x, normal.y, normal.z ); + return this; - // uv + } - uvs.push( u + uOffset, 1 - v ); + copy( source ) { - verticesRow.push( index ++ ); + this.array = new source.array.constructor( source.array ); + this.count = source.count; + this.stride = source.stride; + this.usage = source.usage; - } + return this; - grid.push( verticesRow ); + } - } + copyAt( index1, attribute, index2 ) { - // indices + index1 *= this.stride; + index2 *= attribute.stride; - for ( iy = 0; iy < heightSegments; iy ++ ) { + for ( let i = 0, l = this.stride; i < l; i ++ ) { - for ( ix = 0; ix < widthSegments; ix ++ ) { + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - var a = grid[ iy ][ ix + 1 ]; - var b = grid[ iy ][ ix ]; - var c = grid[ iy + 1 ][ ix ]; - var d = grid[ iy + 1 ][ ix + 1 ]; + } - if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); - if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); + return this; - } + } - } + set( value, offset = 0 ) { - // build geometry + this.array.set( value, offset ); - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + return this; } - SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; - - /** - * @author Kaleb Murphy - * @author Mugen87 / https://github.com/Mugen87 - */ - - // RingGeometry + clone( data ) { - function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + if ( data.arrayBuffers === undefined ) { - Geometry.call( this ); + data.arrayBuffers = {}; - this.type = 'RingGeometry'; + } - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + if ( this.array.buffer._uuid === undefined ) { - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - this.mergeVertices(); + this.array.buffer._uuid = generateUUID(); - } + } - RingGeometry.prototype = Object.create( Geometry.prototype ); - RingGeometry.prototype.constructor = RingGeometry; + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - // RingBufferGeometry + data.arrayBuffers[ this.array.buffer._uuid ] = this.array.slice( 0 ).buffer; - function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + } - BufferGeometry.call( this ); + const array = new this.array.constructor( data.arrayBuffers[ this.array.buffer._uuid ] ); - this.type = 'RingBufferGeometry'; + const ib = new this.constructor( array, this.stride ); + ib.setUsage( this.usage ); - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + return ib; - innerRadius = innerRadius || 0.5; - outerRadius = outerRadius || 1; + } - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + onUpload( callback ) { - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; + this.onUploadCallback = callback; - // buffers + return this; - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + } - // some helper variables + toJSON( data ) { - var segment; - var radius = innerRadius; - var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - var vertex = new Vector3(); - var uv = new Vector2(); - var j, i; + if ( data.arrayBuffers === undefined ) { - // generate vertices, normals and uvs + data.arrayBuffers = {}; - for ( j = 0; j <= phiSegments; j ++ ) { + } - for ( i = 0; i <= thetaSegments; i ++ ) { + // generate UUID for array buffer if necessary - // values are generate from the inside of the ring to the outside + if ( this.array.buffer._uuid === undefined ) { - segment = thetaStart + i / thetaSegments * thetaLength; + this.array.buffer._uuid = generateUUID(); - // vertex + } - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + if ( data.arrayBuffers[ this.array.buffer._uuid ] === undefined ) { - vertices.push( vertex.x, vertex.y, vertex.z ); + data.arrayBuffers[ this.array.buffer._uuid ] = Array.from( new Uint32Array( this.array.buffer ) ); - // normal + } - normals.push( 0, 0, 1 ); + // - // uv + return { + uuid: this.uuid, + buffer: this.array.buffer._uuid, + type: this.array.constructor.name, + stride: this.stride + }; - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; + } - uvs.push( uv.x, uv.y ); +} - } +const _vector$5 = /*@__PURE__*/ new Vector3(); - // increase the radius for next row of vertices +class InterleavedBufferAttribute { - radius += radiusStep; + constructor( interleavedBuffer, itemSize, offset, normalized = false ) { - } + this.isInterleavedBufferAttribute = true; - // indices + this.name = ''; - for ( j = 0; j < phiSegments; j ++ ) { + this.data = interleavedBuffer; + this.itemSize = itemSize; + this.offset = offset; - var thetaSegmentLevel = j * ( thetaSegments + 1 ); + this.normalized = normalized; - for ( i = 0; i < thetaSegments; i ++ ) { + } - segment = i + thetaSegmentLevel; + get count() { - var a = segment; - var b = segment + thetaSegments + 1; - var c = segment + thetaSegments + 2; - var d = segment + 1; + return this.data.count; - // faces + } - indices.push( a, b, d ); - indices.push( b, c, d ); + get array() { - } + return this.data.array; - } + } - // build geometry + set needsUpdate( value ) { - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + this.data.needsUpdate = value; } - RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - RingBufferGeometry.prototype.constructor = RingBufferGeometry; - - /** - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://clara.io - * @author Mugen87 / https://github.com/Mugen87 - */ + applyMatrix4( m ) { - // LatheGeometry + for ( let i = 0, l = this.data.count; i < l; i ++ ) { - function LatheGeometry( points, segments, phiStart, phiLength ) { + _vector$5.fromBufferAttribute( this, i ); - Geometry.call( this ); + _vector$5.applyMatrix4( m ); - this.type = 'LatheGeometry'; + this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; + } - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); + return this; } - LatheGeometry.prototype = Object.create( Geometry.prototype ); - LatheGeometry.prototype.constructor = LatheGeometry; + applyNormalMatrix( m ) { - // LatheBufferGeometry + for ( let i = 0, l = this.count; i < l; i ++ ) { - function LatheBufferGeometry( points, segments, phiStart, phiLength ) { + _vector$5.fromBufferAttribute( this, i ); - BufferGeometry.call( this ); + _vector$5.applyNormalMatrix( m ); - this.type = 'LatheBufferGeometry'; + this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; + } - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; + return this; - // clamp phiLength so it's in range of [ 0, 2PI ] + } - phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 ); + transformDirection( m ) { + for ( let i = 0, l = this.count; i < l; i ++ ) { - // buffers + _vector$5.fromBufferAttribute( this, i ); - var indices = []; - var vertices = []; - var uvs = []; + _vector$5.transformDirection( m ); - // helper variables + this.setXYZ( i, _vector$5.x, _vector$5.y, _vector$5.z ); - var base; - var inverseSegments = 1.0 / segments; - var vertex = new Vector3(); - var uv = new Vector2(); - var i, j; + } - // generate vertices and uvs + return this; - for ( i = 0; i <= segments; i ++ ) { + } - var phi = phiStart + i * inverseSegments * phiLength; + setX( index, x ) { - var sin = Math.sin( phi ); - var cos = Math.cos( phi ); + if ( this.normalized ) x = normalize( x, this.array ); - for ( j = 0; j <= ( points.length - 1 ); j ++ ) { + this.data.array[ index * this.data.stride + this.offset ] = x; - // vertex + return this; - vertex.x = points[ j ].x * sin; - vertex.y = points[ j ].y; - vertex.z = points[ j ].x * cos; + } - vertices.push( vertex.x, vertex.y, vertex.z ); + setY( index, y ) { - // uv + if ( this.normalized ) y = normalize( y, this.array ); - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); + this.data.array[ index * this.data.stride + this.offset + 1 ] = y; - uvs.push( uv.x, uv.y ); + return this; + } - } + setZ( index, z ) { - } + if ( this.normalized ) z = normalize( z, this.array ); - // indices + this.data.array[ index * this.data.stride + this.offset + 2 ] = z; - for ( i = 0; i < segments; i ++ ) { + return this; - for ( j = 0; j < ( points.length - 1 ); j ++ ) { + } - base = j + i * points.length; + setW( index, w ) { - var a = base; - var b = base + points.length; - var c = base + points.length + 1; - var d = base + 1; + if ( this.normalized ) w = normalize( w, this.array ); - // faces + this.data.array[ index * this.data.stride + this.offset + 3 ] = w; - indices.push( a, b, d ); - indices.push( b, c, d ); + return this; - } + } - } + getX( index ) { - // build geometry + let x = this.data.array[ index * this.data.stride + this.offset ]; - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + if ( this.normalized ) x = denormalize( x, this.array ); - // generate normals + return x; - this.computeVertexNormals(); + } - // if the geometry is closed, we need to average the normals along the seam. - // because the corresponding vertices are identical (but still have different UVs). + getY( index ) { - if ( phiLength === Math.PI * 2 ) { + let y = this.data.array[ index * this.data.stride + this.offset + 1 ]; - var normals = this.attributes.normal.array; - var n1 = new Vector3(); - var n2 = new Vector3(); - var n = new Vector3(); + if ( this.normalized ) y = denormalize( y, this.array ); - // this is the buffer offset for the last line of vertices + return y; - base = segments * points.length * 3; + } - for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) { + getZ( index ) { - // select the normal of the vertex in the first line + let z = this.data.array[ index * this.data.stride + this.offset + 2 ]; - n1.x = normals[ j + 0 ]; - n1.y = normals[ j + 1 ]; - n1.z = normals[ j + 2 ]; + if ( this.normalized ) z = denormalize( z, this.array ); - // select the normal of the vertex in the last line + return z; - n2.x = normals[ base + j + 0 ]; - n2.y = normals[ base + j + 1 ]; - n2.z = normals[ base + j + 2 ]; + } - // average normals + getW( index ) { - n.addVectors( n1, n2 ).normalize(); + let w = this.data.array[ index * this.data.stride + this.offset + 3 ]; - // assign the new values to both normals + if ( this.normalized ) w = denormalize( w, this.array ); - normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; - normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; - normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; + return w; - } + } - } + setXY( index, x, y ) { - } + index = index * this.data.stride + this.offset; - LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; + if ( this.normalized ) { - /** - * @author jonobr1 / http://jonobr1.com - * @author Mugen87 / https://github.com/Mugen87 - */ + x = normalize( x, this.array ); + y = normalize( y, this.array ); + + } - // ShapeGeometry + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; - function ShapeGeometry( shapes, curveSegments ) { + return this; - Geometry.call( this ); + } - this.type = 'ShapeGeometry'; + setXYZ( index, x, y, z ) { - if ( typeof curveSegments === 'object' ) { + index = index * this.data.stride + this.offset; - console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' ); + if ( this.normalized ) { - curveSegments = curveSegments.curveSegments; + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); } - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; - this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) ); - this.mergeVertices(); + return this; } - ShapeGeometry.prototype = Object.create( Geometry.prototype ); - ShapeGeometry.prototype.constructor = ShapeGeometry; + setXYZW( index, x, y, z, w ) { - ShapeGeometry.prototype.toJSON = function () { + index = index * this.data.stride + this.offset; - var data = Geometry.prototype.toJSON.call( this ); + if ( this.normalized ) { - var shapes = this.parameters.shapes; + x = normalize( x, this.array ); + y = normalize( y, this.array ); + z = normalize( z, this.array ); + w = normalize( w, this.array ); - return toJSON$1( shapes, data ); + } - }; + this.data.array[ index + 0 ] = x; + this.data.array[ index + 1 ] = y; + this.data.array[ index + 2 ] = z; + this.data.array[ index + 3 ] = w; - // ShapeBufferGeometry + return this; - function ShapeBufferGeometry( shapes, curveSegments ) { + } - BufferGeometry.call( this ); + clone( data ) { - this.type = 'ShapeBufferGeometry'; + if ( data === undefined ) { - this.parameters = { - shapes: shapes, - curveSegments: curveSegments - }; + console.log( 'THREE.InterleavedBufferAttribute.clone(): Cloning an interleaved buffer attribute will de-interleave buffer data.' ); - curveSegments = curveSegments || 12; + const array = []; - // buffers + for ( let i = 0; i < this.count; i ++ ) { - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + const index = i * this.data.stride + this.offset; - // helper variables + for ( let j = 0; j < this.itemSize; j ++ ) { - var groupStart = 0; - var groupCount = 0; + array.push( this.data.array[ index + j ] ); - // allow single and array values for "shapes" parameter + } - if ( Array.isArray( shapes ) === false ) { + } - addShape( shapes ); + return new BufferAttribute( new this.array.constructor( array ), this.itemSize, this.normalized ); } else { - for ( var i = 0; i < shapes.length; i ++ ) { + if ( data.interleavedBuffers === undefined ) { - addShape( shapes[ i ] ); + data.interleavedBuffers = {}; - this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support + } - groupStart += groupCount; - groupCount = 0; + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { + + data.interleavedBuffers[ this.data.uuid ] = this.data.clone( data ); } - } + return new InterleavedBufferAttribute( data.interleavedBuffers[ this.data.uuid ], this.itemSize, this.offset, this.normalized ); - // build geometry + } - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + } + toJSON( data ) { - // helper functions + if ( data === undefined ) { - function addShape( shape ) { + console.log( 'THREE.InterleavedBufferAttribute.toJSON(): Serializing an interleaved buffer attribute will de-interleave buffer data.' ); - var i, l, shapeHole; + const array = []; - var indexOffset = vertices.length / 3; - var points = shape.extractPoints( curveSegments ); + for ( let i = 0; i < this.count; i ++ ) { - var shapeVertices = points.shape; - var shapeHoles = points.holes; + const index = i * this.data.stride + this.offset; - // check direction of vertices + for ( let j = 0; j < this.itemSize; j ++ ) { - if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { + array.push( this.data.array[ index + j ] ); - shapeVertices = shapeVertices.reverse(); + } } - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { + // de-interleave data and save it as an ordinary buffer attribute for now + + return { + itemSize: this.itemSize, + type: this.array.constructor.name, + array: array, + normalized: this.normalized + }; - shapeHole = shapeHoles[ i ]; + } else { - if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + // save as true interleaved attribute - shapeHoles[ i ] = shapeHole.reverse(); + if ( data.interleavedBuffers === undefined ) { - } + data.interleavedBuffers = {}; } - var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); + if ( data.interleavedBuffers[ this.data.uuid ] === undefined ) { - // join vertices of inner and outer paths to a single array + data.interleavedBuffers[ this.data.uuid ] = this.data.toJSON( data ); - for ( i = 0, l = shapeHoles.length; i < l; i ++ ) { + } - shapeHole = shapeHoles[ i ]; - shapeVertices = shapeVertices.concat( shapeHole ); + return { + isInterleavedBufferAttribute: true, + itemSize: this.itemSize, + data: this.data.uuid, + offset: this.offset, + normalized: this.normalized + }; - } + } - // vertices, normals, uvs + } - for ( i = 0, l = shapeVertices.length; i < l; i ++ ) { +} - var vertex = shapeVertices[ i ]; +class SpriteMaterial extends Material { - vertices.push( vertex.x, vertex.y, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( vertex.x, vertex.y ); // world uvs + constructor( parameters ) { - } + super(); - // incides + this.isSpriteMaterial = true; - for ( i = 0, l = faces.length; i < l; i ++ ) { + this.type = 'SpriteMaterial'; - var face = faces[ i ]; + this.color = new Color( 0xffffff ); - var a = face[ 0 ] + indexOffset; - var b = face[ 1 ] + indexOffset; - var c = face[ 2 ] + indexOffset; + this.map = null; - indices.push( a, b, c ); - groupCount += 3; + this.alphaMap = null; - } + this.rotation = 0; - } + this.sizeAttenuation = true; - } + this.transparent = true; - ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry; + this.fog = true; - ShapeBufferGeometry.prototype.toJSON = function () { + this.setValues( parameters ); - var data = BufferGeometry.prototype.toJSON.call( this ); + } - var shapes = this.parameters.shapes; + copy( source ) { - return toJSON$1( shapes, data ); + super.copy( source ); - }; + this.color.copy( source.color ); - // + this.map = source.map; - function toJSON$1( shapes, data ) { + this.alphaMap = source.alphaMap; - data.shapes = []; + this.rotation = source.rotation; - if ( Array.isArray( shapes ) ) { + this.sizeAttenuation = source.sizeAttenuation; - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + this.fog = source.fog; - var shape = shapes[ i ]; + return this; - data.shapes.push( shape.uuid ); + } - } +} - } else { +let _geometry; - data.shapes.push( shapes.uuid ); +const _intersectPoint = /*@__PURE__*/ new Vector3(); +const _worldScale = /*@__PURE__*/ new Vector3(); +const _mvPosition = /*@__PURE__*/ new Vector3(); - } +const _alignedPosition = /*@__PURE__*/ new Vector2(); +const _rotatedPosition = /*@__PURE__*/ new Vector2(); +const _viewWorldMatrix = /*@__PURE__*/ new Matrix4(); - return data; - - } - - /** - * @author WestLangley / http://github.com/WestLangley - * @author Mugen87 / https://github.com/Mugen87 - */ - - function EdgesGeometry( geometry, thresholdAngle ) { - - BufferGeometry.call( this ); +const _vA = /*@__PURE__*/ new Vector3(); +const _vB = /*@__PURE__*/ new Vector3(); +const _vC = /*@__PURE__*/ new Vector3(); - this.type = 'EdgesGeometry'; +const _uvA = /*@__PURE__*/ new Vector2(); +const _uvB = /*@__PURE__*/ new Vector2(); +const _uvC = /*@__PURE__*/ new Vector2(); - this.parameters = { - thresholdAngle: thresholdAngle - }; +class Sprite extends Object3D { - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; + constructor( material = new SpriteMaterial() ) { - // buffer + super(); - var vertices = []; + this.isSprite = true; - // helper variables + this.type = 'Sprite'; - var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle ); - var edge = [ 0, 0 ], edges = {}, edge1, edge2; - var key, keys = [ 'a', 'b', 'c' ]; + if ( _geometry === undefined ) { - // prepare source geometry + _geometry = new BufferGeometry(); - var geometry2; + const float32Array = new Float32Array( [ + - 0.5, - 0.5, 0, 0, 0, + 0.5, - 0.5, 0, 1, 0, + 0.5, 0.5, 0, 1, 1, + - 0.5, 0.5, 0, 0, 1 + ] ); - if ( geometry.isBufferGeometry ) { + const interleavedBuffer = new InterleavedBuffer( float32Array, 5 ); - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); + _geometry.setIndex( [ 0, 1, 2, 0, 2, 3 ] ); + _geometry.setAttribute( 'position', new InterleavedBufferAttribute( interleavedBuffer, 3, 0, false ) ); + _geometry.setAttribute( 'uv', new InterleavedBufferAttribute( interleavedBuffer, 2, 3, false ) ); - } else { + } - geometry2 = geometry.clone(); + this.geometry = _geometry; + this.material = material; - } + this.center = new Vector2( 0.5, 0.5 ); - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); + } - var sourceVertices = geometry2.vertices; - var faces = geometry2.faces; + raycast( raycaster, intersects ) { - // now create a data structure where each entry represents an edge with its adjoining faces + if ( raycaster.camera === null ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + console.error( 'THREE.Sprite: "Raycaster.camera" needs to be set in order to raycast against sprites.' ); - var face = faces[ i ]; + } - for ( var j = 0; j < 3; j ++ ) { + _worldScale.setFromMatrixScale( this.matrixWorld ); - edge1 = face[ keys[ j ] ]; - edge2 = face[ keys[ ( j + 1 ) % 3 ] ]; - edge[ 0 ] = Math.min( edge1, edge2 ); - edge[ 1 ] = Math.max( edge1, edge2 ); + _viewWorldMatrix.copy( raycaster.camera.matrixWorld ); + this.modelViewMatrix.multiplyMatrices( raycaster.camera.matrixWorldInverse, this.matrixWorld ); - key = edge[ 0 ] + ',' + edge[ 1 ]; + _mvPosition.setFromMatrixPosition( this.modelViewMatrix ); - if ( edges[ key ] === undefined ) { + if ( raycaster.camera.isPerspectiveCamera && this.material.sizeAttenuation === false ) { - edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined }; + _worldScale.multiplyScalar( - _mvPosition.z ); - } else { + } - edges[ key ].face2 = i; + const rotation = this.material.rotation; + let sin, cos; - } + if ( rotation !== 0 ) { - } + cos = Math.cos( rotation ); + sin = Math.sin( rotation ); } - // generate vertices + const center = this.center; - for ( key in edges ) { + transformVertex( _vA.set( - 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vB.set( 0.5, - 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + transformVertex( _vC.set( 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); - var e = edges[ key ]; + _uvA.set( 0, 0 ); + _uvB.set( 1, 0 ); + _uvC.set( 1, 1 ); - // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree. + // check first triangle + let intersect = raycaster.ray.intersectTriangle( _vA, _vB, _vC, false, _intersectPoint ); - if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) { + if ( intersect === null ) { - var vertex = sourceVertices[ e.index1 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + // check second triangle + transformVertex( _vB.set( - 0.5, 0.5, 0 ), _mvPosition, center, _worldScale, sin, cos ); + _uvB.set( 0, 1 ); - vertex = sourceVertices[ e.index2 ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + intersect = raycaster.ray.intersectTriangle( _vA, _vC, _vB, false, _intersectPoint ); + if ( intersect === null ) { + + return; } } - // build geometry + const distance = raycaster.ray.origin.distanceTo( _intersectPoint ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + if ( distance < raycaster.near || distance > raycaster.far ) return; - } + intersects.push( { - EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); - EdgesGeometry.prototype.constructor = EdgesGeometry; + distance: distance, + point: _intersectPoint.clone(), + uv: Triangle.getInterpolation( _intersectPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() ), + face: null, + object: this - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ + } ); - // CylinderGeometry + } - function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + copy( source, recursive ) { - Geometry.call( this ); + super.copy( source, recursive ); - this.type = 'CylinderGeometry'; + if ( source.center !== undefined ) this.center.copy( source.center ); - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + this.material = source.material; - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); + return this; } - CylinderGeometry.prototype = Object.create( Geometry.prototype ); - CylinderGeometry.prototype.constructor = CylinderGeometry; +} - // CylinderBufferGeometry +function transformVertex( vertexPosition, mvPosition, center, scale, sin, cos ) { - function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + // compute position in camera space + _alignedPosition.subVectors( vertexPosition, center ).addScalar( 0.5 ).multiply( scale ); - BufferGeometry.call( this ); + // to check if rotation is not zero + if ( sin !== undefined ) { - this.type = 'CylinderBufferGeometry'; + _rotatedPosition.x = ( cos * _alignedPosition.x ) - ( sin * _alignedPosition.y ); + _rotatedPosition.y = ( sin * _alignedPosition.x ) + ( cos * _alignedPosition.y ); - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } else { - var scope = this; + _rotatedPosition.copy( _alignedPosition ); - radiusTop = radiusTop !== undefined ? radiusTop : 1; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 1; - height = height || 1; + } - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; - openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0.0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + vertexPosition.copy( mvPosition ); + vertexPosition.x += _rotatedPosition.x; + vertexPosition.y += _rotatedPosition.y; - // buffers + // transform to world space + vertexPosition.applyMatrix4( _viewWorldMatrix ); - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; +} - // helper variables +const _v1$2 = /*@__PURE__*/ new Vector3(); +const _v2$1 = /*@__PURE__*/ new Vector3(); - var index = 0; - var indexArray = []; - var halfHeight = height / 2; - var groupStart = 0; +class LOD extends Object3D { - // generate geometry + constructor() { - generateTorso(); + super(); - if ( openEnded === false ) { + this._currentLevel = 0; - if ( radiusTop > 0 ) generateCap( true ); - if ( radiusBottom > 0 ) generateCap( false ); + this.type = 'LOD'; - } + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + }, + isLOD: { + value: true, + } + } ); - // build geometry + this.autoUpdate = true; - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + } - function generateTorso() { + copy( source ) { - var x, y; - var normal = new Vector3(); - var vertex = new Vector3(); + super.copy( source, false ); - var groupCount = 0; + const levels = source.levels; - // this will be used to calculate the normal - var slope = ( radiusBottom - radiusTop ) / height; + for ( let i = 0, l = levels.length; i < l; i ++ ) { - // generate vertices, normals and uvs + const level = levels[ i ]; - for ( y = 0; y <= heightSegments; y ++ ) { + this.addLevel( level.object.clone(), level.distance, level.hysteresis ); - var indexRow = []; + } - var v = y / heightSegments; + this.autoUpdate = source.autoUpdate; - // calculate the radius of the current row + return this; - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + } - for ( x = 0; x <= radialSegments; x ++ ) { + addLevel( object, distance = 0, hysteresis = 0 ) { - var u = x / radialSegments; + distance = Math.abs( distance ); - var theta = u * thetaLength + thetaStart; + const levels = this.levels; - var sinTheta = Math.sin( theta ); - var cosTheta = Math.cos( theta ); + let l; - // vertex + for ( l = 0; l < levels.length; l ++ ) { - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); + if ( distance < levels[ l ].distance ) { - // normal + break; - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.push( normal.x, normal.y, normal.z ); + } - // uv + } - uvs.push( u, 1 - v ); + levels.splice( l, 0, { distance: distance, hysteresis: hysteresis, object: object } ); - // save index of vertex in respective row + this.add( object ); - indexRow.push( index ++ ); + return this; - } + } - // now save vertices of the row in our index array + getCurrentLevel() { - indexArray.push( indexRow ); + return this._currentLevel; - } + } - // generate indices - for ( x = 0; x < radialSegments; x ++ ) { - for ( y = 0; y < heightSegments; y ++ ) { + getObjectForDistance( distance ) { - // we use the index array to access the correct indices + const levels = this.levels; - var a = indexArray[ y ][ x ]; - var b = indexArray[ y + 1 ][ x ]; - var c = indexArray[ y + 1 ][ x + 1 ]; - var d = indexArray[ y ][ x + 1 ]; + if ( levels.length > 0 ) { - // faces + let i, l; - indices.push( a, b, d ); - indices.push( b, c, d ); + for ( i = 1, l = levels.length; i < l; i ++ ) { - // update group counter + let levelDistance = levels[ i ].distance; - groupCount += 6; + if ( levels[ i ].object.visible ) { + + levelDistance -= levelDistance * levels[ i ].hysteresis; } - } + if ( distance < levelDistance ) { - // add a group to the geometry. this will ensure multi material support + break; - scope.addGroup( groupStart, groupCount, 0 ); + } - // calculate new start value for groups + } - groupStart += groupCount; + return levels[ i - 1 ].object; } - function generateCap( top ) { + return null; - var x, centerIndexStart, centerIndexEnd; + } - var uv = new Vector2(); - var vertex = new Vector3(); + raycast( raycaster, intersects ) { - var groupCount = 0; + const levels = this.levels; - var radius = ( top === true ) ? radiusTop : radiusBottom; - var sign = ( top === true ) ? 1 : - 1; + if ( levels.length > 0 ) { - // save the index of the first center vertex - centerIndexStart = index; + _v1$2.setFromMatrixPosition( this.matrixWorld ); - // first we generate the center vertex data of the cap. - // because the geometry needs one set of uvs per face, - // we must generate a center vertex per face/segment + const distance = raycaster.ray.origin.distanceTo( _v1$2 ); - for ( x = 1; x <= radialSegments; x ++ ) { + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - // vertex + } - vertices.push( 0, halfHeight * sign, 0 ); + } - // normal + update( camera ) { - normals.push( 0, sign, 0 ); + const levels = this.levels; - // uv + if ( levels.length > 1 ) { - uvs.push( 0.5, 0.5 ); + _v1$2.setFromMatrixPosition( camera.matrixWorld ); + _v2$1.setFromMatrixPosition( this.matrixWorld ); - // increase index + const distance = _v1$2.distanceTo( _v2$1 ) / camera.zoom; - index ++; + levels[ 0 ].object.visible = true; - } + let i, l; - // save the index of the last center vertex + for ( i = 1, l = levels.length; i < l; i ++ ) { - centerIndexEnd = index; + let levelDistance = levels[ i ].distance; - // now we generate the surrounding vertices, normals and uvs + if ( levels[ i ].object.visible ) { - for ( x = 0; x <= radialSegments; x ++ ) { + levelDistance -= levelDistance * levels[ i ].hysteresis; - var u = x / radialSegments; - var theta = u * thetaLength + thetaStart; + } - var cosTheta = Math.cos( theta ); - var sinTheta = Math.sin( theta ); + if ( distance >= levelDistance ) { - // vertex + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.push( vertex.x, vertex.y, vertex.z ); + } else { - // normal + break; - normals.push( 0, sign, 0 ); + } - // uv + } - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.push( uv.x, uv.y ); + this._currentLevel = i - 1; - // increase index + for ( ; i < l; i ++ ) { - index ++; + levels[ i ].object.visible = false; } - // generate indices + } - for ( x = 0; x < radialSegments; x ++ ) { + } - var c = centerIndexStart + x; - var i = centerIndexEnd + x; + toJSON( meta ) { - if ( top === true ) { + const data = super.toJSON( meta ); - // face top + if ( this.autoUpdate === false ) data.object.autoUpdate = false; - indices.push( i, i + 1, c ); + data.object.levels = []; - } else { + const levels = this.levels; - // face bottom + for ( let i = 0, l = levels.length; i < l; i ++ ) { - indices.push( i + 1, i, c ); + const level = levels[ i ]; - } + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance, + hysteresis: level.hysteresis + } ); - groupCount += 3; + } - } + return data; - // add a group to the geometry. this will ensure multi material support + } - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); +} - // calculate new start value for groups +const _basePosition = /*@__PURE__*/ new Vector3(); - groupStart += groupCount; +const _skinIndex = /*@__PURE__*/ new Vector4(); +const _skinWeight = /*@__PURE__*/ new Vector4(); - } +const _vector3 = /*@__PURE__*/ new Vector3(); +const _matrix4 = /*@__PURE__*/ new Matrix4(); +const _vertex = /*@__PURE__*/ new Vector3(); - } +const _sphere$3 = /*@__PURE__*/ new Sphere(); +const _inverseMatrix$2 = /*@__PURE__*/ new Matrix4(); +const _ray$2 = /*@__PURE__*/ new Ray(); - CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; +class SkinnedMesh extends Mesh { - /** - * @author abelnation / http://github.com/abelnation - */ + constructor( geometry, material ) { - // ConeGeometry + super( geometry, material ); - function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + this.isSkinnedMesh = true; - CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); + this.type = 'SkinnedMesh'; - this.type = 'ConeGeometry'; + this.bindMode = 'attached'; + this.bindMatrix = new Matrix4(); + this.bindMatrixInverse = new Matrix4(); - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + this.boundingBox = null; + this.boundingSphere = null; } - ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); - ConeGeometry.prototype.constructor = ConeGeometry; + computeBoundingBox() { - // ConeBufferGeometry + const geometry = this.geometry; - function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + if ( this.boundingBox === null ) { - CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); + this.boundingBox = new Box3(); - this.type = 'ConeBufferGeometry'; + } - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + this.boundingBox.makeEmpty(); + + const positionAttribute = geometry.getAttribute( 'position' ); + + for ( let i = 0; i < positionAttribute.count; i ++ ) { + + _vertex.fromBufferAttribute( positionAttribute, i ); + this.applyBoneTransform( i, _vertex ); + this.boundingBox.expandByPoint( _vertex ); + + } } - ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype ); - ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; + computeBoundingSphere() { - /** - * @author benaadams / https://twitter.com/ben_a_adams - * @author Mugen87 / https://github.com/Mugen87 - * @author hughes - */ + const geometry = this.geometry; + + if ( this.boundingSphere === null ) { - // CircleGeometry + this.boundingSphere = new Sphere(); - function CircleGeometry( radius, segments, thetaStart, thetaLength ) { + } - Geometry.call( this ); + this.boundingSphere.makeEmpty(); - this.type = 'CircleGeometry'; + const positionAttribute = geometry.getAttribute( 'position' ); - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + for ( let i = 0; i < positionAttribute.count; i ++ ) { - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - this.mergeVertices(); + _vertex.fromBufferAttribute( positionAttribute, i ); + this.applyBoneTransform( i, _vertex ); + this.boundingSphere.expandByPoint( _vertex ); + + } } - CircleGeometry.prototype = Object.create( Geometry.prototype ); - CircleGeometry.prototype.constructor = CircleGeometry; + copy( source, recursive ) { - // CircleBufferGeometry + super.copy( source, recursive ); - function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { + this.bindMode = source.bindMode; + this.bindMatrix.copy( source.bindMatrix ); + this.bindMatrixInverse.copy( source.bindMatrixInverse ); - BufferGeometry.call( this ); + this.skeleton = source.skeleton; - this.type = 'CircleBufferGeometry'; + if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); + if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + return this; - radius = radius || 1; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; + } - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + raycast( raycaster, intersects ) { - // buffers + const material = this.material; + const matrixWorld = this.matrixWorld; - var indices = []; - var vertices = []; - var normals = []; - var uvs = []; + if ( material === undefined ) return; - // helper variables + // test with bounding sphere in world space - var i, s; - var vertex = new Vector3(); - var uv = new Vector2(); + if ( this.boundingSphere === null ) this.computeBoundingSphere(); - // center point + _sphere$3.copy( this.boundingSphere ); + _sphere$3.applyMatrix4( matrixWorld ); - vertices.push( 0, 0, 0 ); - normals.push( 0, 0, 1 ); - uvs.push( 0.5, 0.5 ); + if ( raycaster.ray.intersectsSphere( _sphere$3 ) === false ) return; - for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) { + // convert ray to local space of skinned mesh - var segment = thetaStart + s / segments * thetaLength; + _inverseMatrix$2.copy( matrixWorld ).invert(); + _ray$2.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$2 ); - // vertex + // test with bounding box in local space - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + if ( this.boundingBox !== null ) { - vertices.push( vertex.x, vertex.y, vertex.z ); + if ( _ray$2.intersectsBox( this.boundingBox ) === false ) return; - // normal + } - normals.push( 0, 0, 1 ); + // test for intersections with geometry - // uvs + this._computeIntersections( raycaster, intersects, _ray$2 ); - uv.x = ( vertices[ i ] / radius + 1 ) / 2; - uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; + } - uvs.push( uv.x, uv.y ); + getVertexPosition( index, target ) { - } + super.getVertexPosition( index, target ); - // indices + this.applyBoneTransform( index, target ); - for ( i = 1; i <= segments; i ++ ) { + return target; - indices.push( i, i + 1, 0 ); + } - } + bind( skeleton, bindMatrix ) { - // build geometry + this.skeleton = skeleton; - this.setIndex( indices ); - this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - - } - - CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; - - - - var Geometries = /*#__PURE__*/Object.freeze({ - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - ParametricBufferGeometry: ParametricBufferGeometry, - TetrahedronGeometry: TetrahedronGeometry, - TetrahedronBufferGeometry: TetrahedronBufferGeometry, - OctahedronGeometry: OctahedronGeometry, - OctahedronBufferGeometry: OctahedronBufferGeometry, - IcosahedronGeometry: IcosahedronGeometry, - IcosahedronBufferGeometry: IcosahedronBufferGeometry, - DodecahedronGeometry: DodecahedronGeometry, - DodecahedronBufferGeometry: DodecahedronBufferGeometry, - PolyhedronGeometry: PolyhedronGeometry, - PolyhedronBufferGeometry: PolyhedronBufferGeometry, - TubeGeometry: TubeGeometry, - TubeBufferGeometry: TubeBufferGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - TextBufferGeometry: TextBufferGeometry, - SphereGeometry: SphereGeometry, - SphereBufferGeometry: SphereBufferGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneGeometry: PlaneGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ShapeBufferGeometry: ShapeBufferGeometry, - ExtrudeGeometry: ExtrudeGeometry, - ExtrudeBufferGeometry: ExtrudeBufferGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleGeometry: CircleGeometry, - CircleBufferGeometry: CircleBufferGeometry, - BoxGeometry: BoxGeometry, - BoxBufferGeometry: BoxBufferGeometry - }); + if ( bindMatrix === undefined ) { - /** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * color: - * } - */ + this.updateMatrixWorld( true ); - function ShadowMaterial( parameters ) { + this.skeleton.calculateInverses(); - Material.call( this ); + bindMatrix = this.matrixWorld; - this.type = 'ShadowMaterial'; + } - this.color = new Color( 0x000000 ); - this.transparent = true; + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.copy( bindMatrix ).invert(); - this.setValues( parameters ); + } + + pose() { + + this.skeleton.pose(); } - ShadowMaterial.prototype = Object.create( Material.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; + normalizeSkinWeights() { - ShadowMaterial.prototype.isShadowMaterial = true; + const vector = new Vector4(); - ShadowMaterial.prototype.copy = function ( source ) { + const skinWeight = this.geometry.attributes.skinWeight; - Material.prototype.copy.call( this, source ); + for ( let i = 0, l = skinWeight.count; i < l; i ++ ) { - this.color.copy( source.color ); + vector.fromBufferAttribute( skinWeight, i ); - return this; + const scale = 1.0 / vector.manhattanLength(); - }; + if ( scale !== Infinity ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + vector.multiplyScalar( scale ); - function RawShaderMaterial( parameters ) { + } else { - ShaderMaterial.call( this, parameters ); + vector.set( 1, 0, 0, 0 ); // do something reasonable - this.type = 'RawShaderMaterial'; + } + + skinWeight.setXYZW( i, vector.x, vector.y, vector.z, vector.w ); + + } } - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; + updateMatrixWorld( force ) { - RawShaderMaterial.prototype.isRawShaderMaterial = true; + super.updateMatrixWorld( force ); - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * color: , - * roughness: , - * metalness: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * roughnessMap: new THREE.Texture( ), - * - * metalnessMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * envMapIntensity: - * - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + if ( this.bindMode === 'attached' ) { - function MeshStandardMaterial( parameters ) { + this.bindMatrixInverse.copy( this.matrixWorld ).invert(); - Material.call( this ); + } else if ( this.bindMode === 'detached' ) { - this.defines = { 'STANDARD': '' }; + this.bindMatrixInverse.copy( this.bindMatrix ).invert(); - this.type = 'MeshStandardMaterial'; + } else { - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 0.5; - this.metalness = 0.5; + console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode ); - this.map = null; + } - this.lightMap = null; - this.lightMapIntensity = 1.0; + } - this.aoMap = null; - this.aoMapIntensity = 1.0; + applyBoneTransform( index, vector ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + const skeleton = this.skeleton; + const geometry = this.geometry; - this.bumpMap = null; - this.bumpScale = 1; + _skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index ); + _skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index ); - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + _basePosition.copy( vector ).applyMatrix4( this.bindMatrix ); - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + vector.set( 0, 0, 0 ); - this.roughnessMap = null; + for ( let i = 0; i < 4; i ++ ) { - this.metalnessMap = null; + const weight = _skinWeight.getComponent( i ); - this.alphaMap = null; + if ( weight !== 0 ) { - this.envMap = null; - this.envMapIntensity = 1.0; + const boneIndex = _skinIndex.getComponent( i ); - this.refractionRatio = 0.98; + _matrix4.multiplyMatrices( skeleton.bones[ boneIndex ].matrixWorld, skeleton.boneInverses[ boneIndex ] ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + vector.addScaledVector( _vector3.copy( _basePosition ).applyMatrix4( _matrix4 ), weight ); + + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } - this.setValues( parameters ); + return vector.applyMatrix4( this.bindMatrixInverse ); } - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; + boneTransform( index, vector ) { // @deprecated, r151 - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + console.warn( 'THREE.SkinnedMesh: .boneTransform() was renamed to .applyBoneTransform() in r151.' ); + return this.applyBoneTransform( index, vector ); - MeshStandardMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); - this.defines = { 'STANDARD': '' }; +} - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; +class Bone extends Object3D { - this.map = source.map; + constructor() { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + super(); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + this.isBone = true; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + this.type = 'Bone'; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); +} - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; +class DataTexture extends Texture { - this.roughnessMap = source.roughnessMap; + constructor( data = null, width = 1, height = 1, format, type, mapping, wrapS, wrapT, magFilter = NearestFilter, minFilter = NearestFilter, anisotropy, colorSpace ) { - this.metalnessMap = source.metalnessMap; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); - this.alphaMap = source.alphaMap; + this.isDataTexture = true; - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; + this.image = { data: data, width: width, height: height }; - this.refractionRatio = source.refractionRatio; + this.generateMipmaps = false; + this.flipY = false; + this.unpackAlignment = 1; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; +} - return this; +const _offsetMatrix = /*@__PURE__*/ new Matrix4(); +const _identityMatrix = /*@__PURE__*/ new Matrix4(); - }; +class Skeleton { - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * reflectivity: - * clearcoat: - * clearcoatRoughness: - * - * sheen: - * - * clearcoatNormalScale: , - * clearcoatNormalMap: new THREE.Texture( ), - * } - */ + constructor( bones = [], boneInverses = [] ) { - function MeshPhysicalMaterial( parameters ) { + this.uuid = generateUUID(); - MeshStandardMaterial.call( this ); + this.bones = bones.slice( 0 ); + this.boneInverses = boneInverses; + this.boneMatrices = null; - this.defines = { + this.boneTexture = null; + this.boneTextureSize = 0; - 'STANDARD': '', - 'PHYSICAL': '' + this.init(); - }; + } - this.type = 'MeshPhysicalMaterial'; + init() { - this.reflectivity = 0.5; // maps to F0 = 0.04 + const bones = this.bones; + const boneInverses = this.boneInverses; - this.clearcoat = 0.0; - this.clearcoatRoughness = 0.0; + this.boneMatrices = new Float32Array( bones.length * 16 ); - this.sheen = null; // null will disable sheen bsdf + // calculate inverse bone matrices if necessary - this.clearcoatNormalScale = new Vector2( 1, 1 ); - this.clearcoatNormalMap = null; + if ( boneInverses.length === 0 ) { - this.transparency = 0.0; + this.calculateInverses(); - this.setValues( parameters ); + } else { - } + // handle special case - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; + if ( bones.length !== boneInverses.length ) { - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; + console.warn( 'THREE.Skeleton: Number of inverse bone matrices does not match amount of bones.' ); - MeshPhysicalMaterial.prototype.copy = function ( source ) { + this.boneInverses = []; - MeshStandardMaterial.prototype.copy.call( this, source ); + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - this.defines = { + this.boneInverses.push( new Matrix4() ); - 'STANDARD': '', - 'PHYSICAL': '' + } - }; + } - this.reflectivity = source.reflectivity; + } - this.clearcoat = source.clearcoat; - this.clearcoatRoughness = source.clearcoatRoughness; + } - if ( source.sheen ) this.sheen = ( this.sheen || new Color() ).copy( source.sheen ); - else this.sheen = null; + calculateInverses() { - this.clearcoatNormalMap = source.clearcoatNormalMap; - this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); + this.boneInverses.length = 0; - this.transparency = source.transparency; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - return this; + const inverse = new Matrix4(); - }; + if ( this.bones[ i ] ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + inverse.copy( this.bones[ i ].matrixWorld ).invert(); - function MeshPhongMaterial( parameters ) { + } - Material.call( this ); + this.boneInverses.push( inverse ); - this.type = 'MeshPhongMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; + } - this.map = null; + pose() { - this.lightMap = null; - this.lightMapIntensity = 1.0; + // recover the bind-time world matrices - this.aoMap = null; - this.aoMapIntensity = 1.0; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + const bone = this.bones[ i ]; - this.bumpMap = null; - this.bumpScale = 1; + if ( bone ) { - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + bone.matrixWorld.copy( this.boneInverses[ i ] ).invert(); - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.specularMap = null; + } - this.alphaMap = null; + // compute the local matrices, positions, rotations and scales - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + const bone = this.bones[ i ]; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( bone ) { - this.setValues( parameters ); + if ( bone.parent && bone.parent.isBone ) { - } + bone.matrix.copy( bone.parent.matrixWorld ).invert(); + bone.matrix.multiply( bone.matrixWorld ); - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; + } else { - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; + bone.matrix.copy( bone.matrixWorld ); - MeshPhongMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + } - this.map = source.map; + } - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + } - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + update() { - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + const bones = this.bones; + const boneInverses = this.boneInverses; + const boneMatrices = this.boneMatrices; + const boneTexture = this.boneTexture; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + // flatten bone matrices to array - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + for ( let i = 0, il = bones.length; i < il; i ++ ) { - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + // compute the offset between the current and the original transform - this.specularMap = source.specularMap; + const matrix = bones[ i ] ? bones[ i ].matrixWorld : _identityMatrix; - this.alphaMap = source.alphaMap; + _offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] ); + _offsetMatrix.toArray( boneMatrices, i * 16 ); - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + } - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + if ( boneTexture !== null ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - - return this; + boneTexture.needsUpdate = true; - }; + } - /** - * @author takahirox / http://github.com/takahirox - * - * parameters = { - * gradientMap: new THREE.Texture( ) - * } - */ + } - function MeshToonMaterial( parameters ) { + clone() { - MeshPhongMaterial.call( this ); + return new Skeleton( this.bones, this.boneInverses ); - this.defines = { 'TOON': '' }; + } - this.type = 'MeshToonMaterial'; + computeBoneTexture() { - this.gradientMap = null; + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - this.setValues( parameters ); + let size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = ceilPowerOfTwo( size ); + size = Math.max( size, 4 ); - } + const boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel + boneMatrices.set( this.boneMatrices ); // copy current values - MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype ); - MeshToonMaterial.prototype.constructor = MeshToonMaterial; + const boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType ); + boneTexture.needsUpdate = true; - MeshToonMaterial.prototype.isMeshToonMaterial = true; + this.boneMatrices = boneMatrices; + this.boneTexture = boneTexture; + this.boneTextureSize = size; - MeshToonMaterial.prototype.copy = function ( source ) { + return this; - MeshPhongMaterial.prototype.copy.call( this, source ); + } - this.gradientMap = source.gradientMap; + getBoneByName( name ) { - return this; + for ( let i = 0, il = this.bones.length; i < il; i ++ ) { - }; + const bone = this.bones[ i ]; - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * opacity: , - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * wireframe: , - * wireframeLinewidth: - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + if ( bone.name === name ) { - function MeshNormalMaterial( parameters ) { + return bone; - Material.call( this ); + } - this.type = 'MeshNormalMaterial'; + } - this.bumpMap = null; - this.bumpScale = 1; + return undefined; - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + dispose( ) { - this.wireframe = false; - this.wireframeLinewidth = 1; + if ( this.boneTexture !== null ) { - this.fog = false; - this.lights = false; + this.boneTexture.dispose(); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + this.boneTexture = null; - this.setValues( parameters ); + } } - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; + fromJSON( json, bones ) { - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + this.uuid = json.uuid; - MeshNormalMaterial.prototype.copy = function ( source ) { + for ( let i = 0, l = json.bones.length; i < l; i ++ ) { - Material.prototype.copy.call( this, source ); + const uuid = json.bones[ i ]; + let bone = bones[ uuid ]; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + if ( bone === undefined ) { - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + console.warn( 'THREE.Skeleton: No bone found with UUID:', uuid ); + bone = new Bone(); - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + } - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + this.bones.push( bone ); + this.boneInverses.push( new Matrix4().fromArray( json.boneInverses[ i ] ) ); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + } + + this.init(); return this; - }; + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissive: , - * emissiveIntensity: - * emissiveMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * wireframe: , - * wireframeLinewidth: , - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + toJSON() { - function MeshLambertMaterial( parameters ) { + const data = { + metadata: { + version: 4.6, + type: 'Skeleton', + generator: 'Skeleton.toJSON' + }, + bones: [], + boneInverses: [] + }; - Material.call( this ); + data.uuid = this.uuid; - this.type = 'MeshLambertMaterial'; + const bones = this.bones; + const boneInverses = this.boneInverses; - this.color = new Color( 0xffffff ); // diffuse + for ( let i = 0, l = bones.length; i < l; i ++ ) { - this.map = null; + const bone = bones[ i ]; + data.bones.push( bone.uuid ); - this.lightMap = null; - this.lightMapIntensity = 1.0; + const boneInverse = boneInverses[ i ]; + data.boneInverses.push( boneInverse.toArray() ); - this.aoMap = null; - this.aoMapIntensity = 1.0; + } - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + return data; - this.specularMap = null; + } - this.alphaMap = null; +} - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; +class InstancedBufferAttribute extends BufferAttribute { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + constructor( array, itemSize, normalized, meshPerAttribute = 1 ) { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + super( array, itemSize, normalized ); - this.setValues( parameters ); + this.isInstancedBufferAttribute = true; + + this.meshPerAttribute = meshPerAttribute; } - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; + copy( source ) { - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; + super.copy( source ); - MeshLambertMaterial.prototype.copy = function ( source ) { + this.meshPerAttribute = source.meshPerAttribute; - Material.prototype.copy.call( this, source ); + return this; - this.color.copy( source.color ); + } - this.map = source.map; + toJSON() { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + const data = super.toJSON(); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + data.meshPerAttribute = this.meshPerAttribute; - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + data.isInstancedBufferAttribute = true; - this.specularMap = source.specularMap; + return data; - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; +} - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; +const _instanceLocalMatrix = /*@__PURE__*/ new Matrix4(); +const _instanceWorldMatrix = /*@__PURE__*/ new Matrix4(); - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; +const _instanceIntersects = []; - return this; +const _box3 = /*@__PURE__*/ new Box3(); +const _identity = /*@__PURE__*/ new Matrix4(); +const _mesh = /*@__PURE__*/ new Mesh(); +const _sphere$2 = /*@__PURE__*/ new Sphere(); - }; +class InstancedMesh extends Mesh { - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * color: , - * opacity: , - * - * matcap: new THREE.Texture( ), - * - * map: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalMapType: THREE.TangentSpaceNormalMap, - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * alphaMap: new THREE.Texture( ), - * - * skinning: , - * morphTargets: , - * morphNormals: - * } - */ + constructor( geometry, material, count ) { - function MeshMatcapMaterial( parameters ) { + super( geometry, material ); - Material.call( this ); + this.isInstancedMesh = true; - this.defines = { 'MATCAP': '' }; + this.instanceMatrix = new InstancedBufferAttribute( new Float32Array( count * 16 ), 16 ); + this.instanceColor = null; - this.type = 'MeshMatcapMaterial'; + this.count = count; - this.color = new Color( 0xffffff ); // diffuse + this.boundingBox = null; + this.boundingSphere = null; - this.matcap = null; + for ( let i = 0; i < count; i ++ ) { - this.map = null; + this.setMatrixAt( i, _identity ); - this.bumpMap = null; - this.bumpScale = 1; + } - this.normalMap = null; - this.normalMapType = TangentSpaceNormalMap; - this.normalScale = new Vector2( 1, 1 ); + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + computeBoundingBox() { - this.alphaMap = null; + const geometry = this.geometry; + const count = this.count; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( this.boundingBox === null ) { - this.lights = false; + this.boundingBox = new Box3(); - this.setValues( parameters ); + } - } + if ( geometry.boundingBox === null ) { - MeshMatcapMaterial.prototype = Object.create( Material.prototype ); - MeshMatcapMaterial.prototype.constructor = MeshMatcapMaterial; + geometry.computeBoundingBox(); - MeshMatcapMaterial.prototype.isMeshMatcapMaterial = true; + } - MeshMatcapMaterial.prototype.copy = function ( source ) { + this.boundingBox.makeEmpty(); - Material.prototype.copy.call( this, source ); + for ( let i = 0; i < count; i ++ ) { - this.defines = { 'MATCAP': '' }; + this.getMatrixAt( i, _instanceLocalMatrix ); - this.color.copy( source.color ); + _box3.copy( geometry.boundingBox ).applyMatrix4( _instanceLocalMatrix ); - this.matcap = source.matcap; + this.boundingBox.union( _box3 ); - this.map = source.map; + } - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalMapType = source.normalMapType; - this.normalScale.copy( source.normalScale ); + computeBoundingSphere() { - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + const geometry = this.geometry; + const count = this.count; - this.alphaMap = source.alphaMap; + if ( this.boundingSphere === null ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + this.boundingSphere = new Sphere(); - return this; + } - }; + if ( geometry.boundingSphere === null ) { - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ + geometry.computeBoundingSphere(); - function LineDashedMaterial( parameters ) { + } - LineBasicMaterial.call( this ); + this.boundingSphere.makeEmpty(); - this.type = 'LineDashedMaterial'; + for ( let i = 0; i < count; i ++ ) { - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + this.getMatrixAt( i, _instanceLocalMatrix ); - this.setValues( parameters ); + _sphere$2.copy( geometry.boundingSphere ).applyMatrix4( _instanceLocalMatrix ); + + this.boundingSphere.union( _sphere$2 ); + + } } - LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; + copy( source, recursive ) { - LineDashedMaterial.prototype.isLineDashedMaterial = true; + super.copy( source, recursive ); - LineDashedMaterial.prototype.copy = function ( source ) { + this.instanceMatrix.copy( source.instanceMatrix ); - LineBasicMaterial.prototype.copy.call( this, source ); + if ( source.instanceColor !== null ) this.instanceColor = source.instanceColor.clone(); - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + this.count = source.count; + + if ( source.boundingBox !== null ) this.boundingBox = source.boundingBox.clone(); + if ( source.boundingSphere !== null ) this.boundingSphere = source.boundingSphere.clone(); return this; - }; + } + getColorAt( index, color ) { + color.fromArray( this.instanceColor.array, index * 3 ); - var Materials = /*#__PURE__*/Object.freeze({ - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshToonMaterial: MeshToonMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshDistanceMaterial: MeshDistanceMaterial, - MeshBasicMaterial: MeshBasicMaterial, - MeshMatcapMaterial: MeshMatcapMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); + } - /** - * @author tschw - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + getMatrixAt( index, matrix ) { - var AnimationUtils = { + matrix.fromArray( this.instanceMatrix.array, index * 16 ); - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function ( array, from, to ) { + } - if ( AnimationUtils.isTypedArray( array ) ) { + raycast( raycaster, intersects ) { - // in ios9 array.subarray(from, undefined) will return empty array - // but array.subarray(from) or array.subarray(from, len) is correct - return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) ); + const matrixWorld = this.matrixWorld; + const raycastTimes = this.count; - } + _mesh.geometry = this.geometry; + _mesh.material = this.material; - return array.slice( from, to ); + if ( _mesh.material === undefined ) return; - }, + // test with bounding sphere first - // converts an array to a specific type - convertArray: function ( array, type, forceClone ) { + if ( this.boundingSphere === null ) this.computeBoundingSphere(); - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + _sphere$2.copy( this.boundingSphere ); + _sphere$2.applyMatrix4( matrixWorld ); - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + if ( raycaster.ray.intersectsSphere( _sphere$2 ) === false ) return; - return new type( array ); // create typed array + // now test each instance - } + for ( let instanceId = 0; instanceId < raycastTimes; instanceId ++ ) { - return Array.prototype.slice.call( array ); // create Array + // calculate the world matrix for each instance - }, + this.getMatrixAt( instanceId, _instanceLocalMatrix ); - isTypedArray: function ( object ) { + _instanceWorldMatrix.multiplyMatrices( matrixWorld, _instanceLocalMatrix ); - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + // the mesh represents this single instance - }, + _mesh.matrixWorld = _instanceWorldMatrix; + + _mesh.raycast( raycaster, _instanceIntersects ); - // returns an array by which times and values can be sorted - getKeyframeOrder: function ( times ) { + // process the result of raycast - function compareTime( i, j ) { + for ( let i = 0, l = _instanceIntersects.length; i < l; i ++ ) { - return times[ i ] - times[ j ]; + const intersect = _instanceIntersects[ i ]; + intersect.instanceId = instanceId; + intersect.object = this; + intersects.push( intersect ); } - var n = times.length; - var result = new Array( n ); - for ( var i = 0; i !== n; ++ i ) result[ i ] = i; + _instanceIntersects.length = 0; - result.sort( compareTime ); + } - return result; + } - }, + setColorAt( index, color ) { - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function ( values, stride, order ) { + if ( this.instanceColor === null ) { - var nValues = values.length; - var result = new values.constructor( nValues ); + this.instanceColor = new InstancedBufferAttribute( new Float32Array( this.instanceMatrix.count * 3 ), 3 ); - for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + } - var srcOffset = order[ i ] * stride; + color.toArray( this.instanceColor.array, index * 3 ); - for ( var j = 0; j !== stride; ++ j ) { + } - result[ dstOffset ++ ] = values[ srcOffset + j ]; + setMatrixAt( index, matrix ) { - } + matrix.toArray( this.instanceMatrix.array, index * 16 ); - } + } - return result; + updateMorphTargets() { - }, + } - // function for parsing AOS keyframe formats - flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) { + dispose() { - var i = 1, key = jsonKeys[ 0 ]; + this.dispatchEvent( { type: 'dispose' } ); - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + } - key = jsonKeys[ i ++ ]; +} - } +class LineBasicMaterial extends Material { - if ( key === undefined ) return; // no data + constructor( parameters ) { - var value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data + super(); - if ( Array.isArray( value ) ) { + this.isLineBasicMaterial = true; - do { + this.type = 'LineBasicMaterial'; - value = key[ valuePropertyName ]; + this.color = new Color( 0xffffff ); - if ( value !== undefined ) { + this.map = null; - times.push( key.time ); - values.push.apply( values, value ); // push all elements + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - } + this.fog = true; - key = jsonKeys[ i ++ ]; + this.setValues( parameters ); - } while ( key !== undefined ); + } - } else if ( value.toArray !== undefined ) { - // ...assume THREE.Math-ish + copy( source ) { - do { + super.copy( source ); - value = key[ valuePropertyName ]; + this.color.copy( source.color ); - if ( value !== undefined ) { + this.map = source.map; - times.push( key.time ); - value.toArray( values, values.length ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; - } + this.fog = source.fog; - key = jsonKeys[ i ++ ]; + return this; - } while ( key !== undefined ); + } - } else { +} - // otherwise push as-is +const _start$1 = /*@__PURE__*/ new Vector3(); +const _end$1 = /*@__PURE__*/ new Vector3(); +const _inverseMatrix$1 = /*@__PURE__*/ new Matrix4(); +const _ray$1 = /*@__PURE__*/ new Ray(); +const _sphere$1 = /*@__PURE__*/ new Sphere(); - do { +class Line extends Object3D { - value = key[ valuePropertyName ]; + constructor( geometry = new BufferGeometry(), material = new LineBasicMaterial() ) { - if ( value !== undefined ) { + super(); - times.push( key.time ); - values.push( value ); + this.isLine = true; - } + this.type = 'Line'; - key = jsonKeys[ i ++ ]; + this.geometry = geometry; + this.material = material; - } while ( key !== undefined ); + this.updateMorphTargets(); - } + } - } + copy( source, recursive ) { - }; + super.copy( source, recursive ); - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - * @author tschw - */ + this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; + this.geometry = source.geometry; - function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; - - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + return this; } - Object.assign( Interpolant.prototype, { + computeLineDistances() { - evaluate: function ( t ) { + const geometry = this.geometry; - var pp = this.parameterPositions, - i1 = this._cachedIndex, + // we assume non-indexed geometry - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + if ( geometry.index === null ) { - validate_interval: { + const positionAttribute = geometry.attributes.position; + const lineDistances = [ 0 ]; - seek: { + for ( let i = 1, l = positionAttribute.count; i < l; i ++ ) { - var right; + _start$1.fromBufferAttribute( positionAttribute, i - 1 ); + _end$1.fromBufferAttribute( positionAttribute, i ); - linear_scan: { + lineDistances[ i ] = lineDistances[ i - 1 ]; + lineDistances[ i ] += _start$1.distanceTo( _end$1 ); - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + } - for ( var giveUpAt = i1 + 2; ; ) { + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - if ( t1 === undefined ) { + } else { - if ( t < t0 ) break forward_scan; + console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); - // after end + } - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + return this; - } + } - if ( i1 === giveUpAt ) break; // this loop + raycast( raycaster, intersects ) { - t0 = t1; - t1 = pp[ ++ i1 ]; + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Line.threshold; + const drawRange = geometry.drawRange; - if ( t < t1 ) { + // Checking boundingSphere distance to ray - // we have arrived at the sought interval - break seek; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - } + _sphere$1.copy( geometry.boundingSphere ); + _sphere$1.applyMatrix4( matrixWorld ); + _sphere$1.radius += threshold; - } + if ( raycaster.ray.intersectsSphere( _sphere$1 ) === false ) return; - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + // - } + _inverseMatrix$1.copy( matrixWorld ).invert(); + _ray$1.copy( raycaster.ray ).applyMatrix4( _inverseMatrix$1 ); - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - // looping? + const vStart = new Vector3(); + const vEnd = new Vector3(); + const interSegment = new Vector3(); + const interRay = new Vector3(); + const step = this.isLineSegments ? 2 : 1; - var t1global = pp[ 1 ]; + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - if ( t < t1global ) { + if ( index !== null ) { - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - } + for ( let i = start, l = end - 1; i < l; i += step ) { - // linear reverse scan + const a = index.getX( i ); + const b = index.getX( i + 1 ); - for ( var giveUpAt = i1 - 2; ; ) { + vStart.fromBufferAttribute( positionAttribute, a ); + vEnd.fromBufferAttribute( positionAttribute, b ); - if ( t0 === undefined ) { + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - // before start + if ( distSq > localThresholdSq ) continue; - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - } + const distance = raycaster.ray.origin.distanceTo( interRay ); - if ( i1 === giveUpAt ) break; // this loop + if ( distance < raycaster.near || distance > raycaster.far ) continue; - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + intersects.push( { - if ( t >= t0 ) { + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - // we have arrived at the sought interval - break seek; + } ); - } + } - } + } else { - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - } + for ( let i = start, l = end - 1; i < l; i += step ) { - // the interval is valid + vStart.fromBufferAttribute( positionAttribute, i ); + vEnd.fromBufferAttribute( positionAttribute, i + 1 ); - break validate_interval; + const distSq = _ray$1.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - } // linear scan + if ( distSq > localThresholdSq ) continue; - // binary search + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - while ( i1 < right ) { + const distance = raycaster.ray.origin.distanceTo( interRay ); - var mid = ( i1 + right ) >>> 1; + if ( distance < raycaster.near || distance > raycaster.far ) continue; - if ( t < pp[ mid ] ) { + intersects.push( { - right = mid; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this - } else { + } ); - i1 = mid + 1; + } - } + } - } + } - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + updateMorphTargets() { - // check boundary cases, again + const geometry = this.geometry; - if ( t0 === undefined ) { + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + if ( keys.length > 0 ) { - } + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - if ( t1 === undefined ) { + if ( morphAttribute !== undefined ) { - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - } + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - } // seek + const name = morphAttribute[ m ].name || String( m ); - this._cachedIndex = i1; + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - this.intervalChanged_( i1, t0, t1 ); + } - } // validate_interval + } - return this.interpolate_( i1, t0, t, t1 ); + } - }, + } - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. +} - // --- Protected interface +const _start = /*@__PURE__*/ new Vector3(); +const _end = /*@__PURE__*/ new Vector3(); - DefaultSettings_: {}, +class LineSegments extends Line { - getSettings_: function () { + constructor( geometry, material ) { - return this.settings || this.DefaultSettings_; + super( geometry, material ); - }, + this.isLineSegments = true; - copySampleValue_: function ( index ) { + this.type = 'LineSegments'; - // copies a sample value to the result buffer + } - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + computeLineDistances() { - for ( var i = 0; i !== stride; ++ i ) { + const geometry = this.geometry; - result[ i ] = values[ offset + i ]; + // we assume non-indexed geometry - } + if ( geometry.index === null ) { - return result; + const positionAttribute = geometry.attributes.position; + const lineDistances = []; - }, + for ( let i = 0, l = positionAttribute.count; i < l; i += 2 ) { - // Template methods for derived classes: + _start.fromBufferAttribute( positionAttribute, i ); + _end.fromBufferAttribute( positionAttribute, i + 1 ); - interpolate_: function ( /* i1, t0, t, t1 */ ) { + lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ]; + lineDistances[ i + 1 ] = lineDistances[ i ] + _start.distanceTo( _end ); - throw new Error( 'call to abstract method' ); - // implementations shall return this.resultBuffer + } - }, + geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) ); - intervalChanged_: function ( /* i1, t0, t1 */ ) { + } else { - // empty + console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' ); } - } ); - - //!\ DECLARE ALIAS AFTER assign prototype ! - Object.assign( Interpolant.prototype, { + return this; - //( 0, t, t0 ), returns this.resultBuffer - beforeStart_: Interpolant.prototype.copySampleValue_, + } - //( N-1, tN-1, t ), returns this.resultBuffer - afterEnd_: Interpolant.prototype.copySampleValue_, +} - } ); +class LineLoop extends Line { - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - * - * @author tschw - */ + constructor( geometry, material ) { - function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + super( geometry, material ); - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + this.isLineLoop = true; - this._weightPrev = - 0; - this._offsetPrev = - 0; - this._weightNext = - 0; - this._offsetNext = - 0; + this.type = 'LineLoop'; } - CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { - - constructor: CubicInterpolant, +} - DefaultSettings_: { +class PointsMaterial extends Material { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + constructor( parameters ) { - }, + super(); - intervalChanged_: function ( i1, t0, t1 ) { + this.isPointsMaterial = true; - var pp = this.parameterPositions, - iPrev = i1 - 2, - iNext = i1 + 1, + this.type = 'PointsMaterial'; - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; + this.color = new Color( 0xffffff ); - if ( tPrev === undefined ) { + this.map = null; - switch ( this.getSettings_().endingStart ) { + this.alphaMap = null; - case ZeroSlopeEnding: + this.size = 1; + this.sizeAttenuation = true; - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + this.fog = true; - break; + this.setValues( parameters ); - case WrapAroundEnding: + } - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + copy( source ) { - break; + super.copy( source ); - default: // ZeroCurvatureEnding + this.color.copy( source.color ); - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; + this.map = source.map; - } + this.alphaMap = source.alphaMap; - } + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; - if ( tNext === undefined ) { + this.fog = source.fog; - switch ( this.getSettings_().endingEnd ) { + return this; - case ZeroSlopeEnding: + } - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; +} - break; +const _inverseMatrix = /*@__PURE__*/ new Matrix4(); +const _ray = /*@__PURE__*/ new Ray(); +const _sphere = /*@__PURE__*/ new Sphere(); +const _position$2 = /*@__PURE__*/ new Vector3(); - case WrapAroundEnding: +class Points extends Object3D { - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + constructor( geometry = new BufferGeometry(), material = new PointsMaterial() ) { - break; + super(); - default: // ZeroCurvatureEnding + this.isPoints = true; - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + this.type = 'Points'; - } + this.geometry = geometry; + this.material = material; - } + this.updateMorphTargets(); - var halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; + } - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; + copy( source, recursive ) { - }, + super.copy( source, recursive ); - interpolate_: function ( i1, t0, t, t1 ) { + this.material = Array.isArray( source.material ) ? source.material.slice() : source.material; + this.geometry = source.geometry; - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + return this; - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, + } - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; + raycast( raycaster, intersects ) { - // evaluate polynomials + const geometry = this.geometry; + const matrixWorld = this.matrixWorld; + const threshold = raycaster.params.Points.threshold; + const drawRange = geometry.drawRange; - var sP = - wP * ppp + 2 * wP * pp - wP * p; - var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; - var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - var sN = wN * ppp - wN * pp; + // Checking boundingSphere distance to ray - // combine data linearly + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - for ( var i = 0; i !== stride; ++ i ) { + _sphere.copy( geometry.boundingSphere ); + _sphere.applyMatrix4( matrixWorld ); + _sphere.radius += threshold; - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + if ( raycaster.ray.intersectsSphere( _sphere ) === false ) return; - } + // - return result; + _inverseMatrix.copy( matrixWorld ).invert(); + _ray.copy( raycaster.ray ).applyMatrix4( _inverseMatrix ); - } + const localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + const localThresholdSq = localThreshold * localThreshold; - } ); + const index = geometry.index; + const attributes = geometry.attributes; + const positionAttribute = attributes.position; - /** - * @author tschw - */ + if ( index !== null ) { - function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + const start = Math.max( 0, drawRange.start ); + const end = Math.min( index.count, ( drawRange.start + drawRange.count ) ); - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + for ( let i = start, il = end; i < il; i ++ ) { - } + const a = index.getX( i ); - LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + _position$2.fromBufferAttribute( positionAttribute, a ); - constructor: LinearInterpolant, + testPoint( _position$2, a, localThresholdSq, matrixWorld, raycaster, intersects, this ); - interpolate_: function ( i1, t0, t, t1 ) { + } - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } else { - offset1 = i1 * stride, - offset0 = offset1 - stride, + const start = Math.max( 0, drawRange.start ); + const end = Math.min( positionAttribute.count, ( drawRange.start + drawRange.count ) ); - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + for ( let i = start, l = end; i < l; i ++ ) { - for ( var i = 0; i !== stride; ++ i ) { + _position$2.fromBufferAttribute( positionAttribute, i ); - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + testPoint( _position$2, i, localThresholdSq, matrixWorld, raycaster, intersects, this ); } - return result; - } - } ); + } - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - * - * @author tschw - */ + updateMorphTargets() { - function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + const geometry = this.geometry; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + const morphAttributes = geometry.morphAttributes; + const keys = Object.keys( morphAttributes ); - } + if ( keys.length > 0 ) { - DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + const morphAttribute = morphAttributes[ keys[ 0 ] ]; - constructor: DiscreteInterpolant, + if ( morphAttribute !== undefined ) { - interpolate_: function ( i1 /*, t0, t, t1 */ ) { + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - return this.copySampleValue_( i1 - 1 ); + for ( let m = 0, ml = morphAttribute.length; m < ml; m ++ ) { - } + const name = morphAttribute[ m ].name || String( m ); - } ); + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ name ] = m; - /** - * - * A timed sequence of keyframes for a specific property. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function KeyframeTrack( name, times, values, interpolation ) { + } - if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); - if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); + } - this.name = name; + } - this.times = AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = AnimationUtils.convertArray( values, this.ValueBufferType ); +} - this.setInterpolation( interpolation || this.DefaultInterpolation ); +function testPoint( point, index, localThresholdSq, matrixWorld, raycaster, intersects, object ) { - } + const rayPointDistanceSq = _ray.distanceSqToPoint( point ); - // Static methods + if ( rayPointDistanceSq < localThresholdSq ) { - Object.assign( KeyframeTrack, { + const intersectPoint = new Vector3(); - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + _ray.closestPointToPoint( point, intersectPoint ); + intersectPoint.applyMatrix4( matrixWorld ); - toJSON: function ( track ) { + const distance = raycaster.ray.origin.distanceTo( intersectPoint ); - var trackType = track.constructor; + if ( distance < raycaster.near || distance > raycaster.far ) return; - var json; + intersects.push( { - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint, + index: index, + face: null, + object: object - json = trackType.toJSON( track ); + } ); - } else { + } - // by default, we assume the data can be serialized as-is - json = { +} - 'name': track.name, - 'times': AnimationUtils.convertArray( track.times, Array ), - 'values': AnimationUtils.convertArray( track.values, Array ) +class VideoTexture extends Texture { - }; + constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - var interpolation = track.getInterpolation(); + super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( interpolation !== track.DefaultInterpolation ) { + this.isVideoTexture = true; - json.interpolation = interpolation; + this.minFilter = minFilter !== undefined ? minFilter : LinearFilter; + this.magFilter = magFilter !== undefined ? magFilter : LinearFilter; - } + this.generateMipmaps = false; - } + const scope = this; - json.type = track.ValueTypeName; // mandatory + function updateVideo() { - return json; + scope.needsUpdate = true; + video.requestVideoFrameCallback( updateVideo ); } - } ); + if ( 'requestVideoFrameCallback' in video ) { - Object.assign( KeyframeTrack.prototype, { + video.requestVideoFrameCallback( updateVideo ); - constructor: KeyframeTrack, + } - TimeBufferType: Float32Array, + } - ValueBufferType: Float32Array, + clone() { - DefaultInterpolation: InterpolateLinear, + return new this.constructor( this.image ).copy( this ); - InterpolantFactoryMethodDiscrete: function ( result ) { + } - return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); + update() { - }, + const video = this.image; + const hasVideoFrameCallback = 'requestVideoFrameCallback' in video; - InterpolantFactoryMethodLinear: function ( result ) { + if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) { - return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); + this.needsUpdate = true; - }, + } - InterpolantFactoryMethodSmooth: function ( result ) { + } - return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); +} - }, +class FramebufferTexture extends Texture { - setInterpolation: function ( interpolation ) { + constructor( width, height ) { - var factoryMethod; + super( { width, height } ); - switch ( interpolation ) { + this.isFramebufferTexture = true; - case InterpolateDiscrete: + this.magFilter = NearestFilter; + this.minFilter = NearestFilter; - factoryMethod = this.InterpolantFactoryMethodDiscrete; + this.generateMipmaps = false; - break; + this.needsUpdate = true; - case InterpolateLinear: + } - factoryMethod = this.InterpolantFactoryMethodLinear; +} - break; +class CompressedTexture extends Texture { - case InterpolateSmooth: + constructor( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, colorSpace ) { - factoryMethod = this.InterpolantFactoryMethodSmooth; + super( null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, colorSpace ); - break; + this.isCompressedTexture = true; - } + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - if ( factoryMethod === undefined ) { + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - var message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; + this.flipY = false; - if ( this.createInterpolant === undefined ) { + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { + this.generateMipmaps = false; - this.setInterpolation( this.DefaultInterpolation ); + } - } else { +} - throw new Error( message ); // fatal, in this case +class CompressedArrayTexture extends CompressedTexture { - } + constructor( mipmaps, width, height, depth, format, type ) { - } + super( mipmaps, width, height, format, type ); - console.warn( 'THREE.KeyframeTrack:', message ); - return this; + this.isCompressedArrayTexture = true; + this.image.depth = depth; + this.wrapR = ClampToEdgeWrapping; - } + } - this.createInterpolant = factoryMethod; +} - return this; +class CompressedCubeTexture extends CompressedTexture { - }, + constructor( images, format, type ) { - getInterpolation: function () { + super( undefined, images[ 0 ].width, images[ 0 ].height, format, type, CubeReflectionMapping ); - switch ( this.createInterpolant ) { + this.isCompressedCubeTexture = true; + this.isCubeTexture = true; - case this.InterpolantFactoryMethodDiscrete: + this.image = images; - return InterpolateDiscrete; + } - case this.InterpolantFactoryMethodLinear: +} - return InterpolateLinear; +class CanvasTexture extends Texture { - case this.InterpolantFactoryMethodSmooth: + constructor( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - return InterpolateSmooth; + super( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } + this.isCanvasTexture = true; - }, + this.needsUpdate = true; - getValueSize: function () { + } - return this.values.length / this.times.length; +} + +/** + * Extensible curve object. + * + * Some common of curve methods: + * .getPoint( t, optionalTarget ), .getTangent( t, optionalTarget ) + * .getPointAt( u, optionalTarget ), .getTangentAt( u, optionalTarget ) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following curves inherit from THREE.Curve: + * + * -- 2D curves -- + * THREE.ArcCurve + * THREE.CubicBezierCurve + * THREE.EllipseCurve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.SplineCurve + * + * -- 3D curves -- + * THREE.CatmullRomCurve3 + * THREE.CubicBezierCurve3 + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * + * A series of curves can be represented as a THREE.CurvePath. + * + **/ + +class Curve { + + constructor() { - }, + this.type = 'Curve'; - // move all keyframes either forwards or backwards in time - shift: function ( timeOffset ) { + this.arcLengthDivisions = 200; - if ( timeOffset !== 0.0 ) { + } - var times = this.times; + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] - for ( var i = 0, n = times.length; i !== n; ++ i ) { + getPoint( /* t, optionalTarget */ ) { - times[ i ] += timeOffset; + console.warn( 'THREE.Curve: .getPoint() not implemented.' ); + return null; - } + } - } + // Get point at relative position in curve according to arc length + // - u [0 .. 1] - return this; + getPointAt( u, optionalTarget ) { - }, + const t = this.getUtoTmapping( u ); + return this.getPoint( t, optionalTarget ); - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function ( timeScale ) { + } - if ( timeScale !== 1.0 ) { + // Get sequence of points using getPoint( t ) - var times = this.times; + getPoints( divisions = 5 ) { - for ( var i = 0, n = times.length; i !== n; ++ i ) { + const points = []; - times[ i ] *= timeScale; + for ( let d = 0; d <= divisions; d ++ ) { - } + points.push( this.getPoint( d / divisions ) ); - } + } - return this; + return points; - }, + } - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function ( startTime, endTime ) { + // Get sequence of points using getPointAt( u ) - var times = this.times, - nKeys = times.length, - from = 0, - to = nKeys - 1; + getSpacedPoints( divisions = 5 ) { - while ( from !== nKeys && times[ from ] < startTime ) { + const points = []; - ++ from; + for ( let d = 0; d <= divisions; d ++ ) { - } + points.push( this.getPointAt( d / divisions ) ); - while ( to !== - 1 && times[ to ] > endTime ) { + } - -- to; + return points; - } + } - ++ to; // inclusive -> exclusive bound + // Get total curve arc length - if ( from !== 0 || to !== nKeys ) { + getLength() { - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) to = Math.max( to, 1 ), from = to - 1; + const lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - var stride = this.getValueSize(); - this.times = AnimationUtils.arraySlice( times, from, to ); - this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride ); + } - } + // Get list of cumulative segment lengths - return this; + getLengths( divisions = this.arcLengthDivisions ) { - }, + if ( this.cacheArcLengths && + ( this.cacheArcLengths.length === divisions + 1 ) && + ! this.needsUpdate ) { - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function () { + return this.cacheArcLengths; - var valid = true; + } - var valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + this.needsUpdate = false; - console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); - valid = false; + const cache = []; + let current, last = this.getPoint( 0 ); + let sum = 0; - } + cache.push( 0 ); - var times = this.times, - values = this.values, + for ( let p = 1; p <= divisions; p ++ ) { - nKeys = times.length; + current = this.getPoint( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; - if ( nKeys === 0 ) { + } - console.error( 'THREE.KeyframeTrack: Track is empty.', this ); - valid = false; + this.cacheArcLengths = cache; - } + return cache; // { sums: cache, sum: sum }; Sum is in the last element. - var prevTime = null; + } - for ( var i = 0; i !== nKeys; i ++ ) { + updateArcLengths() { - var currTime = times[ i ]; + this.needsUpdate = true; + this.getLengths(); - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + } - console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); - valid = false; - break; + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - } + getUtoTmapping( u, distance ) { - if ( prevTime !== null && prevTime > currTime ) { + const arcLengths = this.getLengths(); - console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); - valid = false; - break; + let i = 0; + const il = arcLengths.length; - } + let targetArcLength; // The targeted u distance value to get - prevTime = currTime; + if ( distance ) { - } + targetArcLength = distance; - if ( values !== undefined ) { + } else { - if ( AnimationUtils.isTypedArray( values ) ) { + targetArcLength = u * arcLengths[ il - 1 ]; - for ( var i = 0, n = values.length; i !== n; ++ i ) { + } - var value = values[ i ]; + // binary search for the index with largest value smaller than target u distance - if ( isNaN( value ) ) { + let low = 0, high = il - 1, comparison; - console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); - valid = false; - break; + while ( low <= high ) { - } + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - } + comparison = arcLengths[ i ] - targetArcLength; - } + if ( comparison < 0 ) { - } + low = i + 1; - return valid; + } else if ( comparison > 0 ) { - }, + high = i - 1; - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function () { + } else { - var times = this.times, - values = this.values, - stride = this.getValueSize(), + high = i; + break; - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + // DONE - writeIndex = 1, - lastIndex = times.length - 1; + } - for ( var i = 1; i < lastIndex; ++ i ) { + } - var keep = false; + i = high; - var time = times[ i ]; - var timeNext = times[ i + 1 ]; + if ( arcLengths[ i ] === targetArcLength ) { - // remove adjacent keyframes scheduled at the same time + return i / ( il - 1 ); - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { + } - if ( ! smoothInterpolation ) { + // we could get finer grain at lengths, or use simple interpolation between two points - // remove unnecessary keyframes same as their neighbors + const lengthBefore = arcLengths[ i ]; + const lengthAfter = arcLengths[ i + 1 ]; - var offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + const segmentLength = lengthAfter - lengthBefore; - for ( var j = 0; j !== stride; ++ j ) { + // determine where we are between the 'before' and 'after' points - var value = values[ offset + j ]; + const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + // add that fractional amount to t - keep = true; - break; + const t = ( i + segmentFraction ) / ( il - 1 ); - } + return t; - } + } - } else { + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation - keep = true; + getTangent( t, optionalTarget ) { - } + const delta = 0.0001; + let t1 = t - delta; + let t2 = t + delta; - } + // Capping in case of danger - // in-place compaction + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - if ( keep ) { + const pt1 = this.getPoint( t1 ); + const pt2 = this.getPoint( t2 ); - if ( i !== writeIndex ) { + const tangent = optionalTarget || ( ( pt1.isVector2 ) ? new Vector2() : new Vector3() ); - times[ writeIndex ] = times[ i ]; + tangent.copy( pt2 ).sub( pt1 ).normalize(); - var readOffset = i * stride, - writeOffset = writeIndex * stride; + return tangent; - for ( var j = 0; j !== stride; ++ j ) { + } - values[ writeOffset + j ] = values[ readOffset + j ]; + getTangentAt( u, optionalTarget ) { - } + const t = this.getUtoTmapping( u ); + return this.getTangent( t, optionalTarget ); - } + } - ++ writeIndex; + computeFrenetFrames( segments, closed ) { - } + // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf - } + const normal = new Vector3(); - // flush last keyframe (compaction looks ahead) + const tangents = []; + const normals = []; + const binormals = []; - if ( lastIndex > 0 ) { + const vec = new Vector3(); + const mat = new Matrix4(); - times[ writeIndex ] = times[ lastIndex ]; + // compute the tangent vectors for each segment on the curve - for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { + for ( let i = 0; i <= segments; i ++ ) { - values[ writeOffset + j ] = values[ readOffset + j ]; + const u = i / segments; - } + tangents[ i ] = this.getTangentAt( u, new Vector3() ); - ++ writeIndex; + } - } + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the minimum tangent xyz component - if ( writeIndex !== times.length ) { + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + let min = Number.MAX_VALUE; + const tx = Math.abs( tangents[ 0 ].x ); + const ty = Math.abs( tangents[ 0 ].y ); + const tz = Math.abs( tangents[ 0 ].z ); - this.times = AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + if ( tx <= min ) { - } + min = tx; + normal.set( 1, 0, 0 ); - return this; + } - }, + if ( ty <= min ) { - clone: function () { + min = ty; + normal.set( 0, 1, 0 ); - var times = AnimationUtils.arraySlice( this.times, 0 ); - var values = AnimationUtils.arraySlice( this.values, 0 ); - - var TypedKeyframeTrack = this.constructor; - var track = new TypedKeyframeTrack( this.name, times, values ); + } - // Interpolant argument to constructor is not saved, so copy the factory method directly. - track.createInterpolant = this.createInterpolant; + if ( tz <= min ) { - return track; + normal.set( 0, 0, 1 ); } - } ); + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - /** - * - * A Track of Boolean keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - function BooleanKeyframeTrack( name, times, values ) { - KeyframeTrack.call( this, name, times, values ); + // compute the slowly-varying normal and binormal vectors for each segment on the curve - } + for ( let i = 1; i <= segments; i ++ ) { - BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + normals[ i ] = normals[ i - 1 ].clone(); - constructor: BooleanKeyframeTrack, + binormals[ i ] = binormals[ i - 1 ].clone(); - ValueTypeName: 'bool', - ValueBufferType: Array, + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - DefaultInterpolation: InterpolateDiscrete, + if ( vec.length() > Number.EPSILON ) { - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined + vec.normalize(); - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". + const theta = Math.acos( clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - } ); + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - /** - * - * A Track of keyframe values that represent color. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function ColorKeyframeTrack( name, times, values, interpolation ) { + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - KeyframeTrack.call( this, name, times, values, interpolation ); + } - } + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + if ( closed === true ) { - constructor: ColorKeyframeTrack, + let theta = Math.acos( clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); + theta /= segments; - ValueTypeName: 'color' + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { - // ValueBufferType is inherited + theta = - theta; - // DefaultInterpolation is inherited + } - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. + for ( let i = 1; i <= segments; i ++ ) { - } ); + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - /** - * - * A Track of numeric keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function NumberKeyframeTrack( name, times, values, interpolation ) { + } - KeyframeTrack.call( this, name, times, values, interpolation ); + return { + tangents: tangents, + normals: normals, + binormals: binormals + }; } - NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + clone() { - constructor: NumberKeyframeTrack, + return new this.constructor().copy( this ); - ValueTypeName: 'number' + } - // ValueBufferType is inherited + copy( source ) { - // DefaultInterpolation is inherited + this.arcLengthDivisions = source.arcLengthDivisions; - } ); + return this; - /** - * Spherical linear unit quaternion interpolant. - * - * @author tschw - */ + } + + toJSON() { + + const data = { + metadata: { + version: 4.6, + type: 'Curve', + generator: 'Curve.toJSON' + } + }; - function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) { + data.arcLengthDivisions = this.arcLengthDivisions; + data.type = this.type; - Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + return data; } - QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), { + fromJSON( json ) { - constructor: QuaternionLinearInterpolant, + this.arcLengthDivisions = json.arcLengthDivisions; - interpolate_: function ( i1, t0, t, t1 ) { + return this; - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + } - offset = i1 * stride, +} - alpha = ( t - t0 ) / ( t1 - t0 ); +class EllipseCurve extends Curve { - for ( var end = offset + stride; offset !== end; offset += 4 ) { + constructor( aX = 0, aY = 0, xRadius = 1, yRadius = 1, aStartAngle = 0, aEndAngle = Math.PI * 2, aClockwise = false, aRotation = 0 ) { - Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); + super(); - } + this.isEllipseCurve = true; - return result; + this.type = 'EllipseCurve'; - } + this.aX = aX; + this.aY = aY; - } ); + this.xRadius = xRadius; + this.yRadius = yRadius; - /** - * - * A Track of quaternion keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - function QuaternionKeyframeTrack( name, times, values, interpolation ) { + this.aClockwise = aClockwise; - KeyframeTrack.call( this, name, times, values, interpolation ); + this.aRotation = aRotation; } - QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + getPoint( t, optionalTarget ) { - constructor: QuaternionKeyframeTrack, + const point = optionalTarget || new Vector2(); - ValueTypeName: 'quaternion', + const twoPi = Math.PI * 2; + let deltaAngle = this.aEndAngle - this.aStartAngle; + const samePoints = Math.abs( deltaAngle ) < Number.EPSILON; - // ValueBufferType is inherited + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; - DefaultInterpolation: InterpolateLinear, + if ( deltaAngle < Number.EPSILON ) { - InterpolantFactoryMethodLinear: function ( result ) { + if ( samePoints ) { - return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); + deltaAngle = 0; - }, + } else { - InterpolantFactoryMethodSmooth: undefined // not yet implemented + deltaAngle = twoPi; - } ); + } - /** - * - * A Track that interpolates Strings - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function StringKeyframeTrack( name, times, values, interpolation ) { + if ( this.aClockwise === true && ! samePoints ) { - KeyframeTrack.call( this, name, times, values, interpolation ); + if ( deltaAngle === twoPi ) { - } + deltaAngle = - twoPi; - StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + } else { - constructor: StringKeyframeTrack, + deltaAngle = deltaAngle - twoPi; - ValueTypeName: 'string', - ValueBufferType: Array, + } - DefaultInterpolation: InterpolateDiscrete, + } - InterpolantFactoryMethodLinear: undefined, + const angle = this.aStartAngle + t * deltaAngle; + let x = this.aX + this.xRadius * Math.cos( angle ); + let y = this.aY + this.yRadius * Math.sin( angle ); - InterpolantFactoryMethodSmooth: undefined + if ( this.aRotation !== 0 ) { - } ); + const cos = Math.cos( this.aRotation ); + const sin = Math.sin( this.aRotation ); - /** - * - * A Track of vectored keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + const tx = x - this.aX; + const ty = y - this.aY; + + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; - function VectorKeyframeTrack( name, times, values, interpolation ) { + } - KeyframeTrack.call( this, name, times, values, interpolation ); + return point.set( x, y ); } - VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), { + copy( source ) { - constructor: VectorKeyframeTrack, + super.copy( source ); - ValueTypeName: 'vector' + this.aX = source.aX; + this.aY = source.aY; - // ValueBufferType is inherited + this.xRadius = source.xRadius; + this.yRadius = source.yRadius; - // DefaultInterpolation is inherited + this.aStartAngle = source.aStartAngle; + this.aEndAngle = source.aEndAngle; - } ); + this.aClockwise = source.aClockwise; - /** - * - * Reusable set of Tracks that represent an animation. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + this.aRotation = source.aRotation; - function AnimationClip( name, duration, tracks ) { + return this; - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : - 1; + } - this.uuid = _Math.generateUUID(); + toJSON() { - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + const data = super.toJSON(); - this.resetDuration(); + data.aX = this.aX; + data.aY = this.aY; - } + data.xRadius = this.xRadius; + data.yRadius = this.yRadius; - } + data.aStartAngle = this.aStartAngle; + data.aEndAngle = this.aEndAngle; - function getTrackTypeForValueTypeName( typeName ) { + data.aClockwise = this.aClockwise; - switch ( typeName.toLowerCase() ) { + data.aRotation = this.aRotation; - case 'scalar': - case 'double': - case 'float': - case 'number': - case 'integer': + return data; - return NumberKeyframeTrack; + } - case 'vector': - case 'vector2': - case 'vector3': - case 'vector4': + fromJSON( json ) { - return VectorKeyframeTrack; + super.fromJSON( json ); - case 'color': + this.aX = json.aX; + this.aY = json.aY; - return ColorKeyframeTrack; + this.xRadius = json.xRadius; + this.yRadius = json.yRadius; - case 'quaternion': + this.aStartAngle = json.aStartAngle; + this.aEndAngle = json.aEndAngle; - return QuaternionKeyframeTrack; + this.aClockwise = json.aClockwise; - case 'bool': - case 'boolean': + this.aRotation = json.aRotation; - return BooleanKeyframeTrack; + return this; - case 'string': + } - return StringKeyframeTrack; +} - } +class ArcCurve extends EllipseCurve { - throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); + constructor( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - } + super( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - function parseKeyframeTrack( json ) { + this.isArcCurve = true; - if ( json.type === undefined ) { + this.type = 'ArcCurve'; - throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); + } - } +} - var trackType = getTrackTypeForValueTypeName( json.type ); +/** + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ - if ( json.times === undefined ) { - var times = [], values = []; +/* +Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM - AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); +This CubicPoly class could be used for reusing some variables and calculations, +but for three.js curve use, it could be possible inlined and flatten into a single function call +which can be placed in CurveUtils. +*/ - json.times = times; - json.values = values; +function CubicPoly() { - } + let c0 = 0, c1 = 0, c2 = 0, c3 = 0; - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + function init( x0, x1, t0, t1 ) { - return trackType.parse( json ); + c0 = x0; + c1 = t0; + c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + c3 = 2 * x0 - 2 * x1 + t0 + t1; - } else { + } - // by default, we assume a constructor compatible with the base - return new trackType( json.name, json.times, json.values, json.interpolation ); + return { - } + initCatmullRom: function ( x0, x1, x2, x3, tension ) { - } + init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - Object.assign( AnimationClip, { + }, - parse: function ( json ) { + initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { - var tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + // compute tangents when parameterized in [t1,t2] + let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); + init( x1, x2, t1, t2 ); - } + }, - return new AnimationClip( json.name, json.duration, tracks ); + calc: function ( t ) { - }, + const t2 = t * t; + const t3 = t2 * t; + return c0 + c1 * t + c2 * t2 + c3 * t3; - toJSON: function ( clip ) { + } - var tracks = [], - clipTracks = clip.tracks; + }; - var json = { +} - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks, - 'uuid': clip.uuid +// - }; +const tmp = /*@__PURE__*/ new Vector3(); +const px = /*@__PURE__*/ new CubicPoly(); +const py = /*@__PURE__*/ new CubicPoly(); +const pz = /*@__PURE__*/ new CubicPoly(); - for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { +class CatmullRomCurve3 extends Curve { - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + constructor( points = [], closed = false, curveType = 'centripetal', tension = 0.5 ) { - } + super(); - return json; + this.isCatmullRomCurve3 = true; - }, + this.type = 'CatmullRomCurve3'; - CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) { + this.points = points; + this.closed = closed; + this.curveType = curveType; + this.tension = tension; - var numMorphTargets = morphTargetSequence.length; - var tracks = []; + } - for ( var i = 0; i < numMorphTargets; i ++ ) { + getPoint( t, optionalTarget = new Vector3() ) { - var times = []; - var values = []; + const point = optionalTarget; - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + const points = this.points; + const l = points.length; - values.push( 0, 1, 0 ); + const p = ( l - ( this.closed ? 0 : 1 ) ) * t; + let intPoint = Math.floor( p ); + let weight = p - intPoint; - var order = AnimationUtils.getKeyframeOrder( times ); - times = AnimationUtils.sortedArray( times, 1, order ); - values = AnimationUtils.sortedArray( values, 1, order ); + if ( this.closed ) { - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; - times.push( numMorphTargets ); - values.push( values[ 0 ] ); + } else if ( weight === 0 && intPoint === l - 1 ) { - } + intPoint = l - 2; + weight = 1; - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); + } - } + let p0, p3; // 4 points (p1 & p2 defined below) - return new AnimationClip( name, - 1, tracks ); + if ( this.closed || intPoint > 0 ) { - }, + p0 = points[ ( intPoint - 1 ) % l ]; - findByName: function ( objectOrClipArray, name ) { + } else { - var clipArray = objectOrClipArray; + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; - if ( ! Array.isArray( objectOrClipArray ) ) { + } - var o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + const p1 = points[ intPoint % l ]; + const p2 = points[ ( intPoint + 1 ) % l ]; - } + if ( this.closed || intPoint + 2 < l ) { - for ( var i = 0; i < clipArray.length; i ++ ) { + p3 = points[ ( intPoint + 2 ) % l ]; - if ( clipArray[ i ].name === name ) { + } else { - return clipArray[ i ]; + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; - } + } - } + if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { - return null; + // init Centripetal / Chordal Catmull-Rom + const pow = this.curveType === 'chordal' ? 0.5 : 0.25; + let dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + let dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + let dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - }, + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; - CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) { + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); - var animationToMorphTargets = {}; + } else if ( this.curveType === 'catmullrom' ) { - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - var pattern = /^([\w-]*?)([\d]+)$/; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + } - var morphTarget = morphTargets[ i ]; - var parts = morphTarget.name.match( pattern ); + point.set( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - if ( parts && parts.length > 1 ) { + return point; - var name = parts[ 1 ]; + } - var animationMorphTargets = animationToMorphTargets[ name ]; - if ( ! animationMorphTargets ) { + copy( source ) { - animationToMorphTargets[ name ] = animationMorphTargets = []; + super.copy( source ); - } + this.points = []; - animationMorphTargets.push( morphTarget ); + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - } + const point = source.points[ i ]; - } + this.points.push( point.clone() ); - var clips = []; + } - for ( var name in animationToMorphTargets ) { + this.closed = source.closed; + this.curveType = source.curveType; + this.tension = source.tension; - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + return this; - } + } - return clips; + toJSON() { - }, + const data = super.toJSON(); - // parse the animation.hierarchy format - parseAnimation: function ( animation, bones ) { + data.points = []; - if ( ! animation ) { + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); - return null; + const point = this.points[ i ]; + data.points.push( point.toArray() ); - } + } - var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { + data.closed = this.closed; + data.curveType = this.curveType; + data.tension = this.tension; - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + return data; - var times = []; - var values = []; + } - AnimationUtils.flattenJSON( animationKeys, times, values, propertyName ); + fromJSON( json ) { - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + super.fromJSON( json ); - destTracks.push( new trackType( trackName, times, values ) ); + this.points = []; - } + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - } + const point = json.points[ i ]; + this.points.push( new Vector3().fromArray( point ) ); - }; + } - var tracks = []; + this.closed = json.closed; + this.curveType = json.curveType; + this.tension = json.tension; - var clipName = animation.name || 'default'; - // automatic length determination in AnimationClip. - var duration = animation.length || - 1; - var fps = animation.fps || 30; + return this; - var hierarchyTracks = animation.hierarchy || []; + } - for ( var h = 0; h < hierarchyTracks.length; h ++ ) { +} - var animationKeys = hierarchyTracks[ h ].keys; +/** + * Bezier Curves formulas obtained from + * https://en.wikipedia.org/wiki/B%C3%A9zier_curve + */ - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; +function CatmullRom( t, p0, p1, p2, p3 ) { - // process morph targets - if ( animationKeys[ 0 ].morphTargets ) { + const v0 = ( p2 - p0 ) * 0.5; + const v1 = ( p3 - p1 ) * 0.5; + const t2 = t * t; + const t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; - // figure out all morph targets used in this track - var morphTargetNames = {}; +} - for ( var k = 0; k < animationKeys.length; k ++ ) { +// - if ( animationKeys[ k ].morphTargets ) { +function QuadraticBezierP0( t, p ) { - for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { + const k = 1 - t; + return k * k * p; - morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; +} - } +function QuadraticBezierP1( t, p ) { - } + return 2 * ( 1 - t ) * t * p; - } +} - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( var morphTargetName in morphTargetNames ) { +function QuadraticBezierP2( t, p ) { - var times = []; - var values = []; + return t * t * p; - for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { +} - var animationKey = animationKeys[ k ]; +function QuadraticBezier( t, p0, p1, p2 ) { - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); + return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + + QuadraticBezierP2( t, p2 ); - } +} - tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); +// - } +function CubicBezierP0( t, p ) { - duration = morphTargetNames.length * ( fps || 1.0 ); + const k = 1 - t; + return k * k * k * p; - } else { +} - // ...assume skeletal animation +function CubicBezierP1( t, p ) { - var boneName = '.bones[' + bones[ h ].name + ']'; + const k = 1 - t; + return 3 * k * k * t * p; - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); +} - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); +function CubicBezierP2( t, p ) { - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); + return 3 * ( 1 - t ) * t * t * p; - } +} - } +function CubicBezierP3( t, p ) { - if ( tracks.length === 0 ) { + return t * t * t * p; - return null; +} - } +function CubicBezier( t, p0, p1, p2, p3 ) { - var clip = new AnimationClip( clipName, duration, tracks ); + return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + + CubicBezierP3( t, p3 ); - return clip; +} - } +class CubicBezierCurve extends Curve { - } ); + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2(), v3 = new Vector2() ) { - Object.assign( AnimationClip.prototype, { + super(); - resetDuration: function () { + this.isCubicBezierCurve = true; - var tracks = this.tracks, duration = 0; + this.type = 'CubicBezierCurve'; - for ( var i = 0, n = tracks.length; i !== n; ++ i ) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - var track = this.tracks[ i ]; + } - duration = Math.max( duration, track.times[ track.times.length - 1 ] ); + getPoint( t, optionalTarget = new Vector2() ) { - } + const point = optionalTarget; - this.duration = duration; + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - return this; - - }, + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) + ); - trim: function () { + return point; - for ( var i = 0; i < this.tracks.length; i ++ ) { + } - this.tracks[ i ].trim( 0, this.duration ); + copy( source ) { - } + super.copy( source ); - return this; + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - }, + return this; - validate: function () { + } - var valid = true; + toJSON() { - for ( var i = 0; i < this.tracks.length; i ++ ) { + const data = super.toJSON(); - valid = valid && this.tracks[ i ].validate(); + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - } + return data; - return valid; + } - }, + fromJSON( json ) { - optimize: function () { + super.fromJSON( json ); - for ( var i = 0; i < this.tracks.length; i ++ ) { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - this.tracks[ i ].optimize(); + return this; - } + } - return this; +} - }, +class CubicBezierCurve3 extends Curve { + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3(), v3 = new Vector3() ) { - clone: function () { + super(); - var tracks = []; + this.isCubicBezierCurve3 = true; - for ( var i = 0; i < this.tracks.length; i ++ ) { + this.type = 'CubicBezierCurve3'; - tracks.push( this.tracks[ i ].clone() ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - } + } - return new AnimationClip( this.name, this.duration, tracks ); + getPoint( t, optionalTarget = new Vector3() ) { - } + const point = optionalTarget; - } ); + const v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; - /** - * @author mrdoob / http://mrdoob.com/ - */ + point.set( + CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), + CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), + CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) + ); - var Cache = { + return point; - enabled: false, + } - files: {}, + copy( source ) { - add: function ( key, file ) { + super.copy( source ); - if ( this.enabled === false ) return; + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); + this.v3.copy( source.v3 ); - // console.log( 'THREE.Cache', 'Adding key:', key ); + return this; - this.files[ key ] = file; + } - }, + toJSON() { - get: function ( key ) { + const data = super.toJSON(); - if ( this.enabled === false ) return; + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); + data.v3 = this.v3.toArray(); - // console.log( 'THREE.Cache', 'Checking key:', key ); + return data; - return this.files[ key ]; + } - }, + fromJSON( json ) { - remove: function ( key ) { + super.fromJSON( json ); - delete this.files[ key ]; + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); + this.v3.fromArray( json.v3 ); - }, + return this; - clear: function () { + } - this.files = {}; +} - } +class LineCurve extends Curve { - }; + constructor( v1 = new Vector2(), v2 = new Vector2() ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + super(); - function LoadingManager( onLoad, onProgress, onError ) { + this.isLineCurve = true; - var scope = this; + this.type = 'LineCurve'; - var isLoading = false; - var itemsLoaded = 0; - var itemsTotal = 0; - var urlModifier = undefined; + this.v1 = v1; + this.v2 = v2; - // Refer to #5689 for the reason why we don't set .onStart - // in the constructor + } - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + getPoint( t, optionalTarget = new Vector2() ) { - this.itemStart = function ( url ) { + const point = optionalTarget; - itemsTotal ++; + if ( t === 1 ) { - if ( isLoading === false ) { + point.copy( this.v2 ); - if ( scope.onStart !== undefined ) { + } else { - scope.onStart( url, itemsLoaded, itemsTotal ); + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - } + } - } + return point; - isLoading = true; + } - }; + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - this.itemEnd = function ( url ) { + return this.getPoint( u, optionalTarget ); - itemsLoaded ++; + } - if ( scope.onProgress !== undefined ) { + getTangent( t, optionalTarget = new Vector2() ) { - scope.onProgress( url, itemsLoaded, itemsTotal ); + return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); - } + } - if ( itemsLoaded === itemsTotal ) { + getTangentAt( u, optionalTarget ) { - isLoading = false; + return this.getTangent( u, optionalTarget ); - if ( scope.onLoad !== undefined ) { + } - scope.onLoad(); + copy( source ) { - } + super.copy( source ); - } + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - }; + return this; - this.itemError = function ( url ) { + } - if ( scope.onError !== undefined ) { + toJSON() { - scope.onError( url ); + const data = super.toJSON(); - } + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - }; + return data; - this.resolveURL = function ( url ) { + } - if ( urlModifier ) { + fromJSON( json ) { - return urlModifier( url ); + super.fromJSON( json ); - } + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - return url; + return this; - }; + } - this.setURLModifier = function ( transform ) { +} - urlModifier = transform; - return this; +class LineCurve3 extends Curve { - }; + constructor( v1 = new Vector3(), v2 = new Vector3() ) { - } + super(); - var DefaultLoadingManager = new LoadingManager(); + this.isLineCurve3 = true; - /** - * @author alteredq / http://alteredqualia.com/ - */ + this.type = 'LineCurve3'; - function Loader( manager ) { + this.v1 = v1; + this.v2 = v2; - this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; + } + getPoint( t, optionalTarget = new Vector3() ) { - this.crossOrigin = 'anonymous'; - this.path = ''; - this.resourcePath = ''; + const point = optionalTarget; - } + if ( t === 1 ) { - Object.assign( Loader.prototype, { + point.copy( this.v2 ); - load: function ( /* url, onLoad, onProgress, onError */ ) {}, + } else { - parse: function ( /* data */ ) {}, + point.copy( this.v2 ).sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - setCrossOrigin: function ( crossOrigin ) { + } - this.crossOrigin = crossOrigin; - return this; + return point; - }, + } + // Line curve is linear, so we can overwrite default getPointAt + getPointAt( u, optionalTarget ) { - setPath: function ( path ) { + return this.getPoint( u, optionalTarget ); - this.path = path; - return this; + } - }, + getTangent( t, optionalTarget = new Vector3() ) { - setResourcePath: function ( resourcePath ) { + return optionalTarget.subVectors( this.v2, this.v1 ).normalize(); - this.resourcePath = resourcePath; - return this; + } - } + getTangentAt( u, optionalTarget ) { - } ); + return this.getTangent( u, optionalTarget ); - // + } - Loader.Handlers = { + copy( source ) { - handlers: [], + super.copy( source ); - add: function ( regex, loader ) { + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - this.handlers.push( regex, loader ); + return this; - }, + } + toJSON() { - get: function ( file ) { + const data = super.toJSON(); - var handlers = this.handlers; + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + return data; - var regex = handlers[ i ]; - var loader = handlers[ i + 1 ]; + } + fromJSON( json ) { - if ( regex.test( file ) ) { + super.fromJSON( json ); - return loader; + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - } + return this; - } + } - return null; +} - } +class QuadraticBezierCurve extends Curve { - }; + constructor( v0 = new Vector2(), v1 = new Vector2(), v2 = new Vector2() ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + super(); - var loading = {}; + this.isQuadraticBezierCurve = true; - function FileLoader( manager ) { + this.type = 'QuadraticBezierCurve'; - Loader.call( this, manager ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; } - FileLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + getPoint( t, optionalTarget = new Vector2() ) { - constructor: FileLoader, + const point = optionalTarget; - load: function ( url, onLoad, onProgress, onError ) { + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - if ( url === undefined ) url = ''; + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ) + ); - if ( this.path !== undefined ) url = this.path + url; + return point; - url = this.manager.resolveURL( url ); + } - var scope = this; + copy( source ) { - var cached = Cache.get( url ); + super.copy( source ); - if ( cached !== undefined ) { + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - scope.manager.itemStart( url ); + return this; - setTimeout( function () { + } - if ( onLoad ) onLoad( cached ); + toJSON() { - scope.manager.itemEnd( url ); + const data = super.toJSON(); - }, 0 ); + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - return cached; + return data; - } + } - // Check if request is duplicate + fromJSON( json ) { - if ( loading[ url ] !== undefined ) { + super.fromJSON( json ); - loading[ url ].push( { + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - onLoad: onLoad, - onProgress: onProgress, - onError: onError + return this; - } ); + } - return; +} - } +class QuadraticBezierCurve3 extends Curve { - // Check for data: URI - var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; - var dataUriRegexResult = url.match( dataUriRegex ); + constructor( v0 = new Vector3(), v1 = new Vector3(), v2 = new Vector3() ) { - // Safari can not handle Data URIs through XMLHttpRequest so process manually - if ( dataUriRegexResult ) { + super(); - var mimeType = dataUriRegexResult[ 1 ]; - var isBase64 = !! dataUriRegexResult[ 2 ]; - var data = dataUriRegexResult[ 3 ]; + this.isQuadraticBezierCurve3 = true; - data = decodeURIComponent( data ); + this.type = 'QuadraticBezierCurve3'; - if ( isBase64 ) data = atob( data ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - try { + } - var response; - var responseType = ( this.responseType || '' ).toLowerCase(); + getPoint( t, optionalTarget = new Vector3() ) { - switch ( responseType ) { + const point = optionalTarget; - case 'arraybuffer': - case 'blob': + const v0 = this.v0, v1 = this.v1, v2 = this.v2; - var view = new Uint8Array( data.length ); + point.set( + QuadraticBezier( t, v0.x, v1.x, v2.x ), + QuadraticBezier( t, v0.y, v1.y, v2.y ), + QuadraticBezier( t, v0.z, v1.z, v2.z ) + ); - for ( var i = 0; i < data.length; i ++ ) { + return point; - view[ i ] = data.charCodeAt( i ); + } - } + copy( source ) { - if ( responseType === 'blob' ) { + super.copy( source ); - response = new Blob( [ view.buffer ], { type: mimeType } ); + this.v0.copy( source.v0 ); + this.v1.copy( source.v1 ); + this.v2.copy( source.v2 ); - } else { + return this; - response = view.buffer; + } - } + toJSON() { - break; + const data = super.toJSON(); - case 'document': + data.v0 = this.v0.toArray(); + data.v1 = this.v1.toArray(); + data.v2 = this.v2.toArray(); - var parser = new DOMParser(); - response = parser.parseFromString( data, mimeType ); + return data; - break; + } - case 'json': + fromJSON( json ) { - response = JSON.parse( data ); + super.fromJSON( json ); - break; + this.v0.fromArray( json.v0 ); + this.v1.fromArray( json.v1 ); + this.v2.fromArray( json.v2 ); - default: // 'text' or other + return this; - response = data; + } - break; +} - } +class SplineCurve extends Curve { - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { + constructor( points = [] ) { - if ( onLoad ) onLoad( response ); + super(); - scope.manager.itemEnd( url ); + this.isSplineCurve = true; - }, 0 ); + this.type = 'SplineCurve'; - } catch ( error ) { + this.points = points; - // Wait for next browser tick like standard XMLHttpRequest event dispatching does - setTimeout( function () { - - if ( onError ) onError( error ); - - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); - - }, 0 ); - - } - - } else { - - // Initialise array for duplicate requests - - loading[ url ] = []; - - loading[ url ].push( { - - onLoad: onLoad, - onProgress: onProgress, - onError: onError - - } ); + } - var request = new XMLHttpRequest(); + getPoint( t, optionalTarget = new Vector2() ) { - request.open( 'GET', url, true ); + const point = optionalTarget; - request.addEventListener( 'load', function ( event ) { + const points = this.points; + const p = ( points.length - 1 ) * t; - var response = this.response; + const intPoint = Math.floor( p ); + const weight = p - intPoint; - Cache.add( url, response ); + const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; + const p1 = points[ intPoint ]; + const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - var callbacks = loading[ url ]; + point.set( + CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), + CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) + ); - delete loading[ url ]; + return point; - if ( this.status === 200 || this.status === 0 ) { + } - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. + copy( source ) { - if ( this.status === 0 ) console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); + super.copy( source ); - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + this.points = []; - var callback = callbacks[ i ]; - if ( callback.onLoad ) callback.onLoad( response ); + for ( let i = 0, l = source.points.length; i < l; i ++ ) { - } + const point = source.points[ i ]; - scope.manager.itemEnd( url ); + this.points.push( point.clone() ); - } else { + } - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + return this; - var callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + } - } + toJSON() { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + const data = super.toJSON(); - } + data.points = []; - }, false ); + for ( let i = 0, l = this.points.length; i < l; i ++ ) { - request.addEventListener( 'progress', function ( event ) { + const point = this.points[ i ]; + data.points.push( point.toArray() ); - var callbacks = loading[ url ]; + } - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + return data; - var callback = callbacks[ i ]; - if ( callback.onProgress ) callback.onProgress( event ); + } - } + fromJSON( json ) { - }, false ); + super.fromJSON( json ); - request.addEventListener( 'error', function ( event ) { + this.points = []; - var callbacks = loading[ url ]; + for ( let i = 0, l = json.points.length; i < l; i ++ ) { - delete loading[ url ]; + const point = json.points[ i ]; + this.points.push( new Vector2().fromArray( point ) ); - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + } - var callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + return this; - } + } - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); +} - }, false ); +var Curves = /*#__PURE__*/Object.freeze({ + __proto__: null, + ArcCurve: ArcCurve, + CatmullRomCurve3: CatmullRomCurve3, + CubicBezierCurve: CubicBezierCurve, + CubicBezierCurve3: CubicBezierCurve3, + EllipseCurve: EllipseCurve, + LineCurve: LineCurve, + LineCurve3: LineCurve3, + QuadraticBezierCurve: QuadraticBezierCurve, + QuadraticBezierCurve3: QuadraticBezierCurve3, + SplineCurve: SplineCurve +}); - request.addEventListener( 'abort', function ( event ) { +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ - var callbacks = loading[ url ]; +class CurvePath extends Curve { - delete loading[ url ]; + constructor() { - for ( var i = 0, il = callbacks.length; i < il; i ++ ) { + super(); - var callback = callbacks[ i ]; - if ( callback.onError ) callback.onError( event ); + this.type = 'CurvePath'; - } + this.curves = []; + this.autoClose = false; // Automatically closes the path - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + } - }, false ); + add( curve ) { - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + this.curves.push( curve ); - if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' ); + } - for ( var header in this.requestHeader ) { + closePath() { - request.setRequestHeader( header, this.requestHeader[ header ] ); + // Add a line curve if start and end of lines are not connected + const startPoint = this.curves[ 0 ].getPoint( 0 ); + const endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - } + if ( ! startPoint.equals( endPoint ) ) { - request.send( null ); + const lineType = ( startPoint.isVector2 === true ) ? 'LineCurve' : 'LineCurve3'; + this.curves.push( new Curves[ lineType ]( endPoint, startPoint ) ); - } + } - scope.manager.itemStart( url ); + return this; - return request; + } - }, + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: - setResponseType: function ( value ) { + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') - this.responseType = value; - return this; + getPoint( t, optionalTarget ) { - }, + const d = t * this.getLength(); + const curveLengths = this.getCurveLengths(); + let i = 0; - setWithCredentials: function ( value ) { + // To think about boundaries points. - this.withCredentials = value; - return this; + while ( i < curveLengths.length ) { - }, + if ( curveLengths[ i ] >= d ) { - setMimeType: function ( value ) { + const diff = curveLengths[ i ] - d; + const curve = this.curves[ i ]; - this.mimeType = value; - return this; + const segmentLength = curve.getLength(); + const u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; - }, + return curve.getPointAt( u, optionalTarget ); - setRequestHeader: function ( value ) { + } - this.requestHeader = value; - return this; + i ++; } - } ); - - /** - * @author bhouston / http://clara.io/ - */ + return null; - function AnimationLoader( manager ) { - - Loader.call( this, manager ); + // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { - } else { + points.push( points[ 0 ] ); - // compressed cubemap texture stored in a single DDS file + } - loader.load( url, function ( buffer ) { + return points; - var texDatas = scope._parser( buffer, true ); + } - if ( texDatas.isCubemap ) { + copy( source ) { - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + super.copy( source ); - for ( var f = 0; f < faces; f ++ ) { + this.curves = []; - images[ f ] = { mipmaps: [] }; + for ( let i = 0, l = source.curves.length; i < l; i ++ ) { - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + const curve = source.curves[ i ]; - images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); - images[ f ].format = texDatas.format; - images[ f ].width = texDatas.width; - images[ f ].height = texDatas.height; + this.curves.push( curve.clone() ); - } + } - } + this.autoClose = source.autoClose; - } else { + return this; - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; + } - } + toJSON() { - if ( texDatas.mipmapCount === 1 ) { + const data = super.toJSON(); - texture.minFilter = LinearFilter; + data.autoClose = this.autoClose; + data.curves = []; - } + for ( let i = 0, l = this.curves.length; i < l; i ++ ) { - texture.format = texDatas.format; - texture.needsUpdate = true; + const curve = this.curves[ i ]; + data.curves.push( curve.toJSON() ); - if ( onLoad ) onLoad( texture ); + } - }, onProgress, onError ); + return data; - } + } - return texture; + fromJSON( json ) { - } + super.fromJSON( json ); - } ); + this.autoClose = json.autoClose; + this.curves = []; - /** - * @author Nikos M. / https://github.com/foo123/ - * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) - */ + for ( let i = 0, l = json.curves.length; i < l; i ++ ) { - function DataTextureLoader( manager ) { + const curve = json.curves[ i ]; + this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); - Loader.call( this, manager ); + } - // override in sub classes - this._parser = null; + return this; } - DataTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { +} - constructor: DataTextureLoader, +class Path extends CurvePath { - load: function ( url, onLoad, onProgress, onError ) { + constructor( points ) { - var scope = this; + super(); - var texture = new DataTexture(); + this.type = 'Path'; - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.load( url, function ( buffer ) { + this.currentPoint = new Vector2(); - var texData = scope._parser( buffer ); + if ( points ) { - if ( ! texData ) return; + this.setFromPoints( points ); - if ( texData.image !== undefined ) { + } - texture.image = texData.image; + } - } else if ( texData.data !== undefined ) { + setFromPoints( points ) { - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; + this.moveTo( points[ 0 ].x, points[ 0 ].y ); - } + for ( let i = 1, l = points.length; i < l; i ++ ) { - texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; + this.lineTo( points[ i ].x, points[ i ].y ); - texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; - texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearMipmapLinearFilter; + } - texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; + return this; - if ( texData.format !== undefined ) { + } - texture.format = texData.format; + moveTo( x, y ) { - } - if ( texData.type !== undefined ) { + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? - texture.type = texData.type; + return this; - } + } - if ( texData.mipmaps !== undefined ) { + lineTo( x, y ) { - texture.mipmaps = texData.mipmaps; + const curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); + this.curves.push( curve ); - } + this.currentPoint.set( x, y ); - if ( texData.mipmapCount === 1 ) { + return this; - texture.minFilter = LinearFilter; + } - } + quadraticCurveTo( aCPx, aCPy, aX, aY ) { - texture.needsUpdate = true; + const curve = new QuadraticBezierCurve( + this.currentPoint.clone(), + new Vector2( aCPx, aCPy ), + new Vector2( aX, aY ) + ); - if ( onLoad ) onLoad( texture, texData ); + this.curves.push( curve ); - }, onProgress, onError ); + this.currentPoint.set( aX, aY ); + return this; - return texture; + } - } + bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - } ); + const curve = new CubicBezierCurve( + this.currentPoint.clone(), + new Vector2( aCP1x, aCP1y ), + new Vector2( aCP2x, aCP2y ), + new Vector2( aX, aY ) + ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + this.curves.push( curve ); - function ImageLoader( manager ) { + this.currentPoint.set( aX, aY ); - Loader.call( this, manager ); + return this; } - ImageLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + splineThru( pts /*Array of Vector*/ ) { - constructor: ImageLoader, + const npts = [ this.currentPoint.clone() ].concat( pts ); - load: function ( url, onLoad, onProgress, onError ) { + const curve = new SplineCurve( npts ); + this.curves.push( curve ); - if ( this.path !== undefined ) url = this.path + url; + this.currentPoint.copy( pts[ pts.length - 1 ] ); - url = this.manager.resolveURL( url ); + return this; - var scope = this; + } - var cached = Cache.get( url ); + arc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - if ( cached !== undefined ) { + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - scope.manager.itemStart( url ); + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - setTimeout( function () { + return this; - if ( onLoad ) onLoad( cached ); + } - scope.manager.itemEnd( url ); + absarc( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - }, 0 ); + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - return cached; + return this; - } + } - var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + ellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - function onImageLoad() { + const x0 = this.currentPoint.x; + const y0 = this.currentPoint.y; - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - Cache.add( url, this ); + return this; - if ( onLoad ) onLoad( this ); + } - scope.manager.itemEnd( url ); + absellipse( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - } + const curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); - function onImageError( event ) { + if ( this.curves.length > 0 ) { - image.removeEventListener( 'load', onImageLoad, false ); - image.removeEventListener( 'error', onImageError, false ); + // if a previous curve is present, attempt to join + const firstPoint = curve.getPoint( 0 ); - if ( onError ) onError( event ); + if ( ! firstPoint.equals( this.currentPoint ) ) { - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + this.lineTo( firstPoint.x, firstPoint.y ); } - image.addEventListener( 'load', onImageLoad, false ); - image.addEventListener( 'error', onImageError, false ); + } - if ( url.substr( 0, 5 ) !== 'data:' ) { + this.curves.push( curve ); - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + const lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); - } + return this; - scope.manager.itemStart( url ); + } - image.src = url; + copy( source ) { - return image; + super.copy( source ); - } + this.currentPoint.copy( source.currentPoint ); - } ); + return this; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } + + toJSON() { + const data = super.toJSON(); - function CubeTextureLoader( manager ) { + data.currentPoint = this.currentPoint.toArray(); - Loader.call( this, manager ); + return data; } - CubeTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + fromJSON( json ) { - constructor: CubeTextureLoader, + super.fromJSON( json ); - load: function ( urls, onLoad, onProgress, onError ) { + this.currentPoint.fromArray( json.currentPoint ); - var texture = new CubeTexture(); + return this; - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + } - var loaded = 0; +} - function loadTexture( i ) { +class LatheGeometry extends BufferGeometry { - loader.load( urls[ i ], function ( image ) { + constructor( points = [ new Vector2( 0, - 0.5 ), new Vector2( 0.5, 0 ), new Vector2( 0, 0.5 ) ], segments = 12, phiStart = 0, phiLength = Math.PI * 2 ) { - texture.images[ i ] = image; + super(); - loaded ++; + this.type = 'LatheGeometry'; - if ( loaded === 6 ) { + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; - texture.needsUpdate = true; + segments = Math.floor( segments ); - if ( onLoad ) onLoad( texture ); + // clamp phiLength so it's in range of [ 0, 2PI ] - } + phiLength = clamp( phiLength, 0, Math.PI * 2 ); - }, undefined, onError ); + // buffers - } + const indices = []; + const vertices = []; + const uvs = []; + const initNormals = []; + const normals = []; - for ( var i = 0; i < urls.length; ++ i ) { + // helper variables - loadTexture( i ); + const inverseSegments = 1.0 / segments; + const vertex = new Vector3(); + const uv = new Vector2(); + const normal = new Vector3(); + const curNormal = new Vector3(); + const prevNormal = new Vector3(); + let dx = 0; + let dy = 0; - } + // pre-compute normals for initial "meridian" - return texture; + for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { - } + switch ( j ) { - } ); + case 0: // special handling for 1st vertex on path - /** - * @author mrdoob / http://mrdoob.com/ - */ + dx = points[ j + 1 ].x - points[ j ].x; + dy = points[ j + 1 ].y - points[ j ].y; - function TextureLoader( manager ) { + normal.x = dy * 1.0; + normal.y = - dx; + normal.z = dy * 0.0; - Loader.call( this, manager ); + prevNormal.copy( normal ); - } + normal.normalize(); - TextureLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + initNormals.push( normal.x, normal.y, normal.z ); - constructor: TextureLoader, + break; - load: function ( url, onLoad, onProgress, onError ) { + case ( points.length - 1 ): // special handling for last Vertex on path - var texture = new Texture(); + initNormals.push( prevNormal.x, prevNormal.y, prevNormal.z ); - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + break; - loader.load( url, function ( image ) { + default: // default handling for all vertices in between - texture.image = image; + dx = points[ j + 1 ].x - points[ j ].x; + dy = points[ j + 1 ].y - points[ j ].y; - // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. - var isJPEG = url.search( /\.jpe?g($|\?)/i ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; + normal.x = dy * 1.0; + normal.y = - dx; + normal.z = dy * 0.0; - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; + curNormal.copy( normal ); - if ( onLoad !== undefined ) { + normal.x += prevNormal.x; + normal.y += prevNormal.y; + normal.z += prevNormal.z; - onLoad( texture ); + normal.normalize(); - } + initNormals.push( normal.x, normal.y, normal.z ); - }, onProgress, onError ); + prevNormal.copy( curNormal ); - return texture; + } } - } ); + // generate vertices, uvs and normals - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Extensible curve object - * - * Some common of curve methods: - * .getPoint( t, optionalTarget ), .getTangent( t ) - * .getPointAt( u, optionalTarget ), .getTangentAt( u ) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following curves inherit from THREE.Curve: - * - * -- 2D curves -- - * THREE.ArcCurve - * THREE.CubicBezierCurve - * THREE.EllipseCurve - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.SplineCurve - * - * -- 3D curves -- - * THREE.CatmullRomCurve3 - * THREE.CubicBezierCurve3 - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * - * A series of curves can be represented as a THREE.CurvePath. - * - **/ + for ( let i = 0; i <= segments; i ++ ) { - /************************************************************** - * Abstract Curve base class - **************************************************************/ + const phi = phiStart + i * inverseSegments * phiLength; - function Curve() { + const sin = Math.sin( phi ); + const cos = Math.cos( phi ); - this.type = 'Curve'; + for ( let j = 0; j <= ( points.length - 1 ); j ++ ) { - this.arcLengthDivisions = 200; + // vertex - } + vertex.x = points[ j ].x * sin; + vertex.y = points[ j ].y; + vertex.z = points[ j ].x * cos; + + vertices.push( vertex.x, vertex.y, vertex.z ); - Object.assign( Curve.prototype, { + // uv - // Virtual base class method to overwrite and implement in subclasses - // - t [0 .. 1] + uv.x = i / segments; + uv.y = j / ( points.length - 1 ); - getPoint: function ( /* t, optionalTarget */ ) { + uvs.push( uv.x, uv.y ); - console.warn( 'THREE.Curve: .getPoint() not implemented.' ); - return null; + // normal - }, + const x = initNormals[ 3 * j + 0 ] * sin; + const y = initNormals[ 3 * j + 1 ]; + const z = initNormals[ 3 * j + 0 ] * cos; - // Get point at relative position in curve according to arc length - // - u [0 .. 1] + normals.push( x, y, z ); - getPointAt: function ( u, optionalTarget ) { + } - var t = this.getUtoTmapping( u ); - return this.getPoint( t, optionalTarget ); + } - }, + // indices - // Get sequence of points using getPoint( t ) + for ( let i = 0; i < segments; i ++ ) { - getPoints: function ( divisions ) { + for ( let j = 0; j < ( points.length - 1 ); j ++ ) { - if ( divisions === undefined ) divisions = 5; + const base = j + i * points.length; - var points = []; + const a = base; + const b = base + points.length; + const c = base + points.length + 1; + const d = base + 1; - for ( var d = 0; d <= divisions; d ++ ) { + // faces - points.push( this.getPoint( d / divisions ) ); + indices.push( a, b, d ); + indices.push( c, d, b ); } - return points; + } - }, + // build geometry - // Get sequence of points using getPointAt( u ) + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); - getSpacedPoints: function ( divisions ) { + } - if ( divisions === undefined ) divisions = 5; + copy( source ) { - var points = []; + super.copy( source ); - for ( var d = 0; d <= divisions; d ++ ) { + this.parameters = Object.assign( {}, source.parameters ); - points.push( this.getPointAt( d / divisions ) ); + return this; - } + } - return points; + static fromJSON( data ) { - }, + return new LatheGeometry( data.points, data.segments, data.phiStart, data.phiLength ); - // Get total curve arc length + } - getLength: function () { +} - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; +class CapsuleGeometry extends LatheGeometry { - }, + constructor( radius = 1, length = 1, capSegments = 4, radialSegments = 8 ) { - // Get list of cumulative segment lengths + const path = new Path(); + path.absarc( 0, - length / 2, radius, Math.PI * 1.5, 0 ); + path.absarc( 0, length / 2, radius, 0, Math.PI * 0.5 ); - getLengths: function ( divisions ) { + super( path.getPoints( capSegments ), radialSegments ); - if ( divisions === undefined ) divisions = this.arcLengthDivisions; + this.type = 'CapsuleGeometry'; - if ( this.cacheArcLengths && - ( this.cacheArcLengths.length === divisions + 1 ) && - ! this.needsUpdate ) { + this.parameters = { + radius: radius, + length: length, + capSegments: capSegments, + radialSegments: radialSegments, + }; - return this.cacheArcLengths; + } - } + static fromJSON( data ) { - this.needsUpdate = false; + return new CapsuleGeometry( data.radius, data.length, data.capSegments, data.radialSegments ); - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; + } - cache.push( 0 ); +} - for ( p = 1; p <= divisions; p ++ ) { +class CircleGeometry extends BufferGeometry { - current = this.getPoint( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + constructor( radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2 ) { - } + super(); - this.cacheArcLengths = cache; + this.type = 'CircleGeometry'; - return cache; // { sums: cache, sum: sum }; Sum is in the last element. + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - }, + segments = Math.max( 3, segments ); - updateArcLengths: function () { + // buffers - this.needsUpdate = true; - this.getLengths(); + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - }, + // helper variables - // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + const vertex = new Vector3(); + const uv = new Vector2(); - getUtoTmapping: function ( u, distance ) { + // center point - var arcLengths = this.getLengths(); + vertices.push( 0, 0, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( 0.5, 0.5 ); - var i = 0, il = arcLengths.length; + for ( let s = 0, i = 3; s <= segments; s ++, i += 3 ) { - var targetArcLength; // The targeted u distance value to get + const segment = thetaStart + s / segments * thetaLength; - if ( distance ) { + // vertex - targetArcLength = distance; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); - } else { + vertices.push( vertex.x, vertex.y, vertex.z ); - targetArcLength = u * arcLengths[ il - 1 ]; + // normal - } + normals.push( 0, 0, 1 ); - // binary search for the index with largest value smaller than target u distance + // uvs - var low = 0, high = il - 1, comparison; + uv.x = ( vertices[ i ] / radius + 1 ) / 2; + uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2; - while ( low <= high ) { + uvs.push( uv.x, uv.y ); - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + } - comparison = arcLengths[ i ] - targetArcLength; + // indices - if ( comparison < 0 ) { + for ( let i = 1; i <= segments; i ++ ) { - low = i + 1; + indices.push( i, i + 1, 0 ); - } else if ( comparison > 0 ) { + } - high = i - 1; + // build geometry - } else { + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - high = i; - break; + } - // DONE + copy( source ) { - } + super.copy( source ); - } + this.parameters = Object.assign( {}, source.parameters ); - i = high; + return this; - if ( arcLengths[ i ] === targetArcLength ) { + } - return i / ( il - 1 ); + static fromJSON( data ) { - } + return new CircleGeometry( data.radius, data.segments, data.thetaStart, data.thetaLength ); - // we could get finer grain at lengths, or use simple interpolation between two points + } - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; +} - var segmentLength = lengthAfter - lengthBefore; +class CylinderGeometry extends BufferGeometry { - // determine where we are between the 'before' and 'after' points + constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + super(); - // add that fractional amount to t + this.type = 'CylinderGeometry'; - var t = ( i + segmentFraction ) / ( il - 1 ); + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - return t; + const scope = this; - }, + radialSegments = Math.floor( radialSegments ); + heightSegments = Math.floor( heightSegments ); - // Returns a unit vector tangent at t - // In case any sub curve does not implement its tangent derivation, - // 2 points a small delta apart will be used to find its gradient - // which seems to give a reasonable approximation + // buffers - getTangent: function ( t ) { + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; + // helper variables - // Capping in case of danger + let index = 0; + const indexArray = []; + const halfHeight = height / 2; + let groupStart = 0; - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + // generate geometry - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); + generateTorso(); - var vec = pt2.clone().sub( pt1 ); - return vec.normalize(); + if ( openEnded === false ) { - }, + if ( radiusTop > 0 ) generateCap( true ); + if ( radiusBottom > 0 ) generateCap( false ); - getTangentAt: function ( u ) { + } - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); + // build geometry - }, + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); - computeFrenetFrames: function ( segments, closed ) { + function generateTorso() { + + const normal = new Vector3(); + const vertex = new Vector3(); - // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf + let groupCount = 0; - var normal = new Vector3(); + // this will be used to calculate the normal + const slope = ( radiusBottom - radiusTop ) / height; - var tangents = []; - var normals = []; - var binormals = []; + // generate vertices, normals and uvs - var vec = new Vector3(); - var mat = new Matrix4(); + for ( let y = 0; y <= heightSegments; y ++ ) { - var i, u, theta; + const indexRow = []; - // compute the tangent vectors for each segment on the curve + const v = y / heightSegments; - for ( i = 0; i <= segments; i ++ ) { + // calculate the radius of the current row - u = i / segments; + const radius = v * ( radiusBottom - radiusTop ) + radiusTop; - tangents[ i ] = this.getTangentAt( u ); - tangents[ i ].normalize(); + for ( let x = 0; x <= radialSegments; x ++ ) { - } + const u = x / radialSegments; - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the minimum tangent xyz component + const theta = u * thetaLength + thetaStart; - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - var min = Number.MAX_VALUE; - var tx = Math.abs( tangents[ 0 ].x ); - var ty = Math.abs( tangents[ 0 ].y ); - var tz = Math.abs( tangents[ 0 ].z ); + const sinTheta = Math.sin( theta ); + const cosTheta = Math.cos( theta ); - if ( tx <= min ) { + // vertex - min = tx; - normal.set( 1, 0, 0 ); + vertex.x = radius * sinTheta; + vertex.y = - v * height + halfHeight; + vertex.z = radius * cosTheta; + vertices.push( vertex.x, vertex.y, vertex.z ); - } + // normal - if ( ty <= min ) { + normal.set( sinTheta, slope, cosTheta ).normalize(); + normals.push( normal.x, normal.y, normal.z ); - min = ty; - normal.set( 0, 1, 0 ); + // uv - } + uvs.push( u, 1 - v ); - if ( tz <= min ) { + // save index of vertex in respective row - normal.set( 0, 0, 1 ); + indexRow.push( index ++ ); - } + } - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + // now save vertices of the row in our index array - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); + indexArray.push( indexRow ); + } - // compute the slowly-varying normal and binormal vectors for each segment on the curve + // generate indices - for ( i = 1; i <= segments; i ++ ) { + for ( let x = 0; x < radialSegments; x ++ ) { - normals[ i ] = normals[ i - 1 ].clone(); + for ( let y = 0; y < heightSegments; y ++ ) { - binormals[ i ] = binormals[ i - 1 ].clone(); + // we use the index array to access the correct indices - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + const a = indexArray[ y ][ x ]; + const b = indexArray[ y + 1 ][ x ]; + const c = indexArray[ y + 1 ][ x + 1 ]; + const d = indexArray[ y ][ x + 1 ]; - if ( vec.length() > Number.EPSILON ) { + // faces - vec.normalize(); + indices.push( a, b, d ); + indices.push( b, c, d ); - theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + // update group counter - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + groupCount += 6; } - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - } - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + // add a group to the geometry. this will ensure multi material support - if ( closed === true ) { + scope.addGroup( groupStart, groupCount, 0 ); - theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) ); - theta /= segments; + // calculate new start value for groups - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) { + groupStart += groupCount; - theta = - theta; + } - } + function generateCap( top ) { - for ( i = 1; i <= segments; i ++ ) { + // save the index of the first center vertex + const centerIndexStart = index; - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + const uv = new Vector2(); + const vertex = new Vector3(); - } + let groupCount = 0; - } + const radius = ( top === true ) ? radiusTop : radiusBottom; + const sign = ( top === true ) ? 1 : - 1; - return { - tangents: tangents, - normals: normals, - binormals: binormals - }; + // first we generate the center vertex data of the cap. + // because the geometry needs one set of uvs per face, + // we must generate a center vertex per face/segment - }, + for ( let x = 1; x <= radialSegments; x ++ ) { - clone: function () { + // vertex - return new this.constructor().copy( this ); + vertices.push( 0, halfHeight * sign, 0 ); - }, + // normal - copy: function ( source ) { + normals.push( 0, sign, 0 ); - this.arcLengthDivisions = source.arcLengthDivisions; + // uv - return this; + uvs.push( 0.5, 0.5 ); - }, + // increase index - toJSON: function () { + index ++; - var data = { - metadata: { - version: 4.5, - type: 'Curve', - generator: 'Curve.toJSON' - } - }; + } - data.arcLengthDivisions = this.arcLengthDivisions; - data.type = this.type; + // save the index of the last center vertex + const centerIndexEnd = index; - return data; + // now we generate the surrounding vertices, normals and uvs - }, + for ( let x = 0; x <= radialSegments; x ++ ) { - fromJSON: function ( json ) { + const u = x / radialSegments; + const theta = u * thetaLength + thetaStart; - this.arcLengthDivisions = json.arcLengthDivisions; + const cosTheta = Math.cos( theta ); + const sinTheta = Math.sin( theta ); - return this; + // vertex - } + vertex.x = radius * sinTheta; + vertex.y = halfHeight * sign; + vertex.z = radius * cosTheta; + vertices.push( vertex.x, vertex.y, vertex.z ); - } ); + // normal - function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + normals.push( 0, sign, 0 ); - Curve.call( this ); + // uv - this.type = 'EllipseCurve'; + uv.x = ( cosTheta * 0.5 ) + 0.5; + uv.y = ( sinTheta * 0.5 * sign ) + 0.5; + uvs.push( uv.x, uv.y ); + + // increase index - this.aX = aX || 0; - this.aY = aY || 0; + index ++; - this.xRadius = xRadius || 1; - this.yRadius = yRadius || 1; + } - this.aStartAngle = aStartAngle || 0; - this.aEndAngle = aEndAngle || 2 * Math.PI; + // generate indices - this.aClockwise = aClockwise || false; + for ( let x = 0; x < radialSegments; x ++ ) { - this.aRotation = aRotation || 0; + const c = centerIndexStart + x; + const i = centerIndexEnd + x; - } + if ( top === true ) { - EllipseCurve.prototype = Object.create( Curve.prototype ); - EllipseCurve.prototype.constructor = EllipseCurve; + // face top - EllipseCurve.prototype.isEllipseCurve = true; + indices.push( i, i + 1, c ); - EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) { + } else { - var point = optionalTarget || new Vector2(); + // face bottom - var twoPi = Math.PI * 2; - var deltaAngle = this.aEndAngle - this.aStartAngle; - var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + indices.push( i + 1, i, c ); - // ensures that deltaAngle is 0 .. 2 PI - while ( deltaAngle < 0 ) deltaAngle += twoPi; - while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + } - if ( deltaAngle < Number.EPSILON ) { + groupCount += 3; - if ( samePoints ) { + } - deltaAngle = 0; + // add a group to the geometry. this will ensure multi material support - } else { + scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - deltaAngle = twoPi; + // calculate new start value for groups - } + groupStart += groupCount; } - if ( this.aClockwise === true && ! samePoints ) { + } - if ( deltaAngle === twoPi ) { + copy( source ) { - deltaAngle = - twoPi; + super.copy( source ); - } else { + this.parameters = Object.assign( {}, source.parameters ); - deltaAngle = deltaAngle - twoPi; + return this; - } + } - } + static fromJSON( data ) { - var angle = this.aStartAngle + t * deltaAngle; - var x = this.aX + this.xRadius * Math.cos( angle ); - var y = this.aY + this.yRadius * Math.sin( angle ); + return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); - if ( this.aRotation !== 0 ) { + } - var cos = Math.cos( this.aRotation ); - var sin = Math.sin( this.aRotation ); +} - var tx = x - this.aX; - var ty = y - this.aY; +class ConeGeometry extends CylinderGeometry { - // Rotate the point about the center of the ellipse. - x = tx * cos - ty * sin + this.aX; - y = tx * sin + ty * cos + this.aY; + constructor( radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) { - } + super( 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ); - return point.set( x, y ); + this.type = 'ConeGeometry'; - }; + this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - EllipseCurve.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + static fromJSON( data ) { - this.aX = source.aX; - this.aY = source.aY; + return new ConeGeometry( data.radius, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength ); - this.xRadius = source.xRadius; - this.yRadius = source.yRadius; + } - this.aStartAngle = source.aStartAngle; - this.aEndAngle = source.aEndAngle; +} - this.aClockwise = source.aClockwise; +class PolyhedronGeometry extends BufferGeometry { - this.aRotation = source.aRotation; + constructor( vertices = [], indices = [], radius = 1, detail = 0 ) { - return this; + super(); - }; + this.type = 'PolyhedronGeometry'; + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; - EllipseCurve.prototype.toJSON = function () { + // default buffer data - var data = Curve.prototype.toJSON.call( this ); + const vertexBuffer = []; + const uvBuffer = []; - data.aX = this.aX; - data.aY = this.aY; + // the subdivision creates the vertex buffer data - data.xRadius = this.xRadius; - data.yRadius = this.yRadius; + subdivide( detail ); - data.aStartAngle = this.aStartAngle; - data.aEndAngle = this.aEndAngle; + // all vertices should lie on a conceptual sphere with a given radius - data.aClockwise = this.aClockwise; + applyRadius( radius ); - data.aRotation = this.aRotation; + // finally, create the uv data - return data; + generateUVs(); - }; + // build non-indexed geometry - EllipseCurve.prototype.fromJSON = function ( json ) { + this.setAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) ); - Curve.prototype.fromJSON.call( this, json ); + if ( detail === 0 ) { - this.aX = json.aX; - this.aY = json.aY; + this.computeVertexNormals(); // flat normals - this.xRadius = json.xRadius; - this.yRadius = json.yRadius; + } else { - this.aStartAngle = json.aStartAngle; - this.aEndAngle = json.aEndAngle; + this.normalizeNormals(); // smooth normals - this.aClockwise = json.aClockwise; + } - this.aRotation = json.aRotation; - - return this; - - }; + // helper functions - function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + function subdivide( detail ) { - EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + const a = new Vector3(); + const b = new Vector3(); + const c = new Vector3(); - this.type = 'ArcCurve'; + // iterate over all faces and apply a subdivision with the given detail value - } + for ( let i = 0; i < indices.length; i += 3 ) { - ArcCurve.prototype = Object.create( EllipseCurve.prototype ); - ArcCurve.prototype.constructor = ArcCurve; + // get the vertices of the face - ArcCurve.prototype.isArcCurve = true; + getVertexByIndex( indices[ i + 0 ], a ); + getVertexByIndex( indices[ i + 1 ], b ); + getVertexByIndex( indices[ i + 2 ], c ); - /** - * @author zz85 https://github.com/zz85 - * - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ + // perform subdivision + subdivideFace( a, b, c, detail ); - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM + } - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ + } - function CubicPoly() { + function subdivideFace( a, b, c, detail ) { - var c0 = 0, c1 = 0, c2 = 0, c3 = 0; + const cols = detail + 1; - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - function init( x0, x1, t0, t1 ) { + // we use this multidimensional array as a data structure for creating the subdivision - c0 = x0; - c1 = t0; - c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - c3 = 2 * x0 - 2 * x1 + t0 + t1; + const v = []; - } + // construct all of the vertices for this subdivision - return { + for ( let i = 0; i <= cols; i ++ ) { - initCatmullRom: function ( x0, x1, x2, x3, tension ) { + v[ i ] = []; - init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + const aj = a.clone().lerp( c, i / cols ); + const bj = b.clone().lerp( c, i / cols ); - }, + const rows = cols - i; - initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) { + for ( let j = 0; j <= rows; j ++ ) { - // compute tangents when parameterized in [t1,t2] - var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + if ( j === 0 && i === cols ) { - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + v[ i ][ j ] = aj; - init( x1, x2, t1, t2 ); + } else { - }, + v[ i ][ j ] = aj.clone().lerp( bj, j / rows ); - calc: function ( t ) { + } - var t2 = t * t; - var t3 = t2 * t; - return c0 + c1 * t + c2 * t2 + c3 * t3; + } } - }; + // construct all of the faces - } + for ( let i = 0; i < cols; i ++ ) { - // + for ( let j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - var tmp = new Vector3(); - var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); + const k = Math.floor( j / 2 ); - function CatmullRomCurve3( points, closed, curveType, tension ) { + if ( j % 2 === 0 ) { - Curve.call( this ); + pushVertex( v[ i ][ k + 1 ] ); + pushVertex( v[ i + 1 ][ k ] ); + pushVertex( v[ i ][ k ] ); - this.type = 'CatmullRomCurve3'; + } else { - this.points = points || []; - this.closed = closed || false; - this.curveType = curveType || 'centripetal'; - this.tension = tension || 0.5; + pushVertex( v[ i ][ k + 1 ] ); + pushVertex( v[ i + 1 ][ k + 1 ] ); + pushVertex( v[ i + 1 ][ k ] ); - } + } - CatmullRomCurve3.prototype = Object.create( Curve.prototype ); - CatmullRomCurve3.prototype.constructor = CatmullRomCurve3; + } - CatmullRomCurve3.prototype.isCatmullRomCurve3 = true; + } - CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) { + } - var point = optionalTarget || new Vector3(); + function applyRadius( radius ) { - var points = this.points; - var l = points.length; + const vertex = new Vector3(); - var p = ( l - ( this.closed ? 0 : 1 ) ) * t; - var intPoint = Math.floor( p ); - var weight = p - intPoint; + // iterate over the entire buffer and apply the radius to each vertex - if ( this.closed ) { + for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l; + vertex.x = vertexBuffer[ i + 0 ]; + vertex.y = vertexBuffer[ i + 1 ]; + vertex.z = vertexBuffer[ i + 2 ]; - } else if ( weight === 0 && intPoint === l - 1 ) { + vertex.normalize().multiplyScalar( radius ); - intPoint = l - 2; - weight = 1; + vertexBuffer[ i + 0 ] = vertex.x; + vertexBuffer[ i + 1 ] = vertex.y; + vertexBuffer[ i + 2 ] = vertex.z; + + } } - var p0, p1, p2, p3; // 4 points + function generateUVs() { - if ( this.closed || intPoint > 0 ) { + const vertex = new Vector3(); - p0 = points[ ( intPoint - 1 ) % l ]; + for ( let i = 0; i < vertexBuffer.length; i += 3 ) { - } else { + vertex.x = vertexBuffer[ i + 0 ]; + vertex.y = vertexBuffer[ i + 1 ]; + vertex.z = vertexBuffer[ i + 2 ]; - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + const u = azimuth( vertex ) / 2 / Math.PI + 0.5; + const v = inclination( vertex ) / Math.PI + 0.5; + uvBuffer.push( u, 1 - v ); + + } + + correctUVs(); + + correctSeam(); } - p1 = points[ intPoint % l ]; - p2 = points[ ( intPoint + 1 ) % l ]; + function correctSeam() { - if ( this.closed || intPoint + 2 < l ) { + // handle case when face straddles the seam, see #3269 - p3 = points[ ( intPoint + 2 ) % l ]; + for ( let i = 0; i < uvBuffer.length; i += 6 ) { - } else { + // uv data of a single face - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); - p3 = tmp; + const x0 = uvBuffer[ i + 0 ]; + const x1 = uvBuffer[ i + 2 ]; + const x2 = uvBuffer[ i + 4 ]; + + const max = Math.max( x0, x1, x2 ); + const min = Math.min( x0, x1, x2 ); + + // 0.9 is somewhat arbitrary + + if ( max > 0.9 && min < 0.1 ) { + + if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1; + if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1; + if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1; + + } + + } } - if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) { + function pushVertex( vertex ) { - // init Centripetal / Chordal Catmull-Rom - var pow = this.curveType === 'chordal' ? 0.5 : 0.25; - var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + vertexBuffer.push( vertex.x, vertex.y, vertex.z ); - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + } - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + function getVertexByIndex( index, vertex ) { - } else if ( this.curveType === 'catmullrom' ) { + const stride = index * 3; - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension ); + vertex.x = vertices[ stride + 0 ]; + vertex.y = vertices[ stride + 1 ]; + vertex.z = vertices[ stride + 2 ]; } - point.set( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + function correctUVs() { - return point; + const a = new Vector3(); + const b = new Vector3(); + const c = new Vector3(); - }; + const centroid = new Vector3(); - CatmullRomCurve3.prototype.copy = function ( source ) { + const uvA = new Vector2(); + const uvB = new Vector2(); + const uvC = new Vector2(); - Curve.prototype.copy.call( this, source ); + for ( let i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) { - this.points = []; + a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] ); + b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] ); + c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] ); - for ( var i = 0, l = source.points.length; i < l; i ++ ) { + uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] ); + uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] ); + uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] ); - var point = source.points[ i ]; + centroid.copy( a ).add( b ).add( c ).divideScalar( 3 ); - this.points.push( point.clone() ); + const azi = azimuth( centroid ); - } + correctUV( uvA, j + 0, a, azi ); + correctUV( uvB, j + 2, b, azi ); + correctUV( uvC, j + 4, c, azi ); - this.closed = source.closed; - this.curveType = source.curveType; - this.tension = source.tension; + } - return this; + } - }; + function correctUV( uv, stride, vector, azimuth ) { - CatmullRomCurve3.prototype.toJSON = function () { + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) { - var data = Curve.prototype.toJSON.call( this ); + uvBuffer[ stride ] = uv.x - 1; - data.points = []; + } - for ( var i = 0, l = this.points.length; i < l; i ++ ) { + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) { - var point = this.points[ i ]; - data.points.push( point.toArray() ); + uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5; + + } } - data.closed = this.closed; - data.curveType = this.curveType; - data.tension = this.tension; + // Angle around the Y axis, counter-clockwise when looking from above. - return data; + function azimuth( vector ) { - }; + return Math.atan2( vector.z, - vector.x ); - CatmullRomCurve3.prototype.fromJSON = function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); - this.points = []; + // Angle above the XZ plane. - for ( var i = 0, l = json.points.length; i < l; i ++ ) { + function inclination( vector ) { - var point = json.points[ i ]; - this.points.push( new Vector3().fromArray( point ) ); + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } - this.closed = json.closed; - this.curveType = json.curveType; - this.tension = json.tension; + } - return this; + copy( source ) { - }; + super.copy( source ); - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Bezier Curves formulas obtained from - * http://en.wikipedia.org/wiki/Bézier_curve - */ + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } - function CatmullRom( t, p0, p1, p2, p3 ) { + static fromJSON( data ) { - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + return new PolyhedronGeometry( data.vertices, data.indices, data.radius, data.details ); } - // +} + +class DodecahedronGeometry extends PolyhedronGeometry { + + constructor( radius = 1, detail = 0 ) { + + const t = ( 1 + Math.sqrt( 5 ) ) / 2; + const r = 1 / t; + + const vertices = [ + + // (±1, ±1, ±1) + - 1, - 1, - 1, - 1, - 1, 1, + - 1, 1, - 1, - 1, 1, 1, + 1, - 1, - 1, 1, - 1, 1, + 1, 1, - 1, 1, 1, 1, + + // (0, ±1/φ, ±φ) + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, + + // (±1/φ, ±φ, 0) + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, + + // (±φ, 0, ±1/φ) + - t, 0, - r, t, 0, - r, + - t, 0, r, t, 0, r + ]; + + const indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; + + super( vertices, indices, radius, detail ); + + this.type = 'DodecahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; - function QuadraticBezierP0( t, p ) { + } + + static fromJSON( data ) { - var k = 1 - t; - return k * k * p; + return new DodecahedronGeometry( data.radius, data.detail ); } - function QuadraticBezierP1( t, p ) { +} + +const _v0 = /*@__PURE__*/ new Vector3(); +const _v1$1 = /*@__PURE__*/ new Vector3(); +const _normal = /*@__PURE__*/ new Vector3(); +const _triangle = /*@__PURE__*/ new Triangle(); + +class EdgesGeometry extends BufferGeometry { + + constructor( geometry = null, thresholdAngle = 1 ) { + + super(); + + this.type = 'EdgesGeometry'; + + this.parameters = { + geometry: geometry, + thresholdAngle: thresholdAngle + }; + + if ( geometry !== null ) { + + const precisionPoints = 4; + const precision = Math.pow( 10, precisionPoints ); + const thresholdDot = Math.cos( DEG2RAD * thresholdAngle ); + + const indexAttr = geometry.getIndex(); + const positionAttr = geometry.getAttribute( 'position' ); + const indexCount = indexAttr ? indexAttr.count : positionAttr.count; + + const indexArr = [ 0, 0, 0 ]; + const vertKeys = [ 'a', 'b', 'c' ]; + const hashes = new Array( 3 ); + + const edgeData = {}; + const vertices = []; + for ( let i = 0; i < indexCount; i += 3 ) { + + if ( indexAttr ) { + + indexArr[ 0 ] = indexAttr.getX( i ); + indexArr[ 1 ] = indexAttr.getX( i + 1 ); + indexArr[ 2 ] = indexAttr.getX( i + 2 ); + + } else { + + indexArr[ 0 ] = i; + indexArr[ 1 ] = i + 1; + indexArr[ 2 ] = i + 2; + + } + + const { a, b, c } = _triangle; + a.fromBufferAttribute( positionAttr, indexArr[ 0 ] ); + b.fromBufferAttribute( positionAttr, indexArr[ 1 ] ); + c.fromBufferAttribute( positionAttr, indexArr[ 2 ] ); + _triangle.getNormal( _normal ); + + // create hashes for the edge from the vertices + hashes[ 0 ] = `${ Math.round( a.x * precision ) },${ Math.round( a.y * precision ) },${ Math.round( a.z * precision ) }`; + hashes[ 1 ] = `${ Math.round( b.x * precision ) },${ Math.round( b.y * precision ) },${ Math.round( b.z * precision ) }`; + hashes[ 2 ] = `${ Math.round( c.x * precision ) },${ Math.round( c.y * precision ) },${ Math.round( c.z * precision ) }`; + + // skip degenerate triangles + if ( hashes[ 0 ] === hashes[ 1 ] || hashes[ 1 ] === hashes[ 2 ] || hashes[ 2 ] === hashes[ 0 ] ) { + + continue; + + } + + // iterate over every edge + for ( let j = 0; j < 3; j ++ ) { + + // get the first and next vertex making up the edge + const jNext = ( j + 1 ) % 3; + const vecHash0 = hashes[ j ]; + const vecHash1 = hashes[ jNext ]; + const v0 = _triangle[ vertKeys[ j ] ]; + const v1 = _triangle[ vertKeys[ jNext ] ]; + + const hash = `${ vecHash0 }_${ vecHash1 }`; + const reverseHash = `${ vecHash1 }_${ vecHash0 }`; + + if ( reverseHash in edgeData && edgeData[ reverseHash ] ) { + + // if we found a sibling edge add it into the vertex array if + // it meets the angle threshold and delete the edge from the map. + if ( _normal.dot( edgeData[ reverseHash ].normal ) <= thresholdDot ) { + + vertices.push( v0.x, v0.y, v0.z ); + vertices.push( v1.x, v1.y, v1.z ); + + } + + edgeData[ reverseHash ] = null; + + } else if ( ! ( hash in edgeData ) ) { + + // if we've already got an edge here then skip adding a new one + edgeData[ hash ] = { + + index0: indexArr[ j ], + index1: indexArr[ jNext ], + normal: _normal.clone(), + + }; + + } + + } + + } + + // iterate over all remaining, unmatched edges and add them to the vertex array + for ( const key in edgeData ) { + + if ( edgeData[ key ] ) { + + const { index0, index1 } = edgeData[ key ]; + _v0.fromBufferAttribute( positionAttr, index0 ); + _v1$1.fromBufferAttribute( positionAttr, index1 ); + + vertices.push( _v0.x, _v0.y, _v0.z ); + vertices.push( _v1$1.x, _v1$1.y, _v1$1.z ); + + } + + } + + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + +} + +class Shape extends Path { + + constructor( points ) { + + super( points ); + + this.uuid = generateUUID(); + + this.type = 'Shape'; + + this.holes = []; + + } + + getPointsHoles( divisions ) { + + const holesPts = []; + + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + + } + + return holesPts; + + } + + // get points of shape and holes (keypoints based on segments parameter) + + extractPoints( divisions ) { + + return { + + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + + } + + copy( source ) { + + super.copy( source ); + + this.holes = []; + + for ( let i = 0, l = source.holes.length; i < l; i ++ ) { + + const hole = source.holes[ i ]; + + this.holes.push( hole.clone() ); + + } + + return this; + + } + + toJSON() { + + const data = super.toJSON(); + + data.uuid = this.uuid; + data.holes = []; + + for ( let i = 0, l = this.holes.length; i < l; i ++ ) { + + const hole = this.holes[ i ]; + data.holes.push( hole.toJSON() ); + + } + + return data; + + } + + fromJSON( json ) { + + super.fromJSON( json ); + + this.uuid = json.uuid; + this.holes = []; + + for ( let i = 0, l = json.holes.length; i < l; i ++ ) { + + const hole = json.holes[ i ]; + this.holes.push( new Path().fromJSON( hole ) ); + + } + + return this; + + } + +} + +/** + * Port from https://github.com/mapbox/earcut (v2.2.4) + */ + +const Earcut = { + + triangulate: function ( data, holeIndices, dim = 2 ) { + + const hasHoles = holeIndices && holeIndices.length; + const outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length; + let outerNode = linkedList( data, 0, outerLen, dim, true ); + const triangles = []; + + if ( ! outerNode || outerNode.next === outerNode.prev ) return triangles; + + let minX, minY, maxX, maxY, x, y, invSize; + + if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim ); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + if ( data.length > 80 * dim ) { + + minX = maxX = data[ 0 ]; + minY = maxY = data[ 1 ]; + + for ( let i = dim; i < outerLen; i += dim ) { + + x = data[ i ]; + y = data[ i + 1 ]; + if ( x < minX ) minX = x; + if ( y < minY ) minY = y; + if ( x > maxX ) maxX = x; + if ( y > maxY ) maxY = y; + + } + + // minX, minY and invSize are later used to transform coords into integers for z-order calculation + invSize = Math.max( maxX - minX, maxY - minY ); + invSize = invSize !== 0 ? 32767 / invSize : 0; + + } + + earcutLinked( outerNode, triangles, dim, minX, minY, invSize, 0 ); + + return triangles; + + } + +}; + +// create a circular doubly linked list from polygon points in the specified winding order +function linkedList( data, start, end, dim, clockwise ) { + + let i, last; + + if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) { + + for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + + } else { + + for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last ); + + } + + if ( last && equals( last, last.next ) ) { + + removeNode( last ); + last = last.next; + + } + + return last; + +} + +// eliminate colinear or duplicate points +function filterPoints( start, end ) { + + if ( ! start ) return start; + if ( ! end ) end = start; + + let p = start, + again; + do { + + again = false; + + if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) { + + removeNode( p ); + p = end = p.prev; + if ( p === p.next ) break; + again = true; + + } else { + + p = p.next; + + } + + } while ( again || p !== end ); + + return end; + +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) { + + if ( ! ear ) return; + + // interlink polygon nodes in z-order + if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize ); + + let stop = ear, + prev, next; + + // iterate through ears, slicing them one by one + while ( ear.prev !== ear.next ) { + + prev = ear.prev; + next = ear.next; + + if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) { + + // cut off the triangle + triangles.push( prev.i / dim | 0 ); + triangles.push( ear.i / dim | 0 ); + triangles.push( next.i / dim | 0 ); + + removeNode( ear ); + + // skipping the next vertex leads to less sliver triangles + ear = next.next; + stop = next.next; + + continue; + + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if ( ear === stop ) { + + // try filtering points and slicing again + if ( ! pass ) { + + earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 ); + + // if this didn't work, try curing all small self-intersections locally + + } else if ( pass === 1 ) { + + ear = cureLocalIntersections( filterPoints( ear ), triangles, dim ); + earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 ); + + // as a last resort, try splitting the remaining polygon into two + + } else if ( pass === 2 ) { + + splitEarcut( ear, triangles, dim, minX, minY, invSize ); + + } + + break; + + } + + } + +} + +// check whether a polygon node forms a valid ear with adjacent nodes +function isEar( ear ) { + + const a = ear.prev, + b = ear, + c = ear.next; + + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox; min & max are calculated like this for speed + const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), + y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), + x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), + y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); + + let p = c.next; + while ( p !== a ) { + + if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && + pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && + area( p.prev, p, p.next ) >= 0 ) return false; + p = p.next; + + } + + return true; + +} + +function isEarHashed( ear, minX, minY, invSize ) { + + const a = ear.prev, + b = ear, + c = ear.next; + + if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear + + const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y; + + // triangle bbox; min & max are calculated like this for speed + const x0 = ax < bx ? ( ax < cx ? ax : cx ) : ( bx < cx ? bx : cx ), + y0 = ay < by ? ( ay < cy ? ay : cy ) : ( by < cy ? by : cy ), + x1 = ax > bx ? ( ax > cx ? ax : cx ) : ( bx > cx ? bx : cx ), + y1 = ay > by ? ( ay > cy ? ay : cy ) : ( by > cy ? by : cy ); + + // z-order range for the current triangle bbox; + const minZ = zOrder( x0, y0, minX, minY, invSize ), + maxZ = zOrder( x1, y1, minX, minY, invSize ); + + let p = ear.prevZ, + n = ear.nextZ; + + // look for points inside the triangle in both directions + while ( p && p.z >= minZ && n && n.z <= maxZ ) { + + if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; + + if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; + + } + + // look for remaining points in decreasing z-order + while ( p && p.z >= minZ ) { + + if ( p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && + pointInTriangle( ax, ay, bx, by, cx, cy, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) return false; + p = p.prevZ; + + } + + // look for remaining points in increasing z-order + while ( n && n.z <= maxZ ) { + + if ( n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && + pointInTriangle( ax, ay, bx, by, cx, cy, n.x, n.y ) && area( n.prev, n, n.next ) >= 0 ) return false; + n = n.nextZ; + + } + + return true; + +} + +// go through all polygon nodes and cure small local self-intersections +function cureLocalIntersections( start, triangles, dim ) { + + let p = start; + do { + + const a = p.prev, + b = p.next.next; + + if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) { + + triangles.push( a.i / dim | 0 ); + triangles.push( p.i / dim | 0 ); + triangles.push( b.i / dim | 0 ); + + // remove two nodes involved + removeNode( p ); + removeNode( p.next ); + + p = start = b; + + } + + p = p.next; + + } while ( p !== start ); + + return filterPoints( p ); + +} + +// try splitting polygon into two and triangulate them independently +function splitEarcut( start, triangles, dim, minX, minY, invSize ) { + + // look for a valid diagonal that divides the polygon into two + let a = start; + do { + + let b = a.next.next; + while ( b !== a.prev ) { + + if ( a.i !== b.i && isValidDiagonal( a, b ) ) { + + // split the polygon in two by the diagonal + let c = splitPolygon( a, b ); + + // filter colinear points around the cuts + a = filterPoints( a, a.next ); + c = filterPoints( c, c.next ); + + // run earcut on each half + earcutLinked( a, triangles, dim, minX, minY, invSize, 0 ); + earcutLinked( c, triangles, dim, minX, minY, invSize, 0 ); + return; + + } + + b = b.next; + + } + + a = a.next; + + } while ( a !== start ); + +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +function eliminateHoles( data, holeIndices, outerNode, dim ) { + + const queue = []; + let i, len, start, end, list; + + for ( i = 0, len = holeIndices.length; i < len; i ++ ) { + + start = holeIndices[ i ] * dim; + end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length; + list = linkedList( data, start, end, dim, false ); + if ( list === list.next ) list.steiner = true; + queue.push( getLeftmost( list ) ); + + } + + queue.sort( compareX ); + + // process holes from left to right + for ( i = 0; i < queue.length; i ++ ) { + + outerNode = eliminateHole( queue[ i ], outerNode ); + + } + + return outerNode; + +} + +function compareX( a, b ) { + + return a.x - b.x; + +} + +// find a bridge between vertices that connects hole with an outer ring and link it +function eliminateHole( hole, outerNode ) { + + const bridge = findHoleBridge( hole, outerNode ); + if ( ! bridge ) { + + return outerNode; + + } + + const bridgeReverse = splitPolygon( bridge, hole ); + + // filter collinear points around the cuts + filterPoints( bridgeReverse, bridgeReverse.next ); + return filterPoints( bridge, bridge.next ); + +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +function findHoleBridge( hole, outerNode ) { + + let p = outerNode, + qx = - Infinity, + m; + + const hx = hole.x, hy = hole.y; + + // find a segment intersected by a ray from the hole's leftmost point to the left; + // segment's endpoint with lesser x will be potential connection point + do { + + if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) { + + const x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y ); + if ( x <= hx && x > qx ) { + + qx = x; + m = p.x < p.next.x ? p : p.next; + if ( x === hx ) return m; // hole touches outer segment; pick leftmost endpoint + + } + + } + + p = p.next; + + } while ( p !== outerNode ); + + if ( ! m ) return null; + + // look for points inside the triangle of hole point, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the point of the minimum angle with the ray as connection point + + const stop = m, + mx = m.x, + my = m.y; + let tanMin = Infinity, tan; + + p = m; + + do { + + if ( hx >= p.x && p.x >= mx && hx !== p.x && + pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) { + + tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential + + if ( locallyInside( p, hole ) && ( tan < tanMin || ( tan === tanMin && ( p.x > m.x || ( p.x === m.x && sectorContainsSector( m, p ) ) ) ) ) ) { + + m = p; + tanMin = tan; + + } + + } + + p = p.next; + + } while ( p !== stop ); + + return m; + +} + +// whether sector in vertex m contains sector in vertex p in the same coordinates +function sectorContainsSector( m, p ) { + + return area( m.prev, m, p.prev ) < 0 && area( p.next, m, m.next ) < 0; + +} + +// interlink polygon nodes in z-order +function indexCurve( start, minX, minY, invSize ) { + + let p = start; + do { + + if ( p.z === 0 ) p.z = zOrder( p.x, p.y, minX, minY, invSize ); + p.prevZ = p.prev; + p.nextZ = p.next; + p = p.next; + + } while ( p !== start ); + + p.prevZ.nextZ = null; + p.prevZ = null; + + sortLinked( p ); + +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +function sortLinked( list ) { + + let i, p, q, e, tail, numMerges, pSize, qSize, + inSize = 1; + + do { + + p = list; + list = null; + tail = null; + numMerges = 0; + + while ( p ) { + + numMerges ++; + q = p; + pSize = 0; + for ( i = 0; i < inSize; i ++ ) { + + pSize ++; + q = q.nextZ; + if ( ! q ) break; + + } + + qSize = inSize; + + while ( pSize > 0 || ( qSize > 0 && q ) ) { + + if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) { + + e = p; + p = p.nextZ; + pSize --; + + } else { + + e = q; + q = q.nextZ; + qSize --; + + } + + if ( tail ) tail.nextZ = e; + else list = e; + + e.prevZ = tail; + tail = e; + + } + + p = q; + + } + + tail.nextZ = null; + inSize *= 2; + + } while ( numMerges > 1 ); + + return list; + +} + +// z-order of a point given coords and inverse of the longer side of data bbox +function zOrder( x, y, minX, minY, invSize ) { + + // coords are transformed into non-negative 15-bit integer range + x = ( x - minX ) * invSize | 0; + y = ( y - minY ) * invSize | 0; + + x = ( x | ( x << 8 ) ) & 0x00FF00FF; + x = ( x | ( x << 4 ) ) & 0x0F0F0F0F; + x = ( x | ( x << 2 ) ) & 0x33333333; + x = ( x | ( x << 1 ) ) & 0x55555555; + + y = ( y | ( y << 8 ) ) & 0x00FF00FF; + y = ( y | ( y << 4 ) ) & 0x0F0F0F0F; + y = ( y | ( y << 2 ) ) & 0x33333333; + y = ( y | ( y << 1 ) ) & 0x55555555; + + return x | ( y << 1 ); + +} + +// find the leftmost node of a polygon ring +function getLeftmost( start ) { + + let p = start, + leftmost = start; + do { + + if ( p.x < leftmost.x || ( p.x === leftmost.x && p.y < leftmost.y ) ) leftmost = p; + p = p.next; + + } while ( p !== start ); + + return leftmost; + +} + +// check if a point lies within a convex triangle +function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) { + + return ( cx - px ) * ( ay - py ) >= ( ax - px ) * ( cy - py ) && + ( ax - px ) * ( by - py ) >= ( bx - px ) * ( ay - py ) && + ( bx - px ) * ( cy - py ) >= ( cx - px ) * ( by - py ); + +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +function isValidDiagonal( a, b ) { + + return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) && // dones't intersect other edges + ( locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b ) && // locally visible + ( area( a.prev, a, b.prev ) || area( a, b.prev, b ) ) || // does not create opposite-facing sectors + equals( a, b ) && area( a.prev, a, a.next ) > 0 && area( b.prev, b, b.next ) > 0 ); // special zero-length case + +} + +// signed area of a triangle +function area( p, q, r ) { + + return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y ); + +} + +// check if two points are equal +function equals( p1, p2 ) { + + return p1.x === p2.x && p1.y === p2.y; + +} + +// check if two segments intersect +function intersects( p1, q1, p2, q2 ) { + + const o1 = sign( area( p1, q1, p2 ) ); + const o2 = sign( area( p1, q1, q2 ) ); + const o3 = sign( area( p2, q2, p1 ) ); + const o4 = sign( area( p2, q2, q1 ) ); + + if ( o1 !== o2 && o3 !== o4 ) return true; // general case + + if ( o1 === 0 && onSegment( p1, p2, q1 ) ) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if ( o2 === 0 && onSegment( p1, q2, q1 ) ) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if ( o3 === 0 && onSegment( p2, p1, q2 ) ) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if ( o4 === 0 && onSegment( p2, q1, q2 ) ) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; + +} + +// for collinear points p, q, r, check if point q lies on segment pr +function onSegment( p, q, r ) { + + return q.x <= Math.max( p.x, r.x ) && q.x >= Math.min( p.x, r.x ) && q.y <= Math.max( p.y, r.y ) && q.y >= Math.min( p.y, r.y ); + +} + +function sign( num ) { + + return num > 0 ? 1 : num < 0 ? - 1 : 0; + +} + +// check if a polygon diagonal intersects any polygon segments +function intersectsPolygon( a, b ) { + + let p = a; + do { + + if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && + intersects( p, p.next, a, b ) ) return true; + p = p.next; + + } while ( p !== a ); + + return false; + +} + +// check if a polygon diagonal is locally inside the polygon +function locallyInside( a, b ) { + + return area( a.prev, a, a.next ) < 0 ? + area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 : + area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0; + +} + +// check if the middle point of a polygon diagonal is inside the polygon +function middleInside( a, b ) { + + let p = a, + inside = false; + const px = ( a.x + b.x ) / 2, + py = ( a.y + b.y ) / 2; + do { + + if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y && + ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) + inside = ! inside; + p = p.next; + + } while ( p !== a ); + + return inside; + +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two; +// if one belongs to the outer ring and another to a hole, it merges it into a single ring +function splitPolygon( a, b ) { + + const a2 = new Node( a.i, a.x, a.y ), + b2 = new Node( b.i, b.x, b.y ), + an = a.next, + bp = b.prev; + + a.next = b; + b.prev = a; + + a2.next = an; + an.prev = a2; + + b2.next = a2; + a2.prev = b2; + + bp.next = b2; + b2.prev = bp; + + return b2; + +} + +// create a node and optionally link it with previous one (in a circular doubly linked list) +function insertNode( i, x, y, last ) { + + const p = new Node( i, x, y ); + + if ( ! last ) { + + p.prev = p; + p.next = p; + + } else { + + p.next = last.next; + p.prev = last; + last.next.prev = p; + last.next = p; + + } + + return p; + +} + +function removeNode( p ) { + + p.next.prev = p.prev; + p.prev.next = p.next; + + if ( p.prevZ ) p.prevZ.nextZ = p.nextZ; + if ( p.nextZ ) p.nextZ.prevZ = p.prevZ; + +} + +function Node( i, x, y ) { + + // vertex index in coordinates array + this.i = i; + + // vertex coordinates + this.x = x; + this.y = y; + + // previous and next vertex nodes in a polygon ring + this.prev = null; + this.next = null; + + // z-order curve value + this.z = 0; + + // previous and next nodes in z-order + this.prevZ = null; + this.nextZ = null; + + // indicates whether this is a steiner point + this.steiner = false; + +} + +function signedArea( data, start, end, dim ) { + + let sum = 0; + for ( let i = start, j = end - dim; i < end; i += dim ) { + + sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] ); + j = i; + + } + + return sum; + +} + +class ShapeUtils { + + // calculate area of the contour polygon + + static area( contour ) { + + const n = contour.length; + let a = 0.0; + + for ( let p = n - 1, q = 0; q < n; p = q ++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + } + + static isClockWise( pts ) { + + return ShapeUtils.area( pts ) < 0; + + } + + static triangulateShape( contour, holes ) { + + const vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ] + const holeIndices = []; // array of hole indices + const faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ] + + removeDupEndPts( contour ); + addContour( vertices, contour ); + + // + + let holeIndex = contour.length; + + holes.forEach( removeDupEndPts ); + + for ( let i = 0; i < holes.length; i ++ ) { + + holeIndices.push( holeIndex ); + holeIndex += holes[ i ].length; + addContour( vertices, holes[ i ] ); + + } + + // + + const triangles = Earcut.triangulate( vertices, holeIndices ); + + // + + for ( let i = 0; i < triangles.length; i += 3 ) { + + faces.push( triangles.slice( i, i + 3 ) ); + + } + + return faces; + + } + +} + +function removeDupEndPts( points ) { + + const l = points.length; + + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + + points.pop(); + + } + +} + +function addContour( vertices, contour ) { + + for ( let i = 0; i < contour.length; i ++ ) { + + vertices.push( contour[ i ].x ); + vertices.push( contour[ i ].y ); + + } + +} + +/** + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * depth: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline (including bevelOffset) is bevel + * bevelOffset: , // how far from shape outline does bevel start + * bevelSegments: , // number of bevel layers + * + * extrudePath: // curve to extrude shape along + * + * UVGenerator: // object that provides UV generator functions + * + * } + */ + + +class ExtrudeGeometry extends BufferGeometry { + + constructor( shapes = new Shape( [ new Vector2( 0.5, 0.5 ), new Vector2( - 0.5, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), options = {} ) { + + super(); + + this.type = 'ExtrudeGeometry'; + + this.parameters = { + shapes: shapes, + options: options + }; + + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + + const scope = this; + + const verticesArray = []; + const uvArray = []; + + for ( let i = 0, l = shapes.length; i < l; i ++ ) { + + const shape = shapes[ i ]; + addShape( shape ); + + } + + // build geometry + + this.setAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) ); + + this.computeVertexNormals(); + + // functions + + function addShape( shape ) { + + const placeholder = []; + + // options + + const curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + const steps = options.steps !== undefined ? options.steps : 1; + const depth = options.depth !== undefined ? options.depth : 1; + + let bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; + let bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 0.2; + let bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 0.1; + let bevelOffset = options.bevelOffset !== undefined ? options.bevelOffset : 0; + let bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + const extrudePath = options.extrudePath; + + const uvgen = options.UVGenerator !== undefined ? options.UVGenerator : WorldUVGenerator; + + // + + let extrudePts, extrudeByPath = false; + let splineTube, binormal, normal, position2; + + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // TODO1 - have a .isClosed in spline? + + splineTube = extrudePath.computeFrenetFrames( steps, false ); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + bevelOffset = 0; + + } + + // Variables initialization + + const shapePoints = shape.extractPoints( curveSegments ); + + let vertices = shapePoints.shape; + const holes = shapePoints.holes; + + const reverse = ! ShapeUtils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + + if ( ShapeUtils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + } + + + const faces = ShapeUtils.triangulateShape( vertices, holes ); + + /* Vertices */ + + const contour = vertices; // vertices has all points but contour has only points of circumference + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2( pt, vec, size ) { + + if ( ! vec ) console.error( 'THREE.ExtrudeGeometry: vec does not exist' ); + + return pt.clone().addScaledVector( vec, size ); + + } + + const vlen = vertices.length, flen = faces.length; + + + // Find directions for point movement + + + function getBevelVec( inPt, inPrev, inNext ) { + + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + let v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt + + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html + + const v_prev_x = inPt.x - inPrev.x, + v_prev_y = inPt.y - inPrev.y; + const v_next_x = inNext.x - inPt.x, + v_next_y = inNext.y - inPt.y; + + const v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for collinear edges + const collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( collinear0 ) > Number.EPSILON ) { + + // not collinear + + // length of vectors for normalizing + + const v_prev_len = Math.sqrt( v_prev_lensq ); + const v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + const ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + const ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + const ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + const ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + const sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + const v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { + + return new Vector2( v_trans_x, v_trans_y ); + + } else { + + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + + } + + } else { + + // handle special case of collinear edges + + let direction_eq = false; // assumes: opposite + + if ( v_prev_x > Number.EPSILON ) { + + if ( v_next_x > Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( v_prev_x < - Number.EPSILON ) { + + if ( v_next_x < - Number.EPSILON ) { + + direction_eq = true; + + } + + } else { + + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + + direction_eq = true; + + } + + } + + } + + if ( direction_eq ) { + + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + + } else { + + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + + } + + } + + return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + + } + + + const contourMovements = []; + + for ( let i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + const holesMovements = []; + let oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( let i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( let b = 0; b < bevelSegments; b ++ ) { + + //for ( b = bevelSegments; b > 0; b -- ) { + + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + + // contract shape + + for ( let i = 0, il = contour.length; i < il; i ++ ) { + + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( let i = 0, il = ahole.length; i < il; i ++ ) { + + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + v( vert.x, vert.y, - z ); + + } + + } + + } + + const bs = bevelSize + bevelOffset; + + // Back facing vertices + + for ( let i = 0; i < vlen; i ++ ) { + + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + for ( let s = 1; s <= steps; s ++ ) { + + for ( let i = 0; i < vlen; i ++ ) { + + const vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, depth / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( let b = bevelSegments - 1; b >= 0; b -- ) { + + const t = b / bevelSegments; + const z = bevelThickness * Math.cos( t * Math.PI / 2 ); + const bs = bevelSize * Math.sin( t * Math.PI / 2 ) + bevelOffset; + + // contract shape + + for ( let i = 0, il = contour.length; i < il; i ++ ) { + + const vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, depth + z ); + + } + + // expand holes + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( let i = 0, il = ahole.length; i < il; i ++ ) { + + const vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( ! extrudeByPath ) { + + v( vert.x, vert.y, depth + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + const start = verticesArray.length / 3; + + if ( bevelEnabled ) { + + let layer = 0; // steps + 1 + let offset = vlen * layer; + + // Bottom faces + + for ( let i = 0; i < flen; i ++ ) { + + const face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( let i = 0; i < flen; i ++ ) { + + const face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); + + } + + } else { + + // Bottom faces + + for ( let i = 0; i < flen; i ++ ) { + + const face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( let i = 0; i < flen; i ++ ) { + + const face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + + } + + } + + scope.addGroup( start, verticesArray.length / 3 - start, 0 ); + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + const start = verticesArray.length / 3; + let layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( let h = 0, hl = holes.length; h < hl; h ++ ) { + + const ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + + scope.addGroup( start, verticesArray.length / 3 - start, 1 ); + + + } + + function sidewalls( contour, layeroffset ) { + + let i = contour.length; + + while ( -- i >= 0 ) { + + const j = i; + let k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + for ( let s = 0, sl = ( steps + bevelSegments * 2 ); s < sl; s ++ ) { + + const slen1 = vlen * s; + const slen2 = vlen * ( s + 1 ); + + const a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d ); + + } + + } + + } + + function v( x, y, z ) { + + placeholder.push( x ); + placeholder.push( y ); + placeholder.push( z ); + + } + + + function f3( a, b, c ) { + + addVertex( a ); + addVertex( b ); + addVertex( c ); + + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); + + } + + function f4( a, b, c, d ) { + + addVertex( a ); + addVertex( b ); + addVertex( d ); + + addVertex( b ); + addVertex( c ); + addVertex( d ); + + + const nextIndex = verticesArray.length / 3; + const uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 ); + + addUV( uvs[ 0 ] ); + addUV( uvs[ 1 ] ); + addUV( uvs[ 3 ] ); + + addUV( uvs[ 1 ] ); + addUV( uvs[ 2 ] ); + addUV( uvs[ 3 ] ); + + } + + function addVertex( index ) { + + verticesArray.push( placeholder[ index * 3 + 0 ] ); + verticesArray.push( placeholder[ index * 3 + 1 ] ); + verticesArray.push( placeholder[ index * 3 + 2 ] ); + + } + + + function addUV( vector2 ) { + + uvArray.push( vector2.x ); + uvArray.push( vector2.y ); + + } + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + toJSON() { + + const data = super.toJSON(); + + const shapes = this.parameters.shapes; + const options = this.parameters.options; + + return toJSON$1( shapes, options, data ); + + } + + static fromJSON( data, shapes ) { + + const geometryShapes = []; + + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { + + const shape = shapes[ data.shapes[ j ] ]; + + geometryShapes.push( shape ); + + } + + const extrudePath = data.options.extrudePath; + + if ( extrudePath !== undefined ) { + + data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); + + } + + return new ExtrudeGeometry( geometryShapes, data.options ); + + } + +} + +const WorldUVGenerator = { + + generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) { + + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; + + return [ + new Vector2( a_x, a_y ), + new Vector2( b_x, b_y ), + new Vector2( c_x, c_y ) + ]; + + }, + + generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) { + + const a_x = vertices[ indexA * 3 ]; + const a_y = vertices[ indexA * 3 + 1 ]; + const a_z = vertices[ indexA * 3 + 2 ]; + const b_x = vertices[ indexB * 3 ]; + const b_y = vertices[ indexB * 3 + 1 ]; + const b_z = vertices[ indexB * 3 + 2 ]; + const c_x = vertices[ indexC * 3 ]; + const c_y = vertices[ indexC * 3 + 1 ]; + const c_z = vertices[ indexC * 3 + 2 ]; + const d_x = vertices[ indexD * 3 ]; + const d_y = vertices[ indexD * 3 + 1 ]; + const d_z = vertices[ indexD * 3 + 2 ]; + + if ( Math.abs( a_y - b_y ) < Math.abs( a_x - b_x ) ) { + + return [ + new Vector2( a_x, 1 - a_z ), + new Vector2( b_x, 1 - b_z ), + new Vector2( c_x, 1 - c_z ), + new Vector2( d_x, 1 - d_z ) + ]; + + } else { + + return [ + new Vector2( a_y, 1 - a_z ), + new Vector2( b_y, 1 - b_z ), + new Vector2( c_y, 1 - c_z ), + new Vector2( d_y, 1 - d_z ) + ]; + + } + + } + +}; + +function toJSON$1( shapes, options, data ) { + + data.shapes = []; + + if ( Array.isArray( shapes ) ) { + + for ( let i = 0, l = shapes.length; i < l; i ++ ) { + + const shape = shapes[ i ]; + + data.shapes.push( shape.uuid ); + + } + + } else { + + data.shapes.push( shapes.uuid ); + + } + + data.options = Object.assign( {}, options ); + + if ( options.extrudePath !== undefined ) data.options.extrudePath = options.extrudePath.toJSON(); + + return data; + +} + +class IcosahedronGeometry extends PolyhedronGeometry { + + constructor( radius = 1, detail = 0 ) { + + const t = ( 1 + Math.sqrt( 5 ) ) / 2; + + const vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; + + const indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; + + super( vertices, indices, radius, detail ); + + this.type = 'IcosahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + + } + + static fromJSON( data ) { + + return new IcosahedronGeometry( data.radius, data.detail ); + + } + +} + +class OctahedronGeometry extends PolyhedronGeometry { + + constructor( radius = 1, detail = 0 ) { + + const vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, + 0, - 1, 0, 0, 0, 1, 0, 0, - 1 + ]; + + const indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, + 0, 5, 2, 1, 2, 5, 1, 5, 3, + 1, 3, 4, 1, 4, 2 + ]; + + super( vertices, indices, radius, detail ); + + this.type = 'OctahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + + } + + static fromJSON( data ) { + + return new OctahedronGeometry( data.radius, data.detail ); + + } + +} + +class RingGeometry extends BufferGeometry { + + constructor( innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2 ) { + + super(); + + this.type = 'RingGeometry'; + + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + thetaSegments = Math.max( 3, thetaSegments ); + phiSegments = Math.max( 1, phiSegments ); + + // buffers + + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; + + // some helper variables + + let radius = innerRadius; + const radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + const vertex = new Vector3(); + const uv = new Vector2(); + + // generate vertices, normals and uvs + + for ( let j = 0; j <= phiSegments; j ++ ) { + + for ( let i = 0; i <= thetaSegments; i ++ ) { + + // values are generate from the inside of the ring to the outside + + const segment = thetaStart + i / thetaSegments * thetaLength; + + // vertex + + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + + vertices.push( vertex.x, vertex.y, vertex.z ); + + // normal + + normals.push( 0, 0, 1 ); + + // uv + + uv.x = ( vertex.x / outerRadius + 1 ) / 2; + uv.y = ( vertex.y / outerRadius + 1 ) / 2; + + uvs.push( uv.x, uv.y ); + + } + + // increase the radius for next row of vertices + + radius += radiusStep; + + } + + // indices + + for ( let j = 0; j < phiSegments; j ++ ) { + + const thetaSegmentLevel = j * ( thetaSegments + 1 ); + + for ( let i = 0; i < thetaSegments; i ++ ) { + + const segment = i + thetaSegmentLevel; + + const a = segment; + const b = segment + thetaSegments + 1; + const c = segment + thetaSegments + 2; + const d = segment + 1; + + // faces + + indices.push( a, b, d ); + indices.push( b, c, d ); + + } + + } + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + static fromJSON( data ) { + + return new RingGeometry( data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength ); + + } + +} + +class ShapeGeometry extends BufferGeometry { + + constructor( shapes = new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] ), curveSegments = 12 ) { + + super(); + + this.type = 'ShapeGeometry'; + + this.parameters = { + shapes: shapes, + curveSegments: curveSegments + }; + + // buffers + + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; + + // helper variables + + let groupStart = 0; + let groupCount = 0; + + // allow single and array values for "shapes" parameter + + if ( Array.isArray( shapes ) === false ) { + + addShape( shapes ); + + } else { + + for ( let i = 0; i < shapes.length; i ++ ) { + + addShape( shapes[ i ] ); + + this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support + + groupStart += groupCount; + groupCount = 0; + + } + + } + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + + // helper functions + + function addShape( shape ) { + + const indexOffset = vertices.length / 3; + const points = shape.extractPoints( curveSegments ); + + let shapeVertices = points.shape; + const shapeHoles = points.holes; + + // check direction of vertices + + if ( ShapeUtils.isClockWise( shapeVertices ) === false ) { + + shapeVertices = shapeVertices.reverse(); + + } + + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + + const shapeHole = shapeHoles[ i ]; + + if ( ShapeUtils.isClockWise( shapeHole ) === true ) { + + shapeHoles[ i ] = shapeHole.reverse(); + + } + + } + + const faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles ); + + // join vertices of inner and outer paths to a single array + + for ( let i = 0, l = shapeHoles.length; i < l; i ++ ) { + + const shapeHole = shapeHoles[ i ]; + shapeVertices = shapeVertices.concat( shapeHole ); + + } + + // vertices, normals, uvs + + for ( let i = 0, l = shapeVertices.length; i < l; i ++ ) { + + const vertex = shapeVertices[ i ]; + + vertices.push( vertex.x, vertex.y, 0 ); + normals.push( 0, 0, 1 ); + uvs.push( vertex.x, vertex.y ); // world uvs + + } + + // indices + + for ( let i = 0, l = faces.length; i < l; i ++ ) { + + const face = faces[ i ]; + + const a = face[ 0 ] + indexOffset; + const b = face[ 1 ] + indexOffset; + const c = face[ 2 ] + indexOffset; + + indices.push( a, b, c ); + groupCount += 3; + + } + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + toJSON() { + + const data = super.toJSON(); + + const shapes = this.parameters.shapes; + + return toJSON( shapes, data ); + + } + + static fromJSON( data, shapes ) { + + const geometryShapes = []; + + for ( let j = 0, jl = data.shapes.length; j < jl; j ++ ) { + + const shape = shapes[ data.shapes[ j ] ]; + + geometryShapes.push( shape ); + + } + + return new ShapeGeometry( geometryShapes, data.curveSegments ); + + } + +} + +function toJSON( shapes, data ) { + + data.shapes = []; + + if ( Array.isArray( shapes ) ) { + + for ( let i = 0, l = shapes.length; i < l; i ++ ) { + + const shape = shapes[ i ]; + + data.shapes.push( shape.uuid ); + + } + + } else { + + data.shapes.push( shapes.uuid ); + + } + + return data; + +} + +class SphereGeometry extends BufferGeometry { + + constructor( radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI ) { + + super(); + + this.type = 'SphereGeometry'; + + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + widthSegments = Math.max( 3, Math.floor( widthSegments ) ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) ); + + const thetaEnd = Math.min( thetaStart + thetaLength, Math.PI ); + + let index = 0; + const grid = []; + + const vertex = new Vector3(); + const normal = new Vector3(); + + // buffers + + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; + + // generate vertices, normals and uvs + + for ( let iy = 0; iy <= heightSegments; iy ++ ) { + + const verticesRow = []; + + const v = iy / heightSegments; + + // special case for the poles + + let uOffset = 0; + + if ( iy === 0 && thetaStart === 0 ) { + + uOffset = 0.5 / widthSegments; + + } else if ( iy === heightSegments && thetaEnd === Math.PI ) { + + uOffset = - 0.5 / widthSegments; + + } + + for ( let ix = 0; ix <= widthSegments; ix ++ ) { + + const u = ix / widthSegments; + + // vertex + + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + vertices.push( vertex.x, vertex.y, vertex.z ); + + // normal + + normal.copy( vertex ).normalize(); + normals.push( normal.x, normal.y, normal.z ); + + // uv + + uvs.push( u + uOffset, 1 - v ); + + verticesRow.push( index ++ ); + + } + + grid.push( verticesRow ); + + } + + // indices + + for ( let iy = 0; iy < heightSegments; iy ++ ) { + + for ( let ix = 0; ix < widthSegments; ix ++ ) { + + const a = grid[ iy ][ ix + 1 ]; + const b = grid[ iy ][ ix ]; + const c = grid[ iy + 1 ][ ix ]; + const d = grid[ iy + 1 ][ ix + 1 ]; + + if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d ); + if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d ); + + } + + } + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + static fromJSON( data ) { + + return new SphereGeometry( data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength ); + + } + +} + +class TetrahedronGeometry extends PolyhedronGeometry { + + constructor( radius = 1, detail = 0 ) { + + const vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; + + const indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; + + super( vertices, indices, radius, detail ); + + this.type = 'TetrahedronGeometry'; + + this.parameters = { + radius: radius, + detail: detail + }; + + } + + static fromJSON( data ) { + + return new TetrahedronGeometry( data.radius, data.detail ); + + } + +} + +class TorusGeometry extends BufferGeometry { + + constructor( radius = 1, tube = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2 ) { + + super(); + + this.type = 'TorusGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; + + radialSegments = Math.floor( radialSegments ); + tubularSegments = Math.floor( tubularSegments ); + + // buffers + + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; + + // helper variables + + const center = new Vector3(); + const vertex = new Vector3(); + const normal = new Vector3(); + + // generate vertices, normals and uvs + + for ( let j = 0; j <= radialSegments; j ++ ) { + + for ( let i = 0; i <= tubularSegments; i ++ ) { + + const u = i / tubularSegments * arc; + const v = j / radialSegments * Math.PI * 2; + + // vertex + + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); + + vertices.push( vertex.x, vertex.y, vertex.z ); + + // normal + + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); + normal.subVectors( vertex, center ).normalize(); + + normals.push( normal.x, normal.y, normal.z ); + + // uv + + uvs.push( i / tubularSegments ); + uvs.push( j / radialSegments ); + + } + + } + + // generate indices + + for ( let j = 1; j <= radialSegments; j ++ ) { + + for ( let i = 1; i <= tubularSegments; i ++ ) { + + // indices + + const a = ( tubularSegments + 1 ) * j + i - 1; + const b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + const c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + const d = ( tubularSegments + 1 ) * j + i; + + // faces + + indices.push( a, b, d ); + indices.push( b, c, d ); + + } + + } + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + static fromJSON( data ) { + + return new TorusGeometry( data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc ); + + } + +} + +class TorusKnotGeometry extends BufferGeometry { + + constructor( radius = 1, tube = 0.4, tubularSegments = 64, radialSegments = 8, p = 2, q = 3 ) { + + super(); + + this.type = 'TorusKnotGeometry'; + + this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; + + tubularSegments = Math.floor( tubularSegments ); + radialSegments = Math.floor( radialSegments ); + + // buffers + + const indices = []; + const vertices = []; + const normals = []; + const uvs = []; + + // helper variables + + const vertex = new Vector3(); + const normal = new Vector3(); + + const P1 = new Vector3(); + const P2 = new Vector3(); + + const B = new Vector3(); + const T = new Vector3(); + const N = new Vector3(); + + // generate vertices, normals and uvs + + for ( let i = 0; i <= tubularSegments; ++ i ) { + + // the radian "u" is used to calculate the position on the torus curve of the current tubular segment + + const u = i / tubularSegments * p * Math.PI * 2; + + // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. + // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions + + calculatePositionOnCurve( u, p, q, radius, P1 ); + calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); + + // calculate orthonormal basis + + T.subVectors( P2, P1 ); + N.addVectors( P2, P1 ); + B.crossVectors( T, N ); + N.crossVectors( B, T ); + + // normalize B, N. T can be ignored, we don't use it + + B.normalize(); + N.normalize(); + + for ( let j = 0; j <= radialSegments; ++ j ) { + + // now calculate the vertices. they are nothing more than an extrusion of the torus curve. + // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. + + const v = j / radialSegments * Math.PI * 2; + const cx = - tube * Math.cos( v ); + const cy = tube * Math.sin( v ); + + // now calculate the final vertex position. + // first we orient the extrusion with our basis vectors, then we add it to the current position on the curve + + vertex.x = P1.x + ( cx * N.x + cy * B.x ); + vertex.y = P1.y + ( cx * N.y + cy * B.y ); + vertex.z = P1.z + ( cx * N.z + cy * B.z ); + + vertices.push( vertex.x, vertex.y, vertex.z ); + + // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) + + normal.subVectors( vertex, P1 ).normalize(); + + normals.push( normal.x, normal.y, normal.z ); + + // uv + + uvs.push( i / tubularSegments ); + uvs.push( j / radialSegments ); + + } + + } + + // generate indices + + for ( let j = 1; j <= tubularSegments; j ++ ) { + + for ( let i = 1; i <= radialSegments; i ++ ) { + + // indices + + const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); + const b = ( radialSegments + 1 ) * j + ( i - 1 ); + const c = ( radialSegments + 1 ) * j + i; + const d = ( radialSegments + 1 ) * ( j - 1 ) + i; + + // faces + + indices.push( a, b, d ); + indices.push( b, c, d ); + + } + + } + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + // this function calculates the current position on the torus curve + + function calculatePositionOnCurve( u, p, q, radius, position ) { + + const cu = Math.cos( u ); + const su = Math.sin( u ); + const quOverP = q / p * u; + const cs = Math.cos( quOverP ); + + position.x = radius * ( 2 + cs ) * 0.5 * cu; + position.y = radius * ( 2 + cs ) * su * 0.5; + position.z = radius * Math.sin( quOverP ) * 0.5; + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + static fromJSON( data ) { + + return new TorusKnotGeometry( data.radius, data.tube, data.tubularSegments, data.radialSegments, data.p, data.q ); + + } + +} + +class TubeGeometry extends BufferGeometry { + + constructor( path = new QuadraticBezierCurve3( new Vector3( - 1, - 1, 0 ), new Vector3( - 1, 1, 0 ), new Vector3( 1, 1, 0 ) ), tubularSegments = 64, radius = 1, radialSegments = 8, closed = false ) { + + super(); + + this.type = 'TubeGeometry'; + + this.parameters = { + path: path, + tubularSegments: tubularSegments, + radius: radius, + radialSegments: radialSegments, + closed: closed + }; + + const frames = path.computeFrenetFrames( tubularSegments, closed ); + + // expose internals + + this.tangents = frames.tangents; + this.normals = frames.normals; + this.binormals = frames.binormals; + + // helper variables + + const vertex = new Vector3(); + const normal = new Vector3(); + const uv = new Vector2(); + let P = new Vector3(); + + // buffer + + const vertices = []; + const normals = []; + const uvs = []; + const indices = []; + + // create buffer data + + generateBufferData(); + + // build geometry + + this.setIndex( indices ); + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) ); + this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) ); + + // functions + + function generateBufferData() { + + for ( let i = 0; i < tubularSegments; i ++ ) { + + generateSegment( i ); + + } + + // if the geometry is not closed, generate the last row of vertices and normals + // at the regular position on the given path + // + // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ) + + generateSegment( ( closed === false ) ? tubularSegments : 0 ); + + // uvs are generated in a separate function. + // this makes it easy compute correct values for closed geometries + + generateUVs(); + + // finally create faces + + generateIndices(); + + } + + function generateSegment( i ) { + + // we use getPointAt to sample evenly distributed points from the given path + + P = path.getPointAt( i / tubularSegments, P ); + + // retrieve corresponding normal and binormal + + const N = frames.normals[ i ]; + const B = frames.binormals[ i ]; + + // generate normals and vertices for the current segment + + for ( let j = 0; j <= radialSegments; j ++ ) { + + const v = j / radialSegments * Math.PI * 2; + + const sin = Math.sin( v ); + const cos = - Math.cos( v ); + + // normal + + normal.x = ( cos * N.x + sin * B.x ); + normal.y = ( cos * N.y + sin * B.y ); + normal.z = ( cos * N.z + sin * B.z ); + normal.normalize(); + + normals.push( normal.x, normal.y, normal.z ); + + // vertex + + vertex.x = P.x + radius * normal.x; + vertex.y = P.y + radius * normal.y; + vertex.z = P.z + radius * normal.z; + + vertices.push( vertex.x, vertex.y, vertex.z ); + + } + + } + + function generateIndices() { + + for ( let j = 1; j <= tubularSegments; j ++ ) { + + for ( let i = 1; i <= radialSegments; i ++ ) { + + const a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); + const b = ( radialSegments + 1 ) * j + ( i - 1 ); + const c = ( radialSegments + 1 ) * j + i; + const d = ( radialSegments + 1 ) * ( j - 1 ) + i; + + // faces + + indices.push( a, b, d ); + indices.push( b, c, d ); + + } + + } + + } + + function generateUVs() { + + for ( let i = 0; i <= tubularSegments; i ++ ) { + + for ( let j = 0; j <= radialSegments; j ++ ) { + + uv.x = i / tubularSegments; + uv.y = j / radialSegments; + + uvs.push( uv.x, uv.y ); + + } + + } + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + + toJSON() { + + const data = super.toJSON(); + + data.path = this.parameters.path.toJSON(); + + return data; + + } + + static fromJSON( data ) { + + // This only works for built-in curves (e.g. CatmullRomCurve3). + // User defined curves or instances of CurvePath will not be deserialized. + return new TubeGeometry( + new Curves[ data.path.type ]().fromJSON( data.path ), + data.tubularSegments, + data.radius, + data.radialSegments, + data.closed + ); + + } + +} + +class WireframeGeometry extends BufferGeometry { + + constructor( geometry = null ) { + + super(); + + this.type = 'WireframeGeometry'; + + this.parameters = { + geometry: geometry + }; + + if ( geometry !== null ) { + + // buffer + + const vertices = []; + const edges = new Set(); + + // helper variables + + const start = new Vector3(); + const end = new Vector3(); + + if ( geometry.index !== null ) { + + // indexed BufferGeometry + + const position = geometry.attributes.position; + const indices = geometry.index; + let groups = geometry.groups; + + if ( groups.length === 0 ) { + + groups = [ { start: 0, count: indices.count, materialIndex: 0 } ]; + + } + + // create a data structure that contains all edges without duplicates + + for ( let o = 0, ol = groups.length; o < ol; ++ o ) { + + const group = groups[ o ]; + + const groupStart = group.start; + const groupCount = group.count; + + for ( let i = groupStart, l = ( groupStart + groupCount ); i < l; i += 3 ) { + + for ( let j = 0; j < 3; j ++ ) { + + const index1 = indices.getX( i + j ); + const index2 = indices.getX( i + ( j + 1 ) % 3 ); + + start.fromBufferAttribute( position, index1 ); + end.fromBufferAttribute( position, index2 ); + + if ( isUniqueEdge( start, end, edges ) === true ) { + + vertices.push( start.x, start.y, start.z ); + vertices.push( end.x, end.y, end.z ); + + } + + } + + } + + } + + } else { + + // non-indexed BufferGeometry + + const position = geometry.attributes.position; + + for ( let i = 0, l = ( position.count / 3 ); i < l; i ++ ) { + + for ( let j = 0; j < 3; j ++ ) { + + // three edges per triangle, an edge is represented as (index1, index2) + // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0) + + const index1 = 3 * i + j; + const index2 = 3 * i + ( ( j + 1 ) % 3 ); + + start.fromBufferAttribute( position, index1 ); + end.fromBufferAttribute( position, index2 ); + + if ( isUniqueEdge( start, end, edges ) === true ) { + + vertices.push( start.x, start.y, start.z ); + vertices.push( end.x, end.y, end.z ); + + } + + } + + } + + } + + // build geometry + + this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + + } + + } + + copy( source ) { + + super.copy( source ); + + this.parameters = Object.assign( {}, source.parameters ); + + return this; + + } + +} + +function isUniqueEdge( start, end, edges ) { + + const hash1 = `${start.x},${start.y},${start.z}-${end.x},${end.y},${end.z}`; + const hash2 = `${end.x},${end.y},${end.z}-${start.x},${start.y},${start.z}`; // coincident edge + + if ( edges.has( hash1 ) === true || edges.has( hash2 ) === true ) { + + return false; + + } else { + + edges.add( hash1 ); + edges.add( hash2 ); + return true; + + } + +} + +var Geometries = /*#__PURE__*/Object.freeze({ + __proto__: null, + BoxGeometry: BoxGeometry, + CapsuleGeometry: CapsuleGeometry, + CircleGeometry: CircleGeometry, + ConeGeometry: ConeGeometry, + CylinderGeometry: CylinderGeometry, + DodecahedronGeometry: DodecahedronGeometry, + EdgesGeometry: EdgesGeometry, + ExtrudeGeometry: ExtrudeGeometry, + IcosahedronGeometry: IcosahedronGeometry, + LatheGeometry: LatheGeometry, + OctahedronGeometry: OctahedronGeometry, + PlaneGeometry: PlaneGeometry, + PolyhedronGeometry: PolyhedronGeometry, + RingGeometry: RingGeometry, + ShapeGeometry: ShapeGeometry, + SphereGeometry: SphereGeometry, + TetrahedronGeometry: TetrahedronGeometry, + TorusGeometry: TorusGeometry, + TorusKnotGeometry: TorusKnotGeometry, + TubeGeometry: TubeGeometry, + WireframeGeometry: WireframeGeometry +}); + +class ShadowMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isShadowMaterial = true; + + this.type = 'ShadowMaterial'; + + this.color = new Color( 0x000000 ); + this.transparent = true; + + this.fog = true; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + + this.fog = source.fog; + + return this; + + } + +} + +class RawShaderMaterial extends ShaderMaterial { + + constructor( parameters ) { + + super( parameters ); + + this.isRawShaderMaterial = true; + + this.type = 'RawShaderMaterial'; + + } + +} + +class MeshStandardMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isMeshStandardMaterial = true; + + this.defines = { 'STANDARD': '' }; + + this.type = 'MeshStandardMaterial'; + + this.color = new Color( 0xffffff ); // diffuse + this.roughness = 1.0; + this.metalness = 0.0; + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.roughnessMap = null; + + this.metalnessMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.envMapIntensity = 1.0; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.flatShading = false; + + this.fog = true; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.defines = { 'STANDARD': '' }; + + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.roughnessMap = source.roughnessMap; + + this.metalnessMap = source.metalnessMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.flatShading = source.flatShading; + + this.fog = source.fog; + + return this; + + } + +} + +class MeshPhysicalMaterial extends MeshStandardMaterial { + + constructor( parameters ) { + + super(); + + this.isMeshPhysicalMaterial = true; + + this.defines = { + + 'STANDARD': '', + 'PHYSICAL': '' + + }; + + this.type = 'MeshPhysicalMaterial'; + + this.anisotropyRotation = 0; + this.anisotropyMap = null; + + this.clearcoatMap = null; + this.clearcoatRoughness = 0.0; + this.clearcoatRoughnessMap = null; + this.clearcoatNormalScale = new Vector2( 1, 1 ); + this.clearcoatNormalMap = null; + + this.ior = 1.5; + + Object.defineProperty( this, 'reflectivity', { + get: function () { + + return ( clamp( 2.5 * ( this.ior - 1 ) / ( this.ior + 1 ), 0, 1 ) ); + + }, + set: function ( reflectivity ) { + + this.ior = ( 1 + 0.4 * reflectivity ) / ( 1 - 0.4 * reflectivity ); + + } + } ); + + this.iridescenceMap = null; + this.iridescenceIOR = 1.3; + this.iridescenceThicknessRange = [ 100, 400 ]; + this.iridescenceThicknessMap = null; + + this.sheenColor = new Color( 0x000000 ); + this.sheenColorMap = null; + this.sheenRoughness = 1.0; + this.sheenRoughnessMap = null; + + this.transmissionMap = null; + + this.thickness = 0; + this.thicknessMap = null; + this.attenuationDistance = Infinity; + this.attenuationColor = new Color( 1, 1, 1 ); + + this.specularIntensity = 1.0; + this.specularIntensityMap = null; + this.specularColor = new Color( 1, 1, 1 ); + this.specularColorMap = null; + + this._anisotropy = 0; + this._clearcoat = 0; + this._iridescence = 0; + this._sheen = 0.0; + this._transmission = 0; + + this.setValues( parameters ); + + } + + get anisotropy() { + + return this._anisotropy; + + } + + set anisotropy( value ) { + + if ( this._anisotropy > 0 !== value > 0 ) { + + this.version ++; + + } + + this._anisotropy = value; + + } + + get clearcoat() { + + return this._clearcoat; + + } + + set clearcoat( value ) { + + if ( this._clearcoat > 0 !== value > 0 ) { + + this.version ++; + + } + + this._clearcoat = value; + + } + + get iridescence() { + + return this._iridescence; + + } + + set iridescence( value ) { + + if ( this._iridescence > 0 !== value > 0 ) { + + this.version ++; + + } + + this._iridescence = value; + + } + + get sheen() { + + return this._sheen; + + } + + set sheen( value ) { + + if ( this._sheen > 0 !== value > 0 ) { + + this.version ++; + + } + + this._sheen = value; + + } + + get transmission() { + + return this._transmission; + + } + + set transmission( value ) { + + if ( this._transmission > 0 !== value > 0 ) { + + this.version ++; + + } + + this._transmission = value; + + } + + copy( source ) { + + super.copy( source ); + + this.defines = { + + 'STANDARD': '', + 'PHYSICAL': '' + + }; + + this.anisotropy = source.anisotropy; + this.anisotropyRotation = source.anisotropyRotation; + this.anisotropyMap = source.anisotropyMap; + + this.clearcoat = source.clearcoat; + this.clearcoatMap = source.clearcoatMap; + this.clearcoatRoughness = source.clearcoatRoughness; + this.clearcoatRoughnessMap = source.clearcoatRoughnessMap; + this.clearcoatNormalMap = source.clearcoatNormalMap; + this.clearcoatNormalScale.copy( source.clearcoatNormalScale ); + + this.ior = source.ior; + + this.iridescence = source.iridescence; + this.iridescenceMap = source.iridescenceMap; + this.iridescenceIOR = source.iridescenceIOR; + this.iridescenceThicknessRange = [ ...source.iridescenceThicknessRange ]; + this.iridescenceThicknessMap = source.iridescenceThicknessMap; + + this.sheen = source.sheen; + this.sheenColor.copy( source.sheenColor ); + this.sheenColorMap = source.sheenColorMap; + this.sheenRoughness = source.sheenRoughness; + this.sheenRoughnessMap = source.sheenRoughnessMap; + + this.transmission = source.transmission; + this.transmissionMap = source.transmissionMap; + + this.thickness = source.thickness; + this.thicknessMap = source.thicknessMap; + this.attenuationDistance = source.attenuationDistance; + this.attenuationColor.copy( source.attenuationColor ); + + this.specularIntensity = source.specularIntensity; + this.specularIntensityMap = source.specularIntensityMap; + this.specularColor.copy( source.specularColor ); + this.specularColorMap = source.specularColorMap; + + return this; + + } + +} + +class MeshPhongMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isMeshPhongMaterial = true; + + this.type = 'MeshPhongMaterial'; + + this.color = new Color( 0xffffff ); // diffuse + this.specular = new Color( 0x111111 ); + this.shininess = 30; + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.flatShading = false; + + this.fog = true; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.flatShading = source.flatShading; + + this.fog = source.fog; + + return this; + + } + +} + +class MeshToonMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isMeshToonMaterial = true; + + this.defines = { 'TOON': '' }; + + this.type = 'MeshToonMaterial'; + + this.color = new Color( 0xffffff ); + + this.map = null; + this.gradientMap = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.alphaMap = null; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.fog = true; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + + this.map = source.map; + this.gradientMap = source.gradientMap; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.alphaMap = source.alphaMap; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.fog = source.fog; + + return this; + + } + +} + +class MeshNormalMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isMeshNormalMaterial = true; + + this.type = 'MeshNormalMaterial'; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.flatShading = false; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + + this.flatShading = source.flatShading; + + return this; + + } + +} + +class MeshLambertMaterial extends Material { + + constructor( parameters ) { + + super(); + + this.isMeshLambertMaterial = true; + + this.type = 'MeshLambertMaterial'; + + this.color = new Color( 0xffffff ); // diffuse + + this.map = null; + + this.lightMap = null; + this.lightMapIntensity = 1.0; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); + + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; + + this.specularMap = null; + + this.alphaMap = null; + + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.flatShading = false; + + this.fog = true; + + this.setValues( parameters ); + + } + + copy( source ) { + + super.copy( source ); + + this.color.copy( source.color ); + + this.map = source.map; + + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; + + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); + + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; + + this.specularMap = source.specularMap; + + this.alphaMap = source.alphaMap; + + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - return 2 * ( 1 - t ) * t * p; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - } + this.flatShading = source.flatShading; - function QuadraticBezierP2( t, p ) { + this.fog = source.fog; - return t * t * p; + return this; } - function QuadraticBezier( t, p0, p1, p2 ) { +} - return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) + - QuadraticBezierP2( t, p2 ); - - } +class MeshMatcapMaterial extends Material { - // + constructor( parameters ) { - function CubicBezierP0( t, p ) { + super(); - var k = 1 - t; - return k * k * k * p; + this.isMeshMatcapMaterial = true; - } + this.defines = { 'MATCAP': '' }; - function CubicBezierP1( t, p ) { + this.type = 'MeshMatcapMaterial'; - var k = 1 - t; - return 3 * k * k * t * p; + this.color = new Color( 0xffffff ); // diffuse - } + this.matcap = null; - function CubicBezierP2( t, p ) { + this.map = null; - return 3 * ( 1 - t ) * t * t * p; + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalMapType = TangentSpaceNormalMap; + this.normalScale = new Vector2( 1, 1 ); - function CubicBezierP3( t, p ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - return t * t * t * p; + this.alphaMap = null; - } + this.flatShading = false; - function CubicBezier( t, p0, p1, p2, p3 ) { + this.fog = true; - return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) + - CubicBezierP3( t, p3 ); + this.setValues( parameters ); } - function CubicBezierCurve( v0, v1, v2, v3 ) { - Curve.call( this ); + copy( source ) { - this.type = 'CubicBezierCurve'; + super.copy( source ); - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); - this.v3 = v3 || new Vector2(); + this.defines = { 'MATCAP': '' }; - } + this.color.copy( source.color ); - CubicBezierCurve.prototype = Object.create( Curve.prototype ); - CubicBezierCurve.prototype.constructor = CubicBezierCurve; + this.matcap = source.matcap; - CubicBezierCurve.prototype.isCubicBezierCurve = true; + this.map = source.map; - CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - var point = optionalTarget || new Vector2(); + this.normalMap = source.normalMap; + this.normalMapType = source.normalMapType; + this.normalScale.copy( source.normalScale ); - var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ) - ); + this.alphaMap = source.alphaMap; - return point; + this.flatShading = source.flatShading; - }; + this.fog = source.fog; - CubicBezierCurve.prototype.copy = function ( source ) { + return this; - Curve.prototype.copy.call( this, source ); + } - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); +} - return this; +class LineDashedMaterial extends LineBasicMaterial { - }; + constructor( parameters ) { - CubicBezierCurve.prototype.toJSON = function () { + super(); - var data = Curve.prototype.toJSON.call( this ); + this.isLineDashedMaterial = true; - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + this.type = 'LineDashedMaterial'; - return data; + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - }; + this.setValues( parameters ); - CubicBezierCurve.prototype.fromJSON = function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); + copy( source ) { - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + super.copy( source ); + + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; return this; - }; + } - function CubicBezierCurve3( v0, v1, v2, v3 ) { +} - Curve.call( this ); +// converts an array to a specific type +function convertArray( array, type, forceClone ) { - this.type = 'CubicBezierCurve3'; + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); - this.v3 = v3 || new Vector3(); + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + + return new type( array ); // create typed array } - CubicBezierCurve3.prototype = Object.create( Curve.prototype ); - CubicBezierCurve3.prototype.constructor = CubicBezierCurve3; + return Array.prototype.slice.call( array ); // create Array - CubicBezierCurve3.prototype.isCubicBezierCurve3 = true; +} - CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { +function isTypedArray( object ) { - var point = optionalTarget || new Vector3(); + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); - var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3; +} - point.set( - CubicBezier( t, v0.x, v1.x, v2.x, v3.x ), - CubicBezier( t, v0.y, v1.y, v2.y, v3.y ), - CubicBezier( t, v0.z, v1.z, v2.z, v3.z ) - ); +// returns an array by which times and values can be sorted +function getKeyframeOrder( times ) { - return point; + function compareTime( i, j ) { - }; + return times[ i ] - times[ j ]; - CubicBezierCurve3.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + const n = times.length; + const result = new Array( n ); + for ( let i = 0; i !== n; ++ i ) result[ i ] = i; - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); - this.v3.copy( source.v3 ); + result.sort( compareTime ); - return this; + return result; - }; +} - CubicBezierCurve3.prototype.toJSON = function () { +// uses the array previously returned by 'getKeyframeOrder' to sort data +function sortedArray( values, stride, order ) { - var data = Curve.prototype.toJSON.call( this ); + const nValues = values.length; + const result = new values.constructor( nValues ); - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); - data.v3 = this.v3.toArray(); + for ( let i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - return data; + const srcOffset = order[ i ] * stride; - }; + for ( let j = 0; j !== stride; ++ j ) { - CubicBezierCurve3.prototype.fromJSON = function ( json ) { + result[ dstOffset ++ ] = values[ srcOffset + j ]; - Curve.prototype.fromJSON.call( this, json ); + } - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); - this.v3.fromArray( json.v3 ); + } - return this; + return result; - }; +} - function LineCurve( v1, v2 ) { +// function for parsing AOS keyframe formats +function flattenJSON( jsonKeys, times, values, valuePropertyName ) { - Curve.call( this ); + let i = 1, key = jsonKeys[ 0 ]; - this.type = 'LineCurve'; + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); + key = jsonKeys[ i ++ ]; } - LineCurve.prototype = Object.create( Curve.prototype ); - LineCurve.prototype.constructor = LineCurve; + if ( key === undefined ) return; // no data - LineCurve.prototype.isLineCurve = true; - - LineCurve.prototype.getPoint = function ( t, optionalTarget ) { - - var point = optionalTarget || new Vector2(); - - if ( t === 1 ) { + let value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - point.copy( this.v2 ); + if ( Array.isArray( value ) ) { - } else { + do { - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + value = key[ valuePropertyName ]; - } + if ( value !== undefined ) { - return point; + times.push( key.time ); + values.push.apply( values, value ); // push all elements - }; + } - // Line curve is linear, so we can overwrite default getPointAt + key = jsonKeys[ i ++ ]; - LineCurve.prototype.getPointAt = function ( u, optionalTarget ) { + } while ( key !== undefined ); - return this.getPoint( u, optionalTarget ); + } else if ( value.toArray !== undefined ) { - }; + // ...assume THREE.Math-ish - LineCurve.prototype.getTangent = function ( /* t */ ) { + do { - var tangent = this.v2.clone().sub( this.v1 ); + value = key[ valuePropertyName ]; - return tangent.normalize(); + if ( value !== undefined ) { - }; + times.push( key.time ); + value.toArray( values, values.length ); - LineCurve.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + key = jsonKeys[ i ++ ]; - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + } while ( key !== undefined ); - return this; + } else { - }; + // otherwise push as-is - LineCurve.prototype.toJSON = function () { + do { - var data = Curve.prototype.toJSON.call( this ); + value = key[ valuePropertyName ]; - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + if ( value !== undefined ) { - return data; + times.push( key.time ); + values.push( value ); - }; + } - LineCurve.prototype.fromJSON = function ( json ) { + key = jsonKeys[ i ++ ]; - Curve.prototype.fromJSON.call( this, json ); + } while ( key !== undefined ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + } - return this; +} - }; +function subclip( sourceClip, name, startFrame, endFrame, fps = 30 ) { - function LineCurve3( v1, v2 ) { + const clip = sourceClip.clone(); - Curve.call( this ); + clip.name = name; - this.type = 'LineCurve3'; + const tracks = []; - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); + for ( let i = 0; i < clip.tracks.length; ++ i ) { - } + const track = clip.tracks[ i ]; + const valueSize = track.getValueSize(); - LineCurve3.prototype = Object.create( Curve.prototype ); - LineCurve3.prototype.constructor = LineCurve3; + const times = []; + const values = []; - LineCurve3.prototype.isLineCurve3 = true; + for ( let j = 0; j < track.times.length; ++ j ) { - LineCurve3.prototype.getPoint = function ( t, optionalTarget ) { + const frame = track.times[ j ] * fps; - var point = optionalTarget || new Vector3(); + if ( frame < startFrame || frame >= endFrame ) continue; - if ( t === 1 ) { + times.push( track.times[ j ] ); - point.copy( this.v2 ); + for ( let k = 0; k < valueSize; ++ k ) { - } else { + values.push( track.values[ j * valueSize + k ] ); - point.copy( this.v2 ).sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + } } - return point; + if ( times.length === 0 ) continue; - }; + track.times = convertArray( times, track.times.constructor ); + track.values = convertArray( values, track.values.constructor ); - // Line curve is linear, so we can overwrite default getPointAt + tracks.push( track ); - LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) { + } - return this.getPoint( u, optionalTarget ); + clip.tracks = tracks; - }; + // find minimum .times value across all tracks in the trimmed clip - LineCurve3.prototype.copy = function ( source ) { + let minStartTime = Infinity; - Curve.prototype.copy.call( this, source ); + for ( let i = 0; i < clip.tracks.length; ++ i ) { - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + if ( minStartTime > clip.tracks[ i ].times[ 0 ] ) { - return this; + minStartTime = clip.tracks[ i ].times[ 0 ]; - }; + } - LineCurve3.prototype.toJSON = function () { + } - var data = Curve.prototype.toJSON.call( this ); + // shift all tracks such that clip begins at t=0 - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + for ( let i = 0; i < clip.tracks.length; ++ i ) { - return data; + clip.tracks[ i ].shift( - 1 * minStartTime ); - }; + } - LineCurve3.prototype.fromJSON = function ( json ) { + clip.resetDuration(); - Curve.prototype.fromJSON.call( this, json ); + return clip; - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); +} - return this; +function makeClipAdditive( targetClip, referenceFrame = 0, referenceClip = targetClip, fps = 30 ) { - }; + if ( fps <= 0 ) fps = 30; - function QuadraticBezierCurve( v0, v1, v2 ) { + const numTracks = referenceClip.tracks.length; + const referenceTime = referenceFrame / fps; - Curve.call( this ); + // Make each track's values relative to the values at the reference frame + for ( let i = 0; i < numTracks; ++ i ) { - this.type = 'QuadraticBezierCurve'; + const referenceTrack = referenceClip.tracks[ i ]; + const referenceTrackType = referenceTrack.ValueTypeName; - this.v0 = v0 || new Vector2(); - this.v1 = v1 || new Vector2(); - this.v2 = v2 || new Vector2(); + // Skip this track if it's non-numeric + if ( referenceTrackType === 'bool' || referenceTrackType === 'string' ) continue; - } + // Find the track in the target clip whose name and type matches the reference track + const targetTrack = targetClip.tracks.find( function ( track ) { - QuadraticBezierCurve.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve; + return track.name === referenceTrack.name + && track.ValueTypeName === referenceTrackType; - QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true; + } ); - QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) { + if ( targetTrack === undefined ) continue; - var point = optionalTarget || new Vector2(); + let referenceOffset = 0; + const referenceValueSize = referenceTrack.getValueSize(); - var v0 = this.v0, v1 = this.v1, v2 = this.v2; + if ( referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ) - ); + referenceOffset = referenceValueSize / 3; - return point; + } - }; + let targetOffset = 0; + const targetValueSize = targetTrack.getValueSize(); - QuadraticBezierCurve.prototype.copy = function ( source ) { + if ( targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline ) { - Curve.prototype.copy.call( this, source ); + targetOffset = targetValueSize / 3; - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); + } - return this; + const lastIndex = referenceTrack.times.length - 1; + let referenceValue; - }; + // Find the value to subtract out of the track + if ( referenceTime <= referenceTrack.times[ 0 ] ) { - QuadraticBezierCurve.prototype.toJSON = function () { + // Reference frame is earlier than the first keyframe, so just use the first keyframe + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + referenceValue = referenceTrack.values.slice( startIndex, endIndex ); - var data = Curve.prototype.toJSON.call( this ); + } else if ( referenceTime >= referenceTrack.times[ lastIndex ] ) { - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + // Reference frame is after the last keyframe, so just use the last keyframe + const startIndex = lastIndex * referenceValueSize + referenceOffset; + const endIndex = startIndex + referenceValueSize - referenceOffset; + referenceValue = referenceTrack.values.slice( startIndex, endIndex ); - return data; + } else { - }; + // Interpolate to the reference value + const interpolant = referenceTrack.createInterpolant(); + const startIndex = referenceOffset; + const endIndex = referenceValueSize - referenceOffset; + interpolant.evaluate( referenceTime ); + referenceValue = interpolant.resultBuffer.slice( startIndex, endIndex ); - QuadraticBezierCurve.prototype.fromJSON = function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); + // Conjugate the quaternion + if ( referenceTrackType === 'quaternion' ) { - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + const referenceQuat = new Quaternion().fromArray( referenceValue ).normalize().conjugate(); + referenceQuat.toArray( referenceValue ); - return this; + } - }; + // Subtract the reference value from all of the track values - function QuadraticBezierCurve3( v0, v1, v2 ) { + const numTimes = targetTrack.times.length; + for ( let j = 0; j < numTimes; ++ j ) { - Curve.call( this ); + const valueStart = j * targetValueSize + targetOffset; - this.type = 'QuadraticBezierCurve3'; + if ( referenceTrackType === 'quaternion' ) { - this.v0 = v0 || new Vector3(); - this.v1 = v1 || new Vector3(); - this.v2 = v2 || new Vector3(); + // Multiply the conjugate for quaternion track types + Quaternion.multiplyQuaternionsFlat( + targetTrack.values, + valueStart, + referenceValue, + 0, + targetTrack.values, + valueStart + ); - } + } else { - QuadraticBezierCurve3.prototype = Object.create( Curve.prototype ); - QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3; + const valueEnd = targetValueSize - targetOffset * 2; - QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true; + // Subtract each value for all other numeric track types + for ( let k = 0; k < valueEnd; ++ k ) { - QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) { + targetTrack.values[ valueStart + k ] -= referenceValue[ k ]; - var point = optionalTarget || new Vector3(); + } - var v0 = this.v0, v1 = this.v1, v2 = this.v2; + } - point.set( - QuadraticBezier( t, v0.x, v1.x, v2.x ), - QuadraticBezier( t, v0.y, v1.y, v2.y ), - QuadraticBezier( t, v0.z, v1.z, v2.z ) - ); + } - return point; + } - }; + targetClip.blendMode = AdditiveAnimationBlendMode; - QuadraticBezierCurve3.prototype.copy = function ( source ) { + return targetClip; - Curve.prototype.copy.call( this, source ); +} - this.v0.copy( source.v0 ); - this.v1.copy( source.v1 ); - this.v2.copy( source.v2 ); +const AnimationUtils = { + convertArray: convertArray, + isTypedArray: isTypedArray, + getKeyframeOrder: getKeyframeOrder, + sortedArray: sortedArray, + flattenJSON: flattenJSON, + subclip: subclip, + makeClipAdditive: makeClipAdditive +}; - return this; +/** + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + */ - }; +class Interpolant { - QuadraticBezierCurve3.prototype.toJSON = function () { + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - var data = Curve.prototype.toJSON.call( this ); + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - data.v0 = this.v0.toArray(); - data.v1 = this.v1.toArray(); - data.v2 = this.v2.toArray(); + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - return data; + this.settings = null; + this.DefaultSettings_ = {}; - }; + } - QuadraticBezierCurve3.prototype.fromJSON = function ( json ) { + evaluate( t ) { - Curve.prototype.fromJSON.call( this, json ); + const pp = this.parameterPositions; + let i1 = this._cachedIndex, + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - this.v0.fromArray( json.v0 ); - this.v1.fromArray( json.v1 ); - this.v2.fromArray( json.v2 ); + validate_interval: { - return this; + seek: { - }; + let right; - function SplineCurve( points /* array of Vector2 */ ) { + linear_scan: { - Curve.call( this ); + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - this.type = 'SplineCurve'; + for ( let giveUpAt = i1 + 2; ; ) { - this.points = points || []; + if ( t1 === undefined ) { - } + if ( t < t0 ) break forward_scan; - SplineCurve.prototype = Object.create( Curve.prototype ); - SplineCurve.prototype.constructor = SplineCurve; + // after end - SplineCurve.prototype.isSplineCurve = true; + i1 = pp.length; + this._cachedIndex = i1; + return this.copySampleValue_( i1 - 1 ); - SplineCurve.prototype.getPoint = function ( t, optionalTarget ) { + } - var point = optionalTarget || new Vector2(); + if ( i1 === giveUpAt ) break; // this loop - var points = this.points; - var p = ( points.length - 1 ) * t; + t0 = t1; + t1 = pp[ ++ i1 ]; - var intPoint = Math.floor( p ); - var weight = p - intPoint; + if ( t < t1 ) { - var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ]; - var p1 = points[ intPoint ]; - var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; + // we have arrived at the sought interval + break seek; - point.set( - CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ), - CatmullRom( weight, p0.y, p1.y, p2.y, p3.y ) - ); + } - return point; + } - }; + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - SplineCurve.prototype.copy = function ( source ) { + } - Curve.prototype.copy.call( this, source ); + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - this.points = []; + // looping? - for ( var i = 0, l = source.points.length; i < l; i ++ ) { + const t1global = pp[ 1 ]; - var point = source.points[ i ]; + if ( t < t1global ) { - this.points.push( point.clone() ); + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - } + } - return this; + // linear reverse scan - }; + for ( let giveUpAt = i1 - 2; ; ) { - SplineCurve.prototype.toJSON = function () { + if ( t0 === undefined ) { - var data = Curve.prototype.toJSON.call( this ); + // before start - data.points = []; + this._cachedIndex = 0; + return this.copySampleValue_( 0 ); - for ( var i = 0, l = this.points.length; i < l; i ++ ) { + } - var point = this.points[ i ]; - data.points.push( point.toArray() ); + if ( i1 === giveUpAt ) break; // this loop - } + t1 = t0; + t0 = pp[ -- i1 - 1 ]; - return data; + if ( t >= t0 ) { - }; + // we have arrived at the sought interval + break seek; - SplineCurve.prototype.fromJSON = function ( json ) { + } - Curve.prototype.fromJSON.call( this, json ); + } - this.points = []; + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - for ( var i = 0, l = json.points.length; i < l; i ++ ) { + } - var point = json.points[ i ]; - this.points.push( new Vector2().fromArray( point ) ); + // the interval is valid - } + break validate_interval; - return this; + } // linear scan - }; + // binary search + while ( i1 < right ) { + const mid = ( i1 + right ) >>> 1; - var Curves = /*#__PURE__*/Object.freeze({ - ArcCurve: ArcCurve, - CatmullRomCurve3: CatmullRomCurve3, - CubicBezierCurve: CubicBezierCurve, - CubicBezierCurve3: CubicBezierCurve3, - EllipseCurve: EllipseCurve, - LineCurve: LineCurve, - LineCurve3: LineCurve3, - QuadraticBezierCurve: QuadraticBezierCurve, - QuadraticBezierCurve3: QuadraticBezierCurve3, - SplineCurve: SplineCurve - }); + if ( t < pp[ mid ] ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ + right = mid; - /************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + } else { - function CurvePath() { + i1 = mid + 1; - Curve.call( this ); + } - this.type = 'CurvePath'; + } - this.curves = []; - this.autoClose = false; // Automatically closes the path + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - } + // check boundary cases, again - CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), { + if ( t0 === undefined ) { - constructor: CurvePath, + this._cachedIndex = 0; + return this.copySampleValue_( 0 ); - add: function ( curve ) { + } - this.curves.push( curve ); + if ( t1 === undefined ) { - }, + i1 = pp.length; + this._cachedIndex = i1; + return this.copySampleValue_( i1 - 1 ); - closePath: function () { + } - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[ 0 ].getPoint( 0 ); - var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + } // seek - if ( ! startPoint.equals( endPoint ) ) { + this._cachedIndex = i1; - this.curves.push( new LineCurve( endPoint, startPoint ) ); + this.intervalChanged_( i1, t0, t1 ); - } + } // validate_interval - }, + return this.interpolate_( i1, t0, t, t1 ); - // To get accurate point with reference to - // entire path distance at time t, - // following has to be done: + } - // 1. Length of each sub path have to be known - // 2. Locate and identify type of curve - // 3. Get t for the curve - // 4. Return curve.getPointAt(t') + getSettings_() { - getPoint: function ( t ) { + return this.settings || this.DefaultSettings_; - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0; + } - // To think about boundaries points. + copySampleValue_( index ) { - while ( i < curveLengths.length ) { + // copies a sample value to the result buffer - if ( curveLengths[ i ] >= d ) { + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - var diff = curveLengths[ i ] - d; - var curve = this.curves[ i ]; + for ( let i = 0; i !== stride; ++ i ) { - var segmentLength = curve.getLength(); - var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + result[ i ] = values[ offset + i ]; - return curve.getPointAt( u ); + } - } + return result; - i ++; + } - } + // Template methods for derived classes: - return null; + interpolate_( /* i1, t0, t, t1 */ ) { - // loop where sum != 0, sum > d , sum+1 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) { + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - points.push( points[ 0 ] ); + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, - } + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; - return points; + // evaluate polynomials - }, + const sP = - wP * ppp + 2 * wP * pp - wP * p; + const s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1; + const s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + const sN = wN * ppp - wN * pp; - copy: function ( source ) { + // combine data linearly - Curve.prototype.copy.call( this, source ); + for ( let i = 0; i !== stride; ++ i ) { - this.curves = []; + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; - for ( var i = 0, l = source.curves.length; i < l; i ++ ) { + } - var curve = source.curves[ i ]; + return result; - this.curves.push( curve.clone() ); + } - } +} - this.autoClose = source.autoClose; +class LinearInterpolant extends Interpolant { - return this; + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - }, + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - toJSON: function () { + } - var data = Curve.prototype.toJSON.call( this ); + interpolate_( i1, t0, t, t1 ) { - data.autoClose = this.autoClose; - data.curves = []; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - for ( var i = 0, l = this.curves.length; i < l; i ++ ) { + offset1 = i1 * stride, + offset0 = offset1 - stride, - var curve = this.curves[ i ]; - data.curves.push( curve.toJSON() ); + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - } + for ( let i = 0; i !== stride; ++ i ) { - return data; + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - }, + } - fromJSON: function ( json ) { + return result; - Curve.prototype.fromJSON.call( this, json ); + } - this.autoClose = json.autoClose; - this.curves = []; +} - for ( var i = 0, l = json.curves.length; i < l; i ++ ) { +/** + * + * Interpolant that evaluates to the sample value at the position preceding + * the parameter. + */ - var curve = json.curves[ i ]; - this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) ); +class DiscreteInterpolant extends Interpolant { - } + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - return this; + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - } + } - } ); + interpolate_( i1 /*, t0, t, t1 */ ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - **/ + return this.copySampleValue_( i1 - 1 ); - function Path( points ) { + } - CurvePath.call( this ); +} - this.type = 'Path'; +class KeyframeTrack { - this.currentPoint = new Vector2(); + constructor( name, times, values, interpolation ) { - if ( points ) { + if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' ); + if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name ); - this.setFromPoints( points ); + this.name = name; - } + this.times = convertArray( times, this.TimeBufferType ); + this.values = convertArray( values, this.ValueBufferType ); + + this.setInterpolation( interpolation || this.DefaultInterpolation ); } - Path.prototype = Object.assign( Object.create( CurvePath.prototype ), { + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): - constructor: Path, + static toJSON( track ) { - setFromPoints: function ( points ) { + const trackType = track.constructor; - this.moveTo( points[ 0 ].x, points[ 0 ].y ); + let json; - for ( var i = 1, l = points.length; i < l; i ++ ) { + // derived classes can define a static toJSON method + if ( trackType.toJSON !== this.toJSON ) { - this.lineTo( points[ i ].x, points[ i ].y ); + json = trackType.toJSON( track ); - } + } else { - }, + // by default, we assume the data can be serialized as-is + json = { - moveTo: function ( x, y ) { + 'name': track.name, + 'times': convertArray( track.times, Array ), + 'values': convertArray( track.values, Array ) - this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + }; - }, + const interpolation = track.getInterpolation(); - lineTo: function ( x, y ) { + if ( interpolation !== track.DefaultInterpolation ) { - var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) ); - this.curves.push( curve ); + json.interpolation = interpolation; - this.currentPoint.set( x, y ); + } - }, + } - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + json.type = track.ValueTypeName; // mandatory - var curve = new QuadraticBezierCurve( - this.currentPoint.clone(), - new Vector2( aCPx, aCPy ), - new Vector2( aX, aY ) - ); + return json; - this.curves.push( curve ); + } - this.currentPoint.set( aX, aY ); + InterpolantFactoryMethodDiscrete( result ) { - }, + return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result ); - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + } - var curve = new CubicBezierCurve( - this.currentPoint.clone(), - new Vector2( aCP1x, aCP1y ), - new Vector2( aCP2x, aCP2y ), - new Vector2( aX, aY ) - ); + InterpolantFactoryMethodLinear( result ) { - this.curves.push( curve ); + return new LinearInterpolant( this.times, this.values, this.getValueSize(), result ); - this.currentPoint.set( aX, aY ); + } - }, + InterpolantFactoryMethodSmooth( result ) { - splineThru: function ( pts /*Array of Vector*/ ) { + return new CubicInterpolant( this.times, this.values, this.getValueSize(), result ); - var npts = [ this.currentPoint.clone() ].concat( pts ); + } - var curve = new SplineCurve( npts ); - this.curves.push( curve ); + setInterpolation( interpolation ) { - this.currentPoint.copy( pts[ pts.length - 1 ] ); + let factoryMethod; - }, + switch ( interpolation ) { - arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + case InterpolateDiscrete: - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; + factoryMethod = this.InterpolantFactoryMethodDiscrete; - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + break; - }, + case InterpolateLinear: - absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + factoryMethod = this.InterpolantFactoryMethodLinear; - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + break; - }, + case InterpolateSmooth: - ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + factoryMethod = this.InterpolantFactoryMethodSmooth; - var x0 = this.currentPoint.x; - var y0 = this.currentPoint.y; + break; - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + } - }, + if ( factoryMethod === undefined ) { - absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + const message = 'unsupported interpolation for ' + + this.ValueTypeName + ' keyframe track named ' + this.name; - var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + if ( this.createInterpolant === undefined ) { - if ( this.curves.length > 0 ) { + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { - // if a previous curve is present, attempt to join - var firstPoint = curve.getPoint( 0 ); + this.setInterpolation( this.DefaultInterpolation ); - if ( ! firstPoint.equals( this.currentPoint ) ) { + } else { - this.lineTo( firstPoint.x, firstPoint.y ); + throw new Error( message ); // fatal, in this case } } - this.curves.push( curve ); + console.warn( 'THREE.KeyframeTrack:', message ); + return this; - var lastPoint = curve.getPoint( 1 ); - this.currentPoint.copy( lastPoint ); + } - }, + this.createInterpolant = factoryMethod; - copy: function ( source ) { + return this; - CurvePath.prototype.copy.call( this, source ); + } - this.currentPoint.copy( source.currentPoint ); + getInterpolation() { - return this; + switch ( this.createInterpolant ) { - }, + case this.InterpolantFactoryMethodDiscrete: - toJSON: function () { + return InterpolateDiscrete; - var data = CurvePath.prototype.toJSON.call( this ); + case this.InterpolantFactoryMethodLinear: - data.currentPoint = this.currentPoint.toArray(); + return InterpolateLinear; - return data; + case this.InterpolantFactoryMethodSmooth: - }, + return InterpolateSmooth; - fromJSON: function ( json ) { + } - CurvePath.prototype.fromJSON.call( this, json ); + } - this.currentPoint.fromArray( json.currentPoint ); + getValueSize() { - return this; + return this.values.length / this.times.length; - } + } - } ); + // move all keyframes either forwards or backwards in time + shift( timeOffset ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. - **/ + if ( timeOffset !== 0.0 ) { - // STEP 1 Create a path. - // STEP 2 Turn path into shape. - // STEP 3 ExtrudeGeometry takes in Shape/Shapes - // STEP 3a - Extract points from each shape, turn to vertices - // STEP 3b - Triangulate each shape, add faces. + const times = this.times; - function Shape( points ) { + for ( let i = 0, n = times.length; i !== n; ++ i ) { - Path.call( this, points ); + times[ i ] += timeOffset; - this.uuid = _Math.generateUUID(); + } - this.type = 'Shape'; + } - this.holes = []; + return this; } - Shape.prototype = Object.assign( Object.create( Path.prototype ), { + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale( timeScale ) { - constructor: Shape, + if ( timeScale !== 1.0 ) { - getPointsHoles: function ( divisions ) { + const times = this.times; - var holesPts = []; + for ( let i = 0, n = times.length; i !== n; ++ i ) { - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { - - holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + times[ i ] *= timeScale; } - return holesPts; + } - }, + return this; - // get points of shape and holes (keypoints based on segments parameter) + } - extractPoints: function ( divisions ) { + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim( startTime, endTime ) { - return { + const times = this.times, + nKeys = times.length; - shape: this.getPoints( divisions ), - holes: this.getPointsHoles( divisions ) + let from = 0, + to = nKeys - 1; - }; + while ( from !== nKeys && times[ from ] < startTime ) { - }, + ++ from; - copy: function ( source ) { + } - Path.prototype.copy.call( this, source ); + while ( to !== - 1 && times[ to ] > endTime ) { - this.holes = []; + -- to; - for ( var i = 0, l = source.holes.length; i < l; i ++ ) { + } - var hole = source.holes[ i ]; + ++ to; // inclusive -> exclusive bound - this.holes.push( hole.clone() ); + if ( from !== 0 || to !== nKeys ) { - } + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) { - return this; + to = Math.max( to, 1 ); + from = to - 1; - }, + } - toJSON: function () { + const stride = this.getValueSize(); + this.times = times.slice( from, to ); + this.values = this.values.slice( from * stride, to * stride ); - var data = Path.prototype.toJSON.call( this ); + } - data.uuid = this.uuid; - data.holes = []; + return this; - for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + } - var hole = this.holes[ i ]; - data.holes.push( hole.toJSON() ); + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate() { - } + let valid = true; - return data; + const valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - }, + console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this ); + valid = false; - fromJSON: function ( json ) { + } - Path.prototype.fromJSON.call( this, json ); + const times = this.times, + values = this.values, - this.uuid = json.uuid; - this.holes = []; + nKeys = times.length; - for ( var i = 0, l = json.holes.length; i < l; i ++ ) { + if ( nKeys === 0 ) { - var hole = json.holes[ i ]; - this.holes.push( new Path().fromJSON( hole ) ); + console.error( 'THREE.KeyframeTrack: Track is empty.', this ); + valid = false; - } + } - return this; + let prevTime = null; - } + for ( let i = 0; i !== nKeys; i ++ ) { - } ); + const currTime = times[ i ]; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - function Light( color, intensity ) { + console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime ); + valid = false; + break; - Object3D.call( this ); + } - this.type = 'Light'; + if ( prevTime !== null && prevTime > currTime ) { - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; + console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime ); + valid = false; + break; + + } - this.receiveShadow = undefined; + prevTime = currTime; - } + } - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { + if ( values !== undefined ) { - constructor: Light, + if ( isTypedArray( values ) ) { - isLight: true, + for ( let i = 0, n = values.length; i !== n; ++ i ) { - copy: function ( source ) { + const value = values[ i ]; - Object3D.prototype.copy.call( this, source ); + if ( isNaN( value ) ) { - this.color.copy( source.color ); - this.intensity = source.intensity; + console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value ); + valid = false; + break; - return this; + } - }, + } - toJSON: function ( meta ) { + } - var data = Object3D.prototype.toJSON.call( this, meta ); + } - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + return valid; - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + } - if ( this.distance !== undefined ) data.object.distance = this.distance; - if ( this.angle !== undefined ) data.object.angle = this.angle; - if ( this.decay !== undefined ) data.object.decay = this.decay; - if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize() { - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); + // times or values may be shared with other tracks, so overwriting is unsafe + const times = this.times.slice(), + values = this.values.slice(), + stride = this.getValueSize(), - return data; + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - } + lastIndex = times.length - 1; - } ); + let writeIndex = 1; - /** - * @author alteredq / http://alteredqualia.com/ - */ + for ( let i = 1; i < lastIndex; ++ i ) { - function HemisphereLight( skyColor, groundColor, intensity ) { + let keep = false; - Light.call( this, skyColor, intensity ); + const time = times[ i ]; + const timeNext = times[ i + 1 ]; - this.type = 'HemisphereLight'; + // remove adjacent keyframes scheduled at the same time - this.castShadow = undefined; + if ( time !== timeNext && ( i !== 1 || time !== times[ 0 ] ) ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + if ( ! smoothInterpolation ) { - this.groundColor = new Color( groundColor ); + // remove unnecessary keyframes same as their neighbors - } + const offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { + for ( let j = 0; j !== stride; ++ j ) { - constructor: HemisphereLight, + const value = values[ offset + j ]; - isHemisphereLight: true, + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - copy: function ( source ) { + keep = true; + break; - Light.prototype.copy.call( this, source ); + } - this.groundColor.copy( source.groundColor ); + } - return this; + } else { - } + keep = true; - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function LightShadow( camera ) { + // in-place compaction - this.camera = camera; + if ( keep ) { - this.bias = 0; - this.radius = 1; + if ( i !== writeIndex ) { - this.mapSize = new Vector2( 512, 512 ); + times[ writeIndex ] = times[ i ]; - this.map = null; - this.mapPass = null; - this.matrix = new Matrix4(); + const readOffset = i * stride, + writeOffset = writeIndex * stride; - this._frustum = new Frustum(); - this._frameExtents = new Vector2( 1, 1 ); + for ( let j = 0; j !== stride; ++ j ) { - this._viewportCount = 1; + values[ writeOffset + j ] = values[ readOffset + j ]; - this._viewports = [ + } - new Vector4( 0, 0, 1, 1 ) + } - ]; + ++ writeIndex; - } + } - Object.assign( LightShadow.prototype, { + } - _projScreenMatrix: new Matrix4(), + // flush last keyframe (compaction looks ahead) - _lightPositionWorld: new Vector3(), + if ( lastIndex > 0 ) { - _lookTarget: new Vector3(), + times[ writeIndex ] = times[ lastIndex ]; - getViewportCount: function () { + for ( let readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) { - return this._viewportCount; + values[ writeOffset + j ] = values[ readOffset + j ]; - }, + } - getFrustum: function () { + ++ writeIndex; - return this._frustum; + } - }, + if ( writeIndex !== times.length ) { - updateMatrices: function ( light ) { + this.times = times.slice( 0, writeIndex ); + this.values = values.slice( 0, writeIndex * stride ); - var shadowCamera = this.camera, - shadowMatrix = this.matrix, - projScreenMatrix = this._projScreenMatrix, - lookTarget = this._lookTarget, - lightPositionWorld = this._lightPositionWorld; + } else { - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - shadowCamera.position.copy( lightPositionWorld ); + this.times = times; + this.values = values; - lookTarget.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( lookTarget ); - shadowCamera.updateMatrixWorld(); + } - projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - this._frustum.setFromMatrix( projScreenMatrix ); + return this; - shadowMatrix.set( - 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 - ); + } - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + clone() { - }, + const times = this.times.slice(); + const values = this.values.slice(); - getViewport: function ( viewportIndex ) { + const TypedKeyframeTrack = this.constructor; + const track = new TypedKeyframeTrack( this.name, times, values ); - return this._viewports[ viewportIndex ]; + // Interpolant argument to constructor is not saved, so copy the factory method directly. + track.createInterpolant = this.createInterpolant; - }, + return track; - getFrameExtents: function () { + } - return this._frameExtents; +} - }, +KeyframeTrack.prototype.TimeBufferType = Float32Array; +KeyframeTrack.prototype.ValueBufferType = Float32Array; +KeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; - copy: function ( source ) { +/** + * A Track of Boolean keyframe values. + */ +class BooleanKeyframeTrack extends KeyframeTrack {} - this.camera = source.camera.clone(); +BooleanKeyframeTrack.prototype.ValueTypeName = 'bool'; +BooleanKeyframeTrack.prototype.ValueBufferType = Array; +BooleanKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; +BooleanKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; +BooleanKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - this.bias = source.bias; - this.radius = source.radius; +/** + * A Track of keyframe values that represent color. + */ +class ColorKeyframeTrack extends KeyframeTrack {} - this.mapSize.copy( source.mapSize ); +ColorKeyframeTrack.prototype.ValueTypeName = 'color'; - return this; +/** + * A Track of numeric keyframe values. + */ +class NumberKeyframeTrack extends KeyframeTrack {} - }, +NumberKeyframeTrack.prototype.ValueTypeName = 'number'; - clone: function () { +/** + * Spherical linear unit quaternion interpolant. + */ - return new this.constructor().copy( this ); +class QuaternionLinearInterpolant extends Interpolant { - }, + constructor( parameterPositions, sampleValues, sampleSize, resultBuffer ) { - toJSON: function () { + super( parameterPositions, sampleValues, sampleSize, resultBuffer ); - var object = {}; + } - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + interpolate_( i1, t0, t, t1 ) { - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + const result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - return object; + alpha = ( t - t0 ) / ( t1 - t0 ); - } + let offset = i1 * stride; - } ); + for ( let end = offset + stride; offset !== end; offset += 4 ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha ); - function SpotLightShadow() { + } - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + return result; } - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { +} - constructor: SpotLightShadow, +/** + * A Track of quaternion keyframe values. + */ +class QuaternionKeyframeTrack extends KeyframeTrack { - isSpotLightShadow: true, + InterpolantFactoryMethodLinear( result ) { - updateMatrices: function ( light, viewCamera, viewportIndex ) { + return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result ); - var camera = this.camera; + } - var fov = _Math.RAD2DEG * 2 * light.angle; - var aspect = this.mapSize.width / this.mapSize.height; - var far = light.distance || camera.far; +} - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { +QuaternionKeyframeTrack.prototype.ValueTypeName = 'quaternion'; +// ValueBufferType is inherited +QuaternionKeyframeTrack.prototype.DefaultInterpolation = InterpolateLinear; +QuaternionKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); +/** + * A Track that interpolates Strings + */ +class StringKeyframeTrack extends KeyframeTrack {} - } +StringKeyframeTrack.prototype.ValueTypeName = 'string'; +StringKeyframeTrack.prototype.ValueBufferType = Array; +StringKeyframeTrack.prototype.DefaultInterpolation = InterpolateDiscrete; +StringKeyframeTrack.prototype.InterpolantFactoryMethodLinear = undefined; +StringKeyframeTrack.prototype.InterpolantFactoryMethodSmooth = undefined; - LightShadow.prototype.updateMatrices.call( this, light, viewCamera, viewportIndex ); +/** + * A Track of vectored keyframe values. + */ +class VectorKeyframeTrack extends KeyframeTrack {} - } +VectorKeyframeTrack.prototype.ValueTypeName = 'vector'; - } ); +class AnimationClip { - /** - * @author alteredq / http://alteredqualia.com/ - */ + constructor( name, duration = - 1, tracks, blendMode = NormalAnimationBlendMode ) { - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + this.name = name; + this.tracks = tracks; + this.duration = duration; + this.blendMode = blendMode; - Light.call( this, color, intensity ); + this.uuid = generateUUID(); - this.type = 'SpotLight'; + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + this.resetDuration(); - this.target = new Object3D(); + } - Object.defineProperty( this, 'power', { - get: function () { + } - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * Math.PI; - }, - set: function ( power ) { + static parse( json ) { - // intensity = power per solid angle. - // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / Math.PI; + const tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - } - } ); + for ( let i = 0, n = jsonTracks.length; i !== n; ++ i ) { - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + tracks.push( parseKeyframeTrack( jsonTracks[ i ] ).scale( frameTime ) ); - this.shadow = new SpotLightShadow(); + } - } + const clip = new this( json.name, json.duration, tracks, json.blendMode ); + clip.uuid = json.uuid; - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { + return clip; - constructor: SpotLight, + } - isSpotLight: true, + static toJSON( clip ) { - copy: function ( source ) { + const tracks = [], + clipTracks = clip.tracks; - Light.prototype.copy.call( this, source ); + const json = { - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks, + 'uuid': clip.uuid, + 'blendMode': clip.blendMode - this.target = source.target.clone(); + }; - this.shadow = source.shadow.clone(); + for ( let i = 0, n = clipTracks.length; i !== n; ++ i ) { - return this; + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); } - } ); + return json; - function PointLightShadow() { + } - LightShadow.call( this, new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + static CreateFromMorphTargetSequence( name, morphTargetSequence, fps, noLoop ) { - this._frameExtents = new Vector2( 4, 2 ); + const numMorphTargets = morphTargetSequence.length; + const tracks = []; - this._viewportCount = 6; + for ( let i = 0; i < numMorphTargets; i ++ ) { - this._viewports = [ - // These viewports map a cube-map onto a 2D texture with the - // following orientation: - // - // xzXZ - // y Y - // - // X - Positive x direction - // x - Negative x direction - // Y - Positive y direction - // y - Negative y direction - // Z - Positive z direction - // z - Negative z direction + let times = []; + let values = []; - // positive X - new Vector4( 2, 1, 1, 1 ), - // negative X - new Vector4( 0, 1, 1, 1 ), - // positive Z - new Vector4( 3, 1, 1, 1 ), - // negative Z - new Vector4( 1, 1, 1, 1 ), - // positive Y - new Vector4( 3, 0, 1, 1 ), - // negative Y - new Vector4( 1, 0, 1, 1 ) - ]; + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - this._cubeDirections = [ - new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), - new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) - ]; + values.push( 0, 1, 0 ); - this._cubeUps = [ - new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), - new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) - ]; + const order = getKeyframeOrder( times ); + times = sortedArray( times, 1, order ); + values = sortedArray( values, 1, order ); - } + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( ! noLoop && times[ 0 ] === 0 ) { + + times.push( numMorphTargets ); + values.push( values[ 0 ] ); - PointLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + } - constructor: PointLightShadow, + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); - isPointLightShadow: true, + } - updateMatrices: function ( light, viewCamera, viewportIndex ) { + return new this( name, - 1, tracks ); - var camera = this.camera, - shadowMatrix = this.matrix, - lightPositionWorld = this._lightPositionWorld, - lookTarget = this._lookTarget, - projScreenMatrix = this._projScreenMatrix; + } - lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); - camera.position.copy( lightPositionWorld ); + static findByName( objectOrClipArray, name ) { - lookTarget.copy( camera.position ); - lookTarget.add( this._cubeDirections[ viewportIndex ] ); - camera.up.copy( this._cubeUps[ viewportIndex ] ); - camera.lookAt( lookTarget ); - camera.updateMatrixWorld(); + let clipArray = objectOrClipArray; - shadowMatrix.makeTranslation( - lightPositionWorld.x, - lightPositionWorld.y, - lightPositionWorld.z ); + if ( ! Array.isArray( objectOrClipArray ) ) { - projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - this._frustum.setFromMatrix( projScreenMatrix ); + const o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; } - } ); + for ( let i = 0; i < clipArray.length; i ++ ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( clipArray[ i ].name === name ) { + return clipArray[ i ]; - function PointLight( color, intensity, distance, decay ) { + } - Light.call( this, color, intensity ); + } - this.type = 'PointLight'; + return null; - Object.defineProperty( this, 'power', { - get: function () { + } - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - return this.intensity * 4 * Math.PI; + static CreateClipsFromMorphTargetSequences( morphTargets, fps, noLoop ) { - }, - set: function ( power ) { + const animationToMorphTargets = {}; - // intensity = power per solid angle. - // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf - this.intensity = power / ( 4 * Math.PI ); + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + const pattern = /^([\w-]*?)([\d]+)$/; - } - } ); + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( let i = 0, il = morphTargets.length; i < il; i ++ ) { - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + const morphTarget = morphTargets[ i ]; + const parts = morphTarget.name.match( pattern ); - this.shadow = new PointLightShadow(); + if ( parts && parts.length > 1 ) { - } + const name = parts[ 1 ]; + + let animationMorphTargets = animationToMorphTargets[ name ]; - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { + if ( ! animationMorphTargets ) { - constructor: PointLight, + animationToMorphTargets[ name ] = animationMorphTargets = []; - isPointLight: true, + } - copy: function ( source ) { + animationMorphTargets.push( morphTarget ); - Light.prototype.copy.call( this, source ); + } - this.distance = source.distance; - this.decay = source.decay; + } - this.shadow = source.shadow.clone(); + const clips = []; - return this; + for ( const name in animationToMorphTargets ) { + + clips.push( this.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); } - } ); + return clips; - /** - * @author alteredq / http://alteredqualia.com/ - * @author arose / http://github.com/arose - */ + } - function OrthographicCamera( left, right, top, bottom, near, far ) { + // parse the animation.hierarchy format + static parseAnimation( animation, bones ) { - Camera.call( this ); + if ( ! animation ) { - this.type = 'OrthographicCamera'; + console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' ); + return null; - this.zoom = 1; - this.view = null; + } - this.left = ( left !== undefined ) ? left : - 1; - this.right = ( right !== undefined ) ? right : 1; - this.top = ( top !== undefined ) ? top : 1; - this.bottom = ( bottom !== undefined ) ? bottom : - 1; + const addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) { - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - this.updateProjectionMatrix(); + const times = []; + const values = []; - } + flattenJSON( animationKeys, times, values, propertyName ); - OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), { + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - constructor: OrthographicCamera, + destTracks.push( new trackType( trackName, times, values ) ); - isOrthographicCamera: true, + } - copy: function ( source, recursive ) { + } - Camera.prototype.copy.call( this, source, recursive ); + }; - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; + const tracks = []; - this.zoom = source.zoom; - this.view = source.view === null ? null : Object.assign( {}, source.view ); + const clipName = animation.name || 'default'; + const fps = animation.fps || 30; + const blendMode = animation.blendMode; - return this; + // automatic length determination in AnimationClip. + let duration = animation.length || - 1; - }, + const hierarchyTracks = animation.hierarchy || []; - setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { + for ( let h = 0; h < hierarchyTracks.length; h ++ ) { - if ( this.view === null ) { + const animationKeys = hierarchyTracks[ h ].keys; - this.view = { - enabled: true, - fullWidth: 1, - fullHeight: 1, - offsetX: 0, - offsetY: 0, - width: 1, - height: 1 - }; + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - } + // process morph targets + if ( animationKeys[ 0 ].morphTargets ) { - this.view.enabled = true; - this.view.fullWidth = fullWidth; - this.view.fullHeight = fullHeight; - this.view.offsetX = x; - this.view.offsetY = y; - this.view.width = width; - this.view.height = height; + // figure out all morph targets used in this track + const morphTargetNames = {}; - this.updateProjectionMatrix(); + let k; - }, + for ( k = 0; k < animationKeys.length; k ++ ) { - clearViewOffset: function () { + if ( animationKeys[ k ].morphTargets ) { - if ( this.view !== null ) { + for ( let m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) { - this.view.enabled = false; + morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1; - } + } - this.updateProjectionMatrix(); + } - }, + } + + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( const morphTargetName in morphTargetNames ) { - updateProjectionMatrix: function () { + const times = []; + const values = []; - var dx = ( this.right - this.left ) / ( 2 * this.zoom ); - var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); - var cx = ( this.right + this.left ) / 2; - var cy = ( this.top + this.bottom ) / 2; + for ( let m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) { - var left = cx - dx; - var right = cx + dx; - var top = cy + dy; - var bottom = cy - dy; + const animationKey = animationKeys[ k ]; - if ( this.view !== null && this.view.enabled ) { + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); - var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); - var scaleW = ( this.right - this.left ) / this.view.width; - var scaleH = ( this.top - this.bottom ) / this.view.height; + } - left += scaleW * ( this.view.offsetX / zoomW ); - right = left + scaleW * ( this.view.width / zoomW ); - top -= scaleH * ( this.view.offsetY / zoomH ); - bottom = top - scaleH * ( this.view.height / zoomH ); + tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - } + } - this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); + duration = morphTargetNames.length * fps; - this.projectionMatrixInverse.getInverse( this.projectionMatrix ); + } else { - }, + // ...assume skeletal animation - toJSON: function ( meta ) { + const boneName = '.bones[' + bones[ h ].name + ']'; - var data = Object3D.prototype.toJSON.call( this, meta ); + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); - if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); - return data; + } } - } ); + if ( tracks.length === 0 ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + return null; + + } - function DirectionalLightShadow() { + const clip = new this( clipName, duration, tracks, blendMode ); - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + return clip; } - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + resetDuration() { - constructor: DirectionalLightShadow, + const tracks = this.tracks; + let duration = 0; - isDirectionalLightShadow: true, + for ( let i = 0, n = tracks.length; i !== n; ++ i ) { - updateMatrices: function ( light, viewCamera, viewportIndex ) { + const track = this.tracks[ i ]; - LightShadow.prototype.updateMatrices.call( this, light, viewCamera, viewportIndex ); + duration = Math.max( duration, track.times[ track.times.length - 1 ] ); } - } ); + this.duration = duration; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + return this; - function DirectionalLight( color, intensity ) { + } - Light.call( this, color, intensity ); + trim() { - this.type = 'DirectionalLight'; + for ( let i = 0; i < this.tracks.length; i ++ ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + this.tracks[ i ].trim( 0, this.duration ); - this.target = new Object3D(); + } - this.shadow = new DirectionalLightShadow(); + return this; } - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - - constructor: DirectionalLight, - - isDirectionalLight: true, + validate() { - copy: function ( source ) { + let valid = true; - Light.prototype.copy.call( this, source ); + for ( let i = 0; i < this.tracks.length; i ++ ) { - this.target = source.target.clone(); - - this.shadow = source.shadow.clone(); - - return this; + valid = valid && this.tracks[ i ].validate(); } - } ); + return valid; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function AmbientLight( color, intensity ) { + optimize() { - Light.call( this, color, intensity ); + for ( let i = 0; i < this.tracks.length; i ++ ) { - this.type = 'AmbientLight'; + this.tracks[ i ].optimize(); - this.castShadow = undefined; + } + + return this; } - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { + clone() { - constructor: AmbientLight, + const tracks = []; - isAmbientLight: true + for ( let i = 0; i < this.tracks.length; i ++ ) { - } ); + tracks.push( this.tracks[ i ].clone() ); - /** - * @author abelnation / http://github.com/abelnation - */ + } - function RectAreaLight( color, intensity, width, height ) { + return new this.constructor( this.name, this.duration, tracks, this.blendMode ); - Light.call( this, color, intensity ); + } - this.type = 'RectAreaLight'; + toJSON() { - this.width = ( width !== undefined ) ? width : 10; - this.height = ( height !== undefined ) ? height : 10; + return this.constructor.toJSON( this ); } - RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), { +} - constructor: RectAreaLight, +function getTrackTypeForValueTypeName( typeName ) { - isRectAreaLight: true, + switch ( typeName.toLowerCase() ) { - copy: function ( source ) { + case 'scalar': + case 'double': + case 'float': + case 'number': + case 'integer': - Light.prototype.copy.call( this, source ); + return NumberKeyframeTrack; - this.width = source.width; - this.height = source.height; + case 'vector': + case 'vector2': + case 'vector3': + case 'vector4': - return this; + return VectorKeyframeTrack; - }, + case 'color': - toJSON: function ( meta ) { + return ColorKeyframeTrack; - var data = Light.prototype.toJSON.call( this, meta ); + case 'quaternion': - data.object.width = this.width; - data.object.height = this.height; + return QuaternionKeyframeTrack; - return data; + case 'bool': + case 'boolean': - } + return BooleanKeyframeTrack; - } ); + case 'string': - /** - * @author mrdoob / http://mrdoob.com/ - */ + return StringKeyframeTrack; - function MaterialLoader( manager ) { + } - Loader.call( this, manager ); + throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName ); - this.textures = {}; +} - } +function parseKeyframeTrack( json ) { - MaterialLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + if ( json.type === undefined ) { - constructor: MaterialLoader, + throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' ); - load: function ( url, onLoad, onProgress, onError ) { + } - var scope = this; + const trackType = getTrackTypeForValueTypeName( json.type ); - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.load( url, function ( text ) { + if ( json.times === undefined ) { - onLoad( scope.parse( JSON.parse( text ) ) ); + const times = [], values = []; - }, onProgress, onError ); + flattenJSON( json.keys, times, values, 'value' ); - }, + json.times = times; + json.values = values; - parse: function ( json ) { + } - var textures = this.textures; + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - function getTexture( name ) { + return trackType.parse( json ); - if ( textures[ name ] === undefined ) { + } else { - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + // by default, we assume a constructor compatible with the base + return new trackType( json.name, json.times, json.values, json.interpolation ); - } + } - return textures[ name ]; +} - } +const Cache = { - var material = new Materials[ json.type ](); + enabled: false, - if ( json.uuid !== undefined ) material.uuid = json.uuid; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.roughness !== undefined ) material.roughness = json.roughness; - if ( json.metalness !== undefined ) material.metalness = json.metalness; - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; - if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; - if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; - if ( json.fog !== undefined ) material.fog = json.fog; - if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.combine !== undefined ) material.combine = json.combine; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; + files: {}, - if ( json.rotation !== undefined ) material.rotation = json.rotation; + add: function ( key, file ) { - if ( json.linewidth !== 1 ) material.linewidth = json.linewidth; - if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; - if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; - if ( json.scale !== undefined ) material.scale = json.scale; + if ( this.enabled === false ) return; - if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; - if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; - if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; + // console.log( 'THREE.Cache', 'Adding key:', key ); - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - if ( json.morphNormals !== undefined ) material.morphNormals = json.morphNormals; - if ( json.dithering !== undefined ) material.dithering = json.dithering; + this.files[ key ] = file; - if ( json.visible !== undefined ) material.visible = json.visible; + }, - if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; + get: function ( key ) { - if ( json.userData !== undefined ) material.userData = json.userData; + if ( this.enabled === false ) return; - // Shader Material + // console.log( 'THREE.Cache', 'Checking key:', key ); - if ( json.uniforms !== undefined ) { + return this.files[ key ]; - for ( var name in json.uniforms ) { + }, - var uniform = json.uniforms[ name ]; + remove: function ( key ) { - material.uniforms[ name ] = {}; + delete this.files[ key ]; - switch ( uniform.type ) { + }, - case 't': - material.uniforms[ name ].value = getTexture( uniform.value ); - break; + clear: function () { - case 'c': - material.uniforms[ name ].value = new Color().setHex( uniform.value ); - break; + this.files = {}; - case 'v2': - material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); - break; + } - case 'v3': - material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); - break; +}; - case 'v4': - material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); - break; +class LoadingManager { - case 'm3': - material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); + constructor( onLoad, onProgress, onError ) { - case 'm4': - material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); - break; + const scope = this; - default: - material.uniforms[ name ].value = uniform.value; + let isLoading = false; + let itemsLoaded = 0; + let itemsTotal = 0; + let urlModifier = undefined; + const handlers = []; - } + // Refer to #5689 for the reason why we don't set .onStart + // in the constructor - } + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - } + this.itemStart = function ( url ) { - if ( json.defines !== undefined ) material.defines = json.defines; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + itemsTotal ++; - if ( json.extensions !== undefined ) { + if ( isLoading === false ) { - for ( var key in json.extensions ) { + if ( scope.onStart !== undefined ) { - material.extensions[ key ] = json.extensions[ key ]; + scope.onStart( url, itemsLoaded, itemsTotal ); } } - // Deprecated - - if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading - - // for PointsMaterial + isLoading = true; - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + }; - // maps + this.itemEnd = function ( url ) { - if ( json.map !== undefined ) material.map = getTexture( json.map ); - if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); + itemsLoaded ++; - if ( json.alphaMap !== undefined ) { + if ( scope.onProgress !== undefined ) { - material.alphaMap = getTexture( json.alphaMap ); - material.transparent = true; + scope.onProgress( url, itemsLoaded, itemsTotal ); } - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; - if ( json.normalScale !== undefined ) { - - var normalScale = json.normalScale; + if ( itemsLoaded === itemsTotal ) { - if ( Array.isArray( normalScale ) === false ) { + isLoading = false; - // Blender exporter used to export a scalar. See #7459 + if ( scope.onLoad !== undefined ) { - normalScale = [ normalScale, normalScale ]; + scope.onLoad(); } - material.normalScale = new Vector2().fromArray( normalScale ); - } - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; + }; - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); + this.itemError = function ( url ) { - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; + if ( scope.onError !== undefined ) { - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; + scope.onError( url ); - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + } - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + }; - if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); + this.resolveURL = function ( url ) { - if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); - if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); + if ( urlModifier ) { - return material; + return urlModifier( url ); - }, + } - setTextures: function ( value ) { + return url; - this.textures = value; - return this; + }; - } + this.setURLModifier = function ( transform ) { - } ); + urlModifier = transform; - /** - * @author Don McCurdy / https://www.donmccurdy.com - */ + return this; - var LoaderUtils = { + }; - decodeText: function ( array ) { + this.addHandler = function ( regex, loader ) { - if ( typeof TextDecoder !== 'undefined' ) { + handlers.push( regex, loader ); - return new TextDecoder().decode( array ); + return this; - } + }; - // Avoid the String.fromCharCode.apply(null, array) shortcut, which - // throws a "maximum call stack size exceeded" error for large arrays. + this.removeHandler = function ( regex ) { - var s = ''; + const index = handlers.indexOf( regex ); - for ( var i = 0, il = array.length; i < il; i ++ ) { + if ( index !== - 1 ) { - // Implicitly assumes little-endian. - s += String.fromCharCode( array[ i ] ); + handlers.splice( index, 2 ); } - try { - - // merges multi-byte utf-8 characters. - - return decodeURIComponent( escape( s ) ); - - } catch ( e ) { // see #16358 - - return s; - - } + return this; - }, + }; - extractUrlBase: function ( url ) { + this.getHandler = function ( file ) { - var index = url.lastIndexOf( '/' ); + for ( let i = 0, l = handlers.length; i < l; i += 2 ) { - if ( index === - 1 ) return './'; + const regex = handlers[ i ]; + const loader = handlers[ i + 1 ]; - return url.substr( 0, index + 1 ); + if ( regex.global ) regex.lastIndex = 0; // see #17920 - } + if ( regex.test( file ) ) { - }; + return loader; - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ + } - function InstancedBufferGeometry() { + } - BufferGeometry.call( this ); + return null; - this.type = 'InstancedBufferGeometry'; - this.maxInstancedCount = undefined; + }; } - InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), { +} - constructor: InstancedBufferGeometry, +const DefaultLoadingManager = /*@__PURE__*/ new LoadingManager(); - isInstancedBufferGeometry: true, +class Loader { - copy: function ( source ) { + constructor( manager ) { - BufferGeometry.prototype.copy.call( this, source ); + this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager; - this.maxInstancedCount = source.maxInstancedCount; + this.crossOrigin = 'anonymous'; + this.withCredentials = false; + this.path = ''; + this.resourcePath = ''; + this.requestHeader = {}; - return this; + } - }, + load( /* url, onLoad, onProgress, onError */ ) {} - clone: function () { + loadAsync( url, onProgress ) { - return new this.constructor().copy( this ); + const scope = this; - }, + return new Promise( function ( resolve, reject ) { - toJSON: function () { + scope.load( url, resolve, onProgress, reject ); - var data = BufferGeometry.prototype.toJSON.call( this ); + } ); - data.maxInstancedCount = this.maxInstancedCount; + } - data.isInstancedBufferGeometry = true; + parse( /* data */ ) {} - return data; + setCrossOrigin( crossOrigin ) { - } + this.crossOrigin = crossOrigin; + return this; - } ); + } - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ + setWithCredentials( value ) { + + this.withCredentials = value; + return this; - function InstancedBufferAttribute( array, itemSize, normalized, meshPerAttribute ) { + } + + setPath( path ) { - if ( typeof ( normalized ) === 'number' ) { + this.path = path; + return this; - meshPerAttribute = normalized; + } - normalized = false; + setResourcePath( resourcePath ) { - console.error( 'THREE.InstancedBufferAttribute: The constructor now expects normalized as the third argument.' ); + this.resourcePath = resourcePath; + return this; - } + } - BufferAttribute.call( this, array, itemSize, normalized ); + setRequestHeader( requestHeader ) { - this.meshPerAttribute = meshPerAttribute || 1; + this.requestHeader = requestHeader; + return this; } - InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), { +} - constructor: InstancedBufferAttribute, +Loader.DEFAULT_MATERIAL_NAME = '__DEFAULT'; - isInstancedBufferAttribute: true, +const loading = {}; - copy: function ( source ) { +class HttpError extends Error { - BufferAttribute.prototype.copy.call( this, source ); + constructor( message, response ) { - this.meshPerAttribute = source.meshPerAttribute; + super( message ); + this.response = response; - return this; + } - }, +} - toJSON: function () { +class FileLoader extends Loader { - var data = BufferAttribute.prototype.toJSON.call( this ); + constructor( manager ) { - data.meshPerAttribute = this.meshPerAttribute; + super( manager ); - data.isInstancedBufferAttribute = true; + } - return data; + load( url, onLoad, onProgress, onError ) { - } + if ( url === undefined ) url = ''; - } ); + if ( this.path !== undefined ) url = this.path + url; - /** - * @author mrdoob / http://mrdoob.com/ - */ + url = this.manager.resolveURL( url ); - function BufferGeometryLoader( manager ) { + const cached = Cache.get( url ); - Loader.call( this, manager ); + if ( cached !== undefined ) { - } + this.manager.itemStart( url ); - BufferGeometryLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + setTimeout( () => { - constructor: BufferGeometryLoader, + if ( onLoad ) onLoad( cached ); - load: function ( url, onLoad, onProgress, onError ) { + this.manager.itemEnd( url ); - var scope = this; + }, 0 ); - var loader = new FileLoader( scope.manager ); - loader.setPath( scope.path ); - loader.load( url, function ( text ) { + return cached; - onLoad( scope.parse( JSON.parse( text ) ) ); + } - }, onProgress, onError ); + // Check if request is duplicate - }, + if ( loading[ url ] !== undefined ) { - parse: function ( json ) { + loading[ url ].push( { - var geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); + onLoad: onLoad, + onProgress: onProgress, + onError: onError - var index = json.data.index; + } ); - if ( index !== undefined ) { + return; - var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); + } - } + // Initialise array for duplicate requests + loading[ url ] = []; - var attributes = json.data.attributes; + loading[ url ].push( { + onLoad: onLoad, + onProgress: onProgress, + onError: onError, + } ); - for ( var key in attributes ) { + // create request + const req = new Request( url, { + headers: new Headers( this.requestHeader ), + credentials: this.withCredentials ? 'include' : 'same-origin', + // An abort controller could be added within a future PR + } ); - var attribute = attributes[ key ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - var bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; - var bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - geometry.addAttribute( key, bufferAttribute ); + // record states ( avoid data race ) + const mimeType = this.mimeType; + const responseType = this.responseType; - } + // start the fetch + fetch( req ) + .then( response => { - var morphAttributes = json.data.morphAttributes; + if ( response.status === 200 || response.status === 0 ) { - if ( morphAttributes ) { + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - for ( var key in morphAttributes ) { + if ( response.status === 0 ) { - var attributeArray = morphAttributes[ key ]; + console.warn( 'THREE.FileLoader: HTTP Status 0 received.' ); - var array = []; + } - for ( var i = 0, il = attributeArray.length; i < il; i ++ ) { + // Workaround: Checking if response.body === undefined for Alipay browser #23548 - var attribute = attributeArray[ i ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); + if ( typeof ReadableStream === 'undefined' || response.body === undefined || response.body.getReader === undefined ) { - var bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); - if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; - array.push( bufferAttribute ); + return response; } - geometry.morphAttributes[ key ] = array; + const callbacks = loading[ url ]; + const reader = response.body.getReader(); - } + // Nginx needs X-File-Size check + // https://serverfault.com/questions/482875/why-does-nginx-remove-content-length-header-for-chunked-content + const contentLength = response.headers.get( 'Content-Length' ) || response.headers.get( 'X-File-Size' ); + const total = contentLength ? parseInt( contentLength ) : 0; + const lengthComputable = total !== 0; + let loaded = 0; - } + // periodically read data into the new stream tracking while download progress + const stream = new ReadableStream( { + start( controller ) { - var groups = json.data.groups || json.data.drawcalls || json.data.offsets; + readData(); - if ( groups !== undefined ) { + function readData() { - for ( var i = 0, n = groups.length; i !== n; ++ i ) { + reader.read().then( ( { done, value } ) => { - var group = groups[ i ]; + if ( done ) { - geometry.addGroup( group.start, group.count, group.materialIndex ); + controller.close(); - } + } else { - } + loaded += value.byteLength; - var boundingSphere = json.data.boundingSphere; + const event = new ProgressEvent( 'progress', { lengthComputable, loaded, total } ); + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - if ( boundingSphere !== undefined ) { + const callback = callbacks[ i ]; + if ( callback.onProgress ) callback.onProgress( event ); - var center = new Vector3(); + } - if ( boundingSphere.center !== undefined ) { + controller.enqueue( value ); + readData(); - center.fromArray( boundingSphere.center ); + } - } + } ); - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); + } - } + } - if ( json.name ) geometry.name = json.name; - if ( json.userData ) geometry.userData = json.userData; + } ); - return geometry; + return new Response( stream ); - } + } else { - } ); + throw new HttpError( `fetch for "${response.url}" responded with ${response.status}: ${response.statusText}`, response ); - var TYPED_ARRAYS = { - Int8Array: Int8Array, - Uint8Array: Uint8Array, - // Workaround for IE11 pre KB2929437. See #11440 - Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array, - Int16Array: Int16Array, - Uint16Array: Uint16Array, - Int32Array: Int32Array, - Uint32Array: Uint32Array, - Float32Array: Float32Array, - Float64Array: Float64Array - }; + } - /** - * @author mrdoob / http://mrdoob.com/ - */ + } ) + .then( response => { - function ObjectLoader( manager ) { + switch ( responseType ) { - Loader.call( this, manager ); + case 'arraybuffer': - } + return response.arrayBuffer(); - ObjectLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + case 'blob': - constructor: ObjectLoader, + return response.blob(); - load: function ( url, onLoad, onProgress, onError ) { + case 'document': - var scope = this; + return response.text() + .then( text => { - var path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; - this.resourcePath = this.resourcePath || path; + const parser = new DOMParser(); + return parser.parseFromString( text, mimeType ); - var loader = new FileLoader( scope.manager ); - loader.setPath( this.path ); - loader.load( url, function ( text ) { + } ); - var json = null; + case 'json': - try { + return response.json(); + + default: - json = JSON.parse( text ); + if ( mimeType === undefined ) { - } catch ( error ) { + return response.text(); - if ( onError !== undefined ) onError( error ); + } else { - console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); + // sniff encoding + const re = /charset="?([^;"\s]*)"?/i; + const exec = re.exec( mimeType ); + const label = exec && exec[ 1 ] ? exec[ 1 ].toLowerCase() : undefined; + const decoder = new TextDecoder( label ); + return response.arrayBuffer().then( ab => decoder.decode( ab ) ); - return; + } } - var metadata = json.metadata; + } ) + .then( data => { - if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { + // Add to cache only on HTTP success, so that we do not cache + // error response bodies as proper responses to requests. + Cache.add( url, data ); - console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); - return; + const callbacks = loading[ url ]; + delete loading[ url ]; - } + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - scope.parse( json, onLoad ); + const callback = callbacks[ i ]; + if ( callback.onLoad ) callback.onLoad( data ); - }, onProgress, onError ); + } - }, + } ) + .catch( err => { - parse: function ( json, onLoad ) { + // Abort errors and other errors are handled the same - var shapes = this.parseShape( json.shapes ); - var geometries = this.parseGeometries( json.geometries, shapes ); + const callbacks = loading[ url ]; - var images = this.parseImages( json.images, function () { + if ( callbacks === undefined ) { - if ( onLoad !== undefined ) onLoad( object ); + // When onLoad was called and url was deleted in `loading` + this.manager.itemError( url ); + throw err; - } ); + } - var textures = this.parseTextures( json.textures, images ); - var materials = this.parseMaterials( json.materials, textures ); + delete loading[ url ]; - var object = this.parseObject( json.object, geometries, materials ); + for ( let i = 0, il = callbacks.length; i < il; i ++ ) { - if ( json.animations ) { + const callback = callbacks[ i ]; + if ( callback.onError ) callback.onError( err ); - object.animations = this.parseAnimations( json.animations ); + } - } + this.manager.itemError( url ); - if ( json.images === undefined || json.images.length === 0 ) { + } ) + .finally( () => { - if ( onLoad !== undefined ) onLoad( object ); + this.manager.itemEnd( url ); - } + } ); - return object; + this.manager.itemStart( url ); - }, + } - parseShape: function ( json ) { + setResponseType( value ) { - var shapes = {}; + this.responseType = value; + return this; - if ( json !== undefined ) { + } - for ( var i = 0, l = json.length; i < l; i ++ ) { + setMimeType( value ) { - var shape = new Shape().fromJSON( json[ i ] ); + this.mimeType = value; + return this; - shapes[ shape.uuid ] = shape; + } - } +} - } +class AnimationLoader extends Loader { - return shapes; + constructor( manager ) { - }, + super( manager ); - parseGeometries: function ( json, shapes ) { + } - var geometries = {}; + load( url, onLoad, onProgress, onError ) { - if ( json !== undefined ) { + const scope = this; - var bufferGeometryLoader = new BufferGeometryLoader(); + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - for ( var i = 0, l = json.length; i < l; i ++ ) { + try { - var geometry; - var data = json[ i ]; + onLoad( scope.parse( JSON.parse( text ) ) ); - switch ( data.type ) { + } catch ( e ) { - case 'PlaneGeometry': - case 'PlaneBufferGeometry': + if ( onError ) { - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); + onError( e ); - break; + } else { - case 'BoxGeometry': - case 'BoxBufferGeometry': - case 'CubeGeometry': // backwards compatible - - geometry = new Geometries[ data.type ]( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); + console.error( e ); - break; + } - case 'CircleGeometry': - case 'CircleBufferGeometry': + scope.manager.itemError( url ); - geometry = new Geometries[ data.type ]( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); + } - break; + }, onProgress, onError ); - case 'CylinderGeometry': - case 'CylinderBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); + } - break; + parse( json ) { - case 'ConeGeometry': - case 'ConeBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); + const animations = []; - break; + for ( let i = 0; i < json.length; i ++ ) { - case 'SphereGeometry': - case 'SphereBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); + const clip = AnimationClip.parse( json[ i ] ); - break; + animations.push( clip ); - case 'DodecahedronGeometry': - case 'DodecahedronBufferGeometry': - case 'IcosahedronGeometry': - case 'IcosahedronBufferGeometry': - case 'OctahedronGeometry': - case 'OctahedronBufferGeometry': - case 'TetrahedronGeometry': - case 'TetrahedronBufferGeometry': - - geometry = new Geometries[ data.type ]( - data.radius, - data.detail - ); + } - break; + return animations; - case 'RingGeometry': - case 'RingBufferGeometry': + } - geometry = new Geometries[ data.type ]( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); +} - break; +/** + * Abstract Base class to block based textures loader (dds, pvr, ...) + * + * Sub classes have to implement the parse() method which will be used in load(). + */ - case 'TorusGeometry': - case 'TorusBufferGeometry': +class CompressedTextureLoader extends Loader { - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); + constructor( manager ) { - break; + super( manager ); - case 'TorusKnotGeometry': - case 'TorusKnotBufferGeometry': + } - geometry = new Geometries[ data.type ]( - data.radius, - data.tube, - data.tubularSegments, - data.radialSegments, - data.p, - data.q - ); + load( url, onLoad, onProgress, onError ) { - break; + const scope = this; - case 'TubeGeometry': - case 'TubeBufferGeometry': - - // This only works for built-in curves (e.g. CatmullRomCurve3). - // User defined curves or instances of CurvePath will not be deserialized. - geometry = new Geometries[ data.type ]( - new Curves[ data.path.type ]().fromJSON( data.path ), - data.tubularSegments, - data.radius, - data.radialSegments, - data.closed - ); + const images = []; - break; + const texture = new CompressedTexture(); - case 'LatheGeometry': - case 'LatheBufferGeometry': + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( scope.withCredentials ); - geometry = new Geometries[ data.type ]( - data.points, - data.segments, - data.phiStart, - data.phiLength - ); + let loaded = 0; - break; + function loadTexture( i ) { - case 'PolyhedronGeometry': - case 'PolyhedronBufferGeometry': + loader.load( url[ i ], function ( buffer ) { - geometry = new Geometries[ data.type ]( - data.vertices, - data.indices, - data.radius, - data.details - ); + const texDatas = scope.parse( buffer, true ); - break; + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; - case 'ShapeGeometry': - case 'ShapeBufferGeometry': + loaded += 1; - var geometryShapes = []; + if ( loaded === 6 ) { - for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { + if ( texDatas.mipmapCount === 1 ) texture.minFilter = LinearFilter; - var shape = shapes[ data.shapes[ j ] ]; + texture.image = images; + texture.format = texDatas.format; + texture.needsUpdate = true; - geometryShapes.push( shape ); + if ( onLoad ) onLoad( texture ); - } + } - geometry = new Geometries[ data.type ]( - geometryShapes, - data.curveSegments - ); + }, onProgress, onError ); - break; + } + if ( Array.isArray( url ) ) { - case 'ExtrudeGeometry': - case 'ExtrudeBufferGeometry': + for ( let i = 0, il = url.length; i < il; ++ i ) { - var geometryShapes = []; + loadTexture( i ); - for ( var j = 0, jl = data.shapes.length; j < jl; j ++ ) { + } - var shape = shapes[ data.shapes[ j ] ]; + } else { - geometryShapes.push( shape ); + // compressed cubemap texture stored in a single DDS file - } + loader.load( url, function ( buffer ) { - var extrudePath = data.options.extrudePath; + const texDatas = scope.parse( buffer, true ); - if ( extrudePath !== undefined ) { + if ( texDatas.isCubemap ) { - data.options.extrudePath = new Curves[ extrudePath.type ]().fromJSON( extrudePath ); + const faces = texDatas.mipmaps.length / texDatas.mipmapCount; - } + for ( let f = 0; f < faces; f ++ ) { - geometry = new Geometries[ data.type ]( - geometryShapes, - data.options - ); + images[ f ] = { mipmaps: [] }; - break; + for ( let i = 0; i < texDatas.mipmapCount; i ++ ) { - case 'BufferGeometry': - case 'InstancedBufferGeometry': + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; - geometry = bufferGeometryLoader.parse( data ); + } - break; + } - case 'Geometry': + texture.image = images; - if ( 'THREE' in window && 'LegacyJSONLoader' in THREE ) { + } else { - var geometryLoader = new THREE.LegacyJSONLoader(); - geometry = geometryLoader.parse( data, this.resourcePath ).geometry; + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + } - } else { + if ( texDatas.mipmapCount === 1 ) { - console.error( 'THREE.ObjectLoader: You have to import LegacyJSONLoader in order load geometry data of type "Geometry".' ); + texture.minFilter = LinearFilter; - } + } - break; + texture.format = texDatas.format; + texture.needsUpdate = true; - default: + if ( onLoad ) onLoad( texture ); - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + }, onProgress, onError ); - continue; + } - } + return texture; - geometry.uuid = data.uuid; + } - if ( data.name !== undefined ) geometry.name = data.name; - if ( geometry.isBufferGeometry === true && data.userData !== undefined ) geometry.userData = data.userData; +} - geometries[ data.uuid ] = geometry; +class ImageLoader extends Loader { - } + constructor( manager ) { - } + super( manager ); - return geometries; + } - }, + load( url, onLoad, onProgress, onError ) { - parseMaterials: function ( json, textures ) { + if ( this.path !== undefined ) url = this.path + url; - var cache = {}; // MultiMaterial - var materials = {}; + url = this.manager.resolveURL( url ); - if ( json !== undefined ) { + const scope = this; - var loader = new MaterialLoader(); - loader.setTextures( textures ); + const cached = Cache.get( url ); - for ( var i = 0, l = json.length; i < l; i ++ ) { + if ( cached !== undefined ) { - var data = json[ i ]; + scope.manager.itemStart( url ); - if ( data.type === 'MultiMaterial' ) { + setTimeout( function () { - // Deprecated + if ( onLoad ) onLoad( cached ); - var array = []; + scope.manager.itemEnd( url ); - for ( var j = 0; j < data.materials.length; j ++ ) { + }, 0 ); - var material = data.materials[ j ]; + return cached; - if ( cache[ material.uuid ] === undefined ) { + } - cache[ material.uuid ] = loader.parse( material ); + const image = createElementNS( 'img' ); - } + function onImageLoad() { - array.push( cache[ material.uuid ] ); + removeEventListeners(); - } + Cache.add( url, this ); - materials[ data.uuid ] = array; + if ( onLoad ) onLoad( this ); - } else { + scope.manager.itemEnd( url ); - if ( cache[ data.uuid ] === undefined ) { + } - cache[ data.uuid ] = loader.parse( data ); + function onImageError( event ) { - } + removeEventListeners(); - materials[ data.uuid ] = cache[ data.uuid ]; + if ( onError ) onError( event ); - } + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - } + } - } + function removeEventListeners() { - return materials; + image.removeEventListener( 'load', onImageLoad, false ); + image.removeEventListener( 'error', onImageError, false ); - }, + } - parseAnimations: function ( json ) { + image.addEventListener( 'load', onImageLoad, false ); + image.addEventListener( 'error', onImageError, false ); - var animations = []; + if ( url.slice( 0, 5 ) !== 'data:' ) { - for ( var i = 0; i < json.length; i ++ ) { + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - var data = json[ i ]; + } - var clip = AnimationClip.parse( data ); + scope.manager.itemStart( url ); - if ( data.uuid !== undefined ) clip.uuid = data.uuid; + image.src = url; - animations.push( clip ); + return image; - } + } - return animations; +} - }, +class CubeTextureLoader extends Loader { - parseImages: function ( json, onLoad ) { + constructor( manager ) { - var scope = this; - var images = {}; + super( manager ); - function loadImage( url ) { + } - scope.manager.itemStart( url ); + load( urls, onLoad, onProgress, onError ) { - return loader.load( url, function () { + const texture = new CubeTexture(); + texture.colorSpace = SRGBColorSpace; - scope.manager.itemEnd( url ); + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - }, undefined, function () { + let loaded = 0; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + function loadTexture( i ) { - } ); + loader.load( urls[ i ], function ( image ) { - } + texture.images[ i ] = image; - if ( json !== undefined && json.length > 0 ) { + loaded ++; - var manager = new LoadingManager( onLoad ); + if ( loaded === 6 ) { - var loader = new ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); + texture.needsUpdate = true; - for ( var i = 0, il = json.length; i < il; i ++ ) { + if ( onLoad ) onLoad( texture ); - var image = json[ i ]; - var url = image.url; + } - if ( Array.isArray( url ) ) { + }, undefined, onError ); - // load array of images e.g CubeTexture + } - images[ image.uuid ] = []; + for ( let i = 0; i < urls.length; ++ i ) { - for ( var j = 0, jl = url.length; j < jl; j ++ ) { + loadTexture( i ); - var currentUrl = url[ j ]; + } - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( currentUrl ) ? currentUrl : scope.resourcePath + currentUrl; + return texture; - images[ image.uuid ].push( loadImage( path ) ); + } - } +} - } else { +/** + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + * + * Sub classes have to implement the parse() method which will be used in load(). + */ - // load single image +class DataTextureLoader extends Loader { - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.resourcePath + image.url; + constructor( manager ) { - images[ image.uuid ] = loadImage( path ); + super( manager ); - } + } - } + load( url, onLoad, onProgress, onError ) { - } + const scope = this; - return images; + const texture = new DataTexture(); - }, + const loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setRequestHeader( this.requestHeader ); + loader.setPath( this.path ); + loader.setWithCredentials( scope.withCredentials ); + loader.load( url, function ( buffer ) { - parseTextures: function ( json, images ) { + let texData; - function parseConstant( value, type ) { + try { - if ( typeof value === 'number' ) return value; + texData = scope.parse( buffer ); - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + } catch ( error ) { - return type[ value ]; + if ( onError !== undefined ) { - } + onError( error ); - var textures = {}; + } else { - if ( json !== undefined ) { + console.error( error ); + return; - for ( var i = 0, l = json.length; i < l; i ++ ) { + } - var data = json[ i ]; + } - if ( data.image === undefined ) { + if ( texData.image !== undefined ) { - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); + texture.image = texData.image; - } + } else if ( texData.data !== undefined ) { - if ( images[ data.image ] === undefined ) { + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + } - } + texture.wrapS = texData.wrapS !== undefined ? texData.wrapS : ClampToEdgeWrapping; + texture.wrapT = texData.wrapT !== undefined ? texData.wrapT : ClampToEdgeWrapping; - var texture; + texture.magFilter = texData.magFilter !== undefined ? texData.magFilter : LinearFilter; + texture.minFilter = texData.minFilter !== undefined ? texData.minFilter : LinearFilter; - if ( Array.isArray( images[ data.image ] ) ) { + texture.anisotropy = texData.anisotropy !== undefined ? texData.anisotropy : 1; - texture = new CubeTexture( images[ data.image ] ); + if ( texData.colorSpace !== undefined ) { - } else { + texture.colorSpace = texData.colorSpace; - texture = new Texture( images[ data.image ] ); + } else if ( texData.encoding !== undefined ) { // @deprecated, r152 - } + texture.encoding = texData.encoding; - texture.needsUpdate = true; + } - texture.uuid = data.uuid; + if ( texData.flipY !== undefined ) { - if ( data.name !== undefined ) texture.name = data.name; + texture.flipY = texData.flipY; - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); + } - if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); - if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); - if ( data.center !== undefined ) texture.center.fromArray( data.center ); - if ( data.rotation !== undefined ) texture.rotation = data.rotation; + if ( texData.format !== undefined ) { - if ( data.wrap !== undefined ) { + texture.format = texData.format; - texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); - texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); + } - } + if ( texData.type !== undefined ) { - if ( data.format !== undefined ) texture.format = data.format; - if ( data.type !== undefined ) texture.type = data.type; - if ( data.encoding !== undefined ) texture.encoding = data.encoding; + texture.type = texData.type; - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; + } - if ( data.flipY !== undefined ) texture.flipY = data.flipY; + if ( texData.mipmaps !== undefined ) { - if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; - if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; + texture.mipmaps = texData.mipmaps; + texture.minFilter = LinearMipmapLinearFilter; // presumably... - textures[ data.uuid ] = texture; + } - } + if ( texData.mipmapCount === 1 ) { - } + texture.minFilter = LinearFilter; - return textures; + } - }, + if ( texData.generateMipmaps !== undefined ) { - parseObject: function ( data, geometries, materials ) { + texture.generateMipmaps = texData.generateMipmaps; - var object; + } - function getGeometry( name ) { + texture.needsUpdate = true; - if ( geometries[ name ] === undefined ) { + if ( onLoad ) onLoad( texture, texData ); - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + }, onProgress, onError ); - } - return geometries[ name ]; + return texture; - } + } - function getMaterial( name ) { +} - if ( name === undefined ) return undefined; +class TextureLoader extends Loader { - if ( Array.isArray( name ) ) { + constructor( manager ) { - var array = []; + super( manager ); - for ( var i = 0, l = name.length; i < l; i ++ ) { + } - var uuid = name[ i ]; + load( url, onLoad, onProgress, onError ) { - if ( materials[ uuid ] === undefined ) { + const texture = new Texture(); - console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); + const loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - } + loader.load( url, function ( image ) { - array.push( materials[ uuid ] ); + texture.image = image; + texture.needsUpdate = true; - } + if ( onLoad !== undefined ) { - return array; + onLoad( texture ); - } + } - if ( materials[ name ] === undefined ) { + }, onProgress, onError ); - console.warn( 'THREE.ObjectLoader: Undefined material', name ); + return texture; - } + } - return materials[ name ]; +} - } +class Light extends Object3D { - switch ( data.type ) { + constructor( color, intensity = 1 ) { - case 'Scene': + super(); - object = new Scene(); + this.isLight = true; - if ( data.background !== undefined ) { + this.type = 'Light'; - if ( Number.isInteger( data.background ) ) { + this.color = new Color( color ); + this.intensity = intensity; - object.background = new Color( data.background ); + } - } + dispose() { - } + // Empty here in base class; some subclasses override. - if ( data.fog !== undefined ) { + } - if ( data.fog.type === 'Fog' ) { + copy( source, recursive ) { - object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); + super.copy( source, recursive ); - } else if ( data.fog.type === 'FogExp2' ) { + this.color.copy( source.color ); + this.intensity = source.intensity; - object.fog = new FogExp2( data.fog.color, data.fog.density ); + return this; - } + } - } + toJSON( meta ) { - break; + const data = super.toJSON( meta ); - case 'PerspectiveCamera': + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - if ( data.focus !== undefined ) object.focus = data.focus; - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; - if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - break; + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - case 'OrthographicCamera': + return data; - object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + } - if ( data.zoom !== undefined ) object.zoom = data.zoom; - if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); +} - break; +class HemisphereLight extends Light { - case 'AmbientLight': + constructor( skyColor, groundColor, intensity ) { - object = new AmbientLight( data.color, data.intensity ); + super( skyColor, intensity ); - break; + this.isHemisphereLight = true; - case 'DirectionalLight': + this.type = 'HemisphereLight'; - object = new DirectionalLight( data.color, data.intensity ); + this.position.copy( Object3D.DEFAULT_UP ); + this.updateMatrix(); - break; + this.groundColor = new Color( groundColor ); - case 'PointLight': + } - object = new PointLight( data.color, data.intensity, data.distance, data.decay ); + copy( source, recursive ) { - break; + super.copy( source, recursive ); - case 'RectAreaLight': + this.groundColor.copy( source.groundColor ); - object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); + return this; - break; + } - case 'SpotLight': +} - object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); +const _projScreenMatrix$1 = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld$1 = /*@__PURE__*/ new Vector3(); +const _lookTarget$1 = /*@__PURE__*/ new Vector3(); - break; +class LightShadow { - case 'HemisphereLight': + constructor( camera ) { - object = new HemisphereLight( data.color, data.groundColor, data.intensity ); + this.camera = camera; - break; + this.bias = 0; + this.normalBias = 0; + this.radius = 1; + this.blurSamples = 8; - case 'SkinnedMesh': + this.mapSize = new Vector2( 512, 512 ); - console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' ); + this.map = null; + this.mapPass = null; + this.matrix = new Matrix4(); - case 'Mesh': + this.autoUpdate = true; + this.needsUpdate = false; - var geometry = getGeometry( data.geometry ); - var material = getMaterial( data.material ); + this._frustum = new Frustum(); + this._frameExtents = new Vector2( 1, 1 ); - if ( geometry.bones && geometry.bones.length > 0 ) { + this._viewportCount = 1; - object = new SkinnedMesh( geometry, material ); + this._viewports = [ - } else { + new Vector4( 0, 0, 1, 1 ) - object = new Mesh( geometry, material ); + ]; - } + } - if ( data.drawMode !== undefined ) object.setDrawMode( data.drawMode ); + getViewportCount() { - break; + return this._viewportCount; - case 'LOD': + } - object = new LOD(); + getFrustum() { - break; + return this._frustum; - case 'Line': + } - object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); + updateMatrices( light ) { - break; + const shadowCamera = this.camera; + const shadowMatrix = this.matrix; - case 'LineLoop': + _lightPositionWorld$1.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld$1 ); - object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); + _lookTarget$1.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget$1 ); + shadowCamera.updateMatrixWorld(); - break; + _projScreenMatrix$1.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix$1 ); - case 'LineSegments': + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); - object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); + shadowMatrix.multiply( _projScreenMatrix$1 ); - break; + } - case 'PointCloud': - case 'Points': + getViewport( viewportIndex ) { - object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); + return this._viewports[ viewportIndex ]; - break; + } - case 'Sprite': + getFrameExtents() { - object = new Sprite( getMaterial( data.material ) ); + return this._frameExtents; - break; + } - case 'Group': + dispose() { - object = new Group(); + if ( this.map ) { - break; + this.map.dispose(); - default: + } - object = new Object3D(); + if ( this.mapPass ) { - } + this.mapPass.dispose(); - object.uuid = data.uuid; + } - if ( data.name !== undefined ) object.name = data.name; + } - if ( data.matrix !== undefined ) { + copy( source ) { - object.matrix.fromArray( data.matrix ); + this.camera = source.camera.clone(); - if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; - if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); + this.bias = source.bias; + this.radius = source.radius; - } else { + this.mapSize.copy( source.mapSize ); - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + return this; - } + } - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; + clone() { - if ( data.shadow ) { + return new this.constructor().copy( this ); - if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; - if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; - if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); - if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); + } - } + toJSON() { - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; - if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; - if ( data.userData !== undefined ) object.userData = data.userData; - if ( data.layers !== undefined ) object.layers.mask = data.layers; + const object = {}; - if ( data.children !== undefined ) { + if ( this.bias !== 0 ) object.bias = this.bias; + if ( this.normalBias !== 0 ) object.normalBias = this.normalBias; + if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - var children = data.children; + object.camera = this.camera.toJSON( false ).object; + delete object.camera.matrix; - for ( var i = 0; i < children.length; i ++ ) { + return object; - object.add( this.parseObject( children[ i ], geometries, materials ) ); + } - } +} - } +class SpotLightShadow extends LightShadow { - if ( data.type === 'LOD' ) { + constructor() { - var levels = data.levels; + super( new PerspectiveCamera( 50, 1, 0.5, 500 ) ); - for ( var l = 0; l < levels.length; l ++ ) { + this.isSpotLightShadow = true; - var level = levels[ l ]; - var child = object.getObjectByProperty( 'uuid', level.object ); + this.focus = 1; - if ( child !== undefined ) { + } - object.addLevel( child, level.distance ); + updateMatrices( light ) { - } + const camera = this.camera; - } + const fov = RAD2DEG * 2 * light.angle * this.focus; + const aspect = this.mapSize.width / this.mapSize.height; + const far = light.distance || camera.far; - } + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - return object; + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); } - } ); + super.updateMatrices( light ); - var TEXTURE_MAPPING = { - UVMapping: UVMapping, - CubeReflectionMapping: CubeReflectionMapping, - CubeRefractionMapping: CubeRefractionMapping, - EquirectangularReflectionMapping: EquirectangularReflectionMapping, - EquirectangularRefractionMapping: EquirectangularRefractionMapping, - SphericalReflectionMapping: SphericalReflectionMapping, - CubeUVReflectionMapping: CubeUVReflectionMapping, - CubeUVRefractionMapping: CubeUVRefractionMapping - }; + } - var TEXTURE_WRAPPING = { - RepeatWrapping: RepeatWrapping, - ClampToEdgeWrapping: ClampToEdgeWrapping, - MirroredRepeatWrapping: MirroredRepeatWrapping - }; + copy( source ) { - var TEXTURE_FILTER = { - NearestFilter: NearestFilter, - NearestMipmapNearestFilter: NearestMipmapNearestFilter, - NearestMipmapLinearFilter: NearestMipmapLinearFilter, - LinearFilter: LinearFilter, - LinearMipmapNearestFilter: LinearMipmapNearestFilter, - LinearMipmapLinearFilter: LinearMipmapLinearFilter - }; + super.copy( source ); - /** - * @author thespite / http://clicktorelease.com/ - */ + this.focus = source.focus; + return this; - function ImageBitmapLoader( manager ) { + } - if ( typeof createImageBitmap === 'undefined' ) { +} - console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); +class SpotLight extends Light { - } + constructor( color, intensity, distance = 0, angle = Math.PI / 3, penumbra = 0, decay = 2 ) { - if ( typeof fetch === 'undefined' ) { + super( color, intensity ); - console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); + this.isSpotLight = true; - } + this.type = 'SpotLight'; - Loader.call( this, manager ); + this.position.copy( Object3D.DEFAULT_UP ); + this.updateMatrix(); - this.options = undefined; + this.target = new Object3D(); - } + this.distance = distance; + this.angle = angle; + this.penumbra = penumbra; + this.decay = decay; - ImageBitmapLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + this.map = null; - constructor: ImageBitmapLoader, + this.shadow = new SpotLightShadow(); - setOptions: function setOptions( options ) { + } - this.options = options; + get power() { - return this; + // compute the light's luminous power (in lumens) from its intensity (in candela) + // by convention for a spotlight, luminous power (lm) = π * luminous intensity (cd) + return this.intensity * Math.PI; - }, + } - load: function ( url, onLoad, onProgress, onError ) { + set power( power ) { - if ( url === undefined ) url = ''; + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / Math.PI; - if ( this.path !== undefined ) url = this.path + url; + } - url = this.manager.resolveURL( url ); + dispose() { - var scope = this; + this.shadow.dispose(); - var cached = Cache.get( url ); + } - if ( cached !== undefined ) { + copy( source, recursive ) { - scope.manager.itemStart( url ); + super.copy( source, recursive ); - setTimeout( function () { + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - if ( onLoad ) onLoad( cached ); + this.target = source.target.clone(); - scope.manager.itemEnd( url ); + this.shadow = source.shadow.clone(); - }, 0 ); + return this; - return cached; + } - } +} - fetch( url ).then( function ( res ) { +const _projScreenMatrix = /*@__PURE__*/ new Matrix4(); +const _lightPositionWorld = /*@__PURE__*/ new Vector3(); +const _lookTarget = /*@__PURE__*/ new Vector3(); - return res.blob(); +class PointLightShadow extends LightShadow { - } ).then( function ( blob ) { + constructor() { - if ( scope.options === undefined ) { + super( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - // Workaround for FireFox. It causes an error if you pass options. - return createImageBitmap( blob ); + this.isPointLightShadow = true; - } else { + this._frameExtents = new Vector2( 4, 2 ); - return createImageBitmap( blob, scope.options ); + this._viewportCount = 6; - } + this._viewports = [ + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction - } ).then( function ( imageBitmap ) { + // positive X + new Vector4( 2, 1, 1, 1 ), + // negative X + new Vector4( 0, 1, 1, 1 ), + // positive Z + new Vector4( 3, 1, 1, 1 ), + // negative Z + new Vector4( 1, 1, 1, 1 ), + // positive Y + new Vector4( 3, 0, 1, 1 ), + // negative Y + new Vector4( 1, 0, 1, 1 ) + ]; - Cache.add( url, imageBitmap ); + this._cubeDirections = [ + new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ), + new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 ) + ]; - if ( onLoad ) onLoad( imageBitmap ); + this._cubeUps = [ + new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), + new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 ) + ]; - scope.manager.itemEnd( url ); + } - } ).catch( function ( e ) { + updateMatrices( light, viewportIndex = 0 ) { - if ( onError ) onError( e ); + const camera = this.camera; + const shadowMatrix = this.matrix; - scope.manager.itemError( url ); - scope.manager.itemEnd( url ); + const far = light.distance || camera.far; - } ); + if ( far !== camera.far ) { - scope.manager.itemStart( url ); + camera.far = far; + camera.updateProjectionMatrix(); } - } ); - - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * minimal class for proxing functions to Path. Replaces old "extractSubpaths()" - **/ - - function ShapePath() { + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + camera.position.copy( _lightPositionWorld ); - this.type = 'ShapePath'; + _lookTarget.copy( camera.position ); + _lookTarget.add( this._cubeDirections[ viewportIndex ] ); + camera.up.copy( this._cubeUps[ viewportIndex ] ); + camera.lookAt( _lookTarget ); + camera.updateMatrixWorld(); - this.color = new Color(); + shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z ); - this.subPaths = []; - this.currentPath = null; + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + this._frustum.setFromProjectionMatrix( _projScreenMatrix ); } - Object.assign( ShapePath.prototype, { +} - moveTo: function ( x, y ) { +class PointLight extends Light { - this.currentPath = new Path(); - this.subPaths.push( this.currentPath ); - this.currentPath.moveTo( x, y ); + constructor( color, intensity, distance = 0, decay = 2 ) { - }, + super( color, intensity ); - lineTo: function ( x, y ) { + this.isPointLight = true; - this.currentPath.lineTo( x, y ); + this.type = 'PointLight'; - }, + this.distance = distance; + this.decay = decay; - quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + this.shadow = new PointLightShadow(); - this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); + } - }, + get power() { - bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + // compute the light's luminous power (in lumens) from its intensity (in candela) + // for an isotropic light source, luminous power (lm) = 4 π luminous intensity (cd) + return this.intensity * 4 * Math.PI; - this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); + } - }, + set power( power ) { - splineThru: function ( pts ) { + // set the light's intensity (in candela) from the desired luminous power (in lumens) + this.intensity = power / ( 4 * Math.PI ); - this.currentPath.splineThru( pts ); + } - }, + dispose() { - toShapes: function ( isCCW, noHoles ) { + this.shadow.dispose(); - function toShapesNoHoles( inSubpaths ) { + } - var shapes = []; + copy( source, recursive ) { - for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { + super.copy( source, recursive ); - var tmpPath = inSubpaths[ i ]; + this.distance = source.distance; + this.decay = source.decay; - var tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; + this.shadow = source.shadow.clone(); - shapes.push( tmpShape ); + return this; - } + } - return shapes; +} - } +class DirectionalLightShadow extends LightShadow { - function isPointInsidePolygon( inPt, inPolygon ) { + constructor() { - var polyLen = inPolygon.length; + super( new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + this.isDirectionalLightShadow = true; - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; + } - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; +} - if ( Math.abs( edgeDy ) > Number.EPSILON ) { +class DirectionalLight extends Light { - // not parallel - if ( edgeDy < 0 ) { + constructor( color, intensity ) { - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + super( color, intensity ); - } - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + this.isDirectionalLight = true; - if ( inPt.y === edgeLowPt.y ) { + this.type = 'DirectionalLight'; - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! + this.position.copy( Object3D.DEFAULT_UP ); + this.updateMatrix(); - } else { + this.target = new Object3D(); - var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt + this.shadow = new DirectionalLightShadow(); - } + } - } else { + dispose() { - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; + this.shadow.dispose(); - } + } - } + copy( source ) { - return inside; + super.copy( source ); - } + this.target = source.target.clone(); + this.shadow = source.shadow.clone(); - var isClockWise = ShapeUtils.isClockWise; + return this; - var subPaths = this.subPaths; - if ( subPaths.length === 0 ) return []; + } - if ( noHoles === true ) return toShapesNoHoles( subPaths ); +} +class AmbientLight extends Light { - var solid, tmpPath, tmpShape, shapes = []; + constructor( color, intensity ) { - if ( subPaths.length === 1 ) { + super( color, intensity ); - tmpPath = subPaths[ 0 ]; - tmpShape = new Shape(); - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + this.isAmbientLight = true; - } + this.type = 'AmbientLight'; - var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; + } - // console.log("Holes first", holesFirst); +} - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; +class RectAreaLight extends Light { - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; + constructor( color, intensity, width = 10, height = 10 ) { - for ( var i = 0, l = subPaths.length; i < l; i ++ ) { + super( color, intensity ); - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; + this.isRectAreaLight = true; - if ( solid ) { + this.type = 'RectAreaLight'; - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + this.width = width; + this.height = height; - newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.curves = tmpPath.curves; + } - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; + get power() { - //console.log('cw', i); + // compute the light's luminous power (in lumens) from its intensity (in nits) + return this.intensity * this.width * this.height * Math.PI; - } else { + } - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + set power( power ) { - //console.log('ccw', i); + // set the light's intensity (in nits) from the desired luminous power (in lumens) + this.intensity = power / ( this.width * this.height * Math.PI ); - } + } - } + copy( source ) { - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + super.copy( source ); + this.width = source.width; + this.height = source.height; - if ( newShapes.length > 1 ) { + return this; - var ambiguous = false; - var toChange = []; + } - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + toJSON( meta ) { - betterShapeHoles[ sIdx ] = []; + const data = super.toJSON( meta ); - } + data.object.width = this.width; + data.object.height = this.height; - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + return data; - var sho = newShapeHoles[ sIdx ]; + } - for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { +} - var ho = sho[ hIdx ]; - var hole_unassigned = true; +/** + * Primary reference: + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * Secondary reference: + * https://www.ppsloan.org/publications/StupidSH36.pdf + */ - for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { +// 3-band SH defined by 9 coefficients - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { +class SphericalHarmonics3 { - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { + constructor() { - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); + this.isSphericalHarmonics3 = true; - } else { + this.coefficients = []; - ambiguous = true; + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients.push( new Vector3() ); - } + } - } - if ( hole_unassigned ) { + } - betterShapeHoles[ sIdx ].push( ho ); + set( coefficients ) { - } + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].copy( coefficients[ i ] ); - } - // console.log("ambiguous: ", ambiguous); - if ( toChange.length > 0 ) { + } - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + return this; - } + } - } + zero() { - var tmpHoles; + for ( let i = 0; i < 9; i ++ ) { - for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + this.coefficients[ i ].set( 0, 0, 0 ); - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; + } - for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + return this; - tmpShape.holes.push( tmpHoles[ j ].h ); + } - } + // get the radiance in the direction of the normal + // target is a Vector3 + getAt( normal, target ) { - } + // normal is assumed to be unit length - //console.log("shape", shapes); + const x = normal.x, y = normal.y, z = normal.z; - return shapes; + const coeff = this.coefficients; - } + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); - } ); + // band 1 + target.addScaledVector( coeff[ 1 ], 0.488603 * y ); + target.addScaledVector( coeff[ 2 ], 0.488603 * z ); + target.addScaledVector( coeff[ 3 ], 0.488603 * x ); - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author mrdoob / http://mrdoob.com/ - */ + // band 2 + target.addScaledVector( coeff[ 4 ], 1.092548 * ( x * y ) ); + target.addScaledVector( coeff[ 5 ], 1.092548 * ( y * z ) ); + target.addScaledVector( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); + target.addScaledVector( coeff[ 7 ], 1.092548 * ( x * z ) ); + target.addScaledVector( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); + return target; - function Font( data ) { + } - this.type = 'Font'; + // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal + // target is a Vector3 + // https://graphics.stanford.edu/papers/envmap/envmap.pdf + getIrradianceAt( normal, target ) { - this.data = data; + // normal is assumed to be unit length - } + const x = normal.x, y = normal.y, z = normal.z; - Object.assign( Font.prototype, { + const coeff = this.coefficients; - isFont: true, + // band 0 + target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 - generateShapes: function ( text, size ) { + // band 1 + target.addScaledVector( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 + target.addScaledVector( coeff[ 2 ], 2.0 * 0.511664 * z ); + target.addScaledVector( coeff[ 3 ], 2.0 * 0.511664 * x ); - if ( size === undefined ) size = 100; + // band 2 + target.addScaledVector( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 + target.addScaledVector( coeff[ 5 ], 2.0 * 0.429043 * y * z ); + target.addScaledVector( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 + target.addScaledVector( coeff[ 7 ], 2.0 * 0.429043 * x * z ); + target.addScaledVector( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 - var shapes = []; - var paths = createPaths( text, size, this.data ); + return target; - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + } - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + add( sh ) { - } + for ( let i = 0; i < 9; i ++ ) { - return shapes; + this.coefficients[ i ].add( sh.coefficients[ i ] ); } - } ); - - function createPaths( text, size, data ) { + return this; - var chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // see #13988 - var scale = size / data.resolution; - var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale; + } - var paths = []; + addScaledSH( sh, s ) { - var offsetX = 0, offsetY = 0; + for ( let i = 0; i < 9; i ++ ) { - for ( var i = 0; i < chars.length; i ++ ) { + this.coefficients[ i ].addScaledVector( sh.coefficients[ i ], s ); - var char = chars[ i ]; + } - if ( char === '\n' ) { + return this; - offsetX = 0; - offsetY -= line_height; + } - } else { + scale( s ) { - var ret = createPath( char, scale, offsetX, offsetY, data ); - offsetX += ret.offsetX; - paths.push( ret.path ); + for ( let i = 0; i < 9; i ++ ) { - } + this.coefficients[ i ].multiplyScalar( s ); } - return paths; + return this; } - function createPath( char, scale, offsetX, offsetY, data ) { + lerp( sh, alpha ) { - var glyph = data.glyphs[ char ] || data.glyphs[ '?' ]; + for ( let i = 0; i < 9; i ++ ) { - if ( ! glyph ) { + this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); - console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' ); + } - return; + return this; - } + } - var path = new ShapePath(); + equals( sh ) { - var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2; + for ( let i = 0; i < 9; i ++ ) { - if ( glyph.o ) { + if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { - var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + return false; - for ( var i = 0, l = outline.length; i < l; ) { + } - var action = outline[ i ++ ]; + } - switch ( action ) { + return true; - case 'm': // moveTo + } - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + copy( sh ) { - path.moveTo( x, y ); + return this.set( sh.coefficients ); - break; + } - case 'l': // lineTo + clone() { - x = outline[ i ++ ] * scale + offsetX; - y = outline[ i ++ ] * scale + offsetY; + return new this.constructor().copy( this ); - path.lineTo( x, y ); + } - break; + fromArray( array, offset = 0 ) { - case 'q': // quadraticCurveTo + const coefficients = this.coefficients; - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; + for ( let i = 0; i < 9; i ++ ) { - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); - break; + } - case 'b': // bezierCurveTo + return this; - cpx = outline[ i ++ ] * scale + offsetX; - cpy = outline[ i ++ ] * scale + offsetY; - cpx1 = outline[ i ++ ] * scale + offsetX; - cpy1 = outline[ i ++ ] * scale + offsetY; - cpx2 = outline[ i ++ ] * scale + offsetX; - cpy2 = outline[ i ++ ] * scale + offsetY; + } - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + toArray( array = [], offset = 0 ) { - break; + const coefficients = this.coefficients; - } + for ( let i = 0; i < 9; i ++ ) { - } + coefficients[ i ].toArray( array, offset + ( i * 3 ) ); } - return { offsetX: glyph.ha * scale, path: path }; + return array; } - /** - * @author mrdoob / http://mrdoob.com/ - */ + // evaluate the basis functions + // shBasis is an Array[ 9 ] + static getBasisAt( normal, shBasis ) { + + // normal is assumed to be unit length + + const x = normal.x, y = normal.y, z = normal.z; + + // band 0 + shBasis[ 0 ] = 0.282095; - function FontLoader( manager ) { + // band 1 + shBasis[ 1 ] = 0.488603 * y; + shBasis[ 2 ] = 0.488603 * z; + shBasis[ 3 ] = 0.488603 * x; - Loader.call( this, manager ); + // band 2 + shBasis[ 4 ] = 1.092548 * x * y; + shBasis[ 5 ] = 1.092548 * y * z; + shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); + shBasis[ 7 ] = 1.092548 * x * z; + shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); } - FontLoader.prototype = Object.assign( Object.create( Loader.prototype ), { +} - constructor: FontLoader, +class LightProbe extends Light { - load: function ( url, onLoad, onProgress, onError ) { + constructor( sh = new SphericalHarmonics3(), intensity = 1 ) { - var scope = this; + super( undefined, intensity ); - var loader = new FileLoader( this.manager ); - loader.setPath( this.path ); - loader.load( url, function ( text ) { + this.isLightProbe = true; - var json; + this.sh = sh; - try { + } - json = JSON.parse( text ); + copy( source ) { - } catch ( e ) { + super.copy( source ); - console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); - json = JSON.parse( text.substring( 65, text.length - 2 ) ); + this.sh.copy( source.sh ); - } + return this; - var font = scope.parse( json ); + } - if ( onLoad ) onLoad( font ); + fromJSON( json ) { - }, onProgress, onError ); + this.intensity = json.intensity; // TODO: Move this bit to Light.fromJSON(); + this.sh.fromArray( json.sh ); - }, + return this; - parse: function ( json ) { + } - return new Font( json ); + toJSON( meta ) { - } + const data = super.toJSON( meta ); - } ); + data.object.sh = this.sh.toArray(); - /** - * @author mrdoob / http://mrdoob.com/ - */ + return data; - var _context; + } - var AudioContext = { +} - getContext: function () { +class MaterialLoader extends Loader { - if ( _context === undefined ) { + constructor( manager ) { - _context = new ( window.AudioContext || window.webkitAudioContext )(); + super( manager ); + this.textures = {}; - } + } - return _context; + load( url, onLoad, onProgress, onError ) { - }, + const scope = this; - setContext: function ( value ) { + const loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setRequestHeader( scope.requestHeader ); + loader.setWithCredentials( scope.withCredentials ); + loader.load( url, function ( text ) { - _context = value; + try { - } + onLoad( scope.parse( JSON.parse( text ) ) ); - }; + } catch ( e ) { - /** - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ + if ( onError ) { - function AudioLoader( manager ) { + onError( e ); - Loader.call( this, manager ); + } else { - } + console.error( e ); - AudioLoader.prototype = Object.assign( Object.create( Loader.prototype ), { + } - constructor: AudioLoader, + scope.manager.itemError( url ); - load: function ( url, onLoad, onProgress, onError ) { + } - var loader = new FileLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); - loader.setPath( this.path ); - loader.load( url, function ( buffer ) { + }, onProgress, onError ); - // Create a copy of the buffer. The `decodeAudioData` method - // detaches the buffer when complete, preventing reuse. - var bufferCopy = buffer.slice( 0 ); + } - var context = AudioContext.getContext(); - context.decodeAudioData( bufferCopy, function ( audioBuffer ) { + parse( json ) { - onLoad( audioBuffer ); + const textures = this.textures; - } ); + function getTexture( name ) { - }, onProgress, onError ); + if ( textures[ name ] === undefined ) { - } + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - } ); + } - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * Primary reference: - * https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * Secondary reference: - * https://www.ppsloan.org/publications/StupidSH36.pdf - */ + return textures[ name ]; - // 3-band SH defined by 9 coefficients + } - function SphericalHarmonics3() { + const material = MaterialLoader.createMaterialFromType( json.type ); - this.coefficients = []; + if ( json.uuid !== undefined ) material.uuid = json.uuid; + if ( json.name !== undefined ) material.name = json.name; + if ( json.color !== undefined && material.color !== undefined ) material.color.setHex( json.color ); + if ( json.roughness !== undefined ) material.roughness = json.roughness; + if ( json.metalness !== undefined ) material.metalness = json.metalness; + if ( json.sheen !== undefined ) material.sheen = json.sheen; + if ( json.sheenColor !== undefined ) material.sheenColor = new Color().setHex( json.sheenColor ); + if ( json.sheenRoughness !== undefined ) material.sheenRoughness = json.sheenRoughness; + if ( json.emissive !== undefined && material.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined && material.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.specularIntensity !== undefined ) material.specularIntensity = json.specularIntensity; + if ( json.specularColor !== undefined && material.specularColor !== undefined ) material.specularColor.setHex( json.specularColor ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat; + if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness; + if ( json.iridescence !== undefined ) material.iridescence = json.iridescence; + if ( json.iridescenceIOR !== undefined ) material.iridescenceIOR = json.iridescenceIOR; + if ( json.iridescenceThicknessRange !== undefined ) material.iridescenceThicknessRange = json.iridescenceThicknessRange; + if ( json.transmission !== undefined ) material.transmission = json.transmission; + if ( json.thickness !== undefined ) material.thickness = json.thickness; + if ( json.attenuationDistance !== undefined ) material.attenuationDistance = json.attenuationDistance; + if ( json.attenuationColor !== undefined && material.attenuationColor !== undefined ) material.attenuationColor.setHex( json.attenuationColor ); + if ( json.anisotropy !== undefined ) material.anisotropy = json.anisotropy; + if ( json.anisotropyRotation !== undefined ) material.anisotropyRotation = json.anisotropyRotation; + if ( json.fog !== undefined ) material.fog = json.fog; + if ( json.flatShading !== undefined ) material.flatShading = json.flatShading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.combine !== undefined ) material.combine = json.combine; + if ( json.side !== undefined ) material.side = json.side; + if ( json.shadowSide !== undefined ) material.shadowSide = json.shadowSide; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; + if ( json.alphaHash !== undefined ) material.alphaHash = json.alphaHash; + if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; + if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; + if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; - for ( var i = 0; i < 9; i ++ ) { + if ( json.stencilWrite !== undefined ) material.stencilWrite = json.stencilWrite; + if ( json.stencilWriteMask !== undefined ) material.stencilWriteMask = json.stencilWriteMask; + if ( json.stencilFunc !== undefined ) material.stencilFunc = json.stencilFunc; + if ( json.stencilRef !== undefined ) material.stencilRef = json.stencilRef; + if ( json.stencilFuncMask !== undefined ) material.stencilFuncMask = json.stencilFuncMask; + if ( json.stencilFail !== undefined ) material.stencilFail = json.stencilFail; + if ( json.stencilZFail !== undefined ) material.stencilZFail = json.stencilZFail; + if ( json.stencilZPass !== undefined ) material.stencilZPass = json.stencilZPass; - this.coefficients.push( new Vector3() ); + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; + if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; + if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; - } + if ( json.rotation !== undefined ) material.rotation = json.rotation; - } + if ( json.linewidth !== undefined ) material.linewidth = json.linewidth; + if ( json.dashSize !== undefined ) material.dashSize = json.dashSize; + if ( json.gapSize !== undefined ) material.gapSize = json.gapSize; + if ( json.scale !== undefined ) material.scale = json.scale; - Object.assign( SphericalHarmonics3.prototype, { + if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset; + if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor; + if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits; - isSphericalHarmonics3: true, + if ( json.dithering !== undefined ) material.dithering = json.dithering; - set: function ( coefficients ) { + if ( json.alphaToCoverage !== undefined ) material.alphaToCoverage = json.alphaToCoverage; + if ( json.premultipliedAlpha !== undefined ) material.premultipliedAlpha = json.premultipliedAlpha; + if ( json.forceSinglePass !== undefined ) material.forceSinglePass = json.forceSinglePass; - for ( var i = 0; i < 9; i ++ ) { + if ( json.visible !== undefined ) material.visible = json.visible; - this.coefficients[ i ].copy( coefficients[ i ] ); + if ( json.toneMapped !== undefined ) material.toneMapped = json.toneMapped; - } + if ( json.userData !== undefined ) material.userData = json.userData; - return this; + if ( json.vertexColors !== undefined ) { - }, + if ( typeof json.vertexColors === 'number' ) { - zero: function () { + material.vertexColors = ( json.vertexColors > 0 ) ? true : false; - for ( var i = 0; i < 9; i ++ ) { + } else { - this.coefficients[ i ].set( 0, 0, 0 ); + material.vertexColors = json.vertexColors; } - return this; - - }, + } - // get the radiance in the direction of the normal - // target is a Vector3 - getAt: function ( normal, target ) { + // Shader Material - // normal is assumed to be unit length + if ( json.uniforms !== undefined ) { - var x = normal.x, y = normal.y, z = normal.z; + for ( const name in json.uniforms ) { - var coeff = this.coefficients; + const uniform = json.uniforms[ name ]; - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.282095 ); + material.uniforms[ name ] = {}; - // band 1 - target.addScale( coeff[ 1 ], 0.488603 * y ); - target.addScale( coeff[ 2 ], 0.488603 * z ); - target.addScale( coeff[ 3 ], 0.488603 * x ); + switch ( uniform.type ) { - // band 2 - target.addScale( coeff[ 4 ], 1.092548 * ( x * y ) ); - target.addScale( coeff[ 5 ], 1.092548 * ( y * z ) ); - target.addScale( coeff[ 6 ], 0.315392 * ( 3.0 * z * z - 1.0 ) ); - target.addScale( coeff[ 7 ], 1.092548 * ( x * z ) ); - target.addScale( coeff[ 8 ], 0.546274 * ( x * x - y * y ) ); + case 't': + material.uniforms[ name ].value = getTexture( uniform.value ); + break; - return target; + case 'c': + material.uniforms[ name ].value = new Color().setHex( uniform.value ); + break; - }, + case 'v2': + material.uniforms[ name ].value = new Vector2().fromArray( uniform.value ); + break; - // get the irradiance (radiance convolved with cosine lobe) in the direction of the normal - // target is a Vector3 - // https://graphics.stanford.edu/papers/envmap/envmap.pdf - getIrradianceAt: function ( normal, target ) { + case 'v3': + material.uniforms[ name ].value = new Vector3().fromArray( uniform.value ); + break; - // normal is assumed to be unit length + case 'v4': + material.uniforms[ name ].value = new Vector4().fromArray( uniform.value ); + break; - var x = normal.x, y = normal.y, z = normal.z; + case 'm3': + material.uniforms[ name ].value = new Matrix3().fromArray( uniform.value ); + break; - var coeff = this.coefficients; + case 'm4': + material.uniforms[ name ].value = new Matrix4().fromArray( uniform.value ); + break; - // band 0 - target.copy( coeff[ 0 ] ).multiplyScalar( 0.886227 ); // π * 0.282095 + default: + material.uniforms[ name ].value = uniform.value; - // band 1 - target.addScale( coeff[ 1 ], 2.0 * 0.511664 * y ); // ( 2 * π / 3 ) * 0.488603 - target.addScale( coeff[ 2 ], 2.0 * 0.511664 * z ); - target.addScale( coeff[ 3 ], 2.0 * 0.511664 * x ); + } - // band 2 - target.addScale( coeff[ 4 ], 2.0 * 0.429043 * x * y ); // ( π / 4 ) * 1.092548 - target.addScale( coeff[ 5 ], 2.0 * 0.429043 * y * z ); - target.addScale( coeff[ 6 ], 0.743125 * z * z - 0.247708 ); // ( π / 4 ) * 0.315392 * 3 - target.addScale( coeff[ 7 ], 2.0 * 0.429043 * x * z ); - target.addScale( coeff[ 8 ], 0.429043 * ( x * x - y * y ) ); // ( π / 4 ) * 0.546274 + } - return target; + } - }, + if ( json.defines !== undefined ) material.defines = json.defines; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.glslVersion !== undefined ) material.glslVersion = json.glslVersion; - add: function ( sh ) { + if ( json.extensions !== undefined ) { - for ( var i = 0; i < 9; i ++ ) { + for ( const key in json.extensions ) { - this.coefficients[ i ].add( sh.coefficients[ i ] ); + material.extensions[ key ] = json.extensions[ key ]; } - return this; + } - }, + if ( json.lights !== undefined ) material.lights = json.lights; + if ( json.clipping !== undefined ) material.clipping = json.clipping; + // for PointsMaterial - scale: function ( s ) { + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - for ( var i = 0; i < 9; i ++ ) { + // maps - this.coefficients[ i ].multiplyScalar( s ); + if ( json.map !== undefined ) material.map = getTexture( json.map ); + if ( json.matcap !== undefined ) material.matcap = getTexture( json.matcap ); - } + if ( json.alphaMap !== undefined ) material.alphaMap = getTexture( json.alphaMap ); - return this; + if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - }, + if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); + if ( json.normalMapType !== undefined ) material.normalMapType = json.normalMapType; + if ( json.normalScale !== undefined ) { - lerp: function ( sh, alpha ) { + let normalScale = json.normalScale; - for ( var i = 0; i < 9; i ++ ) { + if ( Array.isArray( normalScale ) === false ) { - this.coefficients[ i ].lerp( sh.coefficients[ i ], alpha ); + // Blender exporter used to export a scalar. See #7459 + + normalScale = [ normalScale, normalScale ]; } - return this; + material.normalScale = new Vector2().fromArray( normalScale ); - }, + } - equals: function ( sh ) { + if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - for ( var i = 0; i < 9; i ++ ) { + if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); + if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - if ( ! this.coefficients[ i ].equals( sh.coefficients[ i ] ) ) { + if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); + if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - return false; + if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); + if ( json.specularIntensityMap !== undefined ) material.specularIntensityMap = getTexture( json.specularIntensityMap ); + if ( json.specularColorMap !== undefined ) material.specularColorMap = getTexture( json.specularColorMap ); - } + if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); + if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity; - } + if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; + if ( json.refractionRatio !== undefined ) material.refractionRatio = json.refractionRatio; - return true; + if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - }, + if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - copy: function ( sh ) { + if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap ); - return this.set( sh.coefficients ); + if ( json.clearcoatMap !== undefined ) material.clearcoatMap = getTexture( json.clearcoatMap ); + if ( json.clearcoatRoughnessMap !== undefined ) material.clearcoatRoughnessMap = getTexture( json.clearcoatRoughnessMap ); + if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap ); + if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale ); - }, + if ( json.iridescenceMap !== undefined ) material.iridescenceMap = getTexture( json.iridescenceMap ); + if ( json.iridescenceThicknessMap !== undefined ) material.iridescenceThicknessMap = getTexture( json.iridescenceThicknessMap ); - clone: function () { + if ( json.transmissionMap !== undefined ) material.transmissionMap = getTexture( json.transmissionMap ); + if ( json.thicknessMap !== undefined ) material.thicknessMap = getTexture( json.thicknessMap ); - return new this.constructor().copy( this ); + if ( json.anisotropyMap !== undefined ) material.anisotropyMap = getTexture( json.anisotropyMap ); - }, + if ( json.sheenColorMap !== undefined ) material.sheenColorMap = getTexture( json.sheenColorMap ); + if ( json.sheenRoughnessMap !== undefined ) material.sheenRoughnessMap = getTexture( json.sheenRoughnessMap ); - fromArray: function ( array, offset ) { + return material; - if ( offset === undefined ) offset = 0; + } - var coefficients = this.coefficients; + setTextures( value ) { - for ( var i = 0; i < 9; i ++ ) { + this.textures = value; + return this; - coefficients[ i ].fromArray( array, offset + ( i * 3 ) ); + } - } + static createMaterialFromType( type ) { + + const materialLib = { + ShadowMaterial, + SpriteMaterial, + RawShaderMaterial, + ShaderMaterial, + PointsMaterial, + MeshPhysicalMaterial, + MeshStandardMaterial, + MeshPhongMaterial, + MeshToonMaterial, + MeshNormalMaterial, + MeshLambertMaterial, + MeshDepthMaterial, + MeshDistanceMaterial, + MeshBasicMaterial, + MeshMatcapMaterial, + LineDashedMaterial, + LineBasicMaterial, + Material + }; - return this; + return new materialLib[ type ](); - }, + } - toArray: function ( array, offset ) { +} - if ( array === undefined ) array = []; - if ( offset === undefined ) offset = 0; +class LoaderUtils { - var coefficients = this.coefficients; + static decodeText( array ) { - for ( var i = 0; i < 9; i ++ ) { + if ( typeof TextDecoder !== 'undefined' ) { - coefficients[ i ].toArray( array, offset + ( i * 3 ) ); + return new TextDecoder().decode( array ); - } + } - return array; + // Avoid the String.fromCharCode.apply(null, array) shortcut, which + // throws a "maximum call stack size exceeded" error for large arrays. - } + let s = ''; - } ); + for ( let i = 0, il = array.length; i < il; i ++ ) { - Object.assign( SphericalHarmonics3, { + // Implicitly assumes little-endian. + s += String.fromCharCode( array[ i ] ); - // evaluate the basis functions - // shBasis is an Array[ 9 ] - getBasisAt: function ( normal, shBasis ) { + } - // normal is assumed to be unit length + try { - var x = normal.x, y = normal.y, z = normal.z; + // merges multi-byte utf-8 characters. - // band 0 - shBasis[ 0 ] = 0.282095; + return decodeURIComponent( escape( s ) ); - // band 1 - shBasis[ 1 ] = 0.488603 * y; - shBasis[ 2 ] = 0.488603 * z; - shBasis[ 3 ] = 0.488603 * x; + } catch ( e ) { // see #16358 - // band 2 - shBasis[ 4 ] = 1.092548 * x * y; - shBasis[ 5 ] = 1.092548 * y * z; - shBasis[ 6 ] = 0.315392 * ( 3 * z * z - 1 ); - shBasis[ 7 ] = 1.092548 * x * z; - shBasis[ 8 ] = 0.546274 * ( x * x - y * y ); + return s; } - } ); + } - /** - * @author WestLangley / http://github.com/WestLangley - * - * A LightProbe is a source of indirect-diffuse light - */ + static extractUrlBase( url ) { - function LightProbe( sh, intensity ) { + const index = url.lastIndexOf( '/' ); - Light.call( this, undefined, intensity ); + if ( index === - 1 ) return './'; - this.sh = ( sh !== undefined ) ? sh : new SphericalHarmonics3(); + return url.slice( 0, index + 1 ); } - LightProbe.prototype = Object.assign( Object.create( Light.prototype ), { + static resolveURL( url, path ) { - constructor: LightProbe, + // Invalid URL + if ( typeof url !== 'string' || url === '' ) return ''; - isLightProbe: true, + // Host Relative URL + if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) { - copy: function ( source ) { + path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' ); - Light.prototype.copy.call( this, source ); + } - this.sh.copy( source.sh ); - this.intensity = source.intensity; + // Absolute URL http://,https://,// + if ( /^(https?:)?\/\//i.test( url ) ) return url; - return this; + // Data URI + if ( /^data:.*,.*$/i.test( url ) ) return url; - }, + // Blob URL + if ( /^blob:.*$/i.test( url ) ) return url; - toJSON: function ( meta ) { + // Relative URL + return path + url; - var data = Light.prototype.toJSON.call( this, meta ); + } - // data.sh = this.sh.toArray(); // todo +} - return data; +class InstancedBufferGeometry extends BufferGeometry { - } + constructor() { - } ); + super(); - /** - * @author WestLangley / http://github.com/WestLangley - */ + this.isInstancedBufferGeometry = true; - function HemisphereLightProbe( skyColor, groundColor, intensity ) { + this.type = 'InstancedBufferGeometry'; + this.instanceCount = Infinity; - LightProbe.call( this, undefined, intensity ); + } - var color1 = new Color().set( skyColor ); - var color2 = new Color().set( groundColor ); + copy( source ) { - var sky = new Vector3( color1.r, color1.g, color1.b ); - var ground = new Vector3( color2.r, color2.g, color2.b ); + super.copy( source ); - // without extra factor of PI in the shader, should = 1 / Math.sqrt( Math.PI ); - var c0 = Math.sqrt( Math.PI ); - var c1 = c0 * Math.sqrt( 0.75 ); + this.instanceCount = source.instanceCount; - this.sh.coefficients[ 0 ].copy( sky ).add( ground ).multiplyScalar( c0 ); - this.sh.coefficients[ 1 ].copy( sky ).sub( ground ).multiplyScalar( c1 ); + return this; } - HemisphereLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { - - constructor: HemisphereLightProbe, + toJSON() { - isHemisphereLightProbe: true, + const data = super.toJSON(); - copy: function ( source ) { // modifying colors not currently supported + data.instanceCount = this.instanceCount; - LightProbe.prototype.copy.call( this, source ); + data.isInstancedBufferGeometry = true; - return this; + return data; - }, + } - toJSON: function ( meta ) { +} - var data = LightProbe.prototype.toJSON.call( this, meta ); +class BufferGeometryLoader extends Loader { - // data.sh = this.sh.toArray(); // todo + constructor( manager ) { - return data; + super( manager ); - } + } - } ); + load( url, onLoad, onProgress, onError ) { - /** - * @author WestLangley / http://github.com/WestLangley - */ + const scope = this; - function AmbientLightProbe( color, intensity ) { + const loader = new FileLoader( scope.manager ); + loader.setPath( scope.path ); + loader.setRequestHeader( scope.requestHeader ); + loader.setWithCredentials( scope.withCredentials ); + loader.load( url, function ( text ) { - LightProbe.call( this, undefined, intensity ); + try { - var color1 = new Color().set( color ); + onLoad( scope.parse( JSON.parse( text ) ) ); - // without extra factor of PI in the shader, would be 2 / Math.sqrt( Math.PI ); - this.sh.coefficients[ 0 ].set( color1.r, color1.g, color1.b ).multiplyScalar( 2 * Math.sqrt( Math.PI ) ); + } catch ( e ) { - } + if ( onError ) { - AmbientLightProbe.prototype = Object.assign( Object.create( LightProbe.prototype ), { + onError( e ); - constructor: AmbientLightProbe, + } else { - isAmbientLightProbe: true, + console.error( e ); - copy: function ( source ) { // modifying color not currently supported + } - LightProbe.prototype.copy.call( this, source ); + scope.manager.itemError( url ); - return this; + } - }, + }, onProgress, onError ); - toJSON: function ( meta ) { + } - var data = LightProbe.prototype.toJSON.call( this, meta ); + parse( json ) { - // data.sh = this.sh.toArray(); // todo + const interleavedBufferMap = {}; + const arrayBufferMap = {}; - return data; + function getInterleavedBuffer( json, uuid ) { - } + if ( interleavedBufferMap[ uuid ] !== undefined ) return interleavedBufferMap[ uuid ]; - } ); + const interleavedBuffers = json.interleavedBuffers; + const interleavedBuffer = interleavedBuffers[ uuid ]; - var _eyeRight = new Matrix4(); - var _eyeLeft = new Matrix4(); + const buffer = getArrayBuffer( json, interleavedBuffer.buffer ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + const array = getTypedArray( interleavedBuffer.type, buffer ); + const ib = new InterleavedBuffer( array, interleavedBuffer.stride ); + ib.uuid = interleavedBuffer.uuid; - function StereoCamera() { + interleavedBufferMap[ uuid ] = ib; - this.type = 'StereoCamera'; + return ib; - this.aspect = 1; + } - this.eyeSep = 0.064; + function getArrayBuffer( json, uuid ) { - this.cameraL = new PerspectiveCamera(); - this.cameraL.layers.enable( 1 ); - this.cameraL.matrixAutoUpdate = false; + if ( arrayBufferMap[ uuid ] !== undefined ) return arrayBufferMap[ uuid ]; - this.cameraR = new PerspectiveCamera(); - this.cameraR.layers.enable( 2 ); - this.cameraR.matrixAutoUpdate = false; + const arrayBuffers = json.arrayBuffers; + const arrayBuffer = arrayBuffers[ uuid ]; - this._cache = { - focus: null, - fov: null, - aspect: null, - near: null, - far: null, - zoom: null, - eyeSep: null - }; + const ab = new Uint32Array( arrayBuffer ).buffer; - } + arrayBufferMap[ uuid ] = ab; - Object.assign( StereoCamera.prototype, { + return ab; - update: function ( camera ) { + } - var cache = this._cache; + const geometry = json.isInstancedBufferGeometry ? new InstancedBufferGeometry() : new BufferGeometry(); - var needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || - cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || - cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; + const index = json.data.index; - if ( needsUpdate ) { + if ( index !== undefined ) { - cache.focus = camera.focus; - cache.fov = camera.fov; - cache.aspect = camera.aspect * this.aspect; - cache.near = camera.near; - cache.far = camera.far; - cache.zoom = camera.zoom; - cache.eyeSep = this.eyeSep; + const typedArray = getTypedArray( index.type, index.array ); + geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ + } - var projectionMatrix = camera.projectionMatrix.clone(); - var eyeSepHalf = cache.eyeSep / 2; - var eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; - var ymax = ( cache.near * Math.tan( _Math.DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; - var xmin, xmax; + const attributes = json.data.attributes; - // translate xOffset + for ( const key in attributes ) { - _eyeLeft.elements[ 12 ] = - eyeSepHalf; - _eyeRight.elements[ 12 ] = eyeSepHalf; + const attribute = attributes[ key ]; + let bufferAttribute; - // for left eye + if ( attribute.isInterleavedBufferAttribute ) { - xmin = - ymax * cache.aspect + eyeSepOnProjection; - xmax = ymax * cache.aspect + eyeSepOnProjection; + const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); + bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + } else { - this.cameraL.projectionMatrix.copy( projectionMatrix ); + const typedArray = getTypedArray( attribute.type, attribute.array ); + const bufferAttributeConstr = attribute.isInstancedBufferAttribute ? InstancedBufferAttribute : BufferAttribute; + bufferAttribute = new bufferAttributeConstr( typedArray, attribute.itemSize, attribute.normalized ); - // for right eye + } - xmin = - ymax * cache.aspect - eyeSepOnProjection; - xmax = ymax * cache.aspect - eyeSepOnProjection; + if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; + if ( attribute.usage !== undefined ) bufferAttribute.setUsage( attribute.usage ); - projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); - projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); + if ( attribute.updateRange !== undefined ) { - this.cameraR.projectionMatrix.copy( projectionMatrix ); + bufferAttribute.updateRange.offset = attribute.updateRange.offset; + bufferAttribute.updateRange.count = attribute.updateRange.count; } - this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); - this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); + geometry.setAttribute( key, bufferAttribute ); } - } ); - - /** - * @author alteredq / http://alteredqualia.com/ - */ + const morphAttributes = json.data.morphAttributes; - function Clock( autoStart ) { + if ( morphAttributes ) { - this.autoStart = ( autoStart !== undefined ) ? autoStart : true; + for ( const key in morphAttributes ) { - this.startTime = 0; - this.oldTime = 0; - this.elapsedTime = 0; + const attributeArray = morphAttributes[ key ]; - this.running = false; + const array = []; - } + for ( let i = 0, il = attributeArray.length; i < il; i ++ ) { - Object.assign( Clock.prototype, { + const attribute = attributeArray[ i ]; + let bufferAttribute; - start: function () { + if ( attribute.isInterleavedBufferAttribute ) { - this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 + const interleavedBuffer = getInterleavedBuffer( json.data, attribute.data ); + bufferAttribute = new InterleavedBufferAttribute( interleavedBuffer, attribute.itemSize, attribute.offset, attribute.normalized ); - this.oldTime = this.startTime; - this.elapsedTime = 0; - this.running = true; + } else { - }, + const typedArray = getTypedArray( attribute.type, attribute.array ); + bufferAttribute = new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ); - stop: function () { + } - this.getElapsedTime(); - this.running = false; - this.autoStart = false; + if ( attribute.name !== undefined ) bufferAttribute.name = attribute.name; + array.push( bufferAttribute ); - }, + } - getElapsedTime: function () { + geometry.morphAttributes[ key ] = array; - this.getDelta(); - return this.elapsedTime; + } - }, + } - getDelta: function () { + const morphTargetsRelative = json.data.morphTargetsRelative; - var diff = 0; + if ( morphTargetsRelative ) { - if ( this.autoStart && ! this.running ) { + geometry.morphTargetsRelative = true; - this.start(); - return 0; + } - } + const groups = json.data.groups || json.data.drawcalls || json.data.offsets; - if ( this.running ) { + if ( groups !== undefined ) { - var newTime = ( typeof performance === 'undefined' ? Date : performance ).now(); + for ( let i = 0, n = groups.length; i !== n; ++ i ) { - diff = ( newTime - this.oldTime ) / 1000; - this.oldTime = newTime; + const group = groups[ i ]; - this.elapsedTime += diff; + geometry.addGroup( group.start, group.count, group.materialIndex ); } - return diff; - } - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ + const boundingSphere = json.data.boundingSphere; - var _position$2 = new Vector3(); - var _quaternion$3 = new Quaternion(); - var _scale$1 = new Vector3(); - var _orientation = new Vector3(); + if ( boundingSphere !== undefined ) { - function AudioListener() { + const center = new Vector3(); - Object3D.call( this ); - - this.type = 'AudioListener'; + if ( boundingSphere.center !== undefined ) { - this.context = AudioContext.getContext(); + center.fromArray( boundingSphere.center ); - this.gain = this.context.createGain(); - this.gain.connect( this.context.destination ); + } - this.filter = null; + geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - this.timeDelta = 0; + } - // private + if ( json.name ) geometry.name = json.name; + if ( json.userData ) geometry.userData = json.userData; - this._clock = new Clock(); + return geometry; } - AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: AudioListener, - - getInput: function () { - - return this.gain; +} - }, +class ObjectLoader extends Loader { - removeFilter: function ( ) { + constructor( manager ) { - if ( this.filter !== null ) { + super( manager ); - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); - this.gain.connect( this.context.destination ); - this.filter = null; + } - } + load( url, onLoad, onProgress, onError ) { - return this; + const scope = this; - }, + const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; + this.resourcePath = this.resourcePath || path; - getFilter: function () { + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - return this.filter; + let json = null; - }, + try { - setFilter: function ( value ) { + json = JSON.parse( text ); - if ( this.filter !== null ) { + } catch ( error ) { - this.gain.disconnect( this.filter ); - this.filter.disconnect( this.context.destination ); + if ( onError !== undefined ) onError( error ); - } else { + console.error( 'THREE:ObjectLoader: Can\'t parse ' + url + '.', error.message ); - this.gain.disconnect( this.context.destination ); + return; } - this.filter = value; - this.gain.connect( this.filter ); - this.filter.connect( this.context.destination ); - - return this; - - }, + const metadata = json.metadata; - getMasterVolume: function () { + if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - return this.gain.gain.value; + if ( onError !== undefined ) onError( new Error( 'THREE.ObjectLoader: Can\'t load ' + url ) ); - }, + console.error( 'THREE.ObjectLoader: Can\'t load ' + url ); + return; - setMasterVolume: function ( value ) { + } - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); + scope.parse( json, onLoad ); - return this; + }, onProgress, onError ); - }, + } - updateMatrixWorld: function ( force ) { + async loadAsync( url, onProgress ) { - Object3D.prototype.updateMatrixWorld.call( this, force ); + const scope = this; - var listener = this.context.listener; - var up = this.up; + const path = ( this.path === '' ) ? LoaderUtils.extractUrlBase( url ) : this.path; + this.resourcePath = this.resourcePath || path; - this.timeDelta = this._clock.getDelta(); + const loader = new FileLoader( this.manager ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); - this.matrixWorld.decompose( _position$2, _quaternion$3, _scale$1 ); + const text = await loader.loadAsync( url, onProgress ); - _orientation.set( 0, 0, - 1 ).applyQuaternion( _quaternion$3 ); + const json = JSON.parse( text ); - if ( listener.positionX ) { + const metadata = json.metadata; - // code path for Chrome (see #14393) + if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) { - var endTime = this.context.currentTime + this.timeDelta; + throw new Error( 'THREE.ObjectLoader: Can\'t load ' + url ); - listener.positionX.linearRampToValueAtTime( _position$2.x, endTime ); - listener.positionY.linearRampToValueAtTime( _position$2.y, endTime ); - listener.positionZ.linearRampToValueAtTime( _position$2.z, endTime ); - listener.forwardX.linearRampToValueAtTime( _orientation.x, endTime ); - listener.forwardY.linearRampToValueAtTime( _orientation.y, endTime ); - listener.forwardZ.linearRampToValueAtTime( _orientation.z, endTime ); - listener.upX.linearRampToValueAtTime( up.x, endTime ); - listener.upY.linearRampToValueAtTime( up.y, endTime ); - listener.upZ.linearRampToValueAtTime( up.z, endTime ); + } - } else { + return await scope.parseAsync( json ); - listener.setPosition( _position$2.x, _position$2.y, _position$2.z ); - listener.setOrientation( _orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z ); + } - } + parse( json, onLoad ) { - } + const animations = this.parseAnimations( json.animations ); + const shapes = this.parseShapes( json.shapes ); + const geometries = this.parseGeometries( json.geometries, shapes ); - } ); + const images = this.parseImages( json.images, function () { - /** - * @author mrdoob / http://mrdoob.com/ - * @author Reece Aaron Lecrivain / http://reecenotes.com/ - */ + if ( onLoad !== undefined ) onLoad( object ); - function Audio( listener ) { + } ); - Object3D.call( this ); + const textures = this.parseTextures( json.textures, images ); + const materials = this.parseMaterials( json.materials, textures ); - this.type = 'Audio'; + const object = this.parseObject( json.object, geometries, materials, textures, animations ); + const skeletons = this.parseSkeletons( json.skeletons, object ); - this.listener = listener; - this.context = listener.context; + this.bindSkeletons( object, skeletons ); - this.gain = this.context.createGain(); - this.gain.connect( listener.getInput() ); + // - this.autoplay = false; + if ( onLoad !== undefined ) { - this.buffer = null; - this.detune = 0; - this.loop = false; - this.startTime = 0; - this.offset = 0; - this.duration = undefined; - this.playbackRate = 1; - this.isPlaying = false; - this.hasPlaybackControl = true; - this.sourceType = 'empty'; + let hasImages = false; - this.filters = []; + for ( const uuid in images ) { - } + if ( images[ uuid ].data instanceof HTMLImageElement ) { - Audio.prototype = Object.assign( Object.create( Object3D.prototype ), { + hasImages = true; + break; - constructor: Audio, + } - getOutput: function () { + } - return this.gain; + if ( hasImages === false ) onLoad( object ); - }, + } - setNodeSource: function ( audioNode ) { + return object; - this.hasPlaybackControl = false; - this.sourceType = 'audioNode'; - this.source = audioNode; - this.connect(); + } - return this; + async parseAsync( json ) { - }, + const animations = this.parseAnimations( json.animations ); + const shapes = this.parseShapes( json.shapes ); + const geometries = this.parseGeometries( json.geometries, shapes ); - setMediaElementSource: function ( mediaElement ) { + const images = await this.parseImagesAsync( json.images ); - this.hasPlaybackControl = false; - this.sourceType = 'mediaNode'; - this.source = this.context.createMediaElementSource( mediaElement ); - this.connect(); + const textures = this.parseTextures( json.textures, images ); + const materials = this.parseMaterials( json.materials, textures ); - return this; + const object = this.parseObject( json.object, geometries, materials, textures, animations ); + const skeletons = this.parseSkeletons( json.skeletons, object ); - }, + this.bindSkeletons( object, skeletons ); - setBuffer: function ( audioBuffer ) { + return object; - this.buffer = audioBuffer; - this.sourceType = 'buffer'; + } - if ( this.autoplay ) this.play(); + parseShapes( json ) { - return this; + const shapes = {}; - }, + if ( json !== undefined ) { - play: function () { + for ( let i = 0, l = json.length; i < l; i ++ ) { - if ( this.isPlaying === true ) { + const shape = new Shape().fromJSON( json[ i ] ); - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; + shapes[ shape.uuid ] = shape; } - if ( this.hasPlaybackControl === false ) { + } - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + return shapes; - } + } - var source = this.context.createBufferSource(); + parseSkeletons( json, object ) { - source.buffer = this.buffer; - source.loop = this.loop; - source.onended = this.onEnded.bind( this ); - this.startTime = this.context.currentTime; - source.start( this.startTime, this.offset, this.duration ); + const skeletons = {}; + const bones = {}; - this.isPlaying = true; + // generate bone lookup table - this.source = source; + object.traverse( function ( child ) { - this.setDetune( this.detune ); - this.setPlaybackRate( this.playbackRate ); + if ( child.isBone ) bones[ child.uuid ] = child; - return this.connect(); + } ); - }, + // create skeletons - pause: function () { + if ( json !== undefined ) { - if ( this.hasPlaybackControl === false ) { + for ( let i = 0, l = json.length; i < l; i ++ ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + const skeleton = new Skeleton().fromJSON( json[ i ], bones ); + + skeletons[ skeleton.uuid ] = skeleton; } - if ( this.isPlaying === true ) { + } - this.source.stop(); - this.source.onended = null; - this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate; - this.isPlaying = false; + return skeletons; - } + } - return this; + parseGeometries( json, shapes ) { - }, + const geometries = {}; - stop: function () { + if ( json !== undefined ) { - if ( this.hasPlaybackControl === false ) { + const bufferGeometryLoader = new BufferGeometryLoader(); - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + for ( let i = 0, l = json.length; i < l; i ++ ) { - } + let geometry; + const data = json[ i ]; - this.source.stop(); - this.source.onended = null; - this.offset = 0; - this.isPlaying = false; + switch ( data.type ) { - return this; + case 'BufferGeometry': + case 'InstancedBufferGeometry': - }, + geometry = bufferGeometryLoader.parse( data ); + break; + + default: - connect: function () { + if ( data.type in Geometries ) { - if ( this.filters.length > 0 ) { + geometry = Geometries[ data.type ].fromJSON( data, shapes ); - this.source.connect( this.filters[ 0 ] ); + } else { - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { + console.warn( `THREE.ObjectLoader: Unsupported geometry type "${ data.type }"` ); - this.filters[ i - 1 ].connect( this.filters[ i ] ); + } } - this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); + geometry.uuid = data.uuid; - } else { + if ( data.name !== undefined ) geometry.name = data.name; + if ( data.userData !== undefined ) geometry.userData = data.userData; - this.source.connect( this.getOutput() ); + geometries[ data.uuid ] = geometry; } - return this; + } - }, + return geometries; - disconnect: function () { + } - if ( this.filters.length > 0 ) { + parseMaterials( json, textures ) { - this.source.disconnect( this.filters[ 0 ] ); + const cache = {}; // MultiMaterial + const materials = {}; - for ( var i = 1, l = this.filters.length; i < l; i ++ ) { + if ( json !== undefined ) { - this.filters[ i - 1 ].disconnect( this.filters[ i ] ); + const loader = new MaterialLoader(); + loader.setTextures( textures ); - } + for ( let i = 0, l = json.length; i < l; i ++ ) { - this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); + const data = json[ i ]; - } else { + if ( cache[ data.uuid ] === undefined ) { - this.source.disconnect( this.getOutput() ); + cache[ data.uuid ] = loader.parse( data ); - } + } - return this; + materials[ data.uuid ] = cache[ data.uuid ]; - }, + } - getFilters: function () { + } - return this.filters; + return materials; - }, + } - setFilters: function ( value ) { + parseAnimations( json ) { - if ( ! value ) value = []; + const animations = {}; - if ( this.isPlaying === true ) { + if ( json !== undefined ) { - this.disconnect(); - this.filters = value; - this.connect(); + for ( let i = 0; i < json.length; i ++ ) { - } else { + const data = json[ i ]; - this.filters = value; + const clip = AnimationClip.parse( data ); - } + animations[ clip.uuid ] = clip; - return this; + } - }, + } - setDetune: function ( value ) { + return animations; - this.detune = value; + } - if ( this.source.detune === undefined ) return; // only set detune when available + parseImages( json, onLoad ) { - if ( this.isPlaying === true ) { + const scope = this; + const images = {}; - this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); + let loader; - } + function loadImage( url ) { - return this; + scope.manager.itemStart( url ); - }, + return loader.load( url, function () { - getDetune: function () { + scope.manager.itemEnd( url ); - return this.detune; + }, undefined, function () { - }, + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - getFilter: function () { + } ); - return this.getFilters()[ 0 ]; + } - }, + function deserializeImage( image ) { - setFilter: function ( filter ) { + if ( typeof image === 'string' ) { - return this.setFilters( filter ? [ filter ] : [] ); + const url = image; - }, + const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( url ) ? url : scope.resourcePath + url; - setPlaybackRate: function ( value ) { + return loadImage( path ); - if ( this.hasPlaybackControl === false ) { + } else { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + if ( image.data ) { - } + return { + data: getTypedArray( image.type, image.data ), + width: image.width, + height: image.height + }; - this.playbackRate = value; + } else { - if ( this.isPlaying === true ) { + return null; - this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); + } } - return this; - - }, - - getPlaybackRate: function () { + } - return this.playbackRate; + if ( json !== undefined && json.length > 0 ) { - }, + const manager = new LoadingManager( onLoad ); - onEnded: function () { + loader = new ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); - this.isPlaying = false; + for ( let i = 0, il = json.length; i < il; i ++ ) { - }, + const image = json[ i ]; + const url = image.url; - getLoop: function () { + if ( Array.isArray( url ) ) { - if ( this.hasPlaybackControl === false ) { + // load array of images e.g CubeTexture - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return false; + const imageArray = []; - } + for ( let j = 0, jl = url.length; j < jl; j ++ ) { - return this.loop; + const currentUrl = url[ j ]; - }, + const deserializedImage = deserializeImage( currentUrl ); - setLoop: function ( value ) { + if ( deserializedImage !== null ) { - if ( this.hasPlaybackControl === false ) { + if ( deserializedImage instanceof HTMLImageElement ) { - console.warn( 'THREE.Audio: this Audio has no playback control.' ); - return; + imageArray.push( deserializedImage ); - } + } else { - this.loop = value; + // special case: handle array of data textures for cube textures - if ( this.isPlaying === true ) { + imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); - this.source.loop = this.loop; + } - } + } - return this; + } - }, + images[ image.uuid ] = new Source( imageArray ); - getVolume: function () { + } else { - return this.gain.gain.value; + // load single image - }, + const deserializedImage = deserializeImage( image.url ); + images[ image.uuid ] = new Source( deserializedImage ); - setVolume: function ( value ) { - this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); + } - return this; + } } - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - var _position$3 = new Vector3(); - var _quaternion$4 = new Quaternion(); - var _scale$2 = new Vector3(); - var _orientation$1 = new Vector3(); - - function PositionalAudio( listener ) { - - Audio.call( this, listener ); - - this.panner = this.context.createPanner(); - this.panner.panningModel = 'HRTF'; - this.panner.connect( this.gain ); + return images; } - PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), { - - constructor: PositionalAudio, + async parseImagesAsync( json ) { - getOutput: function () { + const scope = this; + const images = {}; - return this.panner; + let loader; - }, + async function deserializeImage( image ) { - getRefDistance: function () { + if ( typeof image === 'string' ) { - return this.panner.refDistance; + const url = image; - }, + const path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( url ) ? url : scope.resourcePath + url; - setRefDistance: function ( value ) { + return await loader.loadAsync( path ); - this.panner.refDistance = value; + } else { - return this; + if ( image.data ) { - }, + return { + data: getTypedArray( image.type, image.data ), + width: image.width, + height: image.height + }; - getRolloffFactor: function () { + } else { - return this.panner.rolloffFactor; + return null; - }, + } - setRolloffFactor: function ( value ) { + } - this.panner.rolloffFactor = value; + } - return this; + if ( json !== undefined && json.length > 0 ) { - }, + loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); - getDistanceModel: function () { + for ( let i = 0, il = json.length; i < il; i ++ ) { - return this.panner.distanceModel; + const image = json[ i ]; + const url = image.url; - }, + if ( Array.isArray( url ) ) { - setDistanceModel: function ( value ) { + // load array of images e.g CubeTexture - this.panner.distanceModel = value; + const imageArray = []; - return this; + for ( let j = 0, jl = url.length; j < jl; j ++ ) { - }, + const currentUrl = url[ j ]; - getMaxDistance: function () { + const deserializedImage = await deserializeImage( currentUrl ); - return this.panner.maxDistance; + if ( deserializedImage !== null ) { - }, + if ( deserializedImage instanceof HTMLImageElement ) { - setMaxDistance: function ( value ) { + imageArray.push( deserializedImage ); - this.panner.maxDistance = value; + } else { - return this; + // special case: handle array of data textures for cube textures - }, + imageArray.push( new DataTexture( deserializedImage.data, deserializedImage.width, deserializedImage.height ) ); - setDirectionalCone: function ( coneInnerAngle, coneOuterAngle, coneOuterGain ) { + } - this.panner.coneInnerAngle = coneInnerAngle; - this.panner.coneOuterAngle = coneOuterAngle; - this.panner.coneOuterGain = coneOuterGain; + } - return this; + } - }, + images[ image.uuid ] = new Source( imageArray ); - updateMatrixWorld: function ( force ) { + } else { - Object3D.prototype.updateMatrixWorld.call( this, force ); + // load single image - if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; + const deserializedImage = await deserializeImage( image.url ); + images[ image.uuid ] = new Source( deserializedImage ); - this.matrixWorld.decompose( _position$3, _quaternion$4, _scale$2 ); + } - _orientation$1.set( 0, 0, 1 ).applyQuaternion( _quaternion$4 ); + } - var panner = this.panner; + } - if ( panner.positionX ) { + return images; - // code path for Chrome and Firefox (see #14393) + } - var endTime = this.context.currentTime + this.listener.timeDelta; + parseTextures( json, images ) { - panner.positionX.linearRampToValueAtTime( _position$3.x, endTime ); - panner.positionY.linearRampToValueAtTime( _position$3.y, endTime ); - panner.positionZ.linearRampToValueAtTime( _position$3.z, endTime ); - panner.orientationX.linearRampToValueAtTime( _orientation$1.x, endTime ); - panner.orientationY.linearRampToValueAtTime( _orientation$1.y, endTime ); - panner.orientationZ.linearRampToValueAtTime( _orientation$1.z, endTime ); + function parseConstant( value, type ) { - } else { + if ( typeof value === 'number' ) return value; - panner.setPosition( _position$3.x, _position$3.y, _position$3.z ); - panner.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z ); + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - } + return type[ value ]; } - } ); - - /** - * @author mrdoob / http://mrdoob.com/ - */ + const textures = {}; - function AudioAnalyser( audio, fftSize ) { + if ( json !== undefined ) { - this.analyser = audio.context.createAnalyser(); - this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; - - this.data = new Uint8Array( this.analyser.frequencyBinCount ); + for ( let i = 0, l = json.length; i < l; i ++ ) { - audio.getOutput().connect( this.analyser ); + const data = json[ i ]; - } + if ( data.image === undefined ) { - Object.assign( AudioAnalyser.prototype, { + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - getFrequencyData: function () { + } - this.analyser.getByteFrequencyData( this.data ); + if ( images[ data.image ] === undefined ) { - return this.data; + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - }, + } - getAverageFrequency: function () { + const source = images[ data.image ]; + const image = source.data; - var value = 0, data = this.getFrequencyData(); + let texture; - for ( var i = 0; i < data.length; i ++ ) { + if ( Array.isArray( image ) ) { - value += data[ i ]; + texture = new CubeTexture(); - } + if ( image.length === 6 ) texture.needsUpdate = true; - return value / data.length; + } else { - } + if ( image && image.data ) { - } ); + texture = new DataTexture(); - /** - * - * Buffered scene graph property that allows weighted accumulation. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } else { - function PropertyMixer( binding, typeName, valueSize ) { + texture = new Texture(); - this.binding = binding; - this.valueSize = valueSize; + } - var bufferType = Float64Array, - mixFunction; + if ( image ) texture.needsUpdate = true; // textures can have undefined image data - switch ( typeName ) { + } - case 'quaternion': - mixFunction = this._slerp; - break; + texture.source = source; - case 'string': - case 'bool': - bufferType = Array; - mixFunction = this._select; - break; + texture.uuid = data.uuid; - default: - mixFunction = this._lerp; + if ( data.name !== undefined ) texture.name = data.name; - } + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING ); + if ( data.channel !== undefined ) texture.channel = data.channel; - this.buffer = new bufferType( valueSize * 4 ); - // layout: [ incoming | accu0 | accu1 | orig ] - // - // interpolators can use .buffer as their .result - // the data then goes to 'incoming' - // - // 'accu0' and 'accu1' are used frame-interleaved for - // the cumulative result and are compared to detect - // changes - // - // 'orig' stores the original state of the property + if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); + if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); + if ( data.center !== undefined ) texture.center.fromArray( data.center ); + if ( data.rotation !== undefined ) texture.rotation = data.rotation; - this._mixBufferRegion = mixFunction; + if ( data.wrap !== undefined ) { - this.cumulativeWeight = 0; + texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING ); + texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING ); - this.useCount = 0; - this.referenceCount = 0; + } - } + if ( data.format !== undefined ) texture.format = data.format; + if ( data.internalFormat !== undefined ) texture.internalFormat = data.internalFormat; + if ( data.type !== undefined ) texture.type = data.type; + if ( data.colorSpace !== undefined ) texture.colorSpace = data.colorSpace; + if ( data.encoding !== undefined ) texture.encoding = data.encoding; // @deprecated, r152 - Object.assign( PropertyMixer.prototype, { + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER ); + if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - // accumulate data in the 'incoming' region into 'accu' - accumulate: function ( accuIndex, weight ) { + if ( data.flipY !== undefined ) texture.flipY = data.flipY; - // note: happily accumulating nothing when weight = 0, the caller knows - // the weight and shouldn't have made the call in the first place + if ( data.generateMipmaps !== undefined ) texture.generateMipmaps = data.generateMipmaps; + if ( data.premultiplyAlpha !== undefined ) texture.premultiplyAlpha = data.premultiplyAlpha; + if ( data.unpackAlignment !== undefined ) texture.unpackAlignment = data.unpackAlignment; + if ( data.compareFunction !== undefined ) texture.compareFunction = data.compareFunction; - var buffer = this.buffer, - stride = this.valueSize, - offset = accuIndex * stride + stride, + if ( data.userData !== undefined ) texture.userData = data.userData; - currentWeight = this.cumulativeWeight; + textures[ data.uuid ] = texture; - if ( currentWeight === 0 ) { + } - // accuN := incoming * weight + } - for ( var i = 0; i !== stride; ++ i ) { + return textures; - buffer[ offset + i ] = buffer[ i ]; + } - } + parseObject( data, geometries, materials, textures, animations ) { - currentWeight = weight; + let object; - } else { + function getGeometry( name ) { - // accuN := accuN + incoming * weight + if ( geometries[ name ] === undefined ) { - currentWeight += weight; - var mix = weight / currentWeight; - this._mixBufferRegion( buffer, offset, 0, mix, stride ); + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); } - this.cumulativeWeight = currentWeight; + return geometries[ name ]; - }, + } - // apply the state of 'accu' to the binding when accus differ - apply: function ( accuIndex ) { + function getMaterial( name ) { - var stride = this.valueSize, - buffer = this.buffer, - offset = accuIndex * stride + stride, + if ( name === undefined ) return undefined; - weight = this.cumulativeWeight, + if ( Array.isArray( name ) ) { - binding = this.binding; + const array = []; - this.cumulativeWeight = 0; + for ( let i = 0, l = name.length; i < l; i ++ ) { - if ( weight < 1 ) { + const uuid = name[ i ]; - // accuN := accuN + original * ( 1 - cumulativeWeight ) + if ( materials[ uuid ] === undefined ) { - var originalValueOffset = stride * 3; + console.warn( 'THREE.ObjectLoader: Undefined material', uuid ); - this._mixBufferRegion( - buffer, offset, originalValueOffset, 1 - weight, stride ); + } - } + array.push( materials[ uuid ] ); - for ( var i = stride, e = stride + stride; i !== e; ++ i ) { + } - if ( buffer[ i ] !== buffer[ i + stride ] ) { + return array; - // value has changed -> update scene graph + } - binding.setValue( buffer, offset ); - break; + if ( materials[ name ] === undefined ) { - } + console.warn( 'THREE.ObjectLoader: Undefined material', name ); } - }, + return materials[ name ]; + + } - // remember the state of the bound property and copy it to both accus - saveOriginalState: function () { + function getTexture( uuid ) { - var binding = this.binding; + if ( textures[ uuid ] === undefined ) { - var buffer = this.buffer, - stride = this.valueSize, + console.warn( 'THREE.ObjectLoader: Undefined texture', uuid ); - originalValueOffset = stride * 3; + } - binding.getValue( buffer, originalValueOffset ); + return textures[ uuid ]; - // accu[0..1] := orig -- initially detect changes against the original - for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { + } - buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; + let geometry, material; - } + switch ( data.type ) { - this.cumulativeWeight = 0; + case 'Scene': - }, + object = new Scene(); - // apply the state previously taken via 'saveOriginalState' to the binding - restoreOriginalState: function () { + if ( data.background !== undefined ) { - var originalValueOffset = this.valueSize * 3; - this.binding.setValue( this.buffer, originalValueOffset ); + if ( Number.isInteger( data.background ) ) { - }, + object.background = new Color( data.background ); + } else { - // mix functions + object.background = getTexture( data.background ); - _select: function ( buffer, dstOffset, srcOffset, t, stride ) { + } - if ( t >= 0.5 ) { + } - for ( var i = 0; i !== stride; ++ i ) { + if ( data.environment !== undefined ) { - buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; + object.environment = getTexture( data.environment ); } - } - - }, + if ( data.fog !== undefined ) { - _slerp: function ( buffer, dstOffset, srcOffset, t ) { + if ( data.fog.type === 'Fog' ) { - Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); + object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far ); - }, + } else if ( data.fog.type === 'FogExp2' ) { - _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) { + object.fog = new FogExp2( data.fog.color, data.fog.density ); - var s = 1 - t; + } - for ( var i = 0; i !== stride; ++ i ) { + if ( data.fog.name !== '' ) { - var j = dstOffset + i; + object.fog.name = data.fog.name; - buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; + } - } + } - } + if ( data.backgroundBlurriness !== undefined ) object.backgroundBlurriness = data.backgroundBlurriness; + if ( data.backgroundIntensity !== undefined ) object.backgroundIntensity = data.backgroundIntensity; - } ); + break; - /** - * - * A reference to a real property in the scene graph. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + case 'PerspectiveCamera': - // Characters [].:/ are reserved for track binding syntax. - var _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; - var _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - - // Attempts to allow node names from any language. ES5's `\w` regexp matches - // only latin characters, and the unicode \p{L} is not yet supported. So - // instead, we exclude reserved characters and match everything else. - var _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; - var _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - - // Parent directories, delimited by '/' or ':'. Currently unused, but must - // be matched to parse the rest of the track name. - var _directoryRe = /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - - // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. - var _nodeRe = /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - - // Object on target node, and accessor. May not contain reserved - // characters. Accessor may contain any character except closing bracket. - var _objectRe = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - - // Property and accessor. May not contain reserved characters. Accessor may - // contain any non-bracket characters. - var _propertyRe = /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - - var _trackRe = new RegExp( '' - + '^' - + _directoryRe - + _nodeRe - + _objectRe - + _propertyRe - + '$' - ); + object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - var _supportedObjectNames = [ 'material', 'materials', 'bones' ]; + if ( data.focus !== undefined ) object.focus = data.focus; + if ( data.zoom !== undefined ) object.zoom = data.zoom; + if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; + if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; + if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - function Composite( targetGroup, path, optionalParsedPath ) { + break; - var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); + case 'OrthographicCamera': - this._targetGroup = targetGroup; - this._bindings = targetGroup.subscribe_( path, parsedPath ); + object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - } + if ( data.zoom !== undefined ) object.zoom = data.zoom; + if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - Object.assign( Composite.prototype, { + break; - getValue: function ( array, offset ) { + case 'AmbientLight': - this.bind(); // bind all binding + object = new AmbientLight( data.color, data.intensity ); - var firstValidIndex = this._targetGroup.nCachedObjects_, - binding = this._bindings[ firstValidIndex ]; + break; - // and only call .getValue on the first - if ( binding !== undefined ) binding.getValue( array, offset ); + case 'DirectionalLight': - }, + object = new DirectionalLight( data.color, data.intensity ); - setValue: function ( array, offset ) { + break; - var bindings = this._bindings; + case 'PointLight': - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + object = new PointLight( data.color, data.intensity, data.distance, data.decay ); - bindings[ i ].setValue( array, offset ); + break; - } + case 'RectAreaLight': - }, + object = new RectAreaLight( data.color, data.intensity, data.width, data.height ); - bind: function () { + break; - var bindings = this._bindings; + case 'SpotLight': - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - bindings[ i ].bind(); + break; - } + case 'HemisphereLight': - }, + object = new HemisphereLight( data.color, data.groundColor, data.intensity ); - unbind: function () { + break; - var bindings = this._bindings; + case 'LightProbe': - for ( var i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { + object = new LightProbe().fromJSON( data ); - bindings[ i ].unbind(); + break; - } + case 'SkinnedMesh': - } + geometry = getGeometry( data.geometry ); + material = getMaterial( data.material ); - } ); + object = new SkinnedMesh( geometry, material ); + if ( data.bindMode !== undefined ) object.bindMode = data.bindMode; + if ( data.bindMatrix !== undefined ) object.bindMatrix.fromArray( data.bindMatrix ); + if ( data.skeleton !== undefined ) object.skeleton = data.skeleton; - function PropertyBinding( rootNode, path, parsedPath ) { + break; - this.path = path; - this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); + case 'Mesh': - this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode; + geometry = getGeometry( data.geometry ); + material = getMaterial( data.material ); - this.rootNode = rootNode; + object = new Mesh( geometry, material ); - } + break; - Object.assign( PropertyBinding, { + case 'InstancedMesh': - Composite: Composite, + geometry = getGeometry( data.geometry ); + material = getMaterial( data.material ); + const count = data.count; + const instanceMatrix = data.instanceMatrix; + const instanceColor = data.instanceColor; - create: function ( root, path, parsedPath ) { + object = new InstancedMesh( geometry, material, count ); + object.instanceMatrix = new InstancedBufferAttribute( new Float32Array( instanceMatrix.array ), 16 ); + if ( instanceColor !== undefined ) object.instanceColor = new InstancedBufferAttribute( new Float32Array( instanceColor.array ), instanceColor.itemSize ); - if ( ! ( root && root.isAnimationObjectGroup ) ) { + break; - return new PropertyBinding( root, path, parsedPath ); + case 'LOD': - } else { + object = new LOD(); - return new PropertyBinding.Composite( root, path, parsedPath ); + break; - } + case 'Line': - }, + object = new Line( getGeometry( data.geometry ), getMaterial( data.material ) ); - /** - * Replaces spaces with underscores and removes unsupported characters from - * node names, to ensure compatibility with parseTrackName(). - * - * @param {string} name Node name to be sanitized. - * @return {string} - */ - sanitizeNodeName: function ( name ) { + break; - return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); + case 'LineLoop': - }, + object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) ); - parseTrackName: function ( trackName ) { + break; - var matches = _trackRe.exec( trackName ); + case 'LineSegments': - if ( ! matches ) { + object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) ); - throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); + break; - } + case 'PointCloud': + case 'Points': - var results = { - // directoryName: matches[ 1 ], // (tschw) currently unused - nodeName: matches[ 2 ], - objectName: matches[ 3 ], - objectIndex: matches[ 4 ], - propertyName: matches[ 5 ], // required - propertyIndex: matches[ 6 ] - }; + object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); + break; - if ( lastDot !== undefined && lastDot !== - 1 ) { + case 'Sprite': - var objectName = results.nodeName.substring( lastDot + 1 ); + object = new Sprite( getMaterial( data.material ) ); - // Object names must be checked against a whitelist. Otherwise, there - // is no way to parse 'foo.bar.baz': 'baz' must be a property, but - // 'bar' could be the objectName, or part of a nodeName (which can - // include '.' characters). - if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { + break; - results.nodeName = results.nodeName.substring( 0, lastDot ); - results.objectName = objectName; + case 'Group': - } + object = new Group(); - } + break; - if ( results.propertyName === null || results.propertyName.length === 0 ) { + case 'Bone': - throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); + object = new Bone(); - } + break; - return results; + default: - }, + object = new Object3D(); - findNode: function ( root, nodeName ) { + } - if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { + object.uuid = data.uuid; - return root; + if ( data.name !== undefined ) object.name = data.name; - } + if ( data.matrix !== undefined ) { - // search into skeleton bones. - if ( root.skeleton ) { + object.matrix.fromArray( data.matrix ); - var bone = root.skeleton.getBoneByName( nodeName ); + if ( data.matrixAutoUpdate !== undefined ) object.matrixAutoUpdate = data.matrixAutoUpdate; + if ( object.matrixAutoUpdate ) object.matrix.decompose( object.position, object.quaternion, object.scale ); - if ( bone !== undefined ) { + } else { - return bone; + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - } + } - } + if ( data.up !== undefined ) object.up.fromArray( data.up ); - // search into node subtree. - if ( root.children ) { + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - var searchNodeSubtree = function ( children ) { + if ( data.shadow ) { - for ( var i = 0; i < children.length; i ++ ) { + if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias; + if ( data.shadow.normalBias !== undefined ) object.shadow.normalBias = data.shadow.normalBias; + if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius; + if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize ); + if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera ); - var childNode = children[ i ]; + } - if ( childNode.name === nodeName || childNode.uuid === nodeName ) { + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled; + if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder; + if ( data.userData !== undefined ) object.userData = data.userData; + if ( data.layers !== undefined ) object.layers.mask = data.layers; - return childNode; + if ( data.children !== undefined ) { - } + const children = data.children; - var result = searchNodeSubtree( childNode.children ); + for ( let i = 0; i < children.length; i ++ ) { - if ( result ) return result; + object.add( this.parseObject( children[ i ], geometries, materials, textures, animations ) ); - } + } - return null; + } - }; + if ( data.animations !== undefined ) { - var subTreeNode = searchNodeSubtree( root.children ); + const objectAnimations = data.animations; - if ( subTreeNode ) { + for ( let i = 0; i < objectAnimations.length; i ++ ) { - return subTreeNode; + const uuid = objectAnimations[ i ]; - } + object.animations.push( animations[ uuid ] ); } - return null; - } - } ); - - Object.assign( PropertyBinding.prototype, { // prototype, continued + if ( data.type === 'LOD' ) { - // these are used to "bind" a nonexistent property - _getValue_unavailable: function () {}, - _setValue_unavailable: function () {}, + if ( data.autoUpdate !== undefined ) object.autoUpdate = data.autoUpdate; - BindingType: { - Direct: 0, - EntireArray: 1, - ArrayElement: 2, - HasFromToArray: 3 - }, + const levels = data.levels; - Versioning: { - None: 0, - NeedsUpdate: 1, - MatrixWorldNeedsUpdate: 2 - }, + for ( let l = 0; l < levels.length; l ++ ) { - GetterByBindingType: [ + const level = levels[ l ]; + const child = object.getObjectByProperty( 'uuid', level.object ); - function getValue_direct( buffer, offset ) { + if ( child !== undefined ) { - buffer[ offset ] = this.node[ this.propertyName ]; + object.addLevel( child, level.distance, level.hysteresis ); - }, + } - function getValue_array( buffer, offset ) { + } - var source = this.resolvedProperty; + } - for ( var i = 0, n = source.length; i !== n; ++ i ) { + return object; - buffer[ offset ++ ] = source[ i ]; + } - } + bindSkeletons( object, skeletons ) { - }, + if ( Object.keys( skeletons ).length === 0 ) return; - function getValue_arrayElement( buffer, offset ) { + object.traverse( function ( child ) { - buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; + if ( child.isSkinnedMesh === true && child.skeleton !== undefined ) { - }, + const skeleton = skeletons[ child.skeleton ]; - function getValue_toArray( buffer, offset ) { + if ( skeleton === undefined ) { - this.resolvedProperty.toArray( buffer, offset ); + console.warn( 'THREE.ObjectLoader: No skeleton found with UUID:', child.skeleton ); - } + } else { - ], + child.bind( skeleton, child.bindMatrix ); - SetterByBindingTypeAndVersioning: [ + } - [ - // Direct + } - function setValue_direct( buffer, offset ) { + } ); - this.targetObject[ this.propertyName ] = buffer[ offset ]; + } - }, +} - function setValue_direct_setNeedsUpdate( buffer, offset ) { +const TEXTURE_MAPPING = { + UVMapping: UVMapping, + CubeReflectionMapping: CubeReflectionMapping, + CubeRefractionMapping: CubeRefractionMapping, + EquirectangularReflectionMapping: EquirectangularReflectionMapping, + EquirectangularRefractionMapping: EquirectangularRefractionMapping, + CubeUVReflectionMapping: CubeUVReflectionMapping +}; - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; +const TEXTURE_WRAPPING = { + RepeatWrapping: RepeatWrapping, + ClampToEdgeWrapping: ClampToEdgeWrapping, + MirroredRepeatWrapping: MirroredRepeatWrapping +}; - }, +const TEXTURE_FILTER = { + NearestFilter: NearestFilter, + NearestMipmapNearestFilter: NearestMipmapNearestFilter, + NearestMipmapLinearFilter: NearestMipmapLinearFilter, + LinearFilter: LinearFilter, + LinearMipmapNearestFilter: LinearMipmapNearestFilter, + LinearMipmapLinearFilter: LinearMipmapLinearFilter +}; - function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { +class ImageBitmapLoader extends Loader { - this.targetObject[ this.propertyName ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + constructor( manager ) { - } + super( manager ); - ], [ + this.isImageBitmapLoader = true; - // EntireArray + if ( typeof createImageBitmap === 'undefined' ) { - function setValue_array( buffer, offset ) { + console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' ); - var dest = this.resolvedProperty; + } - for ( var i = 0, n = dest.length; i !== n; ++ i ) { + if ( typeof fetch === 'undefined' ) { - dest[ i ] = buffer[ offset ++ ]; + console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' ); - } + } - }, + this.options = { premultiplyAlpha: 'none' }; - function setValue_array_setNeedsUpdate( buffer, offset ) { + } - var dest = this.resolvedProperty; + setOptions( options ) { - for ( var i = 0, n = dest.length; i !== n; ++ i ) { + this.options = options; - dest[ i ] = buffer[ offset ++ ]; + return this; - } + } - this.targetObject.needsUpdate = true; + load( url, onLoad, onProgress, onError ) { - }, + if ( url === undefined ) url = ''; - function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { + if ( this.path !== undefined ) url = this.path + url; - var dest = this.resolvedProperty; + url = this.manager.resolveURL( url ); - for ( var i = 0, n = dest.length; i !== n; ++ i ) { + const scope = this; - dest[ i ] = buffer[ offset ++ ]; + const cached = Cache.get( url ); - } + if ( cached !== undefined ) { - this.targetObject.matrixWorldNeedsUpdate = true; + scope.manager.itemStart( url ); - } + setTimeout( function () { - ], [ + if ( onLoad ) onLoad( cached ); - // ArrayElement + scope.manager.itemEnd( url ); - function setValue_arrayElement( buffer, offset ) { + }, 0 ); - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + return cached; - }, + } - function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { + const fetchOptions = {}; + fetchOptions.credentials = ( this.crossOrigin === 'anonymous' ) ? 'same-origin' : 'include'; + fetchOptions.headers = this.requestHeader; - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.needsUpdate = true; + fetch( url, fetchOptions ).then( function ( res ) { - }, + return res.blob(); - function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { + } ).then( function ( blob ) { - this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - this.targetObject.matrixWorldNeedsUpdate = true; + return createImageBitmap( blob, Object.assign( scope.options, { colorSpaceConversion: 'none' } ) ); - } + } ).then( function ( imageBitmap ) { - ], [ + Cache.add( url, imageBitmap ); - // HasToFromArray + if ( onLoad ) onLoad( imageBitmap ); - function setValue_fromArray( buffer, offset ) { + scope.manager.itemEnd( url ); - this.resolvedProperty.fromArray( buffer, offset ); + } ).catch( function ( e ) { - }, + if ( onError ) onError( e ); - function setValue_fromArray_setNeedsUpdate( buffer, offset ) { + scope.manager.itemError( url ); + scope.manager.itemEnd( url ); - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.needsUpdate = true; + } ); - }, + scope.manager.itemStart( url ); - function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { + } - this.resolvedProperty.fromArray( buffer, offset ); - this.targetObject.matrixWorldNeedsUpdate = true; +} - } +let _context; - ] +class AudioContext { - ], + static getContext() { - getValue: function getValue_unbound( targetArray, offset ) { + if ( _context === undefined ) { - this.bind(); - this.getValue( targetArray, offset ); + _context = new ( window.AudioContext || window.webkitAudioContext )(); - // Note: This class uses a State pattern on a per-method basis: - // 'bind' sets 'this.getValue' / 'setValue' and shadows the - // prototype version of these methods with one that represents - // the bound state. When the property is not found, the methods - // become no-ops. + } - }, + return _context; - setValue: function getValue_unbound( sourceArray, offset ) { + } - this.bind(); - this.setValue( sourceArray, offset ); + static setContext( value ) { - }, + _context = value; - // create getter / setter pair for a property in the scene graph - bind: function () { + } - var targetObject = this.node, - parsedPath = this.parsedPath, +} - objectName = parsedPath.objectName, - propertyName = parsedPath.propertyName, - propertyIndex = parsedPath.propertyIndex; +class AudioLoader extends Loader { - if ( ! targetObject ) { + constructor( manager ) { - targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode; + super( manager ); - this.node = targetObject; + } - } + load( url, onLoad, onProgress, onError ) { - // set fail state so we can just 'return' on error - this.getValue = this._getValue_unavailable; - this.setValue = this._setValue_unavailable; + const scope = this; - // ensure there is a value node - if ( ! targetObject ) { + const loader = new FileLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.setPath( this.path ); + loader.setRequestHeader( this.requestHeader ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( buffer ) { - console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\'t found.' ); - return; + try { - } + // Create a copy of the buffer. The `decodeAudioData` method + // detaches the buffer when complete, preventing reuse. + const bufferCopy = buffer.slice( 0 ); - if ( objectName ) { + const context = AudioContext.getContext(); + context.decodeAudioData( bufferCopy, function ( audioBuffer ) { - var objectIndex = parsedPath.objectIndex; + onLoad( audioBuffer ); - // special cases were we need to reach deeper into the hierarchy to get the face materials.... - switch ( objectName ) { + }, handleError ); - case 'materials': + } catch ( e ) { - if ( ! targetObject.material ) { + handleError( e ); - console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); - return; + } - } + }, onProgress, onError ); - if ( ! targetObject.material.materials ) { + function handleError( e ) { - console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); - return; + if ( onError ) { - } + onError( e ); - targetObject = targetObject.material.materials; + } else { - break; + console.error( e ); - case 'bones': + } - if ( ! targetObject.skeleton ) { + scope.manager.itemError( url ); - console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); - return; + } - } + } - // potential future optimization: skip this if propertyIndex is already an integer - // and convert the integer string to a true integer. +} - targetObject = targetObject.skeleton.bones; +const _eyeRight = /*@__PURE__*/ new Matrix4(); +const _eyeLeft = /*@__PURE__*/ new Matrix4(); +const _projectionMatrix = /*@__PURE__*/ new Matrix4(); - // support resolving morphTarget names into indices. - for ( var i = 0; i < targetObject.length; i ++ ) { +class StereoCamera { - if ( targetObject[ i ].name === objectIndex ) { + constructor() { - objectIndex = i; - break; + this.type = 'StereoCamera'; - } + this.aspect = 1; - } + this.eyeSep = 0.064; - break; + this.cameraL = new PerspectiveCamera(); + this.cameraL.layers.enable( 1 ); + this.cameraL.matrixAutoUpdate = false; - default: + this.cameraR = new PerspectiveCamera(); + this.cameraR.layers.enable( 2 ); + this.cameraR.matrixAutoUpdate = false; - if ( targetObject[ objectName ] === undefined ) { + this._cache = { + focus: null, + fov: null, + aspect: null, + near: null, + far: null, + zoom: null, + eyeSep: null + }; - console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); - return; + } - } + update( camera ) { - targetObject = targetObject[ objectName ]; + const cache = this._cache; - } + const needsUpdate = cache.focus !== camera.focus || cache.fov !== camera.fov || + cache.aspect !== camera.aspect * this.aspect || cache.near !== camera.near || + cache.far !== camera.far || cache.zoom !== camera.zoom || cache.eyeSep !== this.eyeSep; + if ( needsUpdate ) { - if ( objectIndex !== undefined ) { + cache.focus = camera.focus; + cache.fov = camera.fov; + cache.aspect = camera.aspect * this.aspect; + cache.near = camera.near; + cache.far = camera.far; + cache.zoom = camera.zoom; + cache.eyeSep = this.eyeSep; - if ( targetObject[ objectIndex ] === undefined ) { + // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ - console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); - return; + _projectionMatrix.copy( camera.projectionMatrix ); + const eyeSepHalf = cache.eyeSep / 2; + const eyeSepOnProjection = eyeSepHalf * cache.near / cache.focus; + const ymax = ( cache.near * Math.tan( DEG2RAD * cache.fov * 0.5 ) ) / cache.zoom; + let xmin, xmax; - } + // translate xOffset - targetObject = targetObject[ objectIndex ]; + _eyeLeft.elements[ 12 ] = - eyeSepHalf; + _eyeRight.elements[ 12 ] = eyeSepHalf; - } + // for left eye - } + xmin = - ymax * cache.aspect + eyeSepOnProjection; + xmax = ymax * cache.aspect + eyeSepOnProjection; - // resolve property - var nodeProperty = targetObject[ propertyName ]; + _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); + _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - if ( nodeProperty === undefined ) { + this.cameraL.projectionMatrix.copy( _projectionMatrix ); - var nodeName = parsedPath.nodeName; + // for right eye - console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + - '.' + propertyName + ' but it wasn\'t found.', targetObject ); - return; + xmin = - ymax * cache.aspect - eyeSepOnProjection; + xmax = ymax * cache.aspect - eyeSepOnProjection; - } + _projectionMatrix.elements[ 0 ] = 2 * cache.near / ( xmax - xmin ); + _projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - // determine versioning scheme - var versioning = this.Versioning.None; + this.cameraR.projectionMatrix.copy( _projectionMatrix ); - this.targetObject = targetObject; + } - if ( targetObject.needsUpdate !== undefined ) { // material + this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeLeft ); + this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( _eyeRight ); - versioning = this.Versioning.NeedsUpdate; + } - } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform +} - versioning = this.Versioning.MatrixWorldNeedsUpdate; +class Clock { - } + constructor( autoStart = true ) { - // determine how the property gets bound - var bindingType = this.BindingType.Direct; + this.autoStart = autoStart; - if ( propertyIndex !== undefined ) { + this.startTime = 0; + this.oldTime = 0; + this.elapsedTime = 0; - // access a sub element of the property array (only primitives are supported right now) + this.running = false; - if ( propertyName === "morphTargetInfluences" ) { + } - // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + start() { - // support resolving morphTarget names into indices. - if ( ! targetObject.geometry ) { + this.startTime = now(); - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); - return; + this.oldTime = this.startTime; + this.elapsedTime = 0; + this.running = true; - } + } - if ( targetObject.geometry.isBufferGeometry ) { + stop() { - if ( ! targetObject.geometry.morphAttributes ) { + this.getElapsedTime(); + this.running = false; + this.autoStart = false; - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); - return; + } - } + getElapsedTime() { - for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) { + this.getDelta(); + return this.elapsedTime; - if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) { + } - propertyIndex = i; - break; + getDelta() { - } + let diff = 0; - } + if ( this.autoStart && ! this.running ) { + this.start(); + return 0; - } else { + } - if ( ! targetObject.geometry.morphTargets ) { + if ( this.running ) { - console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.', this ); - return; + const newTime = now(); - } + diff = ( newTime - this.oldTime ) / 1000; + this.oldTime = newTime; - for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + this.elapsedTime += diff; - if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { + } - propertyIndex = i; - break; + return diff; - } + } - } +} - } +function now() { - } + return ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732 - bindingType = this.BindingType.ArrayElement; +} - this.resolvedProperty = nodeProperty; - this.propertyIndex = propertyIndex; +const _position$1 = /*@__PURE__*/ new Vector3(); +const _quaternion$1 = /*@__PURE__*/ new Quaternion(); +const _scale$1 = /*@__PURE__*/ new Vector3(); +const _orientation$1 = /*@__PURE__*/ new Vector3(); - } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { +class AudioListener extends Object3D { - // must use copy for Object3D.Euler/Quaternion + constructor() { - bindingType = this.BindingType.HasFromToArray; + super(); - this.resolvedProperty = nodeProperty; + this.type = 'AudioListener'; - } else if ( Array.isArray( nodeProperty ) ) { + this.context = AudioContext.getContext(); - bindingType = this.BindingType.EntireArray; + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); - this.resolvedProperty = nodeProperty; + this.filter = null; - } else { + this.timeDelta = 0; - this.propertyName = propertyName; + // private - } + this._clock = new Clock(); - // select getter / setter - this.getValue = this.GetterByBindingType[ bindingType ]; - this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; + } - }, + getInput() { - unbind: function () { + return this.gain; - this.node = null; + } - // back to the prototype version of getValue / setValue - // note: avoiding to mutate the shape of 'this' via 'delete' - this.getValue = this._getValue_unbound; - this.setValue = this._setValue_unbound; + removeFilter() { - } + if ( this.filter !== null ) { - } ); + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); + this.gain.connect( this.context.destination ); + this.filter = null; - //!\ DECLARE ALIAS AFTER assign prototype ! - Object.assign( PropertyBinding.prototype, { + } - // initial state of these methods that calls 'bind' - _getValue_unbound: PropertyBinding.prototype.getValue, - _setValue_unbound: PropertyBinding.prototype.setValue, + return this; - } ); + } - /** - * - * A group of objects that receives a shared animation state. - * - * Usage: - * - * - Add objects you would otherwise pass as 'root' to the - * constructor or the .clipAction method of AnimationMixer. - * - * - Instead pass this object as 'root'. - * - * - You can also add and remove objects later when the mixer - * is running. - * - * Note: - * - * Objects of this class appear as one object to the mixer, - * so cache control of the individual objects must be done - * on the group. - * - * Limitation: - * - * - The animated properties must be compatible among the - * all objects in the group. - * - * - A single property can either be controlled through a - * target group or directly, but not both. - * - * @author tschw - */ + getFilter() { - function AnimationObjectGroup() { + return this.filter; - this.uuid = _Math.generateUUID(); + } - // cached objects followed by the active ones - this._objects = Array.prototype.slice.call( arguments ); + setFilter( value ) { - this.nCachedObjects_ = 0; // threshold - // note: read by PropertyBinding.Composite + if ( this.filter !== null ) { - var indices = {}; - this._indicesByUUID = indices; // for bookkeeping + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + } else { - indices[ arguments[ i ].uuid ] = i; + this.gain.disconnect( this.context.destination ); } - this._paths = []; // inside: string - this._parsedPaths = []; // inside: { we don't care, here } - this._bindings = []; // inside: Array< PropertyBinding > - this._bindingsIndicesByPath = {}; // inside: indices in these arrays + this.filter = value; + this.gain.connect( this.filter ); + this.filter.connect( this.context.destination ); - var scope = this; + return this; - this.stats = { + } - objects: { - get total() { + getMasterVolume() { - return scope._objects.length; + return this.gain.gain.value; - }, - get inUse() { + } - return this.total - scope.nCachedObjects_; + setMasterVolume( value ) { - } - }, - get bindingsPerObject() { + this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - return scope._bindings.length; + return this; - } + } - }; + updateMatrixWorld( force ) { - } + super.updateMatrixWorld( force ); - Object.assign( AnimationObjectGroup.prototype, { + const listener = this.context.listener; + const up = this.up; - isAnimationObjectGroup: true, + this.timeDelta = this._clock.getDelta(); - add: function () { + this.matrixWorld.decompose( _position$1, _quaternion$1, _scale$1 ); - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - nBindings = bindings.length, - knownObject = undefined; + _orientation$1.set( 0, 0, - 1 ).applyQuaternion( _quaternion$1 ); - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + if ( listener.positionX ) { - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; + // code path for Chrome (see #14393) - if ( index === undefined ) { + const endTime = this.context.currentTime + this.timeDelta; - // unknown object -> add it to the ACTIVE region + listener.positionX.linearRampToValueAtTime( _position$1.x, endTime ); + listener.positionY.linearRampToValueAtTime( _position$1.y, endTime ); + listener.positionZ.linearRampToValueAtTime( _position$1.z, endTime ); + listener.forwardX.linearRampToValueAtTime( _orientation$1.x, endTime ); + listener.forwardY.linearRampToValueAtTime( _orientation$1.y, endTime ); + listener.forwardZ.linearRampToValueAtTime( _orientation$1.z, endTime ); + listener.upX.linearRampToValueAtTime( up.x, endTime ); + listener.upY.linearRampToValueAtTime( up.y, endTime ); + listener.upZ.linearRampToValueAtTime( up.z, endTime ); - index = nObjects ++; - indicesByUUID[ uuid ] = index; - objects.push( object ); + } else { - // accounting is done, now do the same for all bindings + listener.setPosition( _position$1.x, _position$1.y, _position$1.z ); + listener.setOrientation( _orientation$1.x, _orientation$1.y, _orientation$1.z, up.x, up.y, up.z ); - for ( var j = 0, m = nBindings; j !== m; ++ j ) { + } - bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); + } - } +} - } else if ( index < nCachedObjects ) { +class Audio extends Object3D { - knownObject = objects[ index ]; + constructor( listener ) { - // move existing object to the ACTIVE region + super(); - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ]; + this.type = 'Audio'; - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; + this.listener = listener; + this.context = listener.context; - indicesByUUID[ uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = object; + this.gain = this.context.createGain(); + this.gain.connect( listener.getInput() ); - // accounting is done, now do the same for all bindings + this.autoplay = false; - for ( var j = 0, m = nBindings; j !== m; ++ j ) { + this.buffer = null; + this.detune = 0; + this.loop = false; + this.loopStart = 0; + this.loopEnd = 0; + this.offset = 0; + this.duration = undefined; + this.playbackRate = 1; + this.isPlaying = false; + this.hasPlaybackControl = true; + this.source = null; + this.sourceType = 'empty'; - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - binding = bindingsForPath[ index ]; + this._startedAt = 0; + this._progress = 0; + this._connected = false; - bindingsForPath[ index ] = lastCached; + this.filters = []; - if ( binding === undefined ) { + } - // since we do not bother to create new bindings - // for objects that are cached, the binding may - // or may not exist + getOutput() { - binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); + return this.gain; - } + } - bindingsForPath[ firstActiveIndex ] = binding; + setNodeSource( audioNode ) { - } + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); - } else if ( objects[ index ] !== knownObject ) { + return this; - console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + - 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); + } - } // else the object is already where we want it to be + setMediaElementSource( mediaElement ) { - } // for arguments + this.hasPlaybackControl = false; + this.sourceType = 'mediaNode'; + this.source = this.context.createMediaElementSource( mediaElement ); + this.connect(); - this.nCachedObjects_ = nCachedObjects; + return this; - }, + } - remove: function () { + setMediaStreamSource( mediaStream ) { - var objects = this._objects, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; + this.hasPlaybackControl = false; + this.sourceType = 'mediaStreamNode'; + this.source = this.context.createMediaStreamSource( mediaStream ); + this.connect(); - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + return this; - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; + } - if ( index !== undefined && index >= nCachedObjects ) { + setBuffer( audioBuffer ) { - // move existing object into the CACHED region + this.buffer = audioBuffer; + this.sourceType = 'buffer'; - var lastCachedIndex = nCachedObjects ++, - firstActiveObject = objects[ lastCachedIndex ]; + if ( this.autoplay ) this.play(); - indicesByUUID[ firstActiveObject.uuid ] = index; - objects[ index ] = firstActiveObject; + return this; - indicesByUUID[ uuid ] = lastCachedIndex; - objects[ lastCachedIndex ] = object; + } - // accounting is done, now do the same for all bindings + play( delay = 0 ) { - for ( var j = 0, m = nBindings; j !== m; ++ j ) { + if ( this.isPlaying === true ) { - var bindingsForPath = bindings[ j ], - firstActive = bindingsForPath[ lastCachedIndex ], - binding = bindingsForPath[ index ]; + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; - bindingsForPath[ index ] = firstActive; - bindingsForPath[ lastCachedIndex ] = binding; + } - } + if ( this.hasPlaybackControl === false ) { - } + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - } // for arguments + } - this.nCachedObjects_ = nCachedObjects; + this._startedAt = this.context.currentTime + delay; - }, + const source = this.context.createBufferSource(); + source.buffer = this.buffer; + source.loop = this.loop; + source.loopStart = this.loopStart; + source.loopEnd = this.loopEnd; + source.onended = this.onEnded.bind( this ); + source.start( this._startedAt, this._progress + this.offset, this.duration ); - // remove & forget - uncache: function () { + this.isPlaying = true; - var objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - indicesByUUID = this._indicesByUUID, - bindings = this._bindings, - nBindings = bindings.length; + this.source = source; - for ( var i = 0, n = arguments.length; i !== n; ++ i ) { + this.setDetune( this.detune ); + this.setPlaybackRate( this.playbackRate ); - var object = arguments[ i ], - uuid = object.uuid, - index = indicesByUUID[ uuid ]; + return this.connect(); - if ( index !== undefined ) { + } - delete indicesByUUID[ uuid ]; + pause() { - if ( index < nCachedObjects ) { + if ( this.hasPlaybackControl === false ) { - // object is cached, shrink the CACHED region + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - var firstActiveIndex = -- nCachedObjects, - lastCachedObject = objects[ firstActiveIndex ], - lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; + } - // last cached object takes this object's place - indicesByUUID[ lastCachedObject.uuid ] = index; - objects[ index ] = lastCachedObject; + if ( this.isPlaying === true ) { - // last object goes to the activated slot and pop - indicesByUUID[ lastObject.uuid ] = firstActiveIndex; - objects[ firstActiveIndex ] = lastObject; - objects.pop(); + // update current progress - // accounting is done, now do the same for all bindings + this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate; - for ( var j = 0, m = nBindings; j !== m; ++ j ) { + if ( this.loop === true ) { - var bindingsForPath = bindings[ j ], - lastCached = bindingsForPath[ firstActiveIndex ], - last = bindingsForPath[ lastIndex ]; + // ensure _progress does not exceed duration with looped audios - bindingsForPath[ index ] = lastCached; - bindingsForPath[ firstActiveIndex ] = last; - bindingsForPath.pop(); + this._progress = this._progress % ( this.duration || this.buffer.duration ); - } + } - } else { + this.source.stop(); + this.source.onended = null; - // object is active, just swap with the last and pop + this.isPlaying = false; - var lastIndex = -- nObjects, - lastObject = objects[ lastIndex ]; + } - indicesByUUID[ lastObject.uuid ] = index; - objects[ index ] = lastObject; - objects.pop(); + return this; - // accounting is done, now do the same for all bindings + } - for ( var j = 0, m = nBindings; j !== m; ++ j ) { + stop() { - var bindingsForPath = bindings[ j ]; + if ( this.hasPlaybackControl === false ) { - bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; - bindingsForPath.pop(); + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - } + } - } // cached or active + this._progress = 0; - } // if object is known + if ( this.source !== null ) { - } // for arguments + this.source.stop(); + this.source.onended = null; - this.nCachedObjects_ = nCachedObjects; + } - }, + this.isPlaying = false; - // Internal interface used by befriended PropertyBinding.Composite: + return this; - subscribe_: function ( path, parsedPath ) { + } - // returns an array of bindings for the given path that is changed - // according to the contained objects in the group + connect() { - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ], - bindings = this._bindings; + if ( this.filters.length > 0 ) { - if ( index !== undefined ) return bindings[ index ]; + this.source.connect( this.filters[ 0 ] ); - var paths = this._paths, - parsedPaths = this._parsedPaths, - objects = this._objects, - nObjects = objects.length, - nCachedObjects = this.nCachedObjects_, - bindingsForPath = new Array( nObjects ); + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - index = bindings.length; + this.filters[ i - 1 ].connect( this.filters[ i ] ); - indicesByPath[ path ] = index; + } - paths.push( path ); - parsedPaths.push( parsedPath ); - bindings.push( bindingsForPath ); + this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) { + } else { - var object = objects[ i ]; - bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); + this.source.connect( this.getOutput() ); - } + } - return bindingsForPath; + this._connected = true; - }, + return this; - unsubscribe_: function ( path ) { + } - // tells the group to forget about a property path and no longer - // update the array previously obtained with 'subscribe_' + disconnect() { - var indicesByPath = this._bindingsIndicesByPath, - index = indicesByPath[ path ]; + if ( this._connected === false ) { - if ( index !== undefined ) { + return; - var paths = this._paths, - parsedPaths = this._parsedPaths, - bindings = this._bindings, - lastBindingsIndex = bindings.length - 1, - lastBindings = bindings[ lastBindingsIndex ], - lastBindingsPath = path[ lastBindingsIndex ]; + } - indicesByPath[ lastBindingsPath ] = index; + if ( this.filters.length > 0 ) { - bindings[ index ] = lastBindings; - bindings.pop(); + this.source.disconnect( this.filters[ 0 ] ); - parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; - parsedPaths.pop(); + for ( let i = 1, l = this.filters.length; i < l; i ++ ) { - paths[ index ] = paths[ lastBindingsIndex ]; - paths.pop(); + this.filters[ i - 1 ].disconnect( this.filters[ i ] ); } + this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); + + } else { + + this.source.disconnect( this.getOutput() ); + } - } ); + this._connected = false; - /** - * - * Action provided by AnimationMixer for scheduling clip playback on specific - * objects. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - * - */ + return this; - function AnimationAction( mixer, clip, localRoot ) { + } - this._mixer = mixer; - this._clip = clip; - this._localRoot = localRoot || null; + getFilters() { - var tracks = clip.tracks, - nTracks = tracks.length, - interpolants = new Array( nTracks ); + return this.filters; - var interpolantSettings = { - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding - }; + } - for ( var i = 0; i !== nTracks; ++ i ) { + setFilters( value ) { - var interpolant = tracks[ i ].createInterpolant( null ); - interpolants[ i ] = interpolant; - interpolant.settings = interpolantSettings; + if ( ! value ) value = []; + + if ( this._connected === true ) { + + this.disconnect(); + this.filters = value.slice(); + this.connect(); + + } else { + + this.filters = value.slice(); } - this._interpolantSettings = interpolantSettings; + return this; - this._interpolants = interpolants; // bound by the mixer + } - // inside: PropertyMixer (managed by the mixer) - this._propertyBindings = new Array( nTracks ); + setDetune( value ) { - this._cacheIndex = null; // for the memory manager - this._byClipCacheIndex = null; // for the memory manager + this.detune = value; - this._timeScaleInterpolant = null; - this._weightInterpolant = null; + if ( this.source.detune === undefined ) return; // only set detune when available - this.loop = LoopRepeat; - this._loopCount = - 1; + if ( this.isPlaying === true ) { - // global mixer time when the action is to be started - // it's set back to 'null' upon start of the action - this._startTime = null; + this.source.detune.setTargetAtTime( this.detune, this.context.currentTime, 0.01 ); - // scaled local time of the action - // gets clamped or wrapped to 0..clip.duration according to loop - this.time = 0; + } - this.timeScale = 1; - this._effectiveTimeScale = 1; + return this; - this.weight = 1; - this._effectiveWeight = 1; + } - this.repetitions = Infinity; // no. of repetitions when looping + getDetune() { - this.paused = false; // true -> zero effective time scale - this.enabled = true; // false -> zero effective weight + return this.detune; - this.clampWhenFinished = false;// keep feeding the last frame? + } - this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate - this.zeroSlopeAtEnd = true;// clips for start, loop and end + getFilter() { + + return this.getFilters()[ 0 ]; } - Object.assign( AnimationAction.prototype, { + setFilter( filter ) { - // State & Scheduling + return this.setFilters( filter ? [ filter ] : [] ); - play: function () { + } - this._mixer._activateAction( this ); + setPlaybackRate( value ) { - return this; + if ( this.hasPlaybackControl === false ) { - }, + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - stop: function () { + } - this._mixer._deactivateAction( this ); + this.playbackRate = value; - return this.reset(); + if ( this.isPlaying === true ) { - }, + this.source.playbackRate.setTargetAtTime( this.playbackRate, this.context.currentTime, 0.01 ); - reset: function () { + } - this.paused = false; - this.enabled = true; + return this; - this.time = 0; // restart clip - this._loopCount = - 1;// forget previous loops - this._startTime = null;// forget scheduling + } - return this.stopFading().stopWarping(); + getPlaybackRate() { - }, + return this.playbackRate; - isRunning: function () { + } - return this.enabled && ! this.paused && this.timeScale !== 0 && - this._startTime === null && this._mixer._isActiveAction( this ); + onEnded() { - }, + this.isPlaying = false; - // return true when play has been called - isScheduled: function () { + } - return this._mixer._isActiveAction( this ); + getLoop() { - }, + if ( this.hasPlaybackControl === false ) { - startAt: function ( time ) { + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return false; - this._startTime = time; + } - return this; + return this.loop; - }, + } - setLoop: function ( mode, repetitions ) { + setLoop( value ) { - this.loop = mode; - this.repetitions = repetitions; + if ( this.hasPlaybackControl === false ) { - return this; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - }, + } - // Weight + this.loop = value; - // set the weight stopping any scheduled fading - // although .enabled = false yields an effective weight of zero, this - // method does *not* change .enabled, because it would be confusing - setEffectiveWeight: function ( weight ) { + if ( this.isPlaying === true ) { - this.weight = weight; + this.source.loop = this.loop; - // note: same logic as when updated at runtime - this._effectiveWeight = this.enabled ? weight : 0; + } - return this.stopFading(); + return this; - }, + } - // return the weight considering fading and .enabled - getEffectiveWeight: function () { + setLoopStart( value ) { - return this._effectiveWeight; + this.loopStart = value; - }, + return this; - fadeIn: function ( duration ) { + } - return this._scheduleFading( duration, 0, 1 ); + setLoopEnd( value ) { - }, + this.loopEnd = value; - fadeOut: function ( duration ) { + return this; - return this._scheduleFading( duration, 1, 0 ); + } - }, + getVolume() { - crossFadeFrom: function ( fadeOutAction, duration, warp ) { + return this.gain.gain.value; - fadeOutAction.fadeOut( duration ); - this.fadeIn( duration ); + } - if ( warp ) { + setVolume( value ) { - var fadeInDuration = this._clip.duration, - fadeOutDuration = fadeOutAction._clip.duration, + this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 ); - startEndRatio = fadeOutDuration / fadeInDuration, - endStartRatio = fadeInDuration / fadeOutDuration; + return this; - fadeOutAction.warp( 1.0, startEndRatio, duration ); - this.warp( endStartRatio, 1.0, duration ); + } - } +} - return this; +const _position = /*@__PURE__*/ new Vector3(); +const _quaternion = /*@__PURE__*/ new Quaternion(); +const _scale = /*@__PURE__*/ new Vector3(); +const _orientation = /*@__PURE__*/ new Vector3(); - }, +class PositionalAudio extends Audio { - crossFadeTo: function ( fadeInAction, duration, warp ) { + constructor( listener ) { - return fadeInAction.crossFadeFrom( this, duration, warp ); + super( listener ); - }, + this.panner = this.context.createPanner(); + this.panner.panningModel = 'HRTF'; + this.panner.connect( this.gain ); - stopFading: function () { + } - var weightInterpolant = this._weightInterpolant; + connect() { - if ( weightInterpolant !== null ) { + super.connect(); - this._weightInterpolant = null; - this._mixer._takeBackControlInterpolant( weightInterpolant ); + this.panner.connect( this.gain ); - } + } - return this; + disconnect() { - }, + super.disconnect(); - // Time Scale Control + this.panner.disconnect( this.gain ); - // set the time scale stopping any scheduled warping - // although .paused = true yields an effective time scale of zero, this - // method does *not* change .paused, because it would be confusing - setEffectiveTimeScale: function ( timeScale ) { + } - this.timeScale = timeScale; - this._effectiveTimeScale = this.paused ? 0 : timeScale; + getOutput() { - return this.stopWarping(); + return this.panner; - }, + } - // return the time scale considering warping and .paused - getEffectiveTimeScale: function () { + getRefDistance() { - return this._effectiveTimeScale; + return this.panner.refDistance; - }, + } - setDuration: function ( duration ) { + setRefDistance( value ) { - this.timeScale = this._clip.duration / duration; + this.panner.refDistance = value; - return this.stopWarping(); + return this; - }, + } - syncWith: function ( action ) { + getRolloffFactor() { - this.time = action.time; - this.timeScale = action.timeScale; + return this.panner.rolloffFactor; - return this.stopWarping(); + } - }, + setRolloffFactor( value ) { - halt: function ( duration ) { + this.panner.rolloffFactor = value; - return this.warp( this._effectiveTimeScale, 0, duration ); + return this; - }, + } - warp: function ( startTimeScale, endTimeScale, duration ) { + getDistanceModel() { - var mixer = this._mixer, now = mixer.time, - interpolant = this._timeScaleInterpolant, + return this.panner.distanceModel; - timeScale = this.timeScale; + } - if ( interpolant === null ) { + setDistanceModel( value ) { - interpolant = mixer._lendControlInterpolant(); - this._timeScaleInterpolant = interpolant; + this.panner.distanceModel = value; - } + return this; - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; + } - times[ 0 ] = now; - times[ 1 ] = now + duration; + getMaxDistance() { - values[ 0 ] = startTimeScale / timeScale; - values[ 1 ] = endTimeScale / timeScale; + return this.panner.maxDistance; - return this; + } - }, + setMaxDistance( value ) { - stopWarping: function () { + this.panner.maxDistance = value; - var timeScaleInterpolant = this._timeScaleInterpolant; + return this; - if ( timeScaleInterpolant !== null ) { + } - this._timeScaleInterpolant = null; - this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); + setDirectionalCone( coneInnerAngle, coneOuterAngle, coneOuterGain ) { - } + this.panner.coneInnerAngle = coneInnerAngle; + this.panner.coneOuterAngle = coneOuterAngle; + this.panner.coneOuterGain = coneOuterGain; - return this; + return this; - }, + } - // Object Accessors + updateMatrixWorld( force ) { - getMixer: function () { + super.updateMatrixWorld( force ); - return this._mixer; + if ( this.hasPlaybackControl === true && this.isPlaying === false ) return; - }, + this.matrixWorld.decompose( _position, _quaternion, _scale ); - getClip: function () { + _orientation.set( 0, 0, 1 ).applyQuaternion( _quaternion ); - return this._clip; + const panner = this.panner; - }, + if ( panner.positionX ) { - getRoot: function () { + // code path for Chrome and Firefox (see #14393) - return this._localRoot || this._mixer._root; + const endTime = this.context.currentTime + this.listener.timeDelta; - }, + panner.positionX.linearRampToValueAtTime( _position.x, endTime ); + panner.positionY.linearRampToValueAtTime( _position.y, endTime ); + panner.positionZ.linearRampToValueAtTime( _position.z, endTime ); + panner.orientationX.linearRampToValueAtTime( _orientation.x, endTime ); + panner.orientationY.linearRampToValueAtTime( _orientation.y, endTime ); + panner.orientationZ.linearRampToValueAtTime( _orientation.z, endTime ); - // Interna + } else { - _update: function ( time, deltaTime, timeDirection, accuIndex ) { + panner.setPosition( _position.x, _position.y, _position.z ); + panner.setOrientation( _orientation.x, _orientation.y, _orientation.z ); - // called by the mixer + } - if ( ! this.enabled ) { + } - // call ._updateWeight() to update ._effectiveWeight +} - this._updateWeight( time ); - return; +class AudioAnalyser { - } + constructor( audio, fftSize = 2048 ) { - var startTime = this._startTime; + this.analyser = audio.context.createAnalyser(); + this.analyser.fftSize = fftSize; - if ( startTime !== null ) { + this.data = new Uint8Array( this.analyser.frequencyBinCount ); - // check for scheduled start of action + audio.getOutput().connect( this.analyser ); - var timeRunning = ( time - startTime ) * timeDirection; - if ( timeRunning < 0 || timeDirection === 0 ) { + } - return; // yet to come / don't decide when delta = 0 - } + getFrequencyData() { - // start + this.analyser.getByteFrequencyData( this.data ); - this._startTime = null; // unschedule - deltaTime = timeDirection * timeRunning; + return this.data; - } + } - // apply time scale and advance time + getAverageFrequency() { - deltaTime *= this._updateTimeScale( time ); - var clipTime = this._updateTime( deltaTime ); + let value = 0; + const data = this.getFrequencyData(); - // note: _updateTime may disable the action resulting in - // an effective weight of 0 + for ( let i = 0; i < data.length; i ++ ) { - var weight = this._updateWeight( time ); + value += data[ i ]; - if ( weight > 0 ) { + } - var interpolants = this._interpolants; - var propertyMixers = this._propertyBindings; + return value / data.length; - for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { + } - interpolants[ j ].evaluate( clipTime ); - propertyMixers[ j ].accumulate( accuIndex, weight ); +} - } +class PropertyMixer { - } + constructor( binding, typeName, valueSize ) { - }, + this.binding = binding; + this.valueSize = valueSize; - _updateWeight: function ( time ) { + let mixFunction, + mixFunctionAdditive, + setIdentity; - var weight = 0; + // buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property + // + // 'add' is used for additive cumulative results + // + // 'work' is optional and is only present for quaternion types. It is used + // to store intermediate quaternion multiplication results - if ( this.enabled ) { + switch ( typeName ) { - weight = this.weight; - var interpolant = this._weightInterpolant; + case 'quaternion': + mixFunction = this._slerp; + mixFunctionAdditive = this._slerpAdditive; + setIdentity = this._setAdditiveIdentityQuaternion; - if ( interpolant !== null ) { + this.buffer = new Float64Array( valueSize * 6 ); + this._workIndex = 5; + break; - var interpolantValue = interpolant.evaluate( time )[ 0 ]; + case 'string': + case 'bool': + mixFunction = this._select; - weight *= interpolantValue; + // Use the regular mix function and for additive on these types, + // additive is not relevant for non-numeric types + mixFunctionAdditive = this._select; - if ( time > interpolant.parameterPositions[ 1 ] ) { + setIdentity = this._setAdditiveIdentityOther; - this.stopFading(); + this.buffer = new Array( valueSize * 5 ); + break; - if ( interpolantValue === 0 ) { + default: + mixFunction = this._lerp; + mixFunctionAdditive = this._lerpAdditive; + setIdentity = this._setAdditiveIdentityNumeric; - // faded out, disable - this.enabled = false; + this.buffer = new Float64Array( valueSize * 5 ); - } + } - } + this._mixBufferRegion = mixFunction; + this._mixBufferRegionAdditive = mixFunctionAdditive; + this._setIdentity = setIdentity; + this._origIndex = 3; + this._addIndex = 4; - } + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - } + this.useCount = 0; + this.referenceCount = 0; - this._effectiveWeight = weight; - return weight; + } - }, + // accumulate data in the 'incoming' region into 'accu' + accumulate( accuIndex, weight ) { - _updateTimeScale: function ( time ) { + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place - var timeScale = 0; + const buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride; - if ( ! this.paused ) { + let currentWeight = this.cumulativeWeight; - timeScale = this.timeScale; + if ( currentWeight === 0 ) { - var interpolant = this._timeScaleInterpolant; + // accuN := incoming * weight - if ( interpolant !== null ) { + for ( let i = 0; i !== stride; ++ i ) { - var interpolantValue = interpolant.evaluate( time )[ 0 ]; + buffer[ offset + i ] = buffer[ i ]; - timeScale *= interpolantValue; + } - if ( time > interpolant.parameterPositions[ 1 ] ) { + currentWeight = weight; - this.stopWarping(); + } else { - if ( timeScale === 0 ) { + // accuN := accuN + incoming * weight - // motion has halted, pause - this.paused = true; + currentWeight += weight; + const mix = weight / currentWeight; + this._mixBufferRegion( buffer, offset, 0, mix, stride ); - } else { + } - // warp done - apply final time scale - this.timeScale = timeScale; + this.cumulativeWeight = currentWeight; - } + } - } + // accumulate data in the 'incoming' region into 'add' + accumulateAdditive( weight ) { - } + const buffer = this.buffer, + stride = this.valueSize, + offset = stride * this._addIndex; - } + if ( this.cumulativeWeightAdditive === 0 ) { - this._effectiveTimeScale = timeScale; - return timeScale; + // add = identity - }, + this._setIdentity(); - _updateTime: function ( deltaTime ) { + } - var time = this.time + deltaTime; - var duration = this._clip.duration; - var loop = this.loop; - var loopCount = this._loopCount; + // add := add + incoming * weight - var pingPong = ( loop === LoopPingPong ); + this._mixBufferRegionAdditive( buffer, offset, 0, weight, stride ); + this.cumulativeWeightAdditive += weight; - if ( deltaTime === 0 ) { + } - if ( loopCount === - 1 ) return time; + // apply the state of 'accu' to the binding when accus differ + apply( accuIndex ) { - return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; + const stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, - } + weight = this.cumulativeWeight, + weightAdditive = this.cumulativeWeightAdditive, - if ( loop === LoopOnce ) { + binding = this.binding; - if ( loopCount === - 1 ) { + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - // just started + if ( weight < 1 ) { - this._loopCount = 0; - this._setEndings( true, true, false ); + // accuN := accuN + original * ( 1 - cumulativeWeight ) - } + const originalValueOffset = stride * this._origIndex; - handle_stop: { + this._mixBufferRegion( + buffer, offset, originalValueOffset, 1 - weight, stride ); - if ( time >= duration ) { + } - time = duration; + if ( weightAdditive > 0 ) { - } else if ( time < 0 ) { + // accuN := accuN + additive accuN - time = 0; + this._mixBufferRegionAdditive( buffer, offset, this._addIndex * stride, 1, stride ); - } else { + } - this.time = time; + for ( let i = stride, e = stride + stride; i !== e; ++ i ) { - break handle_stop; + if ( buffer[ i ] !== buffer[ i + stride ] ) { - } + // value has changed -> update scene graph - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + binding.setValue( buffer, offset ); + break; - this.time = time; + } - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime < 0 ? - 1 : 1 - } ); + } - } + } - } else { // repetitive Repeat or PingPong + // remember the state of the bound property and copy it to both accus + saveOriginalState() { - if ( loopCount === - 1 ) { + const binding = this.binding; - // just started + const buffer = this.buffer, + stride = this.valueSize, - if ( deltaTime >= 0 ) { + originalValueOffset = stride * this._origIndex; - loopCount = 0; + binding.getValue( buffer, originalValueOffset ); - this._setEndings( true, this.repetitions === 0, pingPong ); + // accu[0..1] := orig -- initially detect changes against the original + for ( let i = stride, e = originalValueOffset; i !== e; ++ i ) { - } else { + buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; - // when looping in reverse direction, the initial - // transition through zero counts as a repetition, - // so leave loopCount at -1 + } - this._setEndings( this.repetitions === 0, true, pingPong ); + // Add to identity for additive + this._setIdentity(); - } + this.cumulativeWeight = 0; + this.cumulativeWeightAdditive = 0; - } + } - if ( time >= duration || time < 0 ) { + // apply the state previously taken via 'saveOriginalState' to the binding + restoreOriginalState() { - // wrap around + const originalValueOffset = this.valueSize * 3; + this.binding.setValue( this.buffer, originalValueOffset ); - var loopDelta = Math.floor( time / duration ); // signed - time -= duration * loopDelta; + } - loopCount += Math.abs( loopDelta ); + _setAdditiveIdentityNumeric() { - var pending = this.repetitions - loopCount; + const startIndex = this._addIndex * this.valueSize; + const endIndex = startIndex + this.valueSize; - if ( pending <= 0 ) { + for ( let i = startIndex; i < endIndex; i ++ ) { - // have to stop (switch state, clamp time, fire event) + this.buffer[ i ] = 0; - if ( this.clampWhenFinished ) this.paused = true; - else this.enabled = false; + } - time = deltaTime > 0 ? duration : 0; + } - this.time = time; + _setAdditiveIdentityQuaternion() { - this._mixer.dispatchEvent( { - type: 'finished', action: this, - direction: deltaTime > 0 ? 1 : - 1 - } ); + this._setAdditiveIdentityNumeric(); + this.buffer[ this._addIndex * this.valueSize + 3 ] = 1; - } else { + } - // keep running + _setAdditiveIdentityOther() { - if ( pending === 1 ) { + const startIndex = this._origIndex * this.valueSize; + const targetIndex = this._addIndex * this.valueSize; - // entering the last round + for ( let i = 0; i < this.valueSize; i ++ ) { - var atStart = deltaTime < 0; - this._setEndings( atStart, ! atStart, pingPong ); + this.buffer[ targetIndex + i ] = this.buffer[ startIndex + i ]; - } else { + } - this._setEndings( false, false, pingPong ); + } - } - this._loopCount = loopCount; + // mix functions - this.time = time; + _select( buffer, dstOffset, srcOffset, t, stride ) { - this._mixer.dispatchEvent( { - type: 'loop', action: this, loopDelta: loopDelta - } ); + if ( t >= 0.5 ) { - } + for ( let i = 0; i !== stride; ++ i ) { - } else { + buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - this.time = time; + } - } + } - if ( pingPong && ( loopCount & 1 ) === 1 ) { + } - // invert time for the "pong round" + _slerp( buffer, dstOffset, srcOffset, t ) { - return duration - time; + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t ); - } + } - } + _slerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - return time; + const workOffset = this._workIndex * stride; - }, + // Store result in intermediate buffer offset + Quaternion.multiplyQuaternionsFlat( buffer, workOffset, buffer, dstOffset, buffer, srcOffset ); - _setEndings: function ( atStart, atEnd, pingPong ) { + // Slerp to the intermediate result + Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, workOffset, t ); - var settings = this._interpolantSettings; + } - if ( pingPong ) { + _lerp( buffer, dstOffset, srcOffset, t, stride ) { - settings.endingStart = ZeroSlopeEnding; - settings.endingEnd = ZeroSlopeEnding; + const s = 1 - t; - } else { + for ( let i = 0; i !== stride; ++ i ) { - // assuming for LoopOnce atStart == atEnd == true + const j = dstOffset + i; - if ( atStart ) { + buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; - settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; + } - } else { + } - settings.endingStart = WrapAroundEnding; + _lerpAdditive( buffer, dstOffset, srcOffset, t, stride ) { - } + for ( let i = 0; i !== stride; ++ i ) { - if ( atEnd ) { + const j = dstOffset + i; - settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; + buffer[ j ] = buffer[ j ] + buffer[ srcOffset + i ] * t; - } else { + } - settings.endingEnd = WrapAroundEnding; + } - } +} - } +// Characters [].:/ are reserved for track binding syntax. +const _RESERVED_CHARS_RE = '\\[\\]\\.:\\/'; +const _reservedRe = new RegExp( '[' + _RESERVED_CHARS_RE + ']', 'g' ); - }, +// Attempts to allow node names from any language. ES5's `\w` regexp matches +// only latin characters, and the unicode \p{L} is not yet supported. So +// instead, we exclude reserved characters and match everything else. +const _wordChar = '[^' + _RESERVED_CHARS_RE + ']'; +const _wordCharOrDot = '[^' + _RESERVED_CHARS_RE.replace( '\\.', '' ) + ']'; - _scheduleFading: function ( duration, weightNow, weightThen ) { +// Parent directories, delimited by '/' or ':'. Currently unused, but must +// be matched to parse the rest of the track name. +const _directoryRe = /*@__PURE__*/ /((?:WC+[\/:])*)/.source.replace( 'WC', _wordChar ); - var mixer = this._mixer, now = mixer.time, - interpolant = this._weightInterpolant; +// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'. +const _nodeRe = /*@__PURE__*/ /(WCOD+)?/.source.replace( 'WCOD', _wordCharOrDot ); - if ( interpolant === null ) { +// Object on target node, and accessor. May not contain reserved +// characters. Accessor may contain any character except closing bracket. +const _objectRe = /*@__PURE__*/ /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace( 'WC', _wordChar ); - interpolant = mixer._lendControlInterpolant(); - this._weightInterpolant = interpolant; +// Property and accessor. May not contain reserved characters. Accessor may +// contain any non-bracket characters. +const _propertyRe = /*@__PURE__*/ /\.(WC+)(?:\[(.+)\])?/.source.replace( 'WC', _wordChar ); - } +const _trackRe = new RegExp( '' + + '^' + + _directoryRe + + _nodeRe + + _objectRe + + _propertyRe + + '$' +); - var times = interpolant.parameterPositions, - values = interpolant.sampleValues; +const _supportedObjectNames = [ 'material', 'materials', 'bones', 'map' ]; - times[ 0 ] = now; - values[ 0 ] = weightNow; - times[ 1 ] = now + duration; - values[ 1 ] = weightThen; +class Composite { - return this; + constructor( targetGroup, path, optionalParsedPath ) { - } + const parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path ); - } ); + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); - /** - * - * Player for AnimationClips. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + } - function AnimationMixer( root ) { + getValue( array, offset ) { - this._root = root; - this._initMemoryManager(); - this._accuIndex = 0; + this.bind(); // bind all binding - this.time = 0; + const firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[ firstValidIndex ]; - this.timeScale = 1.0; + // and only call .getValue on the first + if ( binding !== undefined ) binding.getValue( array, offset ); } - AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), { + setValue( array, offset ) { - constructor: AnimationMixer, + const bindings = this._bindings; - _bindAction: function ( action, prototypeAction ) { + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - var root = action._localRoot || this._root, - tracks = action._clip.tracks, - nTracks = tracks.length, - bindings = action._propertyBindings, - interpolants = action._interpolants, - rootUuid = root.uuid, - bindingsByRoot = this._bindingsByRootAndName, - bindingsByName = bindingsByRoot[ rootUuid ]; + bindings[ i ].setValue( array, offset ); - if ( bindingsByName === undefined ) { + } - bindingsByName = {}; - bindingsByRoot[ rootUuid ] = bindingsByName; + } - } + bind() { - for ( var i = 0; i !== nTracks; ++ i ) { + const bindings = this._bindings; - var track = tracks[ i ], - trackName = track.name, - binding = bindingsByName[ trackName ]; + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - if ( binding !== undefined ) { + bindings[ i ].bind(); - bindings[ i ] = binding; + } - } else { + } - binding = bindings[ i ]; + unbind() { - if ( binding !== undefined ) { + const bindings = this._bindings; - // existing binding, make sure the cache knows + for ( let i = this._targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++ i ) { - if ( binding._cacheIndex === null ) { + bindings[ i ].unbind(); - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + } - } + } - continue; +} - } +// Note: This class uses a State pattern on a per-method basis: +// 'bind' sets 'this.getValue' / 'setValue' and shadows the +// prototype version of these methods with one that represents +// the bound state. When the property is not found, the methods +// become no-ops. +class PropertyBinding { - var path = prototypeAction && prototypeAction. - _propertyBindings[ i ].binding.parsedPath; + constructor( rootNode, path, parsedPath ) { - binding = new PropertyMixer( - PropertyBinding.create( root, trackName, path ), - track.ValueTypeName, track.getValueSize() ); + this.path = path; + this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path ); - ++ binding.referenceCount; - this._addInactiveBinding( binding, rootUuid, trackName ); + this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ); - bindings[ i ] = binding; + this.rootNode = rootNode; - } + // initial state of these methods that calls 'bind' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - interpolants[ i ].resultBuffer = binding.buffer; + } - } - }, + static create( root, path, parsedPath ) { - _activateAction: function ( action ) { + if ( ! ( root && root.isAnimationObjectGroup ) ) { - if ( ! this._isActiveAction( action ) ) { + return new PropertyBinding( root, path, parsedPath ); - if ( action._cacheIndex === null ) { + } else { - // this action has been forgotten by the cache, but the user - // appears to be still using it -> rebind + return new PropertyBinding.Composite( root, path, parsedPath ); - var rootUuid = ( action._localRoot || this._root ).uuid, - clipUuid = action._clip.uuid, - actionsForClip = this._actionsByClip[ clipUuid ]; + } - this._bindAction( action, - actionsForClip && actionsForClip.knownActions[ 0 ] ); + } - this._addInactiveAction( action, clipUuid, rootUuid ); + /** + * Replaces spaces with underscores and removes unsupported characters from + * node names, to ensure compatibility with parseTrackName(). + * + * @param {string} name Node name to be sanitized. + * @return {string} + */ + static sanitizeNodeName( name ) { - } + return name.replace( /\s/g, '_' ).replace( _reservedRe, '' ); - var bindings = action._propertyBindings; + } - // increment reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + static parseTrackName( trackName ) { - var binding = bindings[ i ]; + const matches = _trackRe.exec( trackName ); - if ( binding.useCount ++ === 0 ) { + if ( matches === null ) { - this._lendBinding( binding ); - binding.saveOriginalState(); + throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName ); - } + } - } + const results = { + // directoryName: matches[ 1 ], // (tschw) currently unused + nodeName: matches[ 2 ], + objectName: matches[ 3 ], + objectIndex: matches[ 4 ], + propertyName: matches[ 5 ], // required + propertyIndex: matches[ 6 ] + }; - this._lendAction( action ); + const lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' ); - } + if ( lastDot !== undefined && lastDot !== - 1 ) { - }, + const objectName = results.nodeName.substring( lastDot + 1 ); - _deactivateAction: function ( action ) { + // Object names must be checked against an allowlist. Otherwise, there + // is no way to parse 'foo.bar.baz': 'baz' must be a property, but + // 'bar' could be the objectName, or part of a nodeName (which can + // include '.' characters). + if ( _supportedObjectNames.indexOf( objectName ) !== - 1 ) { - if ( this._isActiveAction( action ) ) { + results.nodeName = results.nodeName.substring( 0, lastDot ); + results.objectName = objectName; - var bindings = action._propertyBindings; + } - // decrement reference counts / sort out state - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + } - var binding = bindings[ i ]; + if ( results.propertyName === null || results.propertyName.length === 0 ) { - if ( -- binding.useCount === 0 ) { + throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName ); - binding.restoreOriginalState(); - this._takeBackBinding( binding ); + } - } + return results; - } + } - this._takeBackAction( action ); + static findNode( root, nodeName ) { - } + if ( nodeName === undefined || nodeName === '' || nodeName === '.' || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) { - }, + return root; - // Memory manager + } - _initMemoryManager: function () { + // search into skeleton bones. + if ( root.skeleton ) { - this._actions = []; // 'nActiveActions' followed by inactive ones - this._nActiveActions = 0; + const bone = root.skeleton.getBoneByName( nodeName ); - this._actionsByClip = {}; - // inside: - // { - // knownActions: Array< AnimationAction > - used as prototypes - // actionByRoot: AnimationAction - lookup - // } + if ( bone !== undefined ) { + return bone; - this._bindings = []; // 'nActiveBindings' followed by inactive ones - this._nActiveBindings = 0; + } - this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > + } + // search into node subtree. + if ( root.children ) { - this._controlInterpolants = []; // same game as above - this._nActiveControlInterpolants = 0; + const searchNodeSubtree = function ( children ) { - var scope = this; + for ( let i = 0; i < children.length; i ++ ) { - this.stats = { + const childNode = children[ i ]; - actions: { - get total() { + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - return scope._actions.length; + return childNode; - }, - get inUse() { + } - return scope._nActiveActions; + const result = searchNodeSubtree( childNode.children ); - } - }, - bindings: { - get total() { + if ( result ) return result; - return scope._bindings.length; + } - }, - get inUse() { + return null; - return scope._nActiveBindings; + }; - } - }, - controlInterpolants: { - get total() { + const subTreeNode = searchNodeSubtree( root.children ); - return scope._controlInterpolants.length; + if ( subTreeNode ) { - }, - get inUse() { + return subTreeNode; - return scope._nActiveControlInterpolants; + } - } - } + } - }; + return null; - }, + } - // Memory management for AnimationAction objects + // these are used to "bind" a nonexistent property + _getValue_unavailable() {} + _setValue_unavailable() {} - _isActiveAction: function ( action ) { + // Getters - var index = action._cacheIndex; - return index !== null && index < this._nActiveActions; + _getValue_direct( buffer, offset ) { - }, + buffer[ offset ] = this.targetObject[ this.propertyName ]; - _addInactiveAction: function ( action, clipUuid, rootUuid ) { + } - var actions = this._actions, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; + _getValue_array( buffer, offset ) { - if ( actionsForClip === undefined ) { + const source = this.resolvedProperty; - actionsForClip = { + for ( let i = 0, n = source.length; i !== n; ++ i ) { - knownActions: [ action ], - actionByRoot: {} + buffer[ offset ++ ] = source[ i ]; - }; + } + + } - action._byClipCacheIndex = 0; + _getValue_arrayElement( buffer, offset ) { - actionsByClip[ clipUuid ] = actionsForClip; + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - } else { + } - var knownActions = actionsForClip.knownActions; + _getValue_toArray( buffer, offset ) { - action._byClipCacheIndex = knownActions.length; - knownActions.push( action ); + this.resolvedProperty.toArray( buffer, offset ); - } + } - action._cacheIndex = actions.length; - actions.push( action ); + // Direct - actionsForClip.actionByRoot[ rootUuid ] = action; + _setValue_direct( buffer, offset ) { - }, + this.targetObject[ this.propertyName ] = buffer[ offset ]; - _removeInactiveAction: function ( action ) { + } - var actions = this._actions, - lastInactiveAction = actions[ actions.length - 1 ], - cacheIndex = action._cacheIndex; + _setValue_direct_setNeedsUpdate( buffer, offset ) { - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - action._cacheIndex = null; + } + _setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - var clipUuid = action._clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ], - knownActionsForClip = actionsForClip.knownActions, + this.targetObject[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - lastKnownAction = - knownActionsForClip[ knownActionsForClip.length - 1 ], + } - byClipCacheIndex = action._byClipCacheIndex; + // EntireArray - lastKnownAction._byClipCacheIndex = byClipCacheIndex; - knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; - knownActionsForClip.pop(); + _setValue_array( buffer, offset ) { - action._byClipCacheIndex = null; + const dest = this.resolvedProperty; + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - var actionByRoot = actionsForClip.actionByRoot, - rootUuid = ( action._localRoot || this._root ).uuid; + dest[ i ] = buffer[ offset ++ ]; - delete actionByRoot[ rootUuid ]; + } - if ( knownActionsForClip.length === 0 ) { + } - delete actionsByClip[ clipUuid ]; + _setValue_array_setNeedsUpdate( buffer, offset ) { - } + const dest = this.resolvedProperty; - this._removeInactiveBindingsForAction( action ); + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - }, + dest[ i ] = buffer[ offset ++ ]; - _removeInactiveBindingsForAction: function ( action ) { + } - var bindings = action._propertyBindings; - for ( var i = 0, n = bindings.length; i !== n; ++ i ) { + this.targetObject.needsUpdate = true; - var binding = bindings[ i ]; + } - if ( -- binding.referenceCount === 0 ) { + _setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - this._removeInactiveBinding( binding ); + const dest = this.resolvedProperty; - } + for ( let i = 0, n = dest.length; i !== n; ++ i ) { - } + dest[ i ] = buffer[ offset ++ ]; - }, + } - _lendAction: function ( action ) { + this.targetObject.matrixWorldNeedsUpdate = true; - // [ active actions | inactive actions ] - // [ active actions >| inactive actions ] - // s a - // <-swap-> - // a s + } - var actions = this._actions, - prevIndex = action._cacheIndex, + // ArrayElement - lastActiveIndex = this._nActiveActions ++, + _setValue_arrayElement( buffer, offset ) { - firstInactiveAction = actions[ lastActiveIndex ]; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; - action._cacheIndex = lastActiveIndex; - actions[ lastActiveIndex ] = action; + } - firstInactiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = firstInactiveAction; + _setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - }, + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - _takeBackAction: function ( action ) { + } - // [ active actions | inactive actions ] - // [ active actions |< inactive actions ] - // a s - // <-swap-> - // s a + _setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - var actions = this._actions, - prevIndex = action._cacheIndex, + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - firstInactiveIndex = -- this._nActiveActions, + } - lastActiveAction = actions[ firstInactiveIndex ]; + // HasToFromArray - action._cacheIndex = firstInactiveIndex; - actions[ firstInactiveIndex ] = action; + _setValue_fromArray( buffer, offset ) { - lastActiveAction._cacheIndex = prevIndex; - actions[ prevIndex ] = lastActiveAction; + this.resolvedProperty.fromArray( buffer, offset ); - }, + } - // Memory management for PropertyMixer objects + _setValue_fromArray_setNeedsUpdate( buffer, offset ) { - _addInactiveBinding: function ( binding, rootUuid, trackName ) { + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], + } - bindings = this._bindings; + _setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { - if ( bindingByName === undefined ) { + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; - bindingByName = {}; - bindingsByRoot[ rootUuid ] = bindingByName; + } - } + _getValue_unbound( targetArray, offset ) { - bindingByName[ trackName ] = binding; + this.bind(); + this.getValue( targetArray, offset ); - binding._cacheIndex = bindings.length; - bindings.push( binding ); + } - }, + _setValue_unbound( sourceArray, offset ) { - _removeInactiveBinding: function ( binding ) { + this.bind(); + this.setValue( sourceArray, offset ); - var bindings = this._bindings, - propBinding = binding.binding, - rootUuid = propBinding.rootNode.uuid, - trackName = propBinding.path, - bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ], + } - lastInactiveBinding = bindings[ bindings.length - 1 ], - cacheIndex = binding._cacheIndex; + // create getter / setter pair for a property in the scene graph + bind() { - lastInactiveBinding._cacheIndex = cacheIndex; - bindings[ cacheIndex ] = lastInactiveBinding; - bindings.pop(); + let targetObject = this.node; + const parsedPath = this.parsedPath; - delete bindingByName[ trackName ]; + const objectName = parsedPath.objectName; + const propertyName = parsedPath.propertyName; + let propertyIndex = parsedPath.propertyIndex; - if ( Object.keys( bindingByName ).length === 0 ) { + if ( ! targetObject ) { - delete bindingsByRoot[ rootUuid ]; + targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ); - } + this.node = targetObject; - }, + } - _lendBinding: function ( binding ) { + // set fail state so we can just 'return' on error + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; - var bindings = this._bindings, - prevIndex = binding._cacheIndex, + // ensure there is a value node + if ( ! targetObject ) { - lastActiveIndex = this._nActiveBindings ++, + console.warn( 'THREE.PropertyBinding: No target node found for track: ' + this.path + '.' ); + return; - firstInactiveBinding = bindings[ lastActiveIndex ]; + } - binding._cacheIndex = lastActiveIndex; - bindings[ lastActiveIndex ] = binding; + if ( objectName ) { - firstInactiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = firstInactiveBinding; + let objectIndex = parsedPath.objectIndex; - }, + // special cases were we need to reach deeper into the hierarchy to get the face materials.... + switch ( objectName ) { - _takeBackBinding: function ( binding ) { + case 'materials': - var bindings = this._bindings, - prevIndex = binding._cacheIndex, + if ( ! targetObject.material ) { - firstInactiveIndex = -- this._nActiveBindings, + console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); + return; - lastActiveBinding = bindings[ firstInactiveIndex ]; + } - binding._cacheIndex = firstInactiveIndex; - bindings[ firstInactiveIndex ] = binding; + if ( ! targetObject.material.materials ) { - lastActiveBinding._cacheIndex = prevIndex; - bindings[ prevIndex ] = lastActiveBinding; + console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this ); + return; - }, + } + targetObject = targetObject.material.materials; - // Memory management of Interpolants for weight and time scale + break; - _lendControlInterpolant: function () { + case 'bones': - var interpolants = this._controlInterpolants, - lastActiveIndex = this._nActiveControlInterpolants ++, - interpolant = interpolants[ lastActiveIndex ]; + if ( ! targetObject.skeleton ) { - if ( interpolant === undefined ) { + console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this ); + return; - interpolant = new LinearInterpolant( - new Float32Array( 2 ), new Float32Array( 2 ), - 1, this._controlInterpolantsResultBuffer ); + } - interpolant.__cacheIndex = lastActiveIndex; - interpolants[ lastActiveIndex ] = interpolant; + // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. - } + targetObject = targetObject.skeleton.bones; - return interpolant; + // support resolving morphTarget names into indices. + for ( let i = 0; i < targetObject.length; i ++ ) { - }, + if ( targetObject[ i ].name === objectIndex ) { - _takeBackControlInterpolant: function ( interpolant ) { + objectIndex = i; + break; - var interpolants = this._controlInterpolants, - prevIndex = interpolant.__cacheIndex, + } - firstInactiveIndex = -- this._nActiveControlInterpolants, + } - lastActiveInterpolant = interpolants[ firstInactiveIndex ]; + break; - interpolant.__cacheIndex = firstInactiveIndex; - interpolants[ firstInactiveIndex ] = interpolant; + case 'map': - lastActiveInterpolant.__cacheIndex = prevIndex; - interpolants[ prevIndex ] = lastActiveInterpolant; + if ( 'map' in targetObject ) { - }, + targetObject = targetObject.map; + break; - _controlInterpolantsResultBuffer: new Float32Array( 1 ), + } - // return an action for a clip optionally using a custom root target - // object (this method allocates a lot of dynamic memory in case a - // previously unknown clip/root combination is specified) - clipAction: function ( clip, optionalRoot ) { + if ( ! targetObject.material ) { - var root = optionalRoot || this._root, - rootUuid = root.uuid, + console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this ); + return; - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, + } - clipUuid = clipObject !== null ? clipObject.uuid : clip, + if ( ! targetObject.material.map ) { - actionsForClip = this._actionsByClip[ clipUuid ], - prototypeAction = null; + console.error( 'THREE.PropertyBinding: Can not bind to material.map as node.material does not have a map.', this ); + return; - if ( actionsForClip !== undefined ) { + } - var existingAction = - actionsForClip.actionByRoot[ rootUuid ]; + targetObject = targetObject.material.map; + break; - if ( existingAction !== undefined ) { + default: - return existingAction; + if ( targetObject[ objectName ] === undefined ) { - } + console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this ); + return; - // we know the clip, so we don't have to parse all - // the bindings again but can just copy - prototypeAction = actionsForClip.knownActions[ 0 ]; + } - // also, take the clip from the prototype action - if ( clipObject === null ) - clipObject = prototypeAction._clip; + targetObject = targetObject[ objectName ]; } - // clip must be known when specified via string - if ( clipObject === null ) return null; - // allocate all resources required to run it - var newAction = new AnimationAction( this, clipObject, optionalRoot ); + if ( objectIndex !== undefined ) { - this._bindAction( newAction, prototypeAction ); + if ( targetObject[ objectIndex ] === undefined ) { - // and make the action known to the memory manager - this._addInactiveAction( newAction, clipUuid, rootUuid ); - - return newAction; - - }, + console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject ); + return; - // get an existing action - existingAction: function ( clip, optionalRoot ) { + } - var root = optionalRoot || this._root, - rootUuid = root.uuid, + targetObject = targetObject[ objectIndex ]; - clipObject = typeof clip === 'string' ? - AnimationClip.findByName( root, clip ) : clip, + } - clipUuid = clipObject ? clipObject.uuid : clip, + } - actionsForClip = this._actionsByClip[ clipUuid ]; + // resolve property + const nodeProperty = targetObject[ propertyName ]; - if ( actionsForClip !== undefined ) { + if ( nodeProperty === undefined ) { - return actionsForClip.actionByRoot[ rootUuid ] || null; + const nodeName = parsedPath.nodeName; - } + console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName + + '.' + propertyName + ' but it wasn\'t found.', targetObject ); + return; - return null; + } - }, + // determine versioning scheme + let versioning = this.Versioning.None; - // deactivates all previously scheduled actions - stopAllAction: function () { + this.targetObject = targetObject; - var actions = this._actions, - nActions = this._nActiveActions, - bindings = this._bindings, - nBindings = this._nActiveBindings; + if ( targetObject.needsUpdate !== undefined ) { // material - this._nActiveActions = 0; - this._nActiveBindings = 0; + versioning = this.Versioning.NeedsUpdate; - for ( var i = 0; i !== nActions; ++ i ) { + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - actions[ i ].reset(); + versioning = this.Versioning.MatrixWorldNeedsUpdate; - } + } - for ( var i = 0; i !== nBindings; ++ i ) { + // determine how the property gets bound + let bindingType = this.BindingType.Direct; - bindings[ i ].useCount = 0; + if ( propertyIndex !== undefined ) { - } + // access a sub element of the property array (only primitives are supported right now) - return this; + if ( propertyName === 'morphTargetInfluences' ) { - }, + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - // advance the time and update apply the animation - update: function ( deltaTime ) { + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { - deltaTime *= this.timeScale; + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this ); + return; - var actions = this._actions, - nActions = this._nActiveActions, + } - time = this.time += deltaTime, - timeDirection = Math.sign( deltaTime ), + if ( ! targetObject.geometry.morphAttributes ) { - accuIndex = this._accuIndex ^= 1; + console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this ); + return; - // run active actions + } - for ( var i = 0; i !== nActions; ++ i ) { + if ( targetObject.morphTargetDictionary[ propertyIndex ] !== undefined ) { - var action = actions[ i ]; + propertyIndex = targetObject.morphTargetDictionary[ propertyIndex ]; - action._update( time, deltaTime, timeDirection, accuIndex ); + } } - // update scene graph + bindingType = this.BindingType.ArrayElement; - var bindings = this._bindings, - nBindings = this._nActiveBindings; + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; - for ( var i = 0; i !== nBindings; ++ i ) { + } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { - bindings[ i ].apply( accuIndex ); + // must use copy for Object3D.Euler/Quaternion - } + bindingType = this.BindingType.HasFromToArray; - return this; + this.resolvedProperty = nodeProperty; - }, + } else if ( Array.isArray( nodeProperty ) ) { - // return this mixer's root target object - getRoot: function () { + bindingType = this.BindingType.EntireArray; - return this._root; + this.resolvedProperty = nodeProperty; - }, + } else { - // free all resources specific to a particular clip - uncacheClip: function ( clip ) { + this.propertyName = propertyName; - var actions = this._actions, - clipUuid = clip.uuid, - actionsByClip = this._actionsByClip, - actionsForClip = actionsByClip[ clipUuid ]; + } - if ( actionsForClip !== undefined ) { + // select getter / setter + this.getValue = this.GetterByBindingType[ bindingType ]; + this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; - // note: just calling _removeInactiveAction would mess up the - // iteration state and also require updating the state we can - // just throw away + } - var actionsToRemove = actionsForClip.knownActions; + unbind() { - for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { + this.node = null; - var action = actionsToRemove[ i ]; + // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - this._deactivateAction( action ); + } - var cacheIndex = action._cacheIndex, - lastInactiveAction = actions[ actions.length - 1 ]; +} - action._cacheIndex = null; - action._byClipCacheIndex = null; +PropertyBinding.Composite = Composite; - lastInactiveAction._cacheIndex = cacheIndex; - actions[ cacheIndex ] = lastInactiveAction; - actions.pop(); +PropertyBinding.prototype.BindingType = { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 +}; - this._removeInactiveBindingsForAction( action ); +PropertyBinding.prototype.Versioning = { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 +}; - } +PropertyBinding.prototype.GetterByBindingType = [ - delete actionsByClip[ clipUuid ]; + PropertyBinding.prototype._getValue_direct, + PropertyBinding.prototype._getValue_array, + PropertyBinding.prototype._getValue_arrayElement, + PropertyBinding.prototype._getValue_toArray, - } +]; - }, +PropertyBinding.prototype.SetterByBindingTypeAndVersioning = [ - // free all resources specific to a particular root target object - uncacheRoot: function ( root ) { + [ + // Direct + PropertyBinding.prototype._setValue_direct, + PropertyBinding.prototype._setValue_direct_setNeedsUpdate, + PropertyBinding.prototype._setValue_direct_setMatrixWorldNeedsUpdate, - var rootUuid = root.uuid, - actionsByClip = this._actionsByClip; + ], [ - for ( var clipUuid in actionsByClip ) { + // EntireArray - var actionByRoot = actionsByClip[ clipUuid ].actionByRoot, - action = actionByRoot[ rootUuid ]; + PropertyBinding.prototype._setValue_array, + PropertyBinding.prototype._setValue_array_setNeedsUpdate, + PropertyBinding.prototype._setValue_array_setMatrixWorldNeedsUpdate, - if ( action !== undefined ) { + ], [ - this._deactivateAction( action ); - this._removeInactiveAction( action ); + // ArrayElement + PropertyBinding.prototype._setValue_arrayElement, + PropertyBinding.prototype._setValue_arrayElement_setNeedsUpdate, + PropertyBinding.prototype._setValue_arrayElement_setMatrixWorldNeedsUpdate, - } + ], [ - } + // HasToFromArray + PropertyBinding.prototype._setValue_fromArray, + PropertyBinding.prototype._setValue_fromArray_setNeedsUpdate, + PropertyBinding.prototype._setValue_fromArray_setMatrixWorldNeedsUpdate, - var bindingsByRoot = this._bindingsByRootAndName, - bindingByName = bindingsByRoot[ rootUuid ]; + ] - if ( bindingByName !== undefined ) { +]; - for ( var trackName in bindingByName ) { +/** + * + * A group of objects that receives a shared animation state. + * + * Usage: + * + * - Add objects you would otherwise pass as 'root' to the + * constructor or the .clipAction method of AnimationMixer. + * + * - Instead pass this object as 'root'. + * + * - You can also add and remove objects later when the mixer + * is running. + * + * Note: + * + * Objects of this class appear as one object to the mixer, + * so cache control of the individual objects must be done + * on the group. + * + * Limitation: + * + * - The animated properties must be compatible among the + * all objects in the group. + * + * - A single property can either be controlled through a + * target group or directly, but not both. + */ - var binding = bindingByName[ trackName ]; - binding.restoreOriginalState(); - this._removeInactiveBinding( binding ); +class AnimationObjectGroup { - } + constructor() { - } + this.isAnimationObjectGroup = true; - }, + this.uuid = generateUUID(); - // remove a targeted clip from the cache - uncacheAction: function ( clip, optionalRoot ) { + // cached objects followed by the active ones + this._objects = Array.prototype.slice.call( arguments ); - var action = this.existingAction( clip, optionalRoot ); + this.nCachedObjects_ = 0; // threshold + // note: read by PropertyBinding.Composite - if ( action !== null ) { + const indices = {}; + this._indicesByUUID = indices; // for bookkeeping - this._deactivateAction( action ); - this._removeInactiveAction( action ); + for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - } + indices[ arguments[ i ].uuid ] = i; } - } ); + this._paths = []; // inside: string + this._parsedPaths = []; // inside: { we don't care, here } + this._bindings = []; // inside: Array< PropertyBinding > + this._bindingsIndicesByPath = {}; // inside: indices in these arrays - /** - * @author mrdoob / http://mrdoob.com/ - */ + const scope = this; - function Uniform( value ) { + this.stats = { - if ( typeof value === 'string' ) { + objects: { + get total() { - console.warn( 'THREE.Uniform: Type parameter is no longer needed.' ); - value = arguments[ 1 ]; + return scope._objects.length; - } + }, + get inUse() { - this.value = value; + return this.total - scope.nCachedObjects_; - } + } + }, + get bindingsPerObject() { - Uniform.prototype.clone = function () { + return scope._bindings.length; - return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); + } - }; + }; - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ + } - function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) { + add() { - InterleavedBuffer.call( this, array, stride ); + const objects = this._objects, + indicesByUUID = this._indicesByUUID, + paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + nBindings = bindings.length; - this.meshPerAttribute = meshPerAttribute || 1; + let knownObject = undefined, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_; - } + for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), { + const object = arguments[ i ], + uuid = object.uuid; + let index = indicesByUUID[ uuid ]; - constructor: InstancedInterleavedBuffer, + if ( index === undefined ) { - isInstancedInterleavedBuffer: true, + // unknown object -> add it to the ACTIVE region - copy: function ( source ) { + index = nObjects ++; + indicesByUUID[ uuid ] = index; + objects.push( object ); - InterleavedBuffer.prototype.copy.call( this, source ); + // accounting is done, now do the same for all bindings - this.meshPerAttribute = source.meshPerAttribute; + for ( let j = 0, m = nBindings; j !== m; ++ j ) { - return this; + bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) ); - } + } - } ); + } else if ( index < nCachedObjects ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author bhouston / http://clara.io/ - * @author stephomi / http://stephaneginier.com/ - */ + knownObject = objects[ index ]; - function Raycaster( origin, direction, near, far ) { + // move existing object to the ACTIVE region - this.ray = new Ray( origin, direction ); - // direction is assumed to be normalized (for accurate distance calculations) + const firstActiveIndex = -- nCachedObjects, + lastCachedObject = objects[ firstActiveIndex ]; - this.near = near || 0; - this.far = far || Infinity; - this.camera = null; + indicesByUUID[ lastCachedObject.uuid ] = index; + objects[ index ] = lastCachedObject; - this.params = { - Mesh: {}, - Line: {}, - LOD: {}, - Points: { threshold: 1 }, - Sprite: {} - }; + indicesByUUID[ uuid ] = firstActiveIndex; + objects[ firstActiveIndex ] = object; - Object.defineProperties( this.params, { - PointCloud: { - get: function () { + // accounting is done, now do the same for all bindings - console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); - return this.Points; + for ( let j = 0, m = nBindings; j !== m; ++ j ) { - } - } - } ); + const bindingsForPath = bindings[ j ], + lastCached = bindingsForPath[ firstActiveIndex ]; - } + let binding = bindingsForPath[ index ]; - function ascSort( a, b ) { + bindingsForPath[ index ] = lastCached; - return a.distance - b.distance; + if ( binding === undefined ) { - } + // since we do not bother to create new bindings + // for objects that are cached, the binding may + // or may not exist - function intersectObject( object, raycaster, intersects, recursive ) { + binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ); - if ( object.visible === false ) return; + } - object.raycast( raycaster, intersects ); + bindingsForPath[ firstActiveIndex ] = binding; - if ( recursive === true ) { + } - var children = object.children; + } else if ( objects[ index ] !== knownObject ) { - for ( var i = 0, l = children.length; i < l; i ++ ) { + console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' + + 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' ); - intersectObject( children[ i ], raycaster, intersects, true ); + } // else the object is already where we want it to be - } + } // for arguments - } + this.nCachedObjects_ = nCachedObjects; } - Object.assign( Raycaster.prototype, { + remove() { - linePrecision: 1, + const objects = this._objects, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; - set: function ( origin, direction ) { + let nCachedObjects = this.nCachedObjects_; - // direction is assumed to be normalized (for accurate distance calculations) + for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - this.ray.set( origin, direction ); - - }, + const object = arguments[ i ], + uuid = object.uuid, + index = indicesByUUID[ uuid ]; - setFromCamera: function ( coords, camera ) { + if ( index !== undefined && index >= nCachedObjects ) { - if ( ( camera && camera.isPerspectiveCamera ) ) { + // move existing object into the CACHED region - this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); - this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); - this.camera = camera; + const lastCachedIndex = nCachedObjects ++, + firstActiveObject = objects[ lastCachedIndex ]; - } else if ( ( camera && camera.isOrthographicCamera ) ) { + indicesByUUID[ firstActiveObject.uuid ] = index; + objects[ index ] = firstActiveObject; - this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera - this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); - this.camera = camera; + indicesByUUID[ uuid ] = lastCachedIndex; + objects[ lastCachedIndex ] = object; - } else { + // accounting is done, now do the same for all bindings - console.error( 'THREE.Raycaster: Unsupported camera type.' ); + for ( let j = 0, m = nBindings; j !== m; ++ j ) { - } + const bindingsForPath = bindings[ j ], + firstActive = bindingsForPath[ lastCachedIndex ], + binding = bindingsForPath[ index ]; - }, + bindingsForPath[ index ] = firstActive; + bindingsForPath[ lastCachedIndex ] = binding; - intersectObject: function ( object, recursive, optionalTarget ) { + } - var intersects = optionalTarget || []; + } - intersectObject( object, this, intersects, recursive ); + } // for arguments - intersects.sort( ascSort ); + this.nCachedObjects_ = nCachedObjects; - return intersects; + } - }, + // remove & forget + uncache() { - intersectObjects: function ( objects, recursive, optionalTarget ) { + const objects = this._objects, + indicesByUUID = this._indicesByUUID, + bindings = this._bindings, + nBindings = bindings.length; - var intersects = optionalTarget || []; + let nCachedObjects = this.nCachedObjects_, + nObjects = objects.length; - if ( Array.isArray( objects ) === false ) { + for ( let i = 0, n = arguments.length; i !== n; ++ i ) { - console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); - return intersects; + const object = arguments[ i ], + uuid = object.uuid, + index = indicesByUUID[ uuid ]; - } + if ( index !== undefined ) { - for ( var i = 0, l = objects.length; i < l; i ++ ) { + delete indicesByUUID[ uuid ]; - intersectObject( objects[ i ], this, intersects, recursive ); + if ( index < nCachedObjects ) { - } + // object is cached, shrink the CACHED region - intersects.sort( ascSort ); + const firstActiveIndex = -- nCachedObjects, + lastCachedObject = objects[ firstActiveIndex ], + lastIndex = -- nObjects, + lastObject = objects[ lastIndex ]; - return intersects; + // last cached object takes this object's place + indicesByUUID[ lastCachedObject.uuid ] = index; + objects[ index ] = lastCachedObject; - } + // last object goes to the activated slot and pop + indicesByUUID[ lastObject.uuid ] = firstActiveIndex; + objects[ firstActiveIndex ] = lastObject; + objects.pop(); - } ); + // accounting is done, now do the same for all bindings - /** - * @author bhouston / http://clara.io - * @author WestLangley / http://github.com/WestLangley - * - * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system - * - * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. - * The azimuthal angle (theta) is measured from the positive z-axiz. - */ + for ( let j = 0, m = nBindings; j !== m; ++ j ) { - function Spherical( radius, phi, theta ) { + const bindingsForPath = bindings[ j ], + lastCached = bindingsForPath[ firstActiveIndex ], + last = bindingsForPath[ lastIndex ]; - this.radius = ( radius !== undefined ) ? radius : 1.0; - this.phi = ( phi !== undefined ) ? phi : 0; // polar angle - this.theta = ( theta !== undefined ) ? theta : 0; // azimuthal angle + bindingsForPath[ index ] = lastCached; + bindingsForPath[ firstActiveIndex ] = last; + bindingsForPath.pop(); - return this; + } - } + } else { - Object.assign( Spherical.prototype, { + // object is active, just swap with the last and pop - set: function ( radius, phi, theta ) { + const lastIndex = -- nObjects, + lastObject = objects[ lastIndex ]; - this.radius = radius; - this.phi = phi; - this.theta = theta; + if ( lastIndex > 0 ) { - return this; + indicesByUUID[ lastObject.uuid ] = index; - }, + } - clone: function () { + objects[ index ] = lastObject; + objects.pop(); - return new this.constructor().copy( this ); + // accounting is done, now do the same for all bindings - }, + for ( let j = 0, m = nBindings; j !== m; ++ j ) { - copy: function ( other ) { + const bindingsForPath = bindings[ j ]; - this.radius = other.radius; - this.phi = other.phi; - this.theta = other.theta; + bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; + bindingsForPath.pop(); - return this; + } - }, + } // cached or active - // restrict phi to be betwee EPS and PI-EPS - makeSafe: function () { + } // if object is known - var EPS = 0.000001; - this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); + } // for arguments - return this; + this.nCachedObjects_ = nCachedObjects; - }, + } - setFromVector3: function ( v ) { + // Internal interface used by befriended PropertyBinding.Composite: - return this.setFromCartesianCoords( v.x, v.y, v.z ); + subscribe_( path, parsedPath ) { - }, + // returns an array of bindings for the given path that is changed + // according to the contained objects in the group - setFromCartesianCoords: function ( x, y, z ) { + const indicesByPath = this._bindingsIndicesByPath; + let index = indicesByPath[ path ]; + const bindings = this._bindings; - this.radius = Math.sqrt( x * x + y * y + z * z ); + if ( index !== undefined ) return bindings[ index ]; - if ( this.radius === 0 ) { + const paths = this._paths, + parsedPaths = this._parsedPaths, + objects = this._objects, + nObjects = objects.length, + nCachedObjects = this.nCachedObjects_, + bindingsForPath = new Array( nObjects ); - this.theta = 0; - this.phi = 0; + index = bindings.length; - } else { + indicesByPath[ path ] = index; - this.theta = Math.atan2( x, z ); - this.phi = Math.acos( _Math.clamp( y / this.radius, - 1, 1 ) ); + paths.push( path ); + parsedPaths.push( parsedPath ); + bindings.push( bindingsForPath ); - } + for ( let i = nCachedObjects, n = objects.length; i !== n; ++ i ) { - return this; + const object = objects[ i ]; + bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath ); } - } ); - - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system - * - */ - - function Cylindrical( radius, theta, y ) { - - this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane - this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis - this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane - - return this; + return bindingsForPath; } - Object.assign( Cylindrical.prototype, { + unsubscribe_( path ) { - set: function ( radius, theta, y ) { + // tells the group to forget about a property path and no longer + // update the array previously obtained with 'subscribe_' - this.radius = radius; - this.theta = theta; - this.y = y; + const indicesByPath = this._bindingsIndicesByPath, + index = indicesByPath[ path ]; - return this; + if ( index !== undefined ) { - }, + const paths = this._paths, + parsedPaths = this._parsedPaths, + bindings = this._bindings, + lastBindingsIndex = bindings.length - 1, + lastBindings = bindings[ lastBindingsIndex ], + lastBindingsPath = path[ lastBindingsIndex ]; + + indicesByPath[ lastBindingsPath ] = index; - clone: function () { + bindings[ index ] = lastBindings; + bindings.pop(); - return new this.constructor().copy( this ); + parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; + parsedPaths.pop(); - }, + paths[ index ] = paths[ lastBindingsIndex ]; + paths.pop(); - copy: function ( other ) { + } - this.radius = other.radius; - this.theta = other.theta; - this.y = other.y; + } - return this; +} - }, +class AnimationAction { - setFromVector3: function ( v ) { + constructor( mixer, clip, localRoot = null, blendMode = clip.blendMode ) { - return this.setFromCartesianCoords( v.x, v.y, v.z ); + this._mixer = mixer; + this._clip = clip; + this._localRoot = localRoot; + this.blendMode = blendMode; - }, + const tracks = clip.tracks, + nTracks = tracks.length, + interpolants = new Array( nTracks ); - setFromCartesianCoords: function ( x, y, z ) { + const interpolantSettings = { + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding + }; - this.radius = Math.sqrt( x * x + z * z ); - this.theta = Math.atan2( x, z ); - this.y = y; + for ( let i = 0; i !== nTracks; ++ i ) { - return this; + const interpolant = tracks[ i ].createInterpolant( null ); + interpolants[ i ] = interpolant; + interpolant.settings = interpolantSettings; } - } ); + this._interpolantSettings = interpolantSettings; - /** - * @author bhouston / http://clara.io - */ + this._interpolants = interpolants; // bound by the mixer - var _vector$6 = new Vector2(); + // inside: PropertyMixer (managed by the mixer) + this._propertyBindings = new Array( nTracks ); - function Box2( min, max ) { + this._cacheIndex = null; // for the memory manager + this._byClipCacheIndex = null; // for the memory manager - this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity ); - this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity ); + this._timeScaleInterpolant = null; + this._weightInterpolant = null; - } + this.loop = LoopRepeat; + this._loopCount = - 1; - Object.assign( Box2.prototype, { + // global mixer time when the action is to be started + // it's set back to 'null' upon start of the action + this._startTime = null; - set: function ( min, max ) { + // scaled local time of the action + // gets clamped or wrapped to 0..clip.duration according to loop + this.time = 0; - this.min.copy( min ); - this.max.copy( max ); + this.timeScale = 1; + this._effectiveTimeScale = 1; - return this; + this.weight = 1; + this._effectiveWeight = 1; - }, + this.repetitions = Infinity; // no. of repetitions when looping - setFromPoints: function ( points ) { + this.paused = false; // true -> zero effective time scale + this.enabled = true; // false -> zero effective weight - this.makeEmpty(); + this.clampWhenFinished = false;// keep feeding the last frame? - for ( var i = 0, il = points.length; i < il; i ++ ) { + this.zeroSlopeAtStart = true;// for smooth interpolation w/o separate + this.zeroSlopeAtEnd = true;// clips for start, loop and end - this.expandByPoint( points[ i ] ); + } - } + // State & Scheduling - return this; + play() { - }, + this._mixer._activateAction( this ); - setFromCenterAndSize: function ( center, size ) { + return this; - var halfSize = _vector$6.copy( size ).multiplyScalar( 0.5 ); - this.min.copy( center ).sub( halfSize ); - this.max.copy( center ).add( halfSize ); + } - return this; + stop() { - }, + this._mixer._deactivateAction( this ); - clone: function () { + return this.reset(); - return new this.constructor().copy( this ); + } - }, + reset() { - copy: function ( box ) { + this.paused = false; + this.enabled = true; - this.min.copy( box.min ); - this.max.copy( box.max ); + this.time = 0; // restart clip + this._loopCount = - 1;// forget previous loops + this._startTime = null;// forget scheduling - return this; + return this.stopFading().stopWarping(); - }, + } - makeEmpty: function () { + isRunning() { - this.min.x = this.min.y = + Infinity; - this.max.x = this.max.y = - Infinity; + return this.enabled && ! this.paused && this.timeScale !== 0 && + this._startTime === null && this._mixer._isActiveAction( this ); - return this; + } - }, + // return true when play has been called + isScheduled() { - isEmpty: function () { + return this._mixer._isActiveAction( this ); - // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes + } - return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); + startAt( time ) { - }, + this._startTime = time; - getCenter: function ( target ) { + return this; - if ( target === undefined ) { + } - console.warn( 'THREE.Box2: .getCenter() target is now required' ); - target = new Vector2(); + setLoop( mode, repetitions ) { - } + this.loop = mode; + this.repetitions = repetitions; - return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); + return this; - }, + } - getSize: function ( target ) { + // Weight - if ( target === undefined ) { + // set the weight stopping any scheduled fading + // although .enabled = false yields an effective weight of zero, this + // method does *not* change .enabled, because it would be confusing + setEffectiveWeight( weight ) { - console.warn( 'THREE.Box2: .getSize() target is now required' ); - target = new Vector2(); + this.weight = weight; - } + // note: same logic as when updated at runtime + this._effectiveWeight = this.enabled ? weight : 0; - return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); + return this.stopFading(); - }, + } - expandByPoint: function ( point ) { + // return the weight considering fading and .enabled + getEffectiveWeight() { - this.min.min( point ); - this.max.max( point ); + return this._effectiveWeight; - return this; + } - }, + fadeIn( duration ) { - expandByVector: function ( vector ) { + return this._scheduleFading( duration, 0, 1 ); - this.min.sub( vector ); - this.max.add( vector ); + } - return this; + fadeOut( duration ) { - }, + return this._scheduleFading( duration, 1, 0 ); - expandByScalar: function ( scalar ) { + } - this.min.addScalar( - scalar ); - this.max.addScalar( scalar ); + crossFadeFrom( fadeOutAction, duration, warp ) { - return this; + fadeOutAction.fadeOut( duration ); + this.fadeIn( duration ); - }, + if ( warp ) { - containsPoint: function ( point ) { + const fadeInDuration = this._clip.duration, + fadeOutDuration = fadeOutAction._clip.duration, - return point.x < this.min.x || point.x > this.max.x || - point.y < this.min.y || point.y > this.max.y ? false : true; + startEndRatio = fadeOutDuration / fadeInDuration, + endStartRatio = fadeInDuration / fadeOutDuration; - }, + fadeOutAction.warp( 1.0, startEndRatio, duration ); + this.warp( endStartRatio, 1.0, duration ); - containsBox: function ( box ) { + } - return this.min.x <= box.min.x && box.max.x <= this.max.x && - this.min.y <= box.min.y && box.max.y <= this.max.y; + return this; - }, + } - getParameter: function ( point, target ) { + crossFadeTo( fadeInAction, duration, warp ) { - // This can potentially have a divide by zero if the box - // has a size dimension of 0. + return fadeInAction.crossFadeFrom( this, duration, warp ); - if ( target === undefined ) { + } - console.warn( 'THREE.Box2: .getParameter() target is now required' ); - target = new Vector2(); + stopFading() { - } + const weightInterpolant = this._weightInterpolant; - return target.set( - ( point.x - this.min.x ) / ( this.max.x - this.min.x ), - ( point.y - this.min.y ) / ( this.max.y - this.min.y ) - ); + if ( weightInterpolant !== null ) { - }, + this._weightInterpolant = null; + this._mixer._takeBackControlInterpolant( weightInterpolant ); - intersectsBox: function ( box ) { + } - // using 4 splitting planes to rule out intersections + return this; - return box.max.x < this.min.x || box.min.x > this.max.x || - box.max.y < this.min.y || box.min.y > this.max.y ? false : true; + } - }, + // Time Scale Control - clampPoint: function ( point, target ) { + // set the time scale stopping any scheduled warping + // although .paused = true yields an effective time scale of zero, this + // method does *not* change .paused, because it would be confusing + setEffectiveTimeScale( timeScale ) { - if ( target === undefined ) { + this.timeScale = timeScale; + this._effectiveTimeScale = this.paused ? 0 : timeScale; - console.warn( 'THREE.Box2: .clampPoint() target is now required' ); - target = new Vector2(); + return this.stopWarping(); - } + } - return target.copy( point ).clamp( this.min, this.max ); + // return the time scale considering warping and .paused + getEffectiveTimeScale() { - }, + return this._effectiveTimeScale; - distanceToPoint: function ( point ) { + } - var clampedPoint = _vector$6.copy( point ).clamp( this.min, this.max ); - return clampedPoint.sub( point ).length(); + setDuration( duration ) { - }, + this.timeScale = this._clip.duration / duration; - intersect: function ( box ) { + return this.stopWarping(); - this.min.max( box.min ); - this.max.min( box.max ); + } - return this; + syncWith( action ) { - }, + this.time = action.time; + this.timeScale = action.timeScale; - union: function ( box ) { + return this.stopWarping(); - this.min.min( box.min ); - this.max.max( box.max ); + } - return this; + halt( duration ) { - }, + return this.warp( this._effectiveTimeScale, 0, duration ); - translate: function ( offset ) { + } - this.min.add( offset ); - this.max.add( offset ); + warp( startTimeScale, endTimeScale, duration ) { - return this; + const mixer = this._mixer, + now = mixer.time, + timeScale = this.timeScale; - }, + let interpolant = this._timeScaleInterpolant; - equals: function ( box ) { + if ( interpolant === null ) { - return box.min.equals( this.min ) && box.max.equals( this.max ); + interpolant = mixer._lendControlInterpolant(); + this._timeScaleInterpolant = interpolant; } - } ); - - /** - * @author bhouston / http://clara.io - */ + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; - var _startP = new Vector3(); - var _startEnd = new Vector3(); + times[ 0 ] = now; + times[ 1 ] = now + duration; - function Line3( start, end ) { + values[ 0 ] = startTimeScale / timeScale; + values[ 1 ] = endTimeScale / timeScale; - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); + return this; } - Object.assign( Line3.prototype, { + stopWarping() { - set: function ( start, end ) { + const timeScaleInterpolant = this._timeScaleInterpolant; - this.start.copy( start ); - this.end.copy( end ); - - return this; + if ( timeScaleInterpolant !== null ) { - }, + this._timeScaleInterpolant = null; + this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); - clone: function () { + } - return new this.constructor().copy( this ); + return this; - }, + } - copy: function ( line ) { + // Object Accessors - this.start.copy( line.start ); - this.end.copy( line.end ); + getMixer() { - return this; + return this._mixer; - }, + } - getCenter: function ( target ) { + getClip() { - if ( target === undefined ) { + return this._clip; - console.warn( 'THREE.Line3: .getCenter() target is now required' ); - target = new Vector3(); + } - } + getRoot() { - return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + return this._localRoot || this._mixer._root; - }, + } - delta: function ( target ) { + // Interna - if ( target === undefined ) { + _update( time, deltaTime, timeDirection, accuIndex ) { - console.warn( 'THREE.Line3: .delta() target is now required' ); - target = new Vector3(); + // called by the mixer - } + if ( ! this.enabled ) { - return target.subVectors( this.end, this.start ); + // call ._updateWeight() to update ._effectiveWeight - }, + this._updateWeight( time ); + return; - distanceSq: function () { + } - return this.start.distanceToSquared( this.end ); + const startTime = this._startTime; - }, + if ( startTime !== null ) { - distance: function () { + // check for scheduled start of action - return this.start.distanceTo( this.end ); + const timeRunning = ( time - startTime ) * timeDirection; + if ( timeRunning < 0 || timeDirection === 0 ) { - }, + deltaTime = 0; - at: function ( t, target ) { + } else { - if ( target === undefined ) { - console.warn( 'THREE.Line3: .at() target is now required' ); - target = new Vector3(); + this._startTime = null; // unschedule + deltaTime = timeDirection * timeRunning; } - return this.delta( target ).multiplyScalar( t ).add( this.start ); + } - }, + // apply time scale and advance time - closestPointToPointParameter: function ( point, clampToLine ) { + deltaTime *= this._updateTimeScale( time ); + const clipTime = this._updateTime( deltaTime ); - _startP.subVectors( point, this.start ); - _startEnd.subVectors( this.end, this.start ); + // note: _updateTime may disable the action resulting in + // an effective weight of 0 - var startEnd2 = _startEnd.dot( _startEnd ); - var startEnd_startP = _startEnd.dot( _startP ); + const weight = this._updateWeight( time ); - var t = startEnd_startP / startEnd2; + if ( weight > 0 ) { - if ( clampToLine ) { + const interpolants = this._interpolants; + const propertyMixers = this._propertyBindings; - t = _Math.clamp( t, 0, 1 ); + switch ( this.blendMode ) { - } + case AdditiveAnimationBlendMode: - return t; + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - }, + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulateAdditive( weight ); - closestPointToPoint: function ( point, clampToLine, target ) { + } - var t = this.closestPointToPointParameter( point, clampToLine ); + break; - if ( target === undefined ) { + case NormalAnimationBlendMode: + default: - console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' ); - target = new Vector3(); + for ( let j = 0, m = interpolants.length; j !== m; ++ j ) { - } + interpolants[ j ].evaluate( clipTime ); + propertyMixers[ j ].accumulate( accuIndex, weight ); - return this.delta( target ).multiplyScalar( t ).add( this.start ); + } - }, + } - applyMatrix4: function ( matrix ) { + } - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); + } - return this; + _updateWeight( time ) { - }, + let weight = 0; - equals: function ( line ) { + if ( this.enabled ) { - return line.start.equals( this.start ) && line.end.equals( this.end ); + weight = this.weight; + const interpolant = this._weightInterpolant; - } + if ( interpolant !== null ) { - } ); + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - /** - * @author alteredq / http://alteredqualia.com/ - */ + weight *= interpolantValue; - function ImmediateRenderObject( material ) { + if ( time > interpolant.parameterPositions[ 1 ] ) { - Object3D.call( this ); + this.stopFading(); - this.material = material; - this.render = function ( /* renderCallback */ ) {}; + if ( interpolantValue === 0 ) { - } + // faded out, disable + this.enabled = false; - ImmediateRenderObject.prototype = Object.create( Object3D.prototype ); - ImmediateRenderObject.prototype.constructor = ImmediateRenderObject; + } - ImmediateRenderObject.prototype.isImmediateRenderObject = true; + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ + } - var _v1$5 = new Vector3(); - var _v2$3 = new Vector3(); - var _normalMatrix$1 = new Matrix3(); - var _keys = [ 'a', 'b', 'c' ]; + } - function VertexNormalsHelper( object, size, hex, linewidth ) { + this._effectiveWeight = weight; + return weight; - this.object = object; + } - this.size = ( size !== undefined ) ? size : 1; + _updateTimeScale( time ) { - var color = ( hex !== undefined ) ? hex : 0xff0000; + let timeScale = 0; - var width = ( linewidth !== undefined ) ? linewidth : 1; + if ( ! this.paused ) { - // + timeScale = this.timeScale; - var nNormals = 0; + const interpolant = this._timeScaleInterpolant; - var objGeometry = this.object.geometry; + if ( interpolant !== null ) { - if ( objGeometry && objGeometry.isGeometry ) { + const interpolantValue = interpolant.evaluate( time )[ 0 ]; - nNormals = objGeometry.faces.length * 3; + timeScale *= interpolantValue; - } else if ( objGeometry && objGeometry.isBufferGeometry ) { + if ( time > interpolant.parameterPositions[ 1 ] ) { - nNormals = objGeometry.attributes.normal.count; + this.stopWarping(); - } + if ( timeScale === 0 ) { - // + // motion has halted, pause + this.paused = true; - var geometry = new BufferGeometry(); + } else { - var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); + // warp done - apply final time scale + this.timeScale = timeScale; - geometry.addAttribute( 'position', positions ); + } - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); + } - // + } - this.matrixAutoUpdate = false; + } - this.update(); + this._effectiveTimeScale = timeScale; + return timeScale; } - VertexNormalsHelper.prototype = Object.create( LineSegments.prototype ); - VertexNormalsHelper.prototype.constructor = VertexNormalsHelper; + _updateTime( deltaTime ) { - VertexNormalsHelper.prototype.update = function () { + const duration = this._clip.duration; + const loop = this.loop; - this.object.updateMatrixWorld( true ); + let time = this.time + deltaTime; + let loopCount = this._loopCount; - _normalMatrix$1.getNormalMatrix( this.object.matrixWorld ); + const pingPong = ( loop === LoopPingPong ); - var matrixWorld = this.object.matrixWorld; + if ( deltaTime === 0 ) { - var position = this.geometry.attributes.position; + if ( loopCount === - 1 ) return time; - // + return ( pingPong && ( loopCount & 1 ) === 1 ) ? duration - time : time; + + } - var objGeometry = this.object.geometry; + if ( loop === LoopOnce ) { - if ( objGeometry && objGeometry.isGeometry ) { + if ( loopCount === - 1 ) { - var vertices = objGeometry.vertices; + // just started - var faces = objGeometry.faces; + this._loopCount = 0; + this._setEndings( true, true, false ); - var idx = 0; + } - for ( var i = 0, l = faces.length; i < l; i ++ ) { + handle_stop: { - var face = faces[ i ]; + if ( time >= duration ) { - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + time = duration; - var vertex = vertices[ face[ _keys[ j ] ] ]; + } else if ( time < 0 ) { - var normal = face.vertexNormals[ j ]; + time = 0; - _v1$5.copy( vertex ).applyMatrix4( matrixWorld ); + } else { - _v2$3.copy( normal ).applyMatrix3( _normalMatrix$1 ).normalize().multiplyScalar( this.size ).add( _v1$5 ); + this.time = time; - position.setXYZ( idx, _v1$5.x, _v1$5.y, _v1$5.z ); + break handle_stop; - idx = idx + 1; + } - position.setXYZ( idx, _v2$3.x, _v2$3.y, _v2$3.z ); + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; - idx = idx + 1; + this.time = time; - } + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime < 0 ? - 1 : 1 + } ); } - } else if ( objGeometry && objGeometry.isBufferGeometry ) { + } else { // repetitive Repeat or PingPong - var objPos = objGeometry.attributes.position; + if ( loopCount === - 1 ) { - var objNorm = objGeometry.attributes.normal; + // just started - var idx = 0; + if ( deltaTime >= 0 ) { - // for simplicity, ignore index and drawcalls, and render every normal + loopCount = 0; - for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { + this._setEndings( true, this.repetitions === 0, pingPong ); - _v1$5.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); + } else { - _v2$3.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); + // when looping in reverse direction, the initial + // transition through zero counts as a repetition, + // so leave loopCount at -1 - _v2$3.applyMatrix3( _normalMatrix$1 ).normalize().multiplyScalar( this.size ).add( _v1$5 ); + this._setEndings( this.repetitions === 0, true, pingPong ); - position.setXYZ( idx, _v1$5.x, _v1$5.y, _v1$5.z ); + } - idx = idx + 1; + } - position.setXYZ( idx, _v2$3.x, _v2$3.y, _v2$3.z ); + if ( time >= duration || time < 0 ) { - idx = idx + 1; + // wrap around - } + const loopDelta = Math.floor( time / duration ); // signed + time -= duration * loopDelta; - } + loopCount += Math.abs( loopDelta ); - position.needsUpdate = true; + const pending = this.repetitions - loopCount; - }; + if ( pending <= 0 ) { - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ + // have to stop (switch state, clamp time, fire event) - var _vector$7 = new Vector3(); + if ( this.clampWhenFinished ) this.paused = true; + else this.enabled = false; - function SpotLightHelper( light, color ) { + time = deltaTime > 0 ? duration : 0; - Object3D.call( this ); + this.time = time; - this.light = light; - this.light.updateMatrixWorld(); + this._mixer.dispatchEvent( { + type: 'finished', action: this, + direction: deltaTime > 0 ? 1 : - 1 + } ); - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; + } else { - this.color = color; + // keep running - var geometry = new BufferGeometry(); + if ( pending === 1 ) { - var positions = [ - 0, 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, 1, - 0, 0, 0, - 1, 0, 1, - 0, 0, 0, 0, 1, 1, - 0, 0, 0, 0, - 1, 1 - ]; + // entering the last round - for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { + const atStart = deltaTime < 0; + this._setEndings( atStart, ! atStart, pingPong ); - var p1 = ( i / l ) * Math.PI * 2; - var p2 = ( j / l ) * Math.PI * 2; + } else { - positions.push( - Math.cos( p1 ), Math.sin( p1 ), 1, - Math.cos( p2 ), Math.sin( p2 ), 1 - ); + this._setEndings( false, false, pingPong ); - } + } + + this._loopCount = loopCount; - geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + this.time = time; - var material = new LineBasicMaterial( { fog: false } ); + this._mixer.dispatchEvent( { + type: 'loop', action: this, loopDelta: loopDelta + } ); - this.cone = new LineSegments( geometry, material ); - this.add( this.cone ); + } - this.update(); + } else { - } + this.time = time; - SpotLightHelper.prototype = Object.create( Object3D.prototype ); - SpotLightHelper.prototype.constructor = SpotLightHelper; + } - SpotLightHelper.prototype.dispose = function () { + if ( pingPong && ( loopCount & 1 ) === 1 ) { - this.cone.geometry.dispose(); - this.cone.material.dispose(); + // invert time for the "pong round" - }; + return duration - time; - SpotLightHelper.prototype.update = function () { + } - this.light.updateMatrixWorld(); + } - var coneLength = this.light.distance ? this.light.distance : 1000; - var coneWidth = coneLength * Math.tan( this.light.angle ); + return time; - this.cone.scale.set( coneWidth, coneWidth, coneLength ); + } - _vector$7.setFromMatrixPosition( this.light.target.matrixWorld ); + _setEndings( atStart, atEnd, pingPong ) { - this.cone.lookAt( _vector$7 ); + const settings = this._interpolantSettings; - if ( this.color !== undefined ) { + if ( pingPong ) { - this.cone.material.color.set( this.color ); + settings.endingStart = ZeroSlopeEnding; + settings.endingEnd = ZeroSlopeEnding; } else { - this.cone.material.color.copy( this.light.color ); - - } + // assuming for LoopOnce atStart == atEnd == true - }; + if ( atStart ) { - /** - * @author Sean Griffin / http://twitter.com/sgrif - * @author Michael Guerrero / http://realitymeltdown.com - * @author mrdoob / http://mrdoob.com/ - * @author ikerr / http://verold.com - * @author Mugen87 / https://github.com/Mugen87 - */ + settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding; - var _vector$8 = new Vector3(); - var _boneMatrix = new Matrix4(); - var _matrixWorldInv = new Matrix4(); + } else { - function getBoneList( object ) { + settings.endingStart = WrapAroundEnding; - var boneList = []; + } - if ( object && object.isBone ) { + if ( atEnd ) { - boneList.push( object ); + settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding; - } + } else { - for ( var i = 0; i < object.children.length; i ++ ) { + settings.endingEnd = WrapAroundEnding; - boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); + } } - return boneList; - } - function SkeletonHelper( object ) { + _scheduleFading( duration, weightNow, weightThen ) { - var bones = getBoneList( object ); + const mixer = this._mixer, now = mixer.time; + let interpolant = this._weightInterpolant; - var geometry = new BufferGeometry(); + if ( interpolant === null ) { - var vertices = []; - var colors = []; + interpolant = mixer._lendControlInterpolant(); + this._weightInterpolant = interpolant; - var color1 = new Color( 0, 0, 1 ); - var color2 = new Color( 0, 1, 0 ); + } - for ( var i = 0; i < bones.length; i ++ ) { + const times = interpolant.parameterPositions, + values = interpolant.sampleValues; - var bone = bones[ i ]; + times[ 0 ] = now; + values[ 0 ] = weightNow; + times[ 1 ] = now + duration; + values[ 1 ] = weightThen; - if ( bone.parent && bone.parent.isBone ) { + return this; - vertices.push( 0, 0, 0 ); - vertices.push( 0, 0, 0 ); - colors.push( color1.r, color1.g, color1.b ); - colors.push( color2.r, color2.g, color2.b ); + } - } +} - } +const _controlInterpolantsResultBuffer = new Float32Array( 1 ); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } ); +class AnimationMixer extends EventDispatcher { - LineSegments.call( this, geometry, material ); + constructor( root ) { - this.root = object; - this.bones = bones; + super(); - this.matrix = object.matrixWorld; - this.matrixAutoUpdate = false; + this._root = root; + this._initMemoryManager(); + this._accuIndex = 0; + this.time = 0; + this.timeScale = 1.0; } - SkeletonHelper.prototype = Object.create( LineSegments.prototype ); - SkeletonHelper.prototype.constructor = SkeletonHelper; + _bindAction( action, prototypeAction ) { - SkeletonHelper.prototype.updateMatrixWorld = function ( force ) { + const root = action._localRoot || this._root, + tracks = action._clip.tracks, + nTracks = tracks.length, + bindings = action._propertyBindings, + interpolants = action._interpolants, + rootUuid = root.uuid, + bindingsByRoot = this._bindingsByRootAndName; - var bones = this.bones; + let bindingsByName = bindingsByRoot[ rootUuid ]; - var geometry = this.geometry; - var position = geometry.getAttribute( 'position' ); + if ( bindingsByName === undefined ) { - _matrixWorldInv.getInverse( this.root.matrixWorld ); + bindingsByName = {}; + bindingsByRoot[ rootUuid ] = bindingsByName; - for ( var i = 0, j = 0; i < bones.length; i ++ ) { + } - var bone = bones[ i ]; + for ( let i = 0; i !== nTracks; ++ i ) { - if ( bone.parent && bone.parent.isBone ) { + const track = tracks[ i ], + trackName = track.name; - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); - _vector$8.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j, _vector$8.x, _vector$8.y, _vector$8.z ); + let binding = bindingsByName[ trackName ]; - _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); - _vector$8.setFromMatrixPosition( _boneMatrix ); - position.setXYZ( j + 1, _vector$8.x, _vector$8.y, _vector$8.z ); + if ( binding !== undefined ) { - j += 2; + ++ binding.referenceCount; + bindings[ i ] = binding; - } + } else { - } + binding = bindings[ i ]; - geometry.getAttribute( 'position' ).needsUpdate = true; + if ( binding !== undefined ) { - Object3D.prototype.updateMatrixWorld.call( this, force ); + // existing binding, make sure the cache knows - }; + if ( binding._cacheIndex === null ) { - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); - function PointLightHelper( light, sphereSize, color ) { + } - this.light = light; - this.light.updateMatrixWorld(); + continue; - this.color = color; + } - var geometry = new SphereBufferGeometry( sphereSize, 4, 2 ); - var material = new MeshBasicMaterial( { wireframe: true, fog: false } ); + const path = prototypeAction && prototypeAction. + _propertyBindings[ i ].binding.parsedPath; - Mesh.call( this, geometry, material ); + binding = new PropertyMixer( + PropertyBinding.create( root, trackName, path ), + track.ValueTypeName, track.getValueSize() ); - this.matrix = this.light.matrixWorld; - this.matrixAutoUpdate = false; + ++ binding.referenceCount; + this._addInactiveBinding( binding, rootUuid, trackName ); - this.update(); + bindings[ i ] = binding; + } - /* - var distanceGeometry = new THREE.IcosahedronBufferGeometry( 1, 2 ); - var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + interpolants[ i ].resultBuffer = binding.buffer; - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + } - var d = light.distance; + } - if ( d === 0.0 ) { + _activateAction( action ) { - this.lightDistance.visible = false; + if ( ! this._isActiveAction( action ) ) { - } else { + if ( action._cacheIndex === null ) { - this.lightDistance.scale.set( d, d, d ); + // this action has been forgotten by the cache, but the user + // appears to be still using it -> rebind - } + const rootUuid = ( action._localRoot || this._root ).uuid, + clipUuid = action._clip.uuid, + actionsForClip = this._actionsByClip[ clipUuid ]; - this.add( this.lightDistance ); - */ + this._bindAction( action, + actionsForClip && actionsForClip.knownActions[ 0 ] ); - } + this._addInactiveAction( action, clipUuid, rootUuid ); - PointLightHelper.prototype = Object.create( Mesh.prototype ); - PointLightHelper.prototype.constructor = PointLightHelper; + } - PointLightHelper.prototype.dispose = function () { + const bindings = action._propertyBindings; - this.geometry.dispose(); - this.material.dispose(); + // increment reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - }; + const binding = bindings[ i ]; - PointLightHelper.prototype.update = function () { + if ( binding.useCount ++ === 0 ) { - if ( this.color !== undefined ) { + this._lendBinding( binding ); + binding.saveOriginalState(); - this.material.color.set( this.color ); + } - } else { + } - this.material.color.copy( this.light.color ); + this._lendAction( action ); } - /* - var d = this.light.distance; + } - if ( d === 0.0 ) { + _deactivateAction( action ) { - this.lightDistance.visible = false; + if ( this._isActiveAction( action ) ) { - } else { + const bindings = action._propertyBindings; - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); + // decrement reference counts / sort out state + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - } - */ + const binding = bindings[ i ]; - }; + if ( -- binding.useCount === 0 ) { - /** - * @author abelnation / http://github.com/abelnation - * @author Mugen87 / http://github.com/Mugen87 - * @author WestLangley / http://github.com/WestLangley - * - * This helper must be added as a child of the light - */ + binding.restoreOriginalState(); + this._takeBackBinding( binding ); + + } + + } + + this._takeBackAction( action ); - function RectAreaLightHelper( light, color ) { + } - this.type = 'RectAreaLightHelper'; + } - this.light = light; + // Memory manager - this.color = color; // optional hardwired color for the helper + _initMemoryManager() { - var positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ]; + this._actions = []; // 'nActiveActions' followed by inactive ones + this._nActiveActions = 0; - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - geometry.computeBoundingSphere(); + this._actionsByClip = {}; + // inside: + // { + // knownActions: Array< AnimationAction > - used as prototypes + // actionByRoot: AnimationAction - lookup + // } - var material = new LineBasicMaterial( { fog: false } ); - Line.call( this, geometry, material ); + this._bindings = []; // 'nActiveBindings' followed by inactive ones + this._nActiveBindings = 0; - // + this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > - var positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ]; - var geometry2 = new BufferGeometry(); - geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); - geometry2.computeBoundingSphere(); + this._controlInterpolants = []; // same game as above + this._nActiveControlInterpolants = 0; - this.add( new Mesh( geometry2, new MeshBasicMaterial( { side: BackSide, fog: false } ) ) ); + const scope = this; - this.update(); + this.stats = { - } + actions: { + get total() { - RectAreaLightHelper.prototype = Object.create( Line.prototype ); - RectAreaLightHelper.prototype.constructor = RectAreaLightHelper; + return scope._actions.length; - RectAreaLightHelper.prototype.update = function () { + }, + get inUse() { - this.scale.set( 0.5 * this.light.width, 0.5 * this.light.height, 1 ); + return scope._nActiveActions; - if ( this.color !== undefined ) { + } + }, + bindings: { + get total() { - this.material.color.set( this.color ); - this.children[ 0 ].material.color.set( this.color ); + return scope._bindings.length; - } else { + }, + get inUse() { - this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + return scope._nActiveBindings; - // prevent hue shift - var c = this.material.color; - var max = Math.max( c.r, c.g, c.b ); - if ( max > 1 ) c.multiplyScalar( 1 / max ); + } + }, + controlInterpolants: { + get total() { - this.children[ 0 ].material.color.copy( this.material.color ); + return scope._controlInterpolants.length; - } + }, + get inUse() { - }; + return scope._nActiveControlInterpolants; - RectAreaLightHelper.prototype.dispose = function () { + } + } - this.geometry.dispose(); - this.material.dispose(); - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); + }; - }; + } - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / https://github.com/Mugen87 - */ + // Memory management for AnimationAction objects - var _vector$9 = new Vector3(); - var _color1 = new Color(); - var _color2 = new Color(); + _isActiveAction( action ) { - function HemisphereLightHelper( light, size, color ) { + const index = action._cacheIndex; + return index !== null && index < this._nActiveActions; - Object3D.call( this ); + } - this.light = light; - this.light.updateMatrixWorld(); + _addInactiveAction( action, clipUuid, rootUuid ) { - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; + const actions = this._actions, + actionsByClip = this._actionsByClip; - this.color = color; + let actionsForClip = actionsByClip[ clipUuid ]; - var geometry = new OctahedronBufferGeometry( size ); - geometry.rotateY( Math.PI * 0.5 ); + if ( actionsForClip === undefined ) { - this.material = new MeshBasicMaterial( { wireframe: true, fog: false } ); - if ( this.color === undefined ) this.material.vertexColors = VertexColors; + actionsForClip = { - var position = geometry.getAttribute( 'position' ); - var colors = new Float32Array( position.count * 3 ); + knownActions: [ action ], + actionByRoot: {} - geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) ); + }; - this.add( new Mesh( geometry, this.material ) ); + action._byClipCacheIndex = 0; - this.update(); + actionsByClip[ clipUuid ] = actionsForClip; - } + } else { - HemisphereLightHelper.prototype = Object.create( Object3D.prototype ); - HemisphereLightHelper.prototype.constructor = HemisphereLightHelper; + const knownActions = actionsForClip.knownActions; - HemisphereLightHelper.prototype.dispose = function () { + action._byClipCacheIndex = knownActions.length; + knownActions.push( action ); - this.children[ 0 ].geometry.dispose(); - this.children[ 0 ].material.dispose(); + } - }; + action._cacheIndex = actions.length; + actions.push( action ); - HemisphereLightHelper.prototype.update = function () { + actionsForClip.actionByRoot[ rootUuid ] = action; - var mesh = this.children[ 0 ]; + } - if ( this.color !== undefined ) { + _removeInactiveAction( action ) { - this.material.color.set( this.color ); + const actions = this._actions, + lastInactiveAction = actions[ actions.length - 1 ], + cacheIndex = action._cacheIndex; - } else { + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); - var colors = mesh.geometry.getAttribute( 'color' ); + action._cacheIndex = null; - _color1.copy( this.light.color ); - _color2.copy( this.light.groundColor ); - for ( var i = 0, l = colors.count; i < l; i ++ ) { + const clipUuid = action._clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ], + knownActionsForClip = actionsForClip.knownActions, - var color = ( i < ( l / 2 ) ) ? _color1 : _color2; + lastKnownAction = + knownActionsForClip[ knownActionsForClip.length - 1 ], - colors.setXYZ( i, color.r, color.g, color.b ); + byClipCacheIndex = action._byClipCacheIndex; - } + lastKnownAction._byClipCacheIndex = byClipCacheIndex; + knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; + knownActionsForClip.pop(); - colors.needsUpdate = true; + action._byClipCacheIndex = null; - } - mesh.lookAt( _vector$9.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + const actionByRoot = actionsForClip.actionByRoot, + rootUuid = ( action._localRoot || this._root ).uuid; - }; + delete actionByRoot[ rootUuid ]; - /** - * @author WestLangley / http://github.com/WestLangley - */ + if ( knownActionsForClip.length === 0 ) { - function LightProbeHelper( lightProbe, size ) { + delete actionsByClip[ clipUuid ]; - this.lightProbe = lightProbe; + } - this.size = size; + this._removeInactiveBindingsForAction( action ); - var defines = {}; - defines[ 'GAMMA_OUTPUT' ] = ""; + } - // material - var material = new ShaderMaterial( { + _removeInactiveBindingsForAction( action ) { - defines: defines, + const bindings = action._propertyBindings; - uniforms: { + for ( let i = 0, n = bindings.length; i !== n; ++ i ) { - sh: { value: this.lightProbe.sh.coefficients }, // by reference + const binding = bindings[ i ]; - intensity: { value: this.lightProbe.intensity } + if ( -- binding.referenceCount === 0 ) { - }, + this._removeInactiveBinding( binding ); - vertexShader: [ + } - 'varying vec3 vNormal;', + } - 'void main() {', + } - ' vNormal = normalize( normalMatrix * normal );', + _lendAction( action ) { - ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', + // [ active actions | inactive actions ] + // [ active actions >| inactive actions ] + // s a + // <-swap-> + // a s - '}', + const actions = this._actions, + prevIndex = action._cacheIndex, - ].join( '\n' ), + lastActiveIndex = this._nActiveActions ++, - fragmentShader: [ + firstInactiveAction = actions[ lastActiveIndex ]; - '#define RECIPROCAL_PI 0.318309886', + action._cacheIndex = lastActiveIndex; + actions[ lastActiveIndex ] = action; - 'vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {', + firstInactiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = firstInactiveAction; - ' // matrix is assumed to be orthogonal', + } - ' return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );', + _takeBackAction( action ) { - '}', + // [ active actions | inactive actions ] + // [ active actions |< inactive actions ] + // a s + // <-swap-> + // s a - 'vec3 linearToOutput( in vec3 a ) {', + const actions = this._actions, + prevIndex = action._cacheIndex, - ' #ifdef GAMMA_OUTPUT', + firstInactiveIndex = -- this._nActiveActions, - ' return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );', + lastActiveAction = actions[ firstInactiveIndex ]; - ' #else', + action._cacheIndex = firstInactiveIndex; + actions[ firstInactiveIndex ] = action; - ' return a;', + lastActiveAction._cacheIndex = prevIndex; + actions[ prevIndex ] = lastActiveAction; - ' #endif', + } - '}', + // Memory management for PropertyMixer objects - '// source: https://graphics.stanford.edu/papers/envmap/envmap.pdf', - 'vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {', + _addInactiveBinding( binding, rootUuid, trackName ) { - ' // normal is assumed to have unit length', + const bindingsByRoot = this._bindingsByRootAndName, + bindings = this._bindings; - ' float x = normal.x, y = normal.y, z = normal.z;', + let bindingByName = bindingsByRoot[ rootUuid ]; - ' // band 0', - ' vec3 result = shCoefficients[ 0 ] * 0.886227;', + if ( bindingByName === undefined ) { - ' // band 1', - ' result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;', - ' result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;', - ' result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;', + bindingByName = {}; + bindingsByRoot[ rootUuid ] = bindingByName; - ' // band 2', - ' result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;', - ' result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;', - ' result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );', - ' result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;', - ' result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );', + } - ' return result;', + bindingByName[ trackName ] = binding; - '}', + binding._cacheIndex = bindings.length; + bindings.push( binding ); - 'uniform vec3 sh[ 9 ]; // sh coefficients', + } - 'uniform float intensity; // light probe intensity', + _removeInactiveBinding( binding ) { - 'varying vec3 vNormal;', + const bindings = this._bindings, + propBinding = binding.binding, + rootUuid = propBinding.rootNode.uuid, + trackName = propBinding.path, + bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ], - 'void main() {', + lastInactiveBinding = bindings[ bindings.length - 1 ], + cacheIndex = binding._cacheIndex; - ' vec3 normal = normalize( vNormal );', + lastInactiveBinding._cacheIndex = cacheIndex; + bindings[ cacheIndex ] = lastInactiveBinding; + bindings.pop(); - ' vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );', + delete bindingByName[ trackName ]; - ' vec3 irradiance = shGetIrradianceAt( worldNormal, sh );', + if ( Object.keys( bindingByName ).length === 0 ) { - ' vec3 outgoingLight = RECIPROCAL_PI * irradiance * intensity;', + delete bindingsByRoot[ rootUuid ]; - ' outgoingLight = linearToOutput( outgoingLight );', + } - ' gl_FragColor = vec4( outgoingLight, 1.0 );', + } - '}' + _lendBinding( binding ) { - ].join( '\n' ) + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - } ); + lastActiveIndex = this._nActiveBindings ++, - var geometry = new SphereBufferGeometry( 1, 32, 16 ); + firstInactiveBinding = bindings[ lastActiveIndex ]; - Mesh.call( this, geometry, material ); + binding._cacheIndex = lastActiveIndex; + bindings[ lastActiveIndex ] = binding; - this.onBeforeRender(); + firstInactiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = firstInactiveBinding; } - LightProbeHelper.prototype = Object.create( Mesh.prototype ); - LightProbeHelper.prototype.constructor = LightProbeHelper; + _takeBackBinding( binding ) { - LightProbeHelper.prototype.dispose = function () { + const bindings = this._bindings, + prevIndex = binding._cacheIndex, - this.geometry.dispose(); - this.material.dispose(); + firstInactiveIndex = -- this._nActiveBindings, - }; + lastActiveBinding = bindings[ firstInactiveIndex ]; - LightProbeHelper.prototype.onBeforeRender = function () { + binding._cacheIndex = firstInactiveIndex; + bindings[ firstInactiveIndex ] = binding; - this.position.copy( this.lightProbe.position ); + lastActiveBinding._cacheIndex = prevIndex; + bindings[ prevIndex ] = lastActiveBinding; - this.scale.set( 1, 1, 1 ).multiplyScalar( this.size ); + } - this.material.uniforms.intensity.value = this.lightProbe.intensity; - }; + // Memory management of Interpolants for weight and time scale - /** - * @author mrdoob / http://mrdoob.com/ - */ + _lendControlInterpolant() { - function GridHelper( size, divisions, color1, color2 ) { + const interpolants = this._controlInterpolants, + lastActiveIndex = this._nActiveControlInterpolants ++; - size = size || 10; - divisions = divisions || 10; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); + let interpolant = interpolants[ lastActiveIndex ]; - var center = divisions / 2; - var step = size / divisions; - var halfSize = size / 2; + if ( interpolant === undefined ) { - var vertices = [], colors = []; + interpolant = new LinearInterpolant( + new Float32Array( 2 ), new Float32Array( 2 ), + 1, _controlInterpolantsResultBuffer ); - for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { + interpolant.__cacheIndex = lastActiveIndex; + interpolants[ lastActiveIndex ] = interpolant; - vertices.push( - halfSize, 0, k, halfSize, 0, k ); - vertices.push( k, 0, - halfSize, k, 0, halfSize ); + } - var color = i === center ? color1 : color2; + return interpolant; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; - color.toArray( colors, j ); j += 3; + } - } + _takeBackControlInterpolant( interpolant ) { + + const interpolants = this._controlInterpolants, + prevIndex = interpolant.__cacheIndex, - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + firstInactiveIndex = -- this._nActiveControlInterpolants, - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); + lastActiveInterpolant = interpolants[ firstInactiveIndex ]; - LineSegments.call( this, geometry, material ); + interpolant.__cacheIndex = firstInactiveIndex; + interpolants[ firstInactiveIndex ] = interpolant; + + lastActiveInterpolant.__cacheIndex = prevIndex; + interpolants[ prevIndex ] = lastActiveInterpolant; } - GridHelper.prototype = Object.assign( Object.create( LineSegments.prototype ), { + // return an action for a clip optionally using a custom root target + // object (this method allocates a lot of dynamic memory in case a + // previously unknown clip/root combination is specified) + clipAction( clip, optionalRoot, blendMode ) { - constructor: GridHelper, + const root = optionalRoot || this._root, + rootUuid = root.uuid; - copy: function ( source ) { + let clipObject = typeof clip === 'string' ? AnimationClip.findByName( root, clip ) : clip; - LineSegments.prototype.copy.call( this, source ); + const clipUuid = clipObject !== null ? clipObject.uuid : clip; - this.geometry.copy( source.geometry ); - this.material.copy( source.material ); + const actionsForClip = this._actionsByClip[ clipUuid ]; + let prototypeAction = null; - return this; + if ( blendMode === undefined ) { - }, + if ( clipObject !== null ) { + + blendMode = clipObject.blendMode; + + } else { - clone: function () { + blendMode = NormalAnimationBlendMode; - return new this.constructor().copy( this ); + } } - } ); + if ( actionsForClip !== undefined ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / http://github.com/Mugen87 - * @author Hectate / http://www.github.com/Hectate - */ + const existingAction = actionsForClip.actionByRoot[ rootUuid ]; - function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) { + if ( existingAction !== undefined && existingAction.blendMode === blendMode ) { - radius = radius || 10; - radials = radials || 16; - circles = circles || 8; - divisions = divisions || 64; - color1 = new Color( color1 !== undefined ? color1 : 0x444444 ); - color2 = new Color( color2 !== undefined ? color2 : 0x888888 ); + return existingAction; - var vertices = []; - var colors = []; + } - var x, z; - var v, i, j, r, color; + // we know the clip, so we don't have to parse all + // the bindings again but can just copy + prototypeAction = actionsForClip.knownActions[ 0 ]; - // create the radials + // also, take the clip from the prototype action + if ( clipObject === null ) + clipObject = prototypeAction._clip; - for ( i = 0; i <= radials; i ++ ) { + } - v = ( i / radials ) * ( Math.PI * 2 ); + // clip must be known when specified via string + if ( clipObject === null ) return null; - x = Math.sin( v ) * radius; - z = Math.cos( v ) * radius; + // allocate all resources required to run it + const newAction = new AnimationAction( this, clipObject, optionalRoot, blendMode ); - vertices.push( 0, 0, 0 ); - vertices.push( x, 0, z ); + this._bindAction( newAction, prototypeAction ); - color = ( i & 1 ) ? color1 : color2; + // and make the action known to the memory manager + this._addInactiveAction( newAction, clipUuid, rootUuid ); - colors.push( color.r, color.g, color.b ); - colors.push( color.r, color.g, color.b ); + return newAction; - } + } - // create the circles + // get an existing action + existingAction( clip, optionalRoot ) { - for ( i = 0; i <= circles; i ++ ) { + const root = optionalRoot || this._root, + rootUuid = root.uuid, - color = ( i & 1 ) ? color1 : color2; + clipObject = typeof clip === 'string' ? + AnimationClip.findByName( root, clip ) : clip, - r = radius - ( radius / circles * i ); + clipUuid = clipObject ? clipObject.uuid : clip, - for ( j = 0; j < divisions; j ++ ) { + actionsForClip = this._actionsByClip[ clipUuid ]; - // first vertex + if ( actionsForClip !== undefined ) { - v = ( j / divisions ) * ( Math.PI * 2 ); + return actionsForClip.actionByRoot[ rootUuid ] || null; - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; + } - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); + return null; - // second vertex + } - v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); + // deactivates all previously scheduled actions + stopAllAction() { - x = Math.sin( v ) * r; - z = Math.cos( v ) * r; + const actions = this._actions, + nActions = this._nActiveActions; - vertices.push( x, 0, z ); - colors.push( color.r, color.g, color.b ); + for ( let i = nActions - 1; i >= 0; -- i ) { - } + actions[ i ].stop(); } - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + return this; + + } - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); + // advance the time and update apply the animation + update( deltaTime ) { - LineSegments.call( this, geometry, material ); + deltaTime *= this.timeScale; - } + const actions = this._actions, + nActions = this._nActiveActions, - PolarGridHelper.prototype = Object.create( LineSegments.prototype ); - PolarGridHelper.prototype.constructor = PolarGridHelper; + time = this.time += deltaTime, + timeDirection = Math.sign( deltaTime ), - /** - * @author Mugen87 / http://github.com/Mugen87 - */ + accuIndex = this._accuIndex ^= 1; + + // run active actions - function PositionalAudioHelper( audio, range, divisionsInnerAngle, divisionsOuterAngle ) { + for ( let i = 0; i !== nActions; ++ i ) { - this.audio = audio; - this.range = range || 1; - this.divisionsInnerAngle = divisionsInnerAngle || 16; - this.divisionsOuterAngle = divisionsOuterAngle || 2; + const action = actions[ i ]; - var geometry = new BufferGeometry(); - var divisions = this.divisionsInnerAngle + this.divisionsOuterAngle * 2; - var positions = new Float32Array( ( divisions * 3 + 3 ) * 3 ); - geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); + action._update( time, deltaTime, timeDirection, accuIndex ); - var materialInnerAngle = new LineBasicMaterial( { color: 0x00ff00 } ); - var materialOuterAngle = new LineBasicMaterial( { color: 0xffff00 } ); + } - Line.call( this, geometry, [ materialOuterAngle, materialInnerAngle ] ); + // update scene graph - this.update(); + const bindings = this._bindings, + nBindings = this._nActiveBindings; + + for ( let i = 0; i !== nBindings; ++ i ) { + + bindings[ i ].apply( accuIndex ); + + } + + return this; } - PositionalAudioHelper.prototype = Object.create( Line.prototype ); - PositionalAudioHelper.prototype.constructor = PositionalAudioHelper; + // Allows you to seek to a specific time in an animation. + setTime( timeInSeconds ) { - PositionalAudioHelper.prototype.update = function () { + this.time = 0; // Zero out time attribute for AnimationMixer object; + for ( let i = 0; i < this._actions.length; i ++ ) { - var audio = this.audio; - var range = this.range; - var divisionsInnerAngle = this.divisionsInnerAngle; - var divisionsOuterAngle = this.divisionsOuterAngle; + this._actions[ i ].time = 0; // Zero out time attribute for all associated AnimationAction objects. - var coneInnerAngle = _Math.degToRad( audio.panner.coneInnerAngle ); - var coneOuterAngle = _Math.degToRad( audio.panner.coneOuterAngle ); + } - var halfConeInnerAngle = coneInnerAngle / 2; - var halfConeOuterAngle = coneOuterAngle / 2; + return this.update( timeInSeconds ); // Update used to set exact time. Returns "this" AnimationMixer object. - var start = 0; - var count = 0; - var i, stride; + } - var geometry = this.geometry; - var positionAttribute = geometry.attributes.position; + // return this mixer's root target object + getRoot() { - geometry.clearGroups(); + return this._root; - // + } - function generateSegment( from, to, divisions, materialIndex ) { + // free all resources specific to a particular clip + uncacheClip( clip ) { - var step = ( to - from ) / divisions; + const actions = this._actions, + clipUuid = clip.uuid, + actionsByClip = this._actionsByClip, + actionsForClip = actionsByClip[ clipUuid ]; - positionAttribute.setXYZ( start, 0, 0, 0 ); - count ++; + if ( actionsForClip !== undefined ) { - for ( i = from; i < to; i += step ) { + // note: just calling _removeInactiveAction would mess up the + // iteration state and also require updating the state we can + // just throw away - stride = start + count; + const actionsToRemove = actionsForClip.knownActions; - positionAttribute.setXYZ( stride, Math.sin( i ) * range, 0, Math.cos( i ) * range ); - positionAttribute.setXYZ( stride + 1, Math.sin( Math.min( i + step, to ) ) * range, 0, Math.cos( Math.min( i + step, to ) ) * range ); - positionAttribute.setXYZ( stride + 2, 0, 0, 0 ); + for ( let i = 0, n = actionsToRemove.length; i !== n; ++ i ) { - count += 3; + const action = actionsToRemove[ i ]; - } + this._deactivateAction( action ); - geometry.addGroup( start, count, materialIndex ); + const cacheIndex = action._cacheIndex, + lastInactiveAction = actions[ actions.length - 1 ]; - start += count; - count = 0; + action._cacheIndex = null; + action._byClipCacheIndex = null; - } + lastInactiveAction._cacheIndex = cacheIndex; + actions[ cacheIndex ] = lastInactiveAction; + actions.pop(); - // + this._removeInactiveBindingsForAction( action ); - generateSegment( - halfConeOuterAngle, - halfConeInnerAngle, divisionsOuterAngle, 0 ); - generateSegment( - halfConeInnerAngle, halfConeInnerAngle, divisionsInnerAngle, 1 ); - generateSegment( halfConeInnerAngle, halfConeOuterAngle, divisionsOuterAngle, 0 ); + } - // + delete actionsByClip[ clipUuid ]; - positionAttribute.needsUpdate = true; + } - if ( coneInnerAngle === coneOuterAngle ) this.material[ 0 ].visible = false; + } - }; + // free all resources specific to a particular root target object + uncacheRoot( root ) { - PositionalAudioHelper.prototype.dispose = function () { + const rootUuid = root.uuid, + actionsByClip = this._actionsByClip; - this.geometry.dispose(); - this.material[ 0 ].dispose(); - this.material[ 1 ].dispose(); + for ( const clipUuid in actionsByClip ) { - }; + const actionByRoot = actionsByClip[ clipUuid ].actionByRoot, + action = actionByRoot[ rootUuid ]; - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ + if ( action !== undefined ) { - var _v1$6 = new Vector3(); - var _v2$4 = new Vector3(); - var _normalMatrix$2 = new Matrix3(); + this._deactivateAction( action ); + this._removeInactiveAction( action ); - function FaceNormalsHelper( object, size, hex, linewidth ) { + } - // FaceNormalsHelper only supports THREE.Geometry + } - this.object = object; + const bindingsByRoot = this._bindingsByRootAndName, + bindingByName = bindingsByRoot[ rootUuid ]; - this.size = ( size !== undefined ) ? size : 1; + if ( bindingByName !== undefined ) { - var color = ( hex !== undefined ) ? hex : 0xffff00; + for ( const trackName in bindingByName ) { - var width = ( linewidth !== undefined ) ? linewidth : 1; + const binding = bindingByName[ trackName ]; + binding.restoreOriginalState(); + this._removeInactiveBinding( binding ); - // + } - var nNormals = 0; + } - var objGeometry = this.object.geometry; + } - if ( objGeometry && objGeometry.isGeometry ) { + // remove a targeted clip from the cache + uncacheAction( clip, optionalRoot ) { - nNormals = objGeometry.faces.length; + const action = this.existingAction( clip, optionalRoot ); - } else { + if ( action !== null ) { - console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); + this._deactivateAction( action ); + this._removeInactiveAction( action ); } - // + } - var geometry = new BufferGeometry(); +} - var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 ); +class Uniform { - geometry.addAttribute( 'position', positions ); + constructor( value ) { - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) ); + this.value = value; - // + } - this.matrixAutoUpdate = false; - this.update(); + clone() { + + return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() ); } - FaceNormalsHelper.prototype = Object.create( LineSegments.prototype ); - FaceNormalsHelper.prototype.constructor = FaceNormalsHelper; +} - FaceNormalsHelper.prototype.update = function () { +let _id = 0; - this.object.updateMatrixWorld( true ); +class UniformsGroup extends EventDispatcher { - _normalMatrix$2.getNormalMatrix( this.object.matrixWorld ); + constructor() { - var matrixWorld = this.object.matrixWorld; + super(); - var position = this.geometry.attributes.position; + this.isUniformsGroup = true; - // + Object.defineProperty( this, 'id', { value: _id ++ } ); - var objGeometry = this.object.geometry; + this.name = ''; - var vertices = objGeometry.vertices; + this.usage = StaticDrawUsage; + this.uniforms = []; - var faces = objGeometry.faces; + } - var idx = 0; + add( uniform ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + this.uniforms.push( uniform ); - var face = faces[ i ]; + return this; - var normal = face.normal; + } - _v1$6.copy( vertices[ face.a ] ) - .add( vertices[ face.b ] ) - .add( vertices[ face.c ] ) - .divideScalar( 3 ) - .applyMatrix4( matrixWorld ); + remove( uniform ) { - _v2$4.copy( normal ).applyMatrix3( _normalMatrix$2 ).normalize().multiplyScalar( this.size ).add( _v1$6 ); + const index = this.uniforms.indexOf( uniform ); - position.setXYZ( idx, _v1$6.x, _v1$6.y, _v1$6.z ); + if ( index !== - 1 ) this.uniforms.splice( index, 1 ); - idx = idx + 1; + return this; - position.setXYZ( idx, _v2$4.x, _v2$4.y, _v2$4.z ); + } - idx = idx + 1; + setName( name ) { - } + this.name = name; - position.needsUpdate = true; + return this; - }; + } - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ + setUsage( value ) { - var _v1$7 = new Vector3(); - var _v2$5 = new Vector3(); - var _v3$1 = new Vector3(); + this.usage = value; - function DirectionalLightHelper( light, size, color ) { + return this; - Object3D.call( this ); + } - this.light = light; - this.light.updateMatrixWorld(); + dispose() { - this.matrix = light.matrixWorld; - this.matrixAutoUpdate = false; + this.dispatchEvent( { type: 'dispose' } ); - this.color = color; + return this; - if ( size === undefined ) size = 1; + } - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( [ - - size, size, 0, - size, size, 0, - size, - size, 0, - - size, - size, 0, - - size, size, 0 - ], 3 ) ); + copy( source ) { - var material = new LineBasicMaterial( { fog: false } ); + this.name = source.name; + this.usage = source.usage; - this.lightPlane = new Line( geometry, material ); - this.add( this.lightPlane ); + const uniformsSource = source.uniforms; - geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); + this.uniforms.length = 0; - this.targetLine = new Line( geometry, material ); - this.add( this.targetLine ); + for ( let i = 0, l = uniformsSource.length; i < l; i ++ ) { - this.update(); + this.uniforms.push( uniformsSource[ i ].clone() ); - } + } - DirectionalLightHelper.prototype = Object.create( Object3D.prototype ); - DirectionalLightHelper.prototype.constructor = DirectionalLightHelper; + return this; - DirectionalLightHelper.prototype.dispose = function () { + } - this.lightPlane.geometry.dispose(); - this.lightPlane.material.dispose(); - this.targetLine.geometry.dispose(); - this.targetLine.material.dispose(); + clone() { - }; + return new this.constructor().copy( this ); - DirectionalLightHelper.prototype.update = function () { + } - _v1$7.setFromMatrixPosition( this.light.matrixWorld ); - _v2$5.setFromMatrixPosition( this.light.target.matrixWorld ); - _v3$1.subVectors( _v2$5, _v1$7 ); +} - this.lightPlane.lookAt( _v2$5 ); +class InstancedInterleavedBuffer extends InterleavedBuffer { - if ( this.color !== undefined ) { + constructor( array, stride, meshPerAttribute = 1 ) { - this.lightPlane.material.color.set( this.color ); - this.targetLine.material.color.set( this.color ); + super( array, stride ); - } else { + this.isInstancedInterleavedBuffer = true; - this.lightPlane.material.color.copy( this.light.color ); - this.targetLine.material.color.copy( this.light.color ); + this.meshPerAttribute = meshPerAttribute; - } + } - this.targetLine.lookAt( _v2$5 ); - this.targetLine.scale.z = _v3$1.length(); + copy( source ) { - }; + super.copy( source ); - /** - * @author alteredq / http://alteredqualia.com/ - * @author Mugen87 / https://github.com/Mugen87 - * - * - shows frustum, line of sight and up of the camera - * - suitable for fast updates - * - based on frustum visualization in lightgl.js shadowmap example - * http://evanw.github.com/lightgl.js/tests/shadowmap.html - */ + this.meshPerAttribute = source.meshPerAttribute; - var _vector$a = new Vector3(); - var _camera = new Camera(); + return this; - function CameraHelper( camera ) { + } - var geometry = new BufferGeometry(); - var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } ); + clone( data ) { - var vertices = []; - var colors = []; + const ib = super.clone( data ); - var pointMap = {}; + ib.meshPerAttribute = this.meshPerAttribute; - // colors + return ib; - var colorFrustum = new Color( 0xffaa00 ); - var colorCone = new Color( 0xff0000 ); - var colorUp = new Color( 0x00aaff ); - var colorTarget = new Color( 0xffffff ); - var colorCross = new Color( 0x333333 ); + } - // near + toJSON( data ) { - addLine( 'n1', 'n2', colorFrustum ); - addLine( 'n2', 'n4', colorFrustum ); - addLine( 'n4', 'n3', colorFrustum ); - addLine( 'n3', 'n1', colorFrustum ); + const json = super.toJSON( data ); - // far + json.isInstancedInterleavedBuffer = true; + json.meshPerAttribute = this.meshPerAttribute; - addLine( 'f1', 'f2', colorFrustum ); - addLine( 'f2', 'f4', colorFrustum ); - addLine( 'f4', 'f3', colorFrustum ); - addLine( 'f3', 'f1', colorFrustum ); + return json; - // sides + } - addLine( 'n1', 'f1', colorFrustum ); - addLine( 'n2', 'f2', colorFrustum ); - addLine( 'n3', 'f3', colorFrustum ); - addLine( 'n4', 'f4', colorFrustum ); +} - // cone +class GLBufferAttribute { - addLine( 'p', 'n1', colorCone ); - addLine( 'p', 'n2', colorCone ); - addLine( 'p', 'n3', colorCone ); - addLine( 'p', 'n4', colorCone ); + constructor( buffer, type, itemSize, elementSize, count ) { - // up + this.isGLBufferAttribute = true; - addLine( 'u1', 'u2', colorUp ); - addLine( 'u2', 'u3', colorUp ); - addLine( 'u3', 'u1', colorUp ); + this.name = ''; - // target + this.buffer = buffer; + this.type = type; + this.itemSize = itemSize; + this.elementSize = elementSize; + this.count = count; - addLine( 'c', 't', colorTarget ); - addLine( 'p', 'c', colorCross ); + this.version = 0; - // cross + } - addLine( 'cn1', 'cn2', colorCross ); - addLine( 'cn3', 'cn4', colorCross ); + set needsUpdate( value ) { - addLine( 'cf1', 'cf2', colorCross ); - addLine( 'cf3', 'cf4', colorCross ); + if ( value === true ) this.version ++; - function addLine( a, b, color ) { + } - addPoint( a, color ); - addPoint( b, color ); + setBuffer( buffer ) { - } + this.buffer = buffer; - function addPoint( id, color ) { + return this; - vertices.push( 0, 0, 0 ); - colors.push( color.r, color.g, color.b ); + } - if ( pointMap[ id ] === undefined ) { + setType( type, elementSize ) { - pointMap[ id ] = []; + this.type = type; + this.elementSize = elementSize; - } + return this; - pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); + } - } + setItemSize( itemSize ) { - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + this.itemSize = itemSize; - LineSegments.call( this, geometry, material ); + return this; - this.camera = camera; - if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); + } - this.matrix = camera.matrixWorld; - this.matrixAutoUpdate = false; + setCount( count ) { - this.pointMap = pointMap; + this.count = count; - this.update(); + return this; } - CameraHelper.prototype = Object.create( LineSegments.prototype ); - CameraHelper.prototype.constructor = CameraHelper; +} - CameraHelper.prototype.update = function () { +class Raycaster { - var geometry = this.geometry; - var pointMap = this.pointMap; + constructor( origin, direction, near = 0, far = Infinity ) { - var w = 1, h = 1; + this.ray = new Ray( origin, direction ); + // direction is assumed to be normalized (for accurate distance calculations) - // we need just camera projection matrix inverse - // world matrix must be identity + this.near = near; + this.far = far; + this.camera = null; + this.layers = new Layers(); - _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); + this.params = { + Mesh: {}, + Line: { threshold: 1 }, + LOD: {}, + Points: { threshold: 1 }, + Sprite: {} + }; - // center / target + } - setPoint( 'c', pointMap, geometry, _camera, 0, 0, - 1 ); - setPoint( 't', pointMap, geometry, _camera, 0, 0, 1 ); + set( origin, direction ) { - // near + // direction is assumed to be normalized (for accurate distance calculations) - setPoint( 'n1', pointMap, geometry, _camera, - w, - h, - 1 ); - setPoint( 'n2', pointMap, geometry, _camera, w, - h, - 1 ); - setPoint( 'n3', pointMap, geometry, _camera, - w, h, - 1 ); - setPoint( 'n4', pointMap, geometry, _camera, w, h, - 1 ); + this.ray.set( origin, direction ); - // far + } - setPoint( 'f1', pointMap, geometry, _camera, - w, - h, 1 ); - setPoint( 'f2', pointMap, geometry, _camera, w, - h, 1 ); - setPoint( 'f3', pointMap, geometry, _camera, - w, h, 1 ); - setPoint( 'f4', pointMap, geometry, _camera, w, h, 1 ); + setFromCamera( coords, camera ) { - // up + if ( camera.isPerspectiveCamera ) { - setPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 ); - setPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 ); - setPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, - 1 ); + this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); + this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); + this.camera = camera; - // cross + } else if ( camera.isOrthographicCamera ) { - setPoint( 'cf1', pointMap, geometry, _camera, - w, 0, 1 ); - setPoint( 'cf2', pointMap, geometry, _camera, w, 0, 1 ); - setPoint( 'cf3', pointMap, geometry, _camera, 0, - h, 1 ); - setPoint( 'cf4', pointMap, geometry, _camera, 0, h, 1 ); + this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera + this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); + this.camera = camera; - setPoint( 'cn1', pointMap, geometry, _camera, - w, 0, - 1 ); - setPoint( 'cn2', pointMap, geometry, _camera, w, 0, - 1 ); - setPoint( 'cn3', pointMap, geometry, _camera, 0, - h, - 1 ); - setPoint( 'cn4', pointMap, geometry, _camera, 0, h, - 1 ); + } else { - geometry.getAttribute( 'position' ).needsUpdate = true; + console.error( 'THREE.Raycaster: Unsupported camera type: ' + camera.type ); - }; + } - function setPoint( point, pointMap, geometry, camera, x, y, z ) { + } - _vector$a.set( x, y, z ).unproject( camera ); + intersectObject( object, recursive = true, intersects = [] ) { - var points = pointMap[ point ]; + intersectObject( object, this, intersects, recursive ); - if ( points !== undefined ) { + intersects.sort( ascSort ); - var position = geometry.getAttribute( 'position' ); + return intersects; - for ( var i = 0, l = points.length; i < l; i ++ ) { + } - position.setXYZ( points[ i ], _vector$a.x, _vector$a.y, _vector$a.z ); + intersectObjects( objects, recursive = true, intersects = [] ) { - } + for ( let i = 0, l = objects.length; i < l; i ++ ) { - } + intersectObject( objects[ i ], this, intersects, recursive ); - } + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author Mugen87 / http://github.com/Mugen87 - */ + intersects.sort( ascSort ); - var _box$2 = new Box3(); + return intersects; - function BoxHelper( object, color ) { + } - this.object = object; +} - if ( color === undefined ) color = 0xffff00; +function ascSort( a, b ) { - var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - var positions = new Float32Array( 8 * 3 ); + return a.distance - b.distance; - var geometry = new BufferGeometry(); - geometry.setIndex( new BufferAttribute( indices, 1 ) ); - geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); +} - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); +function intersectObject( object, raycaster, intersects, recursive ) { - this.matrixAutoUpdate = false; + if ( object.layers.test( raycaster.layers ) ) { - this.update(); + object.raycast( raycaster, intersects ); } - BoxHelper.prototype = Object.create( LineSegments.prototype ); - BoxHelper.prototype.constructor = BoxHelper; + if ( recursive === true ) { - BoxHelper.prototype.update = function ( object ) { + const children = object.children; - if ( object !== undefined ) { + for ( let i = 0, l = children.length; i < l; i ++ ) { - console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' ); + intersectObject( children[ i ], raycaster, intersects, true ); } - if ( this.object !== undefined ) { + } - _box$2.setFromObject( this.object ); +} - } +/** + * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system + * + * The polar angle (phi) is measured from the positive y-axis. The positive y-axis is up. + * The azimuthal angle (theta) is measured from the positive z-axis. + */ - if ( _box$2.isEmpty() ) return; - var min = _box$2.min; - var max = _box$2.max; +class Spherical { - /* - 5____4 - 1/___0/| - | 6__|_7 - 2/___3/ + constructor( radius = 1, phi = 0, theta = 0 ) { - 0: max.x, max.y, max.z - 1: min.x, max.y, max.z - 2: min.x, min.y, max.z - 3: max.x, min.y, max.z - 4: max.x, max.y, min.z - 5: min.x, max.y, min.z - 6: min.x, min.y, min.z - 7: max.x, min.y, min.z - */ + this.radius = radius; + this.phi = phi; // polar angle + this.theta = theta; // azimuthal angle - var position = this.geometry.attributes.position; - var array = position.array; + return this; - array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; - array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; - array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; - array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; - array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; - array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; - array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; - array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; + } - position.needsUpdate = true; + set( radius, phi, theta ) { - this.geometry.computeBoundingSphere(); + this.radius = radius; + this.phi = phi; + this.theta = theta; + return this; - }; + } - BoxHelper.prototype.setFromObject = function ( object ) { + copy( other ) { - this.object = object; - this.update(); + this.radius = other.radius; + this.phi = other.phi; + this.theta = other.theta; return this; - }; - - BoxHelper.prototype.copy = function ( source ) { + } - LineSegments.prototype.copy.call( this, source ); + // restrict phi to be between EPS and PI-EPS + makeSafe() { - this.object = source.object; + const EPS = 0.000001; + this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) ); return this; - }; - - BoxHelper.prototype.clone = function () { + } - return new this.constructor().copy( this ); + setFromVector3( v ) { - }; + return this.setFromCartesianCoords( v.x, v.y, v.z ); - /** - * @author WestLangley / http://github.com/WestLangley - */ + } - function Box3Helper( box, color ) { + setFromCartesianCoords( x, y, z ) { - this.type = 'Box3Helper'; + this.radius = Math.sqrt( x * x + y * y + z * z ); - this.box = box; + if ( this.radius === 0 ) { - color = color || 0xffff00; + this.theta = 0; + this.phi = 0; - var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + } else { - var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; + this.theta = Math.atan2( x, z ); + this.phi = Math.acos( clamp( y / this.radius, - 1, 1 ) ); - var geometry = new BufferGeometry(); + } - geometry.setIndex( new BufferAttribute( indices, 1 ) ); + return this; - geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + } - LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) ); + clone() { - this.geometry.computeBoundingSphere(); + return new this.constructor().copy( this ); } - Box3Helper.prototype = Object.create( LineSegments.prototype ); - Box3Helper.prototype.constructor = Box3Helper; +} - Box3Helper.prototype.updateMatrixWorld = function ( force ) { +/** + * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system + */ - var box = this.box; +class Cylindrical { - if ( box.isEmpty() ) return; - - box.getCenter( this.position ); + constructor( radius = 1, theta = 0, y = 0 ) { - box.getSize( this.scale ); + this.radius = radius; // distance from the origin to a point in the x-z plane + this.theta = theta; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis + this.y = y; // height above the x-z plane - this.scale.multiplyScalar( 0.5 ); + return this; - Object3D.prototype.updateMatrixWorld.call( this, force ); + } - }; + set( radius, theta, y ) { - /** - * @author WestLangley / http://github.com/WestLangley - */ + this.radius = radius; + this.theta = theta; + this.y = y; - function PlaneHelper( plane, size, hex ) { + return this; - this.type = 'PlaneHelper'; + } - this.plane = plane; + copy( other ) { - this.size = ( size === undefined ) ? 1 : size; + this.radius = other.radius; + this.theta = other.theta; + this.y = other.y; - var color = ( hex !== undefined ) ? hex : 0xffff00; + return this; - var positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ]; + } - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - geometry.computeBoundingSphere(); + setFromVector3( v ) { - Line.call( this, geometry, new LineBasicMaterial( { color: color } ) ); + return this.setFromCartesianCoords( v.x, v.y, v.z ); - // + } - var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ]; + setFromCartesianCoords( x, y, z ) { - var geometry2 = new BufferGeometry(); - geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); - geometry2.computeBoundingSphere(); + this.radius = Math.sqrt( x * x + z * z ); + this.theta = Math.atan2( x, z ); + this.y = y; - this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) ); + return this; } - PlaneHelper.prototype = Object.create( Line.prototype ); - PlaneHelper.prototype.constructor = PlaneHelper; + clone() { - PlaneHelper.prototype.updateMatrixWorld = function ( force ) { + return new this.constructor().copy( this ); - var scale = - this.plane.constant; + } - if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter +} - this.scale.set( 0.5 * this.size, 0.5 * this.size, scale ); +const _vector$4 = /*@__PURE__*/ new Vector2(); - this.children[ 0 ].material.side = ( scale < 0 ) ? BackSide : FrontSide; // renderer flips side when determinant < 0; flipping not wanted here +class Box2 { - this.lookAt( this.plane.normal ); + constructor( min = new Vector2( + Infinity, + Infinity ), max = new Vector2( - Infinity, - Infinity ) ) { - Object3D.prototype.updateMatrixWorld.call( this, force ); + this.isBox2 = true; - }; + this.min = min; + this.max = max; - /** - * @author WestLangley / http://github.com/WestLangley - * @author zz85 / http://github.com/zz85 - * @author bhouston / http://clara.io - * - * Creates an arrow for visualizing directions - * - * Parameters: - * dir - Vector3 - * origin - Vector3 - * length - Number - * color - color in hex value - * headLength - Number - * headWidth - Number - */ + } - var _axis = new Vector3(); - var _lineGeometry, _coneGeometry; + set( min, max ) { - function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { + this.min.copy( min ); + this.max.copy( max ); - // dir is assumed to be normalized + return this; - Object3D.call( this ); + } - if ( dir === undefined ) dir = new Vector3( 0, 0, 1 ); - if ( origin === undefined ) origin = new Vector3( 0, 0, 0 ); - if ( length === undefined ) length = 1; - if ( color === undefined ) color = 0xffff00; - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; + setFromPoints( points ) { - if ( _lineGeometry === undefined ) { + this.makeEmpty(); - _lineGeometry = new BufferGeometry(); - _lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); + for ( let i = 0, il = points.length; i < il; i ++ ) { - _coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); - _coneGeometry.translate( 0, - 0.5, 0 ); + this.expandByPoint( points[ i ] ); } - this.position.copy( origin ); + return this; - this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); + } - this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); + setFromCenterAndSize( center, size ) { - this.setDirection( dir ); - this.setLength( length, headLength, headWidth ); + const halfSize = _vector$4.copy( size ).multiplyScalar( 0.5 ); + this.min.copy( center ).sub( halfSize ); + this.max.copy( center ).add( halfSize ); - } + return this; - ArrowHelper.prototype = Object.create( Object3D.prototype ); - ArrowHelper.prototype.constructor = ArrowHelper; + } - ArrowHelper.prototype.setDirection = function ( dir ) { + clone() { - // dir is assumed to be normalized + return new this.constructor().copy( this ); - if ( dir.y > 0.99999 ) { + } - this.quaternion.set( 0, 0, 0, 1 ); + copy( box ) { - } else if ( dir.y < - 0.99999 ) { + this.min.copy( box.min ); + this.max.copy( box.max ); - this.quaternion.set( 1, 0, 0, 0 ); + return this; - } else { + } - _axis.set( dir.z, 0, - dir.x ).normalize(); + makeEmpty() { - var radians = Math.acos( dir.y ); + this.min.x = this.min.y = + Infinity; + this.max.x = this.max.y = - Infinity; - this.quaternion.setFromAxisAngle( _axis, radians ); + return this; - } + } - }; + isEmpty() { - ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { + // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; + return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); - this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); - this.line.updateMatrix(); + } - this.cone.scale.set( headWidth, headLength, headWidth ); - this.cone.position.y = length; - this.cone.updateMatrix(); + getCenter( target ) { - }; + return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); - ArrowHelper.prototype.setColor = function ( color ) { + } - this.line.material.color.set( color ); - this.cone.material.color.set( color ); + getSize( target ) { - }; + return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min ); - ArrowHelper.prototype.copy = function ( source ) { + } - Object3D.prototype.copy.call( this, source, false ); + expandByPoint( point ) { - this.line.copy( source.line ); - this.cone.copy( source.cone ); + this.min.min( point ); + this.max.max( point ); return this; - }; - - ArrowHelper.prototype.clone = function () { + } - return new this.constructor().copy( this ); + expandByVector( vector ) { - }; + this.min.sub( vector ); + this.max.add( vector ); - /** - * @author sroucheray / http://sroucheray.org/ - * @author mrdoob / http://mrdoob.com/ - */ + return this; - function AxesHelper( size ) { + } - size = size || 1; + expandByScalar( scalar ) { - var vertices = [ - 0, 0, 0, size, 0, 0, - 0, 0, 0, 0, size, 0, - 0, 0, 0, 0, 0, size - ]; + this.min.addScalar( - scalar ); + this.max.addScalar( scalar ); - var colors = [ - 1, 0, 0, 1, 0.6, 0, - 0, 1, 0, 0.6, 1, 0, - 0, 0, 1, 0, 0.6, 1 - ]; + return this; - var geometry = new BufferGeometry(); - geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); + } - var material = new LineBasicMaterial( { vertexColors: VertexColors } ); + containsPoint( point ) { - LineSegments.call( this, geometry, material ); + return point.x < this.min.x || point.x > this.max.x || + point.y < this.min.y || point.y > this.max.y ? false : true; } - AxesHelper.prototype = Object.create( LineSegments.prototype ); - AxesHelper.prototype.constructor = AxesHelper; - - /** - * @author mrdoob / http://mrdoob.com/ - */ - - function Face4( a, b, c, d, normal, color, materialIndex ) { + containsBox( box ) { - console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); - return new Face3( a, b, c, normal, color, materialIndex ); + return this.min.x <= box.min.x && box.max.x <= this.max.x && + this.min.y <= box.min.y && box.max.y <= this.max.y; } - var LineStrip = 0; + getParameter( point, target ) { - var LinePieces = 1; + // This can potentially have a divide by zero if the box + // has a size dimension of 0. - function MeshFaceMaterial( materials ) { - - console.warn( 'THREE.MeshFaceMaterial has been removed. Use an Array instead.' ); - return materials; + return target.set( + ( point.x - this.min.x ) / ( this.max.x - this.min.x ), + ( point.y - this.min.y ) / ( this.max.y - this.min.y ) + ); } - function MultiMaterial( materials ) { + intersectsBox( box ) { - if ( materials === undefined ) materials = []; + // using 4 splitting planes to rule out intersections - console.warn( 'THREE.MultiMaterial has been removed. Use an Array instead.' ); - materials.isMultiMaterial = true; - materials.materials = materials; - materials.clone = function () { + return box.max.x < this.min.x || box.min.x > this.max.x || + box.max.y < this.min.y || box.min.y > this.max.y ? false : true; - return materials.slice(); + } - }; - return materials; + clampPoint( point, target ) { + + return target.copy( point ).clamp( this.min, this.max ); } - function PointCloud( geometry, material ) { + distanceToPoint( point ) { - console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); - return new Points( geometry, material ); + return this.clampPoint( point, _vector$4 ).distanceTo( point ); } - function Particle( material ) { + intersect( box ) { + + this.min.max( box.min ); + this.max.min( box.max ); - console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' ); - return new Sprite( material ); + if ( this.isEmpty() ) this.makeEmpty(); + + return this; } - function ParticleSystem( geometry, material ) { + union( box ) { - console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); - return new Points( geometry, material ); + this.min.min( box.min ); + this.max.max( box.max ); + + return this; } - function PointCloudMaterial( parameters ) { + translate( offset ) { - console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); + this.min.add( offset ); + this.max.add( offset ); + + return this; } - function ParticleBasicMaterial( parameters ) { + equals( box ) { - console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); + return box.min.equals( this.min ) && box.max.equals( this.max ); } - function ParticleSystemMaterial( parameters ) { +} - console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); - return new PointsMaterial( parameters ); +const _startP = /*@__PURE__*/ new Vector3(); +const _startEnd = /*@__PURE__*/ new Vector3(); - } +class Line3 { - function Vertex( x, y, z ) { + constructor( start = new Vector3(), end = new Vector3() ) { - console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' ); - return new Vector3( x, y, z ); + this.start = start; + this.end = end; } - // + set( start, end ) { - function DynamicBufferAttribute( array, itemSize ) { + this.start.copy( start ); + this.end.copy( end ); - console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); - return new BufferAttribute( array, itemSize ).setDynamic( true ); + return this; } - function Int8Attribute( array, itemSize ) { + copy( line ) { + + this.start.copy( line.start ); + this.end.copy( line.end ); - console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' ); - return new Int8BufferAttribute( array, itemSize ); + return this; } - function Uint8Attribute( array, itemSize ) { + getCenter( target ) { - console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' ); - return new Uint8BufferAttribute( array, itemSize ); + return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); } - function Uint8ClampedAttribute( array, itemSize ) { + delta( target ) { - console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' ); - return new Uint8ClampedBufferAttribute( array, itemSize ); + return target.subVectors( this.end, this.start ); } - function Int16Attribute( array, itemSize ) { + distanceSq() { - console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' ); - return new Int16BufferAttribute( array, itemSize ); + return this.start.distanceToSquared( this.end ); } - function Uint16Attribute( array, itemSize ) { + distance() { - console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' ); - return new Uint16BufferAttribute( array, itemSize ); + return this.start.distanceTo( this.end ); } - function Int32Attribute( array, itemSize ) { + at( t, target ) { - console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' ); - return new Int32BufferAttribute( array, itemSize ); + return this.delta( target ).multiplyScalar( t ).add( this.start ); } - function Uint32Attribute( array, itemSize ) { + closestPointToPointParameter( point, clampToLine ) { - console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' ); - return new Uint32BufferAttribute( array, itemSize ); + _startP.subVectors( point, this.start ); + _startEnd.subVectors( this.end, this.start ); - } + const startEnd2 = _startEnd.dot( _startEnd ); + const startEnd_startP = _startEnd.dot( _startP ); - function Float32Attribute( array, itemSize ) { + let t = startEnd_startP / startEnd2; - console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' ); - return new Float32BufferAttribute( array, itemSize ); + if ( clampToLine ) { - } + t = clamp( t, 0, 1 ); - function Float64Attribute( array, itemSize ) { + } - console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' ); - return new Float64BufferAttribute( array, itemSize ); + return t; } - // - - Curve.create = function ( construct, getPoint ) { + closestPointToPoint( point, clampToLine, target ) { - console.log( 'THREE.Curve.create() has been deprecated' ); + const t = this.closestPointToPointParameter( point, clampToLine ); - construct.prototype = Object.create( Curve.prototype ); - construct.prototype.constructor = construct; - construct.prototype.getPoint = getPoint; + return this.delta( target ).multiplyScalar( t ).add( this.start ); - return construct; + } - }; + applyMatrix4( matrix ) { - // + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); - Object.assign( CurvePath.prototype, { + return this; - createPointsGeometry: function ( divisions ) { + } - console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); + equals( line ) { - // generate geometry from path points (for Line or Points objects) + return line.start.equals( this.start ) && line.end.equals( this.end ); - var pts = this.getPoints( divisions ); - return this.createGeometry( pts ); + } - }, + clone() { - createSpacedPointsGeometry: function ( divisions ) { + return new this.constructor().copy( this ); - console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); + } - // generate geometry from equidistant sampling along the path +} - var pts = this.getSpacedPoints( divisions ); - return this.createGeometry( pts ); +const _vector$3 = /*@__PURE__*/ new Vector3(); - }, +class SpotLightHelper extends Object3D { - createGeometry: function ( points ) { + constructor( light, color ) { - console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' ); + super(); - var geometry = new Geometry(); + this.light = light; - for ( var i = 0, l = points.length; i < l; i ++ ) { + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - var point = points[ i ]; - geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) ); + this.color = color; - } + this.type = 'SpotLightHelper'; - return geometry; + const geometry = new BufferGeometry(); - } + const positions = [ + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 1, + 0, 0, 0, - 1, 0, 1, + 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, - 1, 1 + ]; - } ); + for ( let i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { - // + const p1 = ( i / l ) * Math.PI * 2; + const p2 = ( j / l ) * Math.PI * 2; - Object.assign( Path.prototype, { + positions.push( + Math.cos( p1 ), Math.sin( p1 ), 1, + Math.cos( p2 ), Math.sin( p2 ), 1 + ); - fromPoints: function ( points ) { + } - console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' ); - this.setFromPoints( points ); + geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - } + const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); - } ); + this.cone = new LineSegments( geometry, material ); + this.add( this.cone ); - // + this.update(); - function ClosedSplineCurve3( points ) { + } - console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); + dispose() { - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; - this.closed = true; + this.cone.geometry.dispose(); + this.cone.material.dispose(); } - ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); + update() { - // + this.light.updateWorldMatrix( true, false ); + this.light.target.updateWorldMatrix( true, false ); - function SplineCurve3( points ) { + const coneLength = this.light.distance ? this.light.distance : 1000; + const coneWidth = coneLength * Math.tan( this.light.angle ); - console.warn( 'THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' ); + this.cone.scale.set( coneWidth, coneWidth, coneLength ); - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; + _vector$3.setFromMatrixPosition( this.light.target.matrixWorld ); - } + this.cone.lookAt( _vector$3 ); - SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype ); + if ( this.color !== undefined ) { - // + this.cone.material.color.set( this.color ); - function Spline( points ) { + } else { - console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' ); + this.cone.material.color.copy( this.light.color ); - CatmullRomCurve3.call( this, points ); - this.type = 'catmullrom'; + } } - Spline.prototype = Object.create( CatmullRomCurve3.prototype ); +} - Object.assign( Spline.prototype, { +const _vector$2 = /*@__PURE__*/ new Vector3(); +const _boneMatrix = /*@__PURE__*/ new Matrix4(); +const _matrixWorldInv = /*@__PURE__*/ new Matrix4(); - initFromArray: function ( /* a */ ) { - console.error( 'THREE.Spline: .initFromArray() has been removed.' ); +class SkeletonHelper extends LineSegments { - }, - getControlPointsArray: function ( /* optionalTarget */ ) { + constructor( object ) { - console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' ); + const bones = getBoneList( object ); - }, - reparametrizeByArcLength: function ( /* samplingCoef */ ) { + const geometry = new BufferGeometry(); - console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' ); + const vertices = []; + const colors = []; - } + const color1 = new Color( 0, 0, 1 ); + const color2 = new Color( 0, 1, 0 ); - } ); + for ( let i = 0; i < bones.length; i ++ ) { - // + const bone = bones[ i ]; + + if ( bone.parent && bone.parent.isBone ) { - function AxisHelper( size ) { + vertices.push( 0, 0, 0 ); + vertices.push( 0, 0, 0 ); + colors.push( color1.r, color1.g, color1.b ); + colors.push( color2.r, color2.g, color2.b ); - console.warn( 'THREE.AxisHelper has been renamed to THREE.AxesHelper.' ); - return new AxesHelper( size ); + } - } + } - function BoundingBoxHelper( object, color ) { + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' ); - return new BoxHelper( object, color ); + const material = new LineBasicMaterial( { vertexColors: true, depthTest: false, depthWrite: false, toneMapped: false, transparent: true } ); - } + super( geometry, material ); - function EdgesHelper( object, hex ) { + this.isSkeletonHelper = true; - console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' ); - return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); + this.type = 'SkeletonHelper'; - } + this.root = object; + this.bones = bones; - GridHelper.prototype.setColors = function () { + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); + } - }; + updateMatrixWorld( force ) { - SkeletonHelper.prototype.update = function () { + const bones = this.bones; - console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' ); + const geometry = this.geometry; + const position = geometry.getAttribute( 'position' ); - }; + _matrixWorldInv.copy( this.root.matrixWorld ).invert(); - function WireframeHelper( object, hex ) { + for ( let i = 0, j = 0; i < bones.length; i ++ ) { - console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' ); - return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) ); + const bone = bones[ i ]; - } + if ( bone.parent && bone.parent.isBone ) { - // + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j, _vector$2.x, _vector$2.y, _vector$2.z ); - Object.assign( Loader.prototype, { + _boneMatrix.multiplyMatrices( _matrixWorldInv, bone.parent.matrixWorld ); + _vector$2.setFromMatrixPosition( _boneMatrix ); + position.setXYZ( j + 1, _vector$2.x, _vector$2.y, _vector$2.z ); - extractUrlBase: function ( url ) { + j += 2; - console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' ); - return LoaderUtils.extractUrlBase( url ); + } } - } ); - - function XHRLoader( manager ) { + geometry.getAttribute( 'position' ).needsUpdate = true; - console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' ); - return new FileLoader( manager ); + super.updateMatrixWorld( force ); } - function BinaryTextureLoader( manager ) { + dispose() { - console.warn( 'THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.' ); - return new DataTextureLoader( manager ); + this.geometry.dispose(); + this.material.dispose(); } - Object.assign( ObjectLoader.prototype, { - - setTexturePath: function ( value ) { - - console.warn( 'THREE.ObjectLoader: .setTexturePath() has been renamed to .setResourcePath().' ); - return this.setResourcePath( value ); - - } +} - } ); - // +function getBoneList( object ) { - Object.assign( Box2.prototype, { + const boneList = []; - center: function ( optionalTarget ) { + if ( object.isBone === true ) { - console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + boneList.push( object ); - }, - empty: function () { + } - console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + for ( let i = 0; i < object.children.length; i ++ ) { - }, - isIntersectionBox: function ( box ) { + boneList.push.apply( boneList, getBoneList( object.children[ i ] ) ); - console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + } - }, - size: function ( optionalTarget ) { + return boneList; - console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); +} - } - } ); +class PointLightHelper extends Mesh { - Object.assign( Box3.prototype, { + constructor( light, sphereSize, color ) { - center: function ( optionalTarget ) { + const geometry = new SphereGeometry( sphereSize, 4, 2 ); + const material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); - console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + super( geometry, material ); - }, - empty: function () { + this.light = light; - console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); - return this.isEmpty(); + this.color = color; - }, - isIntersectionBox: function ( box ) { + this.type = 'PointLightHelper'; - console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; - }, - isIntersectionSphere: function ( sphere ) { + this.update(); - console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); - }, - size: function ( optionalTarget ) { + /* + // TODO: delete this comment? + const distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + const distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' ); - return this.getSize( optionalTarget ); + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - } - } ); + const d = light.distance; - Line3.prototype.center = function ( optionalTarget ) { + if ( d === 0.0 ) { - console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); - return this.getCenter( optionalTarget ); + this.lightDistance.visible = false; - }; + } else { - Object.assign( _Math, { + this.lightDistance.scale.set( d, d, d ); - random16: function () { + } - console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' ); - return Math.random(); + this.add( this.lightDistance ); + */ - }, + } - nearestPowerOfTwo: function ( value ) { + dispose() { - console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' ); - return _Math.floorPowerOfTwo( value ); + this.geometry.dispose(); + this.material.dispose(); - }, + } - nextPowerOfTwo: function ( value ) { + update() { - console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' ); - return _Math.ceilPowerOfTwo( value ); + this.light.updateWorldMatrix( true, false ); - } + if ( this.color !== undefined ) { - } ); + this.material.color.set( this.color ); - Object.assign( Matrix3.prototype, { + } else { - flattenToArrayOffset: function ( array, offset ) { + this.material.color.copy( this.light.color ); - console.warn( "THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); + } - }, - multiplyVector3: function ( vector ) { + /* + const d = this.light.distance; - console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); - return vector.applyMatrix3( this ); + if ( d === 0.0 ) { - }, - multiplyVector3Array: function ( /* a */ ) { + this.lightDistance.visible = false; - console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' ); + } else { - }, - applyToBuffer: function ( buffer /*, offset, length */ ) { + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); - console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); - return this.applyToBufferAttribute( buffer ); + } + */ - }, - applyToVector3Array: function ( /* array, offset, length */ ) { + } - console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' ); +} - } +const _vector$1 = /*@__PURE__*/ new Vector3(); +const _color1 = /*@__PURE__*/ new Color(); +const _color2 = /*@__PURE__*/ new Color(); - } ); +class HemisphereLightHelper extends Object3D { - Object.assign( Matrix4.prototype, { + constructor( light, size, color ) { - extractPosition: function ( m ) { + super(); - console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); - return this.copyPosition( m ); + this.light = light; - }, - flattenToArrayOffset: function ( array, offset ) { + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - console.warn( "THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead." ); - return this.toArray( array, offset ); + this.color = color; - }, - getPosition: function () { + this.type = 'HemisphereLightHelper'; - console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); - return new Vector3().setFromMatrixColumn( this, 3 ); + const geometry = new OctahedronGeometry( size ); + geometry.rotateY( Math.PI * 0.5 ); - }, - setRotationFromQuaternion: function ( q ) { + this.material = new MeshBasicMaterial( { wireframe: true, fog: false, toneMapped: false } ); + if ( this.color === undefined ) this.material.vertexColors = true; - console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); - return this.makeRotationFromQuaternion( q ); + const position = geometry.getAttribute( 'position' ); + const colors = new Float32Array( position.count * 3 ); - }, - multiplyToArray: function () { + geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) ); - console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' ); + this.add( new Mesh( geometry, this.material ) ); - }, - multiplyVector3: function ( vector ) { + this.update(); - console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + } - }, - multiplyVector4: function ( vector ) { + dispose() { - console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + this.children[ 0 ].geometry.dispose(); + this.children[ 0 ].material.dispose(); - }, - multiplyVector3Array: function ( /* a */ ) { + } - console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' ); + update() { - }, - rotateAxis: function ( v ) { + const mesh = this.children[ 0 ]; - console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); - v.transformDirection( this ); + if ( this.color !== undefined ) { - }, - crossVector: function ( vector ) { + this.material.color.set( this.color ); - console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); - return vector.applyMatrix4( this ); + } else { - }, - translate: function () { + const colors = mesh.geometry.getAttribute( 'color' ); - console.error( 'THREE.Matrix4: .translate() has been removed.' ); + _color1.copy( this.light.color ); + _color2.copy( this.light.groundColor ); - }, - rotateX: function () { + for ( let i = 0, l = colors.count; i < l; i ++ ) { - console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + const color = ( i < ( l / 2 ) ) ? _color1 : _color2; - }, - rotateY: function () { + colors.setXYZ( i, color.r, color.g, color.b ); - console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + } - }, - rotateZ: function () { + colors.needsUpdate = true; - console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + } - }, - rotateByAxis: function () { + this.light.updateWorldMatrix( true, false ); - console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + mesh.lookAt( _vector$1.setFromMatrixPosition( this.light.matrixWorld ).negate() ); - }, - applyToBuffer: function ( buffer /*, offset, length */ ) { + } - console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' ); - return this.applyToBufferAttribute( buffer ); +} - }, - applyToVector3Array: function ( /* array, offset, length */ ) { +class GridHelper extends LineSegments { - console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' ); + constructor( size = 10, divisions = 10, color1 = 0x444444, color2 = 0x888888 ) { - }, - makeFrustum: function ( left, right, bottom, top, near, far ) { + color1 = new Color( color1 ); + color2 = new Color( color2 ); - console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' ); - return this.makePerspective( left, right, top, bottom, near, far ); + const center = divisions / 2; + const step = size / divisions; + const halfSize = size / 2; - } + const vertices = [], colors = []; - } ); + for ( let i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) { - Plane.prototype.isIntersectionLine = function ( line ) { + vertices.push( - halfSize, 0, k, halfSize, 0, k ); + vertices.push( k, 0, - halfSize, k, 0, halfSize ); - console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); - return this.intersectsLine( line ); + const color = i === center ? color1 : color2; - }; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; - Quaternion.prototype.multiplyVector3 = function ( vector ) { + } - console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); - return vector.applyQuaternion( this ); + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - }; + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - Object.assign( Ray.prototype, { + super( geometry, material ); - isIntersectionBox: function ( box ) { + this.type = 'GridHelper'; - console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); - return this.intersectsBox( box ); + } - }, - isIntersectionPlane: function ( plane ) { + dispose() { - console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); - return this.intersectsPlane( plane ); + this.geometry.dispose(); + this.material.dispose(); - }, - isIntersectionSphere: function ( sphere ) { + } - console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); - return this.intersectsSphere( sphere ); +} - } +class PolarGridHelper extends LineSegments { - } ); + constructor( radius = 10, sectors = 16, rings = 8, divisions = 64, color1 = 0x444444, color2 = 0x888888 ) { - Object.assign( Triangle.prototype, { + color1 = new Color( color1 ); + color2 = new Color( color2 ); - area: function () { + const vertices = []; + const colors = []; - console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' ); - return this.getArea(); + // create the sectors - }, - barycoordFromPoint: function ( point, target ) { + if ( sectors > 1 ) { - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return this.getBarycoord( point, target ); + for ( let i = 0; i < sectors; i ++ ) { - }, - midpoint: function ( target ) { + const v = ( i / sectors ) * ( Math.PI * 2 ); - console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' ); - return this.getMidpoint( target ); + const x = Math.sin( v ) * radius; + const z = Math.cos( v ) * radius; - }, - normal: function ( target ) { + vertices.push( 0, 0, 0 ); + vertices.push( x, 0, z ); - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return this.getNormal( target ); + const color = ( i & 1 ) ? color1 : color2; - }, - plane: function ( target ) { + colors.push( color.r, color.g, color.b ); + colors.push( color.r, color.g, color.b ); - console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' ); - return this.getPlane( target ); + } } - } ); - - Object.assign( Triangle, { + // create the rings - barycoordFromPoint: function ( point, a, b, c, target ) { + for ( let i = 0; i < rings; i ++ ) { - console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' ); - return Triangle.getBarycoord( point, a, b, c, target ); + const color = ( i & 1 ) ? color1 : color2; - }, - normal: function ( a, b, c, target ) { + const r = radius - ( radius / rings * i ); - console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' ); - return Triangle.getNormal( a, b, c, target ); + for ( let j = 0; j < divisions; j ++ ) { - } + // first vertex - } ); + let v = ( j / divisions ) * ( Math.PI * 2 ); - Object.assign( Shape.prototype, { + let x = Math.sin( v ) * r; + let z = Math.cos( v ) * r; - extractAllPoints: function ( divisions ) { + vertices.push( x, 0, z ); + colors.push( color.r, color.g, color.b ); - console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' ); - return this.extractPoints( divisions ); + // second vertex - }, - extrude: function ( options ) { + v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 ); - console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); - return new ExtrudeGeometry( this, options ); + x = Math.sin( v ) * r; + z = Math.cos( v ) * r; - }, - makeGeometry: function ( options ) { + vertices.push( x, 0, z ); + colors.push( color.r, color.g, color.b ); - console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); - return new ShapeGeometry( this, options ); + } } - } ); + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - Object.assign( Vector2.prototype, { + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - fromAttribute: function ( attribute, index, offset ) { + super( geometry, material ); - console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + this.type = 'PolarGridHelper'; - }, - distanceToManhattan: function ( v ) { + } - console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + dispose() { - }, - lengthManhattan: function () { + this.geometry.dispose(); + this.material.dispose(); - console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + } - } +} - } ); +const _v1 = /*@__PURE__*/ new Vector3(); +const _v2 = /*@__PURE__*/ new Vector3(); +const _v3 = /*@__PURE__*/ new Vector3(); - Object.assign( Vector3.prototype, { +class DirectionalLightHelper extends Object3D { - setEulerFromRotationMatrix: function () { + constructor( light, size, color ) { - console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + super(); - }, - setEulerFromQuaternion: function () { + this.light = light; - console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - }, - getPositionFromMatrix: function ( m ) { + this.color = color; - console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); - return this.setFromMatrixPosition( m ); + this.type = 'DirectionalLightHelper'; - }, - getScaleFromMatrix: function ( m ) { + if ( size === undefined ) size = 1; - console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); - return this.setFromMatrixScale( m ); + let geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( [ + - size, size, 0, + size, size, 0, + size, - size, 0, + - size, - size, 0, + - size, size, 0 + ], 3 ) ); - }, - getColumnFromMatrix: function ( index, matrix ) { + const material = new LineBasicMaterial( { fog: false, toneMapped: false } ); - console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); - return this.setFromMatrixColumn( matrix, index ); + this.lightPlane = new Line( geometry, material ); + this.add( this.lightPlane ); - }, - applyProjection: function ( m ) { + geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); - console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' ); - return this.applyMatrix4( m ); + this.targetLine = new Line( geometry, material ); + this.add( this.targetLine ); - }, - fromAttribute: function ( attribute, index, offset ) { + this.update(); - console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + } - }, - distanceToManhattan: function ( v ) { + dispose() { - console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' ); - return this.manhattanDistanceTo( v ); + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); - }, - lengthManhattan: function () { + } - console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + update() { - } + this.light.updateWorldMatrix( true, false ); + this.light.target.updateWorldMatrix( true, false ); - } ); + _v1.setFromMatrixPosition( this.light.matrixWorld ); + _v2.setFromMatrixPosition( this.light.target.matrixWorld ); + _v3.subVectors( _v2, _v1 ); - Object.assign( Vector4.prototype, { + this.lightPlane.lookAt( _v2 ); - fromAttribute: function ( attribute, index, offset ) { + if ( this.color !== undefined ) { - console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' ); - return this.fromBufferAttribute( attribute, index, offset ); + this.lightPlane.material.color.set( this.color ); + this.targetLine.material.color.set( this.color ); - }, - lengthManhattan: function () { + } else { - console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' ); - return this.manhattanLength(); + this.lightPlane.material.color.copy( this.light.color ); + this.targetLine.material.color.copy( this.light.color ); } - } ); - - // - - Object.assign( Geometry.prototype, { + this.targetLine.lookAt( _v2 ); + this.targetLine.scale.z = _v3.length(); - computeTangents: function () { + } - console.error( 'THREE.Geometry: .computeTangents() has been removed.' ); +} - }, - computeLineDistances: function () { +const _vector = /*@__PURE__*/ new Vector3(); +const _camera = /*@__PURE__*/ new Camera(); - console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' ); +/** + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * https://github.com/evanw/lightgl.js/blob/master/tests/shadowmap.html + */ - } +class CameraHelper extends LineSegments { - } ); + constructor( camera ) { - Object.assign( Object3D.prototype, { + const geometry = new BufferGeometry(); + const material = new LineBasicMaterial( { color: 0xffffff, vertexColors: true, toneMapped: false } ); - getChildByName: function ( name ) { + const vertices = []; + const colors = []; - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name ); + const pointMap = {}; - }, - renderDepth: function () { + // near - console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + addLine( 'n1', 'n2' ); + addLine( 'n2', 'n4' ); + addLine( 'n4', 'n3' ); + addLine( 'n3', 'n1' ); - }, - translate: function ( distance, axis ) { + // far - console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); - return this.translateOnAxis( axis, distance ); + addLine( 'f1', 'f2' ); + addLine( 'f2', 'f4' ); + addLine( 'f4', 'f3' ); + addLine( 'f3', 'f1' ); - }, - getWorldRotation: function () { + // sides - console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' ); + addLine( 'n1', 'f1' ); + addLine( 'n2', 'f2' ); + addLine( 'n3', 'f3' ); + addLine( 'n4', 'f4' ); - } + // cone - } ); + addLine( 'p', 'n1' ); + addLine( 'p', 'n2' ); + addLine( 'p', 'n3' ); + addLine( 'p', 'n4' ); - Object.defineProperties( Object3D.prototype, { + // up - eulerOrder: { - get: function () { + addLine( 'u1', 'u2' ); + addLine( 'u2', 'u3' ); + addLine( 'u3', 'u1' ); - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - return this.rotation.order; + // target - }, - set: function ( value ) { + addLine( 'c', 't' ); + addLine( 'p', 'c' ); - console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); - this.rotation.order = value; + // cross - } - }, - useQuaternion: { - get: function () { + addLine( 'cn1', 'cn2' ); + addLine( 'cn3', 'cn4' ); - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + addLine( 'cf1', 'cf2' ); + addLine( 'cf3', 'cf4' ); - }, - set: function () { + function addLine( a, b ) { - console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + addPoint( a ); + addPoint( b ); - } } - } ); + function addPoint( id ) { - Object.defineProperties( LOD.prototype, { + vertices.push( 0, 0, 0 ); + colors.push( 0, 0, 0 ); - objects: { - get: function () { + if ( pointMap[ id ] === undefined ) { - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; + pointMap[ id ] = []; } - } - } ); + pointMap[ id ].push( ( vertices.length / 3 ) - 1 ); - Object.defineProperty( Skeleton.prototype, 'useVertexTexture', { + } - get: function () { + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); + super( geometry, material ); - }, - set: function () { + this.type = 'CameraHelper'; - console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' ); + this.camera = camera; + if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); - } + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; - } ); + this.pointMap = pointMap; - SkinnedMesh.prototype.initBones = function () { + this.update(); - console.error( 'THREE.SkinnedMesh: initBones() has been removed.' ); + // colors - }; + const colorFrustum = new Color( 0xffaa00 ); + const colorCone = new Color( 0xff0000 ); + const colorUp = new Color( 0x00aaff ); + const colorTarget = new Color( 0xffffff ); + const colorCross = new Color( 0x333333 ); - Object.defineProperty( Curve.prototype, '__arcLengthDivisions', { + this.setColors( colorFrustum, colorCone, colorUp, colorTarget, colorCross ); - get: function () { + } - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - return this.arcLengthDivisions; + setColors( frustum, cone, up, target, cross ) { - }, - set: function ( value ) { + const geometry = this.geometry; - console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' ); - this.arcLengthDivisions = value; + const colorAttribute = geometry.getAttribute( 'color' ); - } + // near - } ); + colorAttribute.setXYZ( 0, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 1, frustum.r, frustum.g, frustum.b ); // n1, n2 + colorAttribute.setXYZ( 2, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 3, frustum.r, frustum.g, frustum.b ); // n2, n4 + colorAttribute.setXYZ( 4, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 5, frustum.r, frustum.g, frustum.b ); // n4, n3 + colorAttribute.setXYZ( 6, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 7, frustum.r, frustum.g, frustum.b ); // n3, n1 - // + // far - PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { + colorAttribute.setXYZ( 8, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 9, frustum.r, frustum.g, frustum.b ); // f1, f2 + colorAttribute.setXYZ( 10, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 11, frustum.r, frustum.g, frustum.b ); // f2, f4 + colorAttribute.setXYZ( 12, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 13, frustum.r, frustum.g, frustum.b ); // f4, f3 + colorAttribute.setXYZ( 14, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 15, frustum.r, frustum.g, frustum.b ); // f3, f1 - console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + - "Use .setFocalLength and .filmGauge for a photographic setup." ); + // sides - if ( filmGauge !== undefined ) this.filmGauge = filmGauge; - this.setFocalLength( focalLength ); + colorAttribute.setXYZ( 16, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 17, frustum.r, frustum.g, frustum.b ); // n1, f1 + colorAttribute.setXYZ( 18, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 19, frustum.r, frustum.g, frustum.b ); // n2, f2 + colorAttribute.setXYZ( 20, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 21, frustum.r, frustum.g, frustum.b ); // n3, f3 + colorAttribute.setXYZ( 22, frustum.r, frustum.g, frustum.b ); colorAttribute.setXYZ( 23, frustum.r, frustum.g, frustum.b ); // n4, f4 - }; + // cone - // + colorAttribute.setXYZ( 24, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 25, cone.r, cone.g, cone.b ); // p, n1 + colorAttribute.setXYZ( 26, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 27, cone.r, cone.g, cone.b ); // p, n2 + colorAttribute.setXYZ( 28, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 29, cone.r, cone.g, cone.b ); // p, n3 + colorAttribute.setXYZ( 30, cone.r, cone.g, cone.b ); colorAttribute.setXYZ( 31, cone.r, cone.g, cone.b ); // p, n4 - Object.defineProperties( Light.prototype, { - onlyShadow: { - set: function () { + // up - console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + colorAttribute.setXYZ( 32, up.r, up.g, up.b ); colorAttribute.setXYZ( 33, up.r, up.g, up.b ); // u1, u2 + colorAttribute.setXYZ( 34, up.r, up.g, up.b ); colorAttribute.setXYZ( 35, up.r, up.g, up.b ); // u2, u3 + colorAttribute.setXYZ( 36, up.r, up.g, up.b ); colorAttribute.setXYZ( 37, up.r, up.g, up.b ); // u3, u1 - } - }, - shadowCameraFov: { - set: function ( value ) { + // target - console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); - this.shadow.camera.fov = value; + colorAttribute.setXYZ( 38, target.r, target.g, target.b ); colorAttribute.setXYZ( 39, target.r, target.g, target.b ); // c, t + colorAttribute.setXYZ( 40, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 41, cross.r, cross.g, cross.b ); // p, c - } - }, - shadowCameraLeft: { - set: function ( value ) { + // cross - console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); - this.shadow.camera.left = value; + colorAttribute.setXYZ( 42, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 43, cross.r, cross.g, cross.b ); // cn1, cn2 + colorAttribute.setXYZ( 44, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 45, cross.r, cross.g, cross.b ); // cn3, cn4 - } - }, - shadowCameraRight: { - set: function ( value ) { + colorAttribute.setXYZ( 46, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 47, cross.r, cross.g, cross.b ); // cf1, cf2 + colorAttribute.setXYZ( 48, cross.r, cross.g, cross.b ); colorAttribute.setXYZ( 49, cross.r, cross.g, cross.b ); // cf3, cf4 - console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); - this.shadow.camera.right = value; + colorAttribute.needsUpdate = true; - } - }, - shadowCameraTop: { - set: function ( value ) { + } - console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); - this.shadow.camera.top = value; + update() { - } - }, - shadowCameraBottom: { - set: function ( value ) { + const geometry = this.geometry; + const pointMap = this.pointMap; - console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); - this.shadow.camera.bottom = value; + const w = 1, h = 1; - } - }, - shadowCameraNear: { - set: function ( value ) { + // we need just camera projection matrix inverse + // world matrix must be identity - console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); - this.shadow.camera.near = value; + _camera.projectionMatrixInverse.copy( this.camera.projectionMatrixInverse ); - } - }, - shadowCameraFar: { - set: function ( value ) { + // center / target - console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); - this.shadow.camera.far = value; + setPoint( 'c', pointMap, geometry, _camera, 0, 0, - 1 ); + setPoint( 't', pointMap, geometry, _camera, 0, 0, 1 ); - } - }, - shadowCameraVisible: { - set: function () { + // near - console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); + setPoint( 'n1', pointMap, geometry, _camera, - w, - h, - 1 ); + setPoint( 'n2', pointMap, geometry, _camera, w, - h, - 1 ); + setPoint( 'n3', pointMap, geometry, _camera, - w, h, - 1 ); + setPoint( 'n4', pointMap, geometry, _camera, w, h, - 1 ); - } - }, - shadowBias: { - set: function ( value ) { + // far - console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); - this.shadow.bias = value; + setPoint( 'f1', pointMap, geometry, _camera, - w, - h, 1 ); + setPoint( 'f2', pointMap, geometry, _camera, w, - h, 1 ); + setPoint( 'f3', pointMap, geometry, _camera, - w, h, 1 ); + setPoint( 'f4', pointMap, geometry, _camera, w, h, 1 ); - } - }, - shadowDarkness: { - set: function () { + // up - console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); + setPoint( 'u1', pointMap, geometry, _camera, w * 0.7, h * 1.1, - 1 ); + setPoint( 'u2', pointMap, geometry, _camera, - w * 0.7, h * 1.1, - 1 ); + setPoint( 'u3', pointMap, geometry, _camera, 0, h * 2, - 1 ); - } - }, - shadowMapWidth: { - set: function ( value ) { + // cross - console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); - this.shadow.mapSize.width = value; + setPoint( 'cf1', pointMap, geometry, _camera, - w, 0, 1 ); + setPoint( 'cf2', pointMap, geometry, _camera, w, 0, 1 ); + setPoint( 'cf3', pointMap, geometry, _camera, 0, - h, 1 ); + setPoint( 'cf4', pointMap, geometry, _camera, 0, h, 1 ); - } - }, - shadowMapHeight: { - set: function ( value ) { + setPoint( 'cn1', pointMap, geometry, _camera, - w, 0, - 1 ); + setPoint( 'cn2', pointMap, geometry, _camera, w, 0, - 1 ); + setPoint( 'cn3', pointMap, geometry, _camera, 0, - h, - 1 ); + setPoint( 'cn4', pointMap, geometry, _camera, 0, h, - 1 ); - console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); - this.shadow.mapSize.height = value; + geometry.getAttribute( 'position' ).needsUpdate = true; - } - } - } ); + } - // + dispose() { - Object.defineProperties( BufferAttribute.prototype, { + this.geometry.dispose(); + this.material.dispose(); - length: { - get: function () { + } - console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' ); - return this.array.length; +} - } - }, - copyIndicesArray: function ( /* indices */ ) { - console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' ); +function setPoint( point, pointMap, geometry, camera, x, y, z ) { - } + _vector.set( x, y, z ).unproject( camera ); - } ); + const points = pointMap[ point ]; - Object.assign( BufferGeometry.prototype, { + if ( points !== undefined ) { - addIndex: function ( index ) { + const position = geometry.getAttribute( 'position' ); - console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); - this.setIndex( index ); + for ( let i = 0, l = points.length; i < l; i ++ ) { - }, - addDrawCall: function ( start, count, indexOffset ) { + position.setXYZ( points[ i ], _vector.x, _vector.y, _vector.z ); - if ( indexOffset !== undefined ) { + } - console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + } - } - console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); - this.addGroup( start, count ); +} - }, - clearDrawCalls: function () { +const _box = /*@__PURE__*/ new Box3(); - console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); - this.clearGroups(); +class BoxHelper extends LineSegments { - }, - computeTangents: function () { + constructor( object, color = 0xffff00 ) { - console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); + const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + const positions = new Float32Array( 8 * 3 ); - }, - computeOffsets: function () { + const geometry = new BufferGeometry(); + geometry.setIndex( new BufferAttribute( indices, 1 ) ); + geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) ); - console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - } + this.object = object; + this.type = 'BoxHelper'; - } ); + this.matrixAutoUpdate = false; - Object.defineProperties( BufferGeometry.prototype, { + this.update(); - drawcalls: { - get: function () { + } - console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); - return this.groups; + update( object ) { - } - }, - offsets: { - get: function () { + if ( object !== undefined ) { - console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); - return this.groups; + console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' ); - } } - } ); - - // - - Object.assign( ExtrudeBufferGeometry.prototype, { + if ( this.object !== undefined ) { - getArrays: function () { + _box.setFromObject( this.object ); - console.error( 'THREE.ExtrudeBufferGeometry: .getArrays() has been removed.' ); + } - }, + if ( _box.isEmpty() ) return; - addShapeList: function () { + const min = _box.min; + const max = _box.max; - console.error( 'THREE.ExtrudeBufferGeometry: .addShapeList() has been removed.' ); + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ - }, + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ - addShape: function () { + const position = this.geometry.attributes.position; + const array = position.array; - console.error( 'THREE.ExtrudeBufferGeometry: .addShape() has been removed.' ); + array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; + array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; + array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; + array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; + array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; + array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; + array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; + array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; - } + position.needsUpdate = true; - } ); + this.geometry.computeBoundingSphere(); - // + } - Object.defineProperties( Uniform.prototype, { + setFromObject( object ) { - dynamic: { - set: function () { + this.object = object; + this.update(); - console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' ); + return this; - } - }, - onUpdate: { - value: function () { + } - console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' ); - return this; + copy( source, recursive ) { - } - } + super.copy( source, recursive ); - } ); + this.object = source.object; - // + return this; - Object.defineProperties( Material.prototype, { + } - wrapAround: { - get: function () { + dispose() { - console.warn( 'THREE.Material: .wrapAround has been removed.' ); + this.geometry.dispose(); + this.material.dispose(); - }, - set: function () { + } - console.warn( 'THREE.Material: .wrapAround has been removed.' ); +} - } - }, +class Box3Helper extends LineSegments { - overdraw: { - get: function () { + constructor( box, color = 0xffff00 ) { - console.warn( 'THREE.Material: .overdraw has been removed.' ); + const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); - }, - set: function () { + const positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ]; - console.warn( 'THREE.Material: .overdraw has been removed.' ); + const geometry = new BufferGeometry(); - } - }, + geometry.setIndex( new BufferAttribute( indices, 1 ) ); - wrapRGB: { - get: function () { + geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); - console.warn( 'THREE.Material: .wrapRGB has been removed.' ); - return new Color(); + super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - } - }, + this.box = box; - shading: { - get: function () { + this.type = 'Box3Helper'; - console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); + this.geometry.computeBoundingSphere(); - }, - set: function ( value ) { + } - console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' ); - this.flatShading = ( value === FlatShading ); + updateMatrixWorld( force ) { - } - } + const box = this.box; - } ); + if ( box.isEmpty() ) return; - Object.defineProperties( MeshPhongMaterial.prototype, { + box.getCenter( this.position ); - metal: { - get: function () { + box.getSize( this.scale ); - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); - return false; + this.scale.multiplyScalar( 0.5 ); - }, - set: function () { + super.updateMatrixWorld( force ); - console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); + } - } - } + dispose() { - } ); + this.geometry.dispose(); + this.material.dispose(); - Object.defineProperties( ShaderMaterial.prototype, { + } - derivatives: { - get: function () { +} - console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - return this.extensions.derivatives; +class PlaneHelper extends Line { - }, - set: function ( value ) { + constructor( plane, size = 1, hex = 0xffff00 ) { - console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); - this.extensions.derivatives = value; + const color = hex; - } - } + const positions = [ 1, - 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ]; - } ); + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) ); + geometry.computeBoundingSphere(); - // + super( geometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); - Object.assign( WebGLRenderer.prototype, { + this.type = 'PlaneHelper'; - clearTarget: function ( renderTarget, color, depth, stencil ) { + this.plane = plane; - console.warn( 'THREE.WebGLRenderer: .clearTarget() has been deprecated. Use .setRenderTarget() and .clear() instead.' ); - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + this.size = size; - }, - animate: function ( callback ) { + const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ]; - console.warn( 'THREE.WebGLRenderer: .animate() is now .setAnimationLoop().' ); - this.setAnimationLoop( callback ); + const geometry2 = new BufferGeometry(); + geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) ); + geometry2.computeBoundingSphere(); - }, - getCurrentRenderTarget: function () { + this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false, toneMapped: false } ) ) ); - console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' ); - return this.getRenderTarget(); + } - }, - getMaxAnisotropy: function () { + updateMatrixWorld( force ) { - console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' ); - return this.capabilities.getMaxAnisotropy(); + this.position.set( 0, 0, 0 ); - }, - getPrecision: function () { + this.scale.set( 0.5 * this.size, 0.5 * this.size, 1 ); - console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' ); - return this.capabilities.precision; + this.lookAt( this.plane.normal ); - }, - resetGLState: function () { + this.translateZ( - this.plane.constant ); - console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' ); - return this.state.reset(); + super.updateMatrixWorld( force ); - }, - supportsFloatTextures: function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return this.extensions.get( 'OES_texture_float' ); + dispose() { - }, - supportsHalfFloatTextures: function () { + this.geometry.dispose(); + this.material.dispose(); + this.children[ 0 ].geometry.dispose(); + this.children[ 0 ].material.dispose(); - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return this.extensions.get( 'OES_texture_half_float' ); + } - }, - supportsStandardDerivatives: function () { +} - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return this.extensions.get( 'OES_standard_derivatives' ); +const _axis = /*@__PURE__*/ new Vector3(); +let _lineGeometry, _coneGeometry; - }, - supportsCompressedTextureS3TC: function () { +class ArrowHelper extends Object3D { - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); + // dir is assumed to be normalized - }, - supportsCompressedTexturePVRTC: function () { + constructor( dir = new Vector3( 0, 0, 1 ), origin = new Vector3( 0, 0, 0 ), length = 1, color = 0xffff00, headLength = length * 0.2, headWidth = headLength * 0.2 ) { - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + super(); - }, - supportsBlendMinMax: function () { + this.type = 'ArrowHelper'; - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return this.extensions.get( 'EXT_blend_minmax' ); + if ( _lineGeometry === undefined ) { - }, - supportsVertexTextures: function () { + _lineGeometry = new BufferGeometry(); + _lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); - console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' ); - return this.capabilities.vertexTextures; + _coneGeometry = new CylinderGeometry( 0, 0.5, 1, 5, 1 ); + _coneGeometry.translate( 0, - 0.5, 0 ); - }, - supportsInstancedArrays: function () { + } - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return this.extensions.get( 'ANGLE_instanced_arrays' ); + this.position.copy( origin ); - }, - enableScissorTest: function ( boolean ) { + this.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); - console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); - this.setScissorTest( boolean ); + this.cone = new Mesh( _coneGeometry, new MeshBasicMaterial( { color: color, toneMapped: false } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); - }, - initMaterial: function () { + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + } - }, - addPrePlugin: function () { + setDirection( dir ) { - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + // dir is assumed to be normalized - }, - addPostPlugin: function () { + if ( dir.y > 0.99999 ) { - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + this.quaternion.set( 0, 0, 0, 1 ); - }, - updateShadowMap: function () { + } else if ( dir.y < - 0.99999 ) { - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + this.quaternion.set( 1, 0, 0, 0 ); - }, - setFaceCulling: function () { + } else { - console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' ); + _axis.set( dir.z, 0, - dir.x ).normalize(); - }, - allocTextureUnit: function () { + const radians = Math.acos( dir.y ); - console.warn( 'THREE.WebGLRenderer: .allocTextureUnit() has been removed.' ); + this.quaternion.setFromAxisAngle( _axis, radians ); - }, - setTexture: function () { + } - console.warn( 'THREE.WebGLRenderer: .setTexture() has been removed.' ); + } - }, - setTexture2D: function () { + setLength( length, headLength = length * 0.2, headWidth = headLength * 0.2 ) { - console.warn( 'THREE.WebGLRenderer: .setTexture2D() has been removed.' ); + this.line.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458 + this.line.updateMatrix(); - }, - setTextureCube: function () { + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); - console.warn( 'THREE.WebGLRenderer: .setTextureCube() has been removed.' ); + } - }, - getActiveMipMapLevel: function () { + setColor( color ) { - console.warn( 'THREE.WebGLRenderer: .getActiveMipMapLevel() is now .getActiveMipmapLevel().' ); - return this.getActiveMipmapLevel(); + this.line.material.color.set( color ); + this.cone.material.color.set( color ); - } + } - } ); + copy( source ) { - Object.defineProperties( WebGLRenderer.prototype, { + super.copy( source, false ); - shadowMapEnabled: { - get: function () { + this.line.copy( source.line ); + this.cone.copy( source.cone ); - return this.shadowMap.enabled; + return this; - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - this.shadowMap.enabled = value; + dispose() { - } - }, - shadowMapType: { - get: function () { + this.line.geometry.dispose(); + this.line.material.dispose(); + this.cone.geometry.dispose(); + this.cone.material.dispose(); - return this.shadowMap.type; + } - }, - set: function ( value ) { +} - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - this.shadowMap.type = value; +class AxesHelper extends LineSegments { - } - }, - shadowMapCullFace: { - get: function () { + constructor( size = 1 ) { - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + const vertices = [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ]; - }, - set: function ( /* value */ ) { + const colors = [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ]; - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' ); + const geometry = new BufferGeometry(); + geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) ); + geometry.setAttribute( 'color', new Float32BufferAttribute( colors, 3 ) ); - } - }, - context: { - get: function () { + const material = new LineBasicMaterial( { vertexColors: true, toneMapped: false } ); - console.warn( 'THREE.WebGLRenderer: .context has been removed. Use .getContext() instead.' ); - return this.getContext(); + super( geometry, material ); - } - } + this.type = 'AxesHelper'; - } ); + } - Object.defineProperties( WebGLShadowMap.prototype, { + setColors( xAxisColor, yAxisColor, zAxisColor ) { - cullFace: { - get: function () { + const color = new Color(); + const array = this.geometry.attributes.color.array; - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); - return undefined; + color.set( xAxisColor ); + color.toArray( array, 0 ); + color.toArray( array, 3 ); - }, - set: function ( /* cullFace */ ) { + color.set( yAxisColor ); + color.toArray( array, 6 ); + color.toArray( array, 9 ); - console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' ); + color.set( zAxisColor ); + color.toArray( array, 12 ); + color.toArray( array, 15 ); - } - }, - renderReverseSided: { - get: function () { + this.geometry.attributes.color.needsUpdate = true; - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); - return undefined; + return this; - }, - set: function () { + } - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' ); + dispose() { - } - }, - renderSingleSided: { - get: function () { + this.geometry.dispose(); + this.material.dispose(); - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); - return undefined; + } - }, - set: function () { +} - console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' ); +class ShapePath { - } - } + constructor() { - } ); + this.type = 'ShapePath'; - // + this.color = new Color(); - Object.defineProperties( WebGLRenderTargetCube.prototype, { + this.subPaths = []; + this.currentPath = null; - activeCubeFace: { - set: function ( /* value */ ) { + } - console.warn( 'THREE.WebGLRenderTargetCube: .activeCubeFace has been removed. It is now the second parameter of WebGLRenderer.setRenderTarget().' ); + moveTo( x, y ) { - } - }, - activeMipMapLevel: { - set: function ( /* value */ ) { + this.currentPath = new Path(); + this.subPaths.push( this.currentPath ); + this.currentPath.moveTo( x, y ); - console.warn( 'THREE.WebGLRenderTargetCube: .activeMipMapLevel has been removed. It is now the third parameter of WebGLRenderer.setRenderTarget().' ); + return this; - } - } + } - } ); + lineTo( x, y ) { - // + this.currentPath.lineTo( x, y ); - Object.defineProperties( WebGLRenderTarget.prototype, { + return this; - wrapS: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - return this.texture.wrapS; + quadraticCurveTo( aCPx, aCPy, aX, aY ) { - }, - set: function ( value ) { + this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); - console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); - this.texture.wrapS = value; + return this; - } - }, - wrapT: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - return this.texture.wrapT; + bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { - }, - set: function ( value ) { + this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); - console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); - this.texture.wrapT = value; + return this; - } - }, - magFilter: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - return this.texture.magFilter; + splineThru( pts ) { - }, - set: function ( value ) { + this.currentPath.splineThru( pts ); - console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); - this.texture.magFilter = value; + return this; - } - }, - minFilter: { - get: function () { + } - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - return this.texture.minFilter; + toShapes( isCCW ) { - }, - set: function ( value ) { + function toShapesNoHoles( inSubpaths ) { - console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); - this.texture.minFilter = value; + const shapes = []; - } - }, - anisotropy: { - get: function () { + for ( let i = 0, l = inSubpaths.length; i < l; i ++ ) { - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - return this.texture.anisotropy; + const tmpPath = inSubpaths[ i ]; - }, - set: function ( value ) { + const tmpShape = new Shape(); + tmpShape.curves = tmpPath.curves; - console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); - this.texture.anisotropy = value; + shapes.push( tmpShape ); } - }, - offset: { - get: function () { - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - return this.texture.offset; + return shapes; - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); - this.texture.offset = value; + function isPointInsidePolygon( inPt, inPolygon ) { - } - }, - repeat: { - get: function () { + const polyLen = inPolygon.length; - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - return this.texture.repeat; + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + let inside = false; + for ( let p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - }, - set: function ( value ) { + let edgeLowPt = inPolygon[ p ]; + let edgeHighPt = inPolygon[ q ]; - console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); - this.texture.repeat = value; + let edgeDx = edgeHighPt.x - edgeLowPt.x; + let edgeDy = edgeHighPt.y - edgeLowPt.y; - } - }, - format: { - get: function () { + if ( Math.abs( edgeDy ) > Number.EPSILON ) { - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - return this.texture.format; + // not parallel + if ( edgeDy < 0 ) { - }, - set: function ( value ) { + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); - this.texture.format = value; + } - } - }, - type: { - get: function () { + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - return this.texture.type; + if ( inPt.y === edgeLowPt.y ) { - }, - set: function ( value ) { + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! - console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); - this.texture.type = value; + } else { - } - }, - generateMipmaps: { - get: function () { + const perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - return this.texture.generateMipmaps; + } - }, - set: function ( value ) { + } else { + + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; - console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); - this.texture.generateMipmaps = value; + } } - } - } ); + return inside; - // + } - Object.defineProperties( WebVRManager.prototype, { + const isClockWise = ShapeUtils.isClockWise; - standing: { - set: function ( /* value */ ) { + const subPaths = this.subPaths; + if ( subPaths.length === 0 ) return []; - console.warn( 'THREE.WebVRManager: .standing has been removed.' ); + let solid, tmpPath, tmpShape; + const shapes = []; - } - }, - userHeight: { - set: function ( /* value */ ) { + if ( subPaths.length === 1 ) { - console.warn( 'THREE.WebVRManager: .userHeight has been removed.' ); + tmpPath = subPaths[ 0 ]; + tmpShape = new Shape(); + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; - } } - } ); - - // + let holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; - Audio.prototype.load = function ( file ) { + // console.log("Holes first", holesFirst); - console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' ); - var scope = this; - var audioLoader = new AudioLoader(); - audioLoader.load( file, function ( buffer ) { + const betterShapeHoles = []; + const newShapes = []; + let newShapeHoles = []; + let mainIdx = 0; + let tmpPoints; - scope.setBuffer( buffer ); + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; - } ); - return this; + for ( let i = 0, l = subPaths.length; i < l; i ++ ) { - }; + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; - AudioAnalyser.prototype.getData = function () { + if ( solid ) { - console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' ); - return this.getFrequencyData(); + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - }; + newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.curves = tmpPath.curves; - // + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; - CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) { + //console.log('cw', i); - console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' ); - return this.update( renderer, scene ); + } else { - }; + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - // + //console.log('ccw', i); - var GeometryUtils = { + } - merge: function ( geometry1, geometry2, materialIndexOffset ) { + } - console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); - var matrix; + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); - if ( geometry2.isMesh ) { - geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + if ( newShapes.length > 1 ) { - matrix = geometry2.matrix; - geometry2 = geometry2.geometry; + let ambiguous = false; + let toChange = 0; - } + for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - geometry1.merge( geometry2, matrix, materialIndexOffset ); + betterShapeHoles[ sIdx ] = []; - }, + } - center: function ( geometry ) { + for ( let sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); - return geometry.center(); + const sho = newShapeHoles[ sIdx ]; - } + for ( let hIdx = 0; hIdx < sho.length; hIdx ++ ) { - }; + const ho = sho[ hIdx ]; + let hole_unassigned = true; - ImageUtils.crossOrigin = undefined; + for ( let s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - ImageUtils.loadTexture = function ( url, mapping, onLoad, onError ) { + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); + if ( sIdx !== s2Idx ) toChange ++; - var loader = new TextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + if ( hole_unassigned ) { - var texture = loader.load( url, onLoad, undefined, onError ); + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); - if ( mapping ) texture.mapping = mapping; + } else { - return texture; + ambiguous = true; - }; + } - ImageUtils.loadTextureCube = function ( urls, mapping, onLoad, onError ) { + } - console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); + } - var loader = new CubeTextureLoader(); - loader.setCrossOrigin( this.crossOrigin ); + if ( hole_unassigned ) { - var texture = loader.load( urls, onLoad, undefined, onError ); + betterShapeHoles[ sIdx ].push( ho ); - if ( mapping ) texture.mapping = mapping; + } - return texture; + } - }; + } - ImageUtils.loadCompressedTexture = function () { + if ( toChange > 0 && ambiguous === false ) { - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); + newShapeHoles = betterShapeHoles; - }; + } - ImageUtils.loadCompressedTextureCube = function () { + } - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + let tmpHoles; - }; + for ( let i = 0, il = newShapes.length; i < il; i ++ ) { - // + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; - function CanvasRenderer() { + for ( let j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - console.error( 'THREE.CanvasRenderer has been removed' ); + tmpShape.holes.push( tmpHoles[ j ].h ); - } + } - // + } - function JSONLoader() { + //console.log("shape", shapes); - console.error( 'THREE.JSONLoader has been removed.' ); + return shapes; } - // - - var SceneUtils = { - - createMultiMaterialObject: function ( /* geometry, materials */ ) { +} - console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' ); +if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) { - }, + __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'register', { detail: { + revision: REVISION, + } } ) ); - detach: function ( /* child, parent, scene */ ) { +} - console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' ); +if ( typeof window !== 'undefined' ) { - }, + if ( window.__THREE__ ) { - attach: function ( /* child, scene, parent */ ) { + console.warn( 'WARNING: Multiple instances of Three.js being imported.' ); - console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' ); + } else { - } + window.__THREE__ = REVISION; - }; + } - // +} - function LensFlare() { - - console.error( 'THREE.LensFlare has been moved to /examples/js/objects/Lensflare.js' ); - - } - - exports.ACESFilmicToneMapping = ACESFilmicToneMapping; - exports.AddEquation = AddEquation; - exports.AddOperation = AddOperation; - exports.AdditiveBlending = AdditiveBlending; - exports.AlphaFormat = AlphaFormat; - exports.AlwaysDepth = AlwaysDepth; - exports.AlwaysStencilFunc = AlwaysStencilFunc; - exports.AmbientLight = AmbientLight; - exports.AmbientLightProbe = AmbientLightProbe; - exports.AnimationClip = AnimationClip; - exports.AnimationLoader = AnimationLoader; - exports.AnimationMixer = AnimationMixer; - exports.AnimationObjectGroup = AnimationObjectGroup; - exports.AnimationUtils = AnimationUtils; - exports.ArcCurve = ArcCurve; - exports.ArrayCamera = ArrayCamera; - exports.ArrowHelper = ArrowHelper; - exports.Audio = Audio; - exports.AudioAnalyser = AudioAnalyser; - exports.AudioContext = AudioContext; - exports.AudioListener = AudioListener; - exports.AudioLoader = AudioLoader; - exports.AxesHelper = AxesHelper; - exports.AxisHelper = AxisHelper; - exports.BackSide = BackSide; - exports.BasicDepthPacking = BasicDepthPacking; - exports.BasicShadowMap = BasicShadowMap; - exports.BinaryTextureLoader = BinaryTextureLoader; - exports.Bone = Bone; - exports.BooleanKeyframeTrack = BooleanKeyframeTrack; - exports.BoundingBoxHelper = BoundingBoxHelper; - exports.Box2 = Box2; - exports.Box3 = Box3; - exports.Box3Helper = Box3Helper; - exports.BoxBufferGeometry = BoxBufferGeometry; - exports.BoxGeometry = BoxGeometry; - exports.BoxHelper = BoxHelper; - exports.BufferAttribute = BufferAttribute; - exports.BufferGeometry = BufferGeometry; - exports.BufferGeometryLoader = BufferGeometryLoader; - exports.ByteType = ByteType; - exports.Cache = Cache; - exports.Camera = Camera; - exports.CameraHelper = CameraHelper; - exports.CanvasRenderer = CanvasRenderer; - exports.CanvasTexture = CanvasTexture; - exports.CatmullRomCurve3 = CatmullRomCurve3; - exports.CineonToneMapping = CineonToneMapping; - exports.CircleBufferGeometry = CircleBufferGeometry; - exports.CircleGeometry = CircleGeometry; - exports.ClampToEdgeWrapping = ClampToEdgeWrapping; - exports.Clock = Clock; - exports.ClosedSplineCurve3 = ClosedSplineCurve3; - exports.Color = Color; - exports.ColorKeyframeTrack = ColorKeyframeTrack; - exports.CompressedTexture = CompressedTexture; - exports.CompressedTextureLoader = CompressedTextureLoader; - exports.ConeBufferGeometry = ConeBufferGeometry; - exports.ConeGeometry = ConeGeometry; - exports.CubeCamera = CubeCamera; - exports.CubeGeometry = BoxGeometry; - exports.CubeReflectionMapping = CubeReflectionMapping; - exports.CubeRefractionMapping = CubeRefractionMapping; - exports.CubeTexture = CubeTexture; - exports.CubeTextureLoader = CubeTextureLoader; - exports.CubeUVReflectionMapping = CubeUVReflectionMapping; - exports.CubeUVRefractionMapping = CubeUVRefractionMapping; - exports.CubicBezierCurve = CubicBezierCurve; - exports.CubicBezierCurve3 = CubicBezierCurve3; - exports.CubicInterpolant = CubicInterpolant; - exports.CullFaceBack = CullFaceBack; - exports.CullFaceFront = CullFaceFront; - exports.CullFaceFrontBack = CullFaceFrontBack; - exports.CullFaceNone = CullFaceNone; - exports.Curve = Curve; - exports.CurvePath = CurvePath; - exports.CustomBlending = CustomBlending; - exports.CylinderBufferGeometry = CylinderBufferGeometry; - exports.CylinderGeometry = CylinderGeometry; - exports.Cylindrical = Cylindrical; - exports.DataTexture = DataTexture; - exports.DataTexture2DArray = DataTexture2DArray; - exports.DataTexture3D = DataTexture3D; - exports.DataTextureLoader = DataTextureLoader; - exports.DecrementStencilOp = DecrementStencilOp; - exports.DecrementWrapStencilOp = DecrementWrapStencilOp; - exports.DefaultLoadingManager = DefaultLoadingManager; - exports.DepthFormat = DepthFormat; - exports.DepthStencilFormat = DepthStencilFormat; - exports.DepthTexture = DepthTexture; - exports.DirectionalLight = DirectionalLight; - exports.DirectionalLightHelper = DirectionalLightHelper; - exports.DirectionalLightShadow = DirectionalLightShadow; - exports.DiscreteInterpolant = DiscreteInterpolant; - exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry; - exports.DodecahedronGeometry = DodecahedronGeometry; - exports.DoubleSide = DoubleSide; - exports.DstAlphaFactor = DstAlphaFactor; - exports.DstColorFactor = DstColorFactor; - exports.DynamicBufferAttribute = DynamicBufferAttribute; - exports.EdgesGeometry = EdgesGeometry; - exports.EdgesHelper = EdgesHelper; - exports.EllipseCurve = EllipseCurve; - exports.EqualDepth = EqualDepth; - exports.EqualStencilFunc = EqualStencilFunc; - exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping; - exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping; - exports.Euler = Euler; - exports.EventDispatcher = EventDispatcher; - exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry; - exports.ExtrudeGeometry = ExtrudeGeometry; - exports.Face3 = Face3; - exports.Face4 = Face4; - exports.FaceColors = FaceColors; - exports.FaceNormalsHelper = FaceNormalsHelper; - exports.FileLoader = FileLoader; - exports.FlatShading = FlatShading; - exports.Float32Attribute = Float32Attribute; - exports.Float32BufferAttribute = Float32BufferAttribute; - exports.Float64Attribute = Float64Attribute; - exports.Float64BufferAttribute = Float64BufferAttribute; - exports.FloatType = FloatType; - exports.Fog = Fog; - exports.FogExp2 = FogExp2; - exports.Font = Font; - exports.FontLoader = FontLoader; - exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW; - exports.FrontFaceDirectionCW = FrontFaceDirectionCW; - exports.FrontSide = FrontSide; - exports.Frustum = Frustum; - exports.GammaEncoding = GammaEncoding; - exports.Geometry = Geometry; - exports.GeometryUtils = GeometryUtils; - exports.GreaterDepth = GreaterDepth; - exports.GreaterEqualDepth = GreaterEqualDepth; - exports.GreaterEqualStencilFunc = GreaterEqualStencilFunc; - exports.GreaterStencilFunc = GreaterStencilFunc; - exports.GridHelper = GridHelper; - exports.Group = Group; - exports.HalfFloatType = HalfFloatType; - exports.HemisphereLight = HemisphereLight; - exports.HemisphereLightHelper = HemisphereLightHelper; - exports.HemisphereLightProbe = HemisphereLightProbe; - exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry; - exports.IcosahedronGeometry = IcosahedronGeometry; - exports.ImageBitmapLoader = ImageBitmapLoader; - exports.ImageLoader = ImageLoader; - exports.ImageUtils = ImageUtils; - exports.ImmediateRenderObject = ImmediateRenderObject; - exports.IncrementStencilOp = IncrementStencilOp; - exports.IncrementWrapStencilOp = IncrementWrapStencilOp; - exports.InstancedBufferAttribute = InstancedBufferAttribute; - exports.InstancedBufferGeometry = InstancedBufferGeometry; - exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer; - exports.Int16Attribute = Int16Attribute; - exports.Int16BufferAttribute = Int16BufferAttribute; - exports.Int32Attribute = Int32Attribute; - exports.Int32BufferAttribute = Int32BufferAttribute; - exports.Int8Attribute = Int8Attribute; - exports.Int8BufferAttribute = Int8BufferAttribute; - exports.IntType = IntType; - exports.InterleavedBuffer = InterleavedBuffer; - exports.InterleavedBufferAttribute = InterleavedBufferAttribute; - exports.Interpolant = Interpolant; - exports.InterpolateDiscrete = InterpolateDiscrete; - exports.InterpolateLinear = InterpolateLinear; - exports.InterpolateSmooth = InterpolateSmooth; - exports.InvertStencilOp = InvertStencilOp; - exports.JSONLoader = JSONLoader; - exports.KeepStencilOp = KeepStencilOp; - exports.KeyframeTrack = KeyframeTrack; - exports.LOD = LOD; - exports.LatheBufferGeometry = LatheBufferGeometry; - exports.LatheGeometry = LatheGeometry; - exports.Layers = Layers; - exports.LensFlare = LensFlare; - exports.LessDepth = LessDepth; - exports.LessEqualDepth = LessEqualDepth; - exports.LessEqualStencilFunc = LessEqualStencilFunc; - exports.LessStencilFunc = LessStencilFunc; - exports.Light = Light; - exports.LightProbe = LightProbe; - exports.LightProbeHelper = LightProbeHelper; - exports.LightShadow = LightShadow; - exports.Line = Line; - exports.Line3 = Line3; - exports.LineBasicMaterial = LineBasicMaterial; - exports.LineCurve = LineCurve; - exports.LineCurve3 = LineCurve3; - exports.LineDashedMaterial = LineDashedMaterial; - exports.LineLoop = LineLoop; - exports.LinePieces = LinePieces; - exports.LineSegments = LineSegments; - exports.LineStrip = LineStrip; - exports.LinearEncoding = LinearEncoding; - exports.LinearFilter = LinearFilter; - exports.LinearInterpolant = LinearInterpolant; - exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter; - exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter; - exports.LinearMipmapLinearFilter = LinearMipmapLinearFilter; - exports.LinearMipmapNearestFilter = LinearMipmapNearestFilter; - exports.LinearToneMapping = LinearToneMapping; - exports.Loader = Loader; - exports.LoaderUtils = LoaderUtils; - exports.LoadingManager = LoadingManager; - exports.LogLuvEncoding = LogLuvEncoding; - exports.LoopOnce = LoopOnce; - exports.LoopPingPong = LoopPingPong; - exports.LoopRepeat = LoopRepeat; - exports.LuminanceAlphaFormat = LuminanceAlphaFormat; - exports.LuminanceFormat = LuminanceFormat; - exports.MOUSE = MOUSE; - exports.Material = Material; - exports.MaterialLoader = MaterialLoader; - exports.Math = _Math; - exports.Matrix3 = Matrix3; - exports.Matrix4 = Matrix4; - exports.MaxEquation = MaxEquation; - exports.Mesh = Mesh; - exports.MeshBasicMaterial = MeshBasicMaterial; - exports.MeshDepthMaterial = MeshDepthMaterial; - exports.MeshDistanceMaterial = MeshDistanceMaterial; - exports.MeshFaceMaterial = MeshFaceMaterial; - exports.MeshLambertMaterial = MeshLambertMaterial; - exports.MeshMatcapMaterial = MeshMatcapMaterial; - exports.MeshNormalMaterial = MeshNormalMaterial; - exports.MeshPhongMaterial = MeshPhongMaterial; - exports.MeshPhysicalMaterial = MeshPhysicalMaterial; - exports.MeshStandardMaterial = MeshStandardMaterial; - exports.MeshToonMaterial = MeshToonMaterial; - exports.MinEquation = MinEquation; - exports.MirroredRepeatWrapping = MirroredRepeatWrapping; - exports.MixOperation = MixOperation; - exports.MultiMaterial = MultiMaterial; - exports.MultiplyBlending = MultiplyBlending; - exports.MultiplyOperation = MultiplyOperation; - exports.NearestFilter = NearestFilter; - exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter; - exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter; - exports.NearestMipmapLinearFilter = NearestMipmapLinearFilter; - exports.NearestMipmapNearestFilter = NearestMipmapNearestFilter; - exports.NeverDepth = NeverDepth; - exports.NeverStencilFunc = NeverStencilFunc; - exports.NoBlending = NoBlending; - exports.NoColors = NoColors; - exports.NoToneMapping = NoToneMapping; - exports.NormalBlending = NormalBlending; - exports.NotEqualDepth = NotEqualDepth; - exports.NotEqualStencilFunc = NotEqualStencilFunc; - exports.NumberKeyframeTrack = NumberKeyframeTrack; - exports.Object3D = Object3D; - exports.ObjectLoader = ObjectLoader; - exports.ObjectSpaceNormalMap = ObjectSpaceNormalMap; - exports.OctahedronBufferGeometry = OctahedronBufferGeometry; - exports.OctahedronGeometry = OctahedronGeometry; - exports.OneFactor = OneFactor; - exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor; - exports.OneMinusDstColorFactor = OneMinusDstColorFactor; - exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor; - exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor; - exports.OrthographicCamera = OrthographicCamera; - exports.PCFShadowMap = PCFShadowMap; - exports.PCFSoftShadowMap = PCFSoftShadowMap; - exports.ParametricBufferGeometry = ParametricBufferGeometry; - exports.ParametricGeometry = ParametricGeometry; - exports.Particle = Particle; - exports.ParticleBasicMaterial = ParticleBasicMaterial; - exports.ParticleSystem = ParticleSystem; - exports.ParticleSystemMaterial = ParticleSystemMaterial; - exports.Path = Path; - exports.PerspectiveCamera = PerspectiveCamera; - exports.Plane = Plane; - exports.PlaneBufferGeometry = PlaneBufferGeometry; - exports.PlaneGeometry = PlaneGeometry; - exports.PlaneHelper = PlaneHelper; - exports.PointCloud = PointCloud; - exports.PointCloudMaterial = PointCloudMaterial; - exports.PointLight = PointLight; - exports.PointLightHelper = PointLightHelper; - exports.Points = Points; - exports.PointsMaterial = PointsMaterial; - exports.PolarGridHelper = PolarGridHelper; - exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry; - exports.PolyhedronGeometry = PolyhedronGeometry; - exports.PositionalAudio = PositionalAudio; - exports.PositionalAudioHelper = PositionalAudioHelper; - exports.PropertyBinding = PropertyBinding; - exports.PropertyMixer = PropertyMixer; - exports.QuadraticBezierCurve = QuadraticBezierCurve; - exports.QuadraticBezierCurve3 = QuadraticBezierCurve3; - exports.Quaternion = Quaternion; - exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack; - exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant; - exports.REVISION = REVISION; - exports.RGBADepthPacking = RGBADepthPacking; - exports.RGBAFormat = RGBAFormat; - exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format; - exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format; - exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format; - exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format; - exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format; - exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format; - exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format; - exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format; - exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format; - exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format; - exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format; - exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format; - exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format; - exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format; - exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format; - exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format; - exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format; - exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format; - exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format; - exports.RGBDEncoding = RGBDEncoding; - exports.RGBEEncoding = RGBEEncoding; - exports.RGBEFormat = RGBEFormat; - exports.RGBFormat = RGBFormat; - exports.RGBM16Encoding = RGBM16Encoding; - exports.RGBM7Encoding = RGBM7Encoding; - exports.RGB_ETC1_Format = RGB_ETC1_Format; - exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format; - exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format; - exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format; - exports.RawShaderMaterial = RawShaderMaterial; - exports.Ray = Ray; - exports.Raycaster = Raycaster; - exports.RectAreaLight = RectAreaLight; - exports.RectAreaLightHelper = RectAreaLightHelper; - exports.RedFormat = RedFormat; - exports.ReinhardToneMapping = ReinhardToneMapping; - exports.RepeatWrapping = RepeatWrapping; - exports.ReplaceStencilOp = ReplaceStencilOp; - exports.ReverseSubtractEquation = ReverseSubtractEquation; - exports.RingBufferGeometry = RingBufferGeometry; - exports.RingGeometry = RingGeometry; - exports.Scene = Scene; - exports.SceneUtils = SceneUtils; - exports.ShaderChunk = ShaderChunk; - exports.ShaderLib = ShaderLib; - exports.ShaderMaterial = ShaderMaterial; - exports.ShadowMaterial = ShadowMaterial; - exports.Shape = Shape; - exports.ShapeBufferGeometry = ShapeBufferGeometry; - exports.ShapeGeometry = ShapeGeometry; - exports.ShapePath = ShapePath; - exports.ShapeUtils = ShapeUtils; - exports.ShortType = ShortType; - exports.Skeleton = Skeleton; - exports.SkeletonHelper = SkeletonHelper; - exports.SkinnedMesh = SkinnedMesh; - exports.SmoothShading = SmoothShading; - exports.Sphere = Sphere; - exports.SphereBufferGeometry = SphereBufferGeometry; - exports.SphereGeometry = SphereGeometry; - exports.Spherical = Spherical; - exports.SphericalHarmonics3 = SphericalHarmonics3; - exports.SphericalReflectionMapping = SphericalReflectionMapping; - exports.Spline = Spline; - exports.SplineCurve = SplineCurve; - exports.SplineCurve3 = SplineCurve3; - exports.SpotLight = SpotLight; - exports.SpotLightHelper = SpotLightHelper; - exports.SpotLightShadow = SpotLightShadow; - exports.Sprite = Sprite; - exports.SpriteMaterial = SpriteMaterial; - exports.SrcAlphaFactor = SrcAlphaFactor; - exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor; - exports.SrcColorFactor = SrcColorFactor; - exports.StereoCamera = StereoCamera; - exports.StringKeyframeTrack = StringKeyframeTrack; - exports.SubtractEquation = SubtractEquation; - exports.SubtractiveBlending = SubtractiveBlending; - exports.TOUCH = TOUCH; - exports.TangentSpaceNormalMap = TangentSpaceNormalMap; - exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry; - exports.TetrahedronGeometry = TetrahedronGeometry; - exports.TextBufferGeometry = TextBufferGeometry; - exports.TextGeometry = TextGeometry; - exports.Texture = Texture; - exports.TextureLoader = TextureLoader; - exports.TorusBufferGeometry = TorusBufferGeometry; - exports.TorusGeometry = TorusGeometry; - exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry; - exports.TorusKnotGeometry = TorusKnotGeometry; - exports.Triangle = Triangle; - exports.TriangleFanDrawMode = TriangleFanDrawMode; - exports.TriangleStripDrawMode = TriangleStripDrawMode; - exports.TrianglesDrawMode = TrianglesDrawMode; - exports.TubeBufferGeometry = TubeBufferGeometry; - exports.TubeGeometry = TubeGeometry; - exports.UVMapping = UVMapping; - exports.Uint16Attribute = Uint16Attribute; - exports.Uint16BufferAttribute = Uint16BufferAttribute; - exports.Uint32Attribute = Uint32Attribute; - exports.Uint32BufferAttribute = Uint32BufferAttribute; - exports.Uint8Attribute = Uint8Attribute; - exports.Uint8BufferAttribute = Uint8BufferAttribute; - exports.Uint8ClampedAttribute = Uint8ClampedAttribute; - exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute; - exports.Uncharted2ToneMapping = Uncharted2ToneMapping; - exports.Uniform = Uniform; - exports.UniformsLib = UniformsLib; - exports.UniformsUtils = UniformsUtils; - exports.UnsignedByteType = UnsignedByteType; - exports.UnsignedInt248Type = UnsignedInt248Type; - exports.UnsignedIntType = UnsignedIntType; - exports.UnsignedShort4444Type = UnsignedShort4444Type; - exports.UnsignedShort5551Type = UnsignedShort5551Type; - exports.UnsignedShort565Type = UnsignedShort565Type; - exports.UnsignedShortType = UnsignedShortType; - exports.VSMShadowMap = VSMShadowMap; - exports.Vector2 = Vector2; - exports.Vector3 = Vector3; - exports.Vector4 = Vector4; - exports.VectorKeyframeTrack = VectorKeyframeTrack; - exports.Vertex = Vertex; - exports.VertexColors = VertexColors; - exports.VertexNormalsHelper = VertexNormalsHelper; - exports.VideoTexture = VideoTexture; - exports.WebGLMultisampleRenderTarget = WebGLMultisampleRenderTarget; - exports.WebGLRenderTarget = WebGLRenderTarget; - exports.WebGLRenderTargetCube = WebGLRenderTargetCube; - exports.WebGLRenderer = WebGLRenderer; - exports.WebGLUtils = WebGLUtils; - exports.WireframeGeometry = WireframeGeometry; - exports.WireframeHelper = WireframeHelper; - exports.WrapAroundEnding = WrapAroundEnding; - exports.XHRLoader = XHRLoader; - exports.ZeroCurvatureEnding = ZeroCurvatureEnding; - exports.ZeroFactor = ZeroFactor; - exports.ZeroSlopeEnding = ZeroSlopeEnding; - exports.ZeroStencilOp = ZeroStencilOp; - exports.sRGBEncoding = sRGBEncoding; - - Object.defineProperty(exports, '__esModule', { value: true }); - -})); +export { ACESFilmicToneMapping, AddEquation, AddOperation, AdditiveAnimationBlendMode, AdditiveBlending, AlphaFormat, AlwaysCompare, AlwaysDepth, AlwaysStencilFunc, AmbientLight, AnimationAction, AnimationClip, AnimationLoader, AnimationMixer, AnimationObjectGroup, AnimationUtils, ArcCurve, ArrayCamera, ArrowHelper, Audio, AudioAnalyser, AudioContext, AudioListener, AudioLoader, AxesHelper, BackSide, BasicDepthPacking, BasicShadowMap, Bone, BooleanKeyframeTrack, Box2, Box3, Box3Helper, BoxGeometry, BoxHelper, BufferAttribute, BufferGeometry, BufferGeometryLoader, ByteType, Cache, Camera, CameraHelper, CanvasTexture, CapsuleGeometry, CatmullRomCurve3, CineonToneMapping, CircleGeometry, ClampToEdgeWrapping, Clock, Color, ColorKeyframeTrack, ColorManagement, CompressedArrayTexture, CompressedCubeTexture, CompressedTexture, CompressedTextureLoader, ConeGeometry, CubeCamera, CubeReflectionMapping, CubeRefractionMapping, CubeTexture, CubeTextureLoader, CubeUVReflectionMapping, CubicBezierCurve, CubicBezierCurve3, CubicInterpolant, CullFaceBack, CullFaceFront, CullFaceFrontBack, CullFaceNone, Curve, CurvePath, CustomBlending, CustomToneMapping, CylinderGeometry, Cylindrical, Data3DTexture, DataArrayTexture, DataTexture, DataTextureLoader, DataUtils, DecrementStencilOp, DecrementWrapStencilOp, DefaultLoadingManager, DepthFormat, DepthStencilFormat, DepthTexture, DirectionalLight, DirectionalLightHelper, DiscreteInterpolant, DisplayP3ColorSpace, DodecahedronGeometry, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicCopyUsage, DynamicDrawUsage, DynamicReadUsage, EdgesGeometry, EllipseCurve, EqualCompare, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, Euler, EventDispatcher, ExtrudeGeometry, FileLoader, Float16BufferAttribute, Float32BufferAttribute, Float64BufferAttribute, FloatType, Fog, FogExp2, FramebufferTexture, FrontSide, Frustum, GLBufferAttribute, GLSL1, GLSL3, GreaterCompare, GreaterDepth, GreaterEqualCompare, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, GridHelper, Group, HalfFloatType, HemisphereLight, HemisphereLightHelper, IcosahedronGeometry, ImageBitmapLoader, ImageLoader, ImageUtils, IncrementStencilOp, IncrementWrapStencilOp, InstancedBufferAttribute, InstancedBufferGeometry, InstancedInterleavedBuffer, InstancedMesh, Int16BufferAttribute, Int32BufferAttribute, Int8BufferAttribute, IntType, InterleavedBuffer, InterleavedBufferAttribute, Interpolant, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InvertStencilOp, KeepStencilOp, KeyframeTrack, LOD, LatheGeometry, Layers, LessCompare, LessDepth, LessEqualCompare, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, Light, LightProbe, Line, Line3, LineBasicMaterial, LineCurve, LineCurve3, LineDashedMaterial, LineLoop, LineSegments, LinearDisplayP3ColorSpace, LinearEncoding, LinearFilter, LinearInterpolant, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearSRGBColorSpace, LinearToneMapping, LinearTransfer, Loader, LoaderUtils, LoadingManager, LoopOnce, LoopPingPong, LoopRepeat, LuminanceAlphaFormat, LuminanceFormat, MOUSE, Material, MaterialLoader, MathUtils, Matrix3, Matrix4, MaxEquation, Mesh, MeshBasicMaterial, MeshDepthMaterial, MeshDistanceMaterial, MeshLambertMaterial, MeshMatcapMaterial, MeshNormalMaterial, MeshPhongMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MeshToonMaterial, MinEquation, MirroredRepeatWrapping, MixOperation, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeverCompare, NeverDepth, NeverStencilFunc, NoBlending, NoColorSpace, NoToneMapping, NormalAnimationBlendMode, NormalBlending, NotEqualCompare, NotEqualDepth, NotEqualStencilFunc, NumberKeyframeTrack, Object3D, ObjectLoader, ObjectSpaceNormalMap, OctahedronGeometry, OneFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, OrthographicCamera, P3Primaries, PCFShadowMap, PCFSoftShadowMap, PMREMGenerator, Path, PerspectiveCamera, Plane, PlaneGeometry, PlaneHelper, PointLight, PointLightHelper, Points, PointsMaterial, PolarGridHelper, PolyhedronGeometry, PositionalAudio, PropertyBinding, PropertyMixer, QuadraticBezierCurve, QuadraticBezierCurve3, Quaternion, QuaternionKeyframeTrack, QuaternionLinearInterpolant, RED_GREEN_RGTC2_Format, RED_RGTC1_Format, REVISION, RGBADepthPacking, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, 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_BPTC_Format, RGBA_ETC2_EAC_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGB_BPTC_SIGNED_Format, RGB_BPTC_UNSIGNED_Format, RGB_ETC1_Format, RGB_ETC2_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RawShaderMaterial, Ray, Raycaster, Rec709Primaries, RectAreaLight, RedFormat, RedIntegerFormat, ReinhardToneMapping, RenderTarget, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, RingGeometry, SIGNED_RED_GREEN_RGTC2_Format, SIGNED_RED_RGTC1_Format, SRGBColorSpace, SRGBTransfer, Scene, ShaderChunk, ShaderLib, ShaderMaterial, ShadowMaterial, Shape, ShapeGeometry, ShapePath, ShapeUtils, ShortType, Skeleton, SkeletonHelper, SkinnedMesh, Source, Sphere, SphereGeometry, Spherical, SphericalHarmonics3, SplineCurve, SpotLight, SpotLightHelper, Sprite, SpriteMaterial, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StaticCopyUsage, StaticDrawUsage, StaticReadUsage, StereoCamera, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, StringKeyframeTrack, SubtractEquation, SubtractiveBlending, TOUCH, TangentSpaceNormalMap, TetrahedronGeometry, Texture, TextureLoader, TorusGeometry, TorusKnotGeometry, Triangle, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, TubeGeometry, TwoPassDoubleSide, UVMapping, Uint16BufferAttribute, Uint32BufferAttribute, Uint8BufferAttribute, Uint8ClampedBufferAttribute, Uniform, UniformsGroup, UniformsLib, UniformsUtils, UnsignedByteType, UnsignedInt248Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShortType, VSMShadowMap, Vector2, Vector3, Vector4, VectorKeyframeTrack, VideoTexture, WebGL1Renderer, WebGL3DRenderTarget, WebGLArrayRenderTarget, WebGLCoordinateSystem, WebGLCubeRenderTarget, WebGLMultipleRenderTargets, WebGLRenderTarget, WebGLRenderer, WebGLUtils, WebGPUCoordinateSystem, WireframeGeometry, WrapAroundEnding, ZeroCurvatureEnding, ZeroFactor, ZeroSlopeEnding, ZeroStencilOp, _SRGBAFormat, createCanvasElement, sRGBEncoding }; \ No newline at end of file diff --git a/main.js b/main.js index 8bce681..bfc5bb0 100644 --- a/main.js +++ b/main.js @@ -1,9 +1,12 @@ +import * as THREE from '/lib/three.js' +import { OrbitControls } from '/lib/orbitcontrols.js' + // Add any resources you want to load here // You will then be able to reference them in initialise_scene // e.g. as "resources.vert_shader" -RESOURCES = [ +const RESOURCES = [ // format is: - // ["name", "path-to-resource"] + // ["name", "path-to-resource"]` ["vert_shader", "shaders/default.vert"], ["frag_shader", "shaders/default.frag"], ["vertl_shader", "shaders/light.vert"], @@ -77,7 +80,7 @@ const main = function (resources) { // camera controls camera.position.set(cameraDist, cameraHeight, cameraDist); - const controls = new THREE.OrbitControls(camera, renderer.domElement); + const controls = new OrbitControls(camera, renderer.domElement); controls.maxPolarAngle = Math.PI * 0.5; controls.minDistance = near; controls.maxDistance = far; @@ -310,12 +313,13 @@ const main = function (resources) { */ // separate shader for moving lights (they have a texture) - function getLigtShader(center) { + function getLightShader(center) { + const texture = new THREE.TextureLoader().load("img/light.png") return new THREE.ShaderMaterial({ uniforms: { - texture: { + uTexture: { type: "t", - value: (new THREE.TextureLoader()).load( "img/light.png" ) + value: texture }, center: { value: center @@ -341,7 +345,7 @@ const main = function (resources) { for (let l = 0; l < count; ++l) { const streetNo = getRandomIntInclusive(1, repeatCount - 1); const direction = getRandomIntInclusive(1, 4); - const collectionName = collectionKeys[direction-1]; + const collectionName = collectionKeys[direction - 1]; lights[collectionName].push(createLight(streetNo, direction)); } @@ -383,7 +387,7 @@ const main = function (resources) { const centerPos = new THREE.Vector3(x, y, z); const geometry = new THREE.CircleGeometry(carRadius, 32); - const material = getLigtShader(carColor, centerPos); + const material = getLightShader(carColor, centerPos); const circle = new THREE.Mesh(geometry, material); circle.position.set(x, y, z); diff --git a/serve.sh b/serve.sh deleted file mode 100644 index f4e3ade..0000000 --- a/serve.sh +++ /dev/null @@ -1 +0,0 @@ -python -m http.server \ No newline at end of file diff --git a/shaders/light.frag b/shaders/light.frag index 4bee522..1c62ca4 100644 --- a/shaders/light.frag +++ b/shaders/light.frag @@ -1,6 +1,6 @@ varying vec2 vUv; -uniform sampler2D texture; +uniform sampler2D uTexture; void main() { - gl_FragColor = texture2D(texture, vUv); + gl_FragColor = texture(uTexture, vUv); } \ No newline at end of file diff --git a/shaders/light.vert b/shaders/light.vert index 2d76eb0..3574279 100644 --- a/shaders/light.vert +++ b/shaders/light.vert @@ -7,5 +7,4 @@ void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); vUv = uv; - } \ No newline at end of file