Browse Source

r130 (bis)

Mr.doob 4 years ago
parent
commit
1477331eb0

+ 5 - 7
build/three.js

@@ -15171,11 +15171,11 @@
 			return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;
 			return texture.generateMipmaps && supportsMips && texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;
 		}
 		}
 
 
-		function generateMipmap(target, texture, width, height) {
+		function generateMipmap(target, texture, width, height, depth = 1) {
 			_gl.generateMipmap(target);
 			_gl.generateMipmap(target);
 
 
 			const textureProperties = properties.get(texture);
 			const textureProperties = properties.get(texture);
-			textureProperties.__maxMipLevel = Math.log2(Math.max(width, height));
+			textureProperties.__maxMipLevel = Math.log2(Math.max(width, height, depth));
 		}
 		}
 
 
 		function getInternalFormat(internalFormatName, glFormat, glType) {
 		function getInternalFormat(internalFormatName, glFormat, glType) {
@@ -15936,10 +15936,10 @@
 				setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType);
 				setupFrameBufferTexture(renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType);
 
 
 				if (textureNeedsGenerateMipmaps(texture, supportsMips)) {
 				if (textureNeedsGenerateMipmaps(texture, supportsMips)) {
-					generateMipmap(_gl.TEXTURE_2D, texture, renderTarget.width, renderTarget.height);
+					generateMipmap(glTextureType, texture, renderTarget.width, renderTarget.height, renderTarget.depth);
 				}
 				}
 
 
-				state.bindTexture(_gl.TEXTURE_2D, null);
+				state.bindTexture(glTextureType, null);
 			} // Setup depth and stencil buffers
 			} // Setup depth and stencil buffers
 
 
 
 
@@ -16765,15 +16765,13 @@
 							viewport = baseLayer.getViewport(view);
 							viewport = baseLayer.getViewport(view);
 						} else {
 						} else {
 							const glSubImage = glBinding.getViewSubImage(glProjLayer, view);
 							const glSubImage = glBinding.getViewSubImage(glProjLayer, view);
-							gl.bindFramebuffer(gl.FRAMEBUFFER, glFramebuffer);
+							state.bindXRFramebuffer(glFramebuffer);
 							gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glSubImage.colorTexture, 0);
 							gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, glSubImage.colorTexture, 0);
 
 
 							if (glSubImage.depthStencilTexture !== undefined) {
 							if (glSubImage.depthStencilTexture !== undefined) {
 								gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, glSubImage.depthStencilTexture, 0);
 								gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, glSubImage.depthStencilTexture, 0);
 							}
 							}
 
 
-							gl.bindFramebuffer(gl.FRAMEBUFFER, null);
-							state.bindXRFramebuffer(glFramebuffer);
 							viewport = glSubImage.viewport;
 							viewport = glSubImage.viewport;
 						}
 						}
 
 

File diff suppressed because it is too large
+ 0 - 0
build/three.min.js


+ 5 - 9
build/three.module.js

@@ -20394,13 +20394,13 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 
 	}
 	}
 
 
-	function generateMipmap( target, texture, width, height ) {
+	function generateMipmap( target, texture, width, height, depth = 1 ) {
 
 
 		_gl.generateMipmap( target );
 		_gl.generateMipmap( target );
 
 
 		const textureProperties = properties.get( texture );
 		const textureProperties = properties.get( texture );
 
 
-		textureProperties.__maxMipLevel = Math.log2( Math.max( width, height ) );
+		textureProperties.__maxMipLevel = Math.log2( Math.max( width, height, depth ) );
 
 
 	}
 	}
 
 
@@ -21502,11 +21502,11 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 
 			if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
 			if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
 
 
-				generateMipmap( 3553, texture, renderTarget.width, renderTarget.height );
+				generateMipmap( glTextureType, texture, renderTarget.width, renderTarget.height, renderTarget.depth );
 
 
 			}
 			}
 
 
-			state.bindTexture( 3553, null );
+			state.bindTexture( glTextureType, null );
 
 
 		}
 		}
 
 
@@ -22667,7 +22667,7 @@ class WebXRManager extends EventDispatcher {
 
 
 						const glSubImage = glBinding.getViewSubImage( glProjLayer, view );
 						const glSubImage = glBinding.getViewSubImage( glProjLayer, view );
 
 
-						gl.bindFramebuffer( 36160, glFramebuffer );
+						state.bindXRFramebuffer( glFramebuffer );
 
 
 						gl.framebufferTexture2D( 36160, 36064, 3553, glSubImage.colorTexture, 0 );
 						gl.framebufferTexture2D( 36160, 36064, 3553, glSubImage.colorTexture, 0 );
 
 
@@ -22677,10 +22677,6 @@ class WebXRManager extends EventDispatcher {
 
 
 						}
 						}
 
 
-						gl.bindFramebuffer( 36160, null );
-
-						state.bindXRFramebuffer( glFramebuffer );
-
 						viewport = glSubImage.viewport;
 						viewport = glSubImage.viewport;
 
 
 					}
 					}

+ 3 - 3
docs/api/en/animation/AnimationMixer.html

@@ -94,17 +94,17 @@
 		<h3>[method:null uncacheClip]([param:AnimationClip clip])</h3>
 		<h3>[method:null uncacheClip]([param:AnimationClip clip])</h3>
 
 
 		<p>
 		<p>
-			Deallocates all memory resources for a clip.
+			Deallocates all memory resources for a clip. Before using this method make sure to call [page:AnimationAction.stop]() for all related actions.
 		</p>
 		</p>
 
 
 		<h3>[method:null uncacheRoot]([param:Object3D root]) </h3>
 		<h3>[method:null uncacheRoot]([param:Object3D root]) </h3>
 		<p>
 		<p>
-			Deallocates all memory resources for a root object.
+			Deallocates all memory resources for a root object. Before using this method make sure to call [page:AnimationAction.stop]() for all related actions.
 		</p>
 		</p>
 
 
 		<h3>[method:null uncacheAction]([param:AnimationClip clip], [param:Object3D optionalRoot])</h3>
 		<h3>[method:null uncacheAction]([param:AnimationClip clip], [param:Object3D optionalRoot])</h3>
 		<p>
 		<p>
-			Deallocates all memory resources for an action.
+			Deallocates all memory resources for an action. Before using this method make sure to call [page:AnimationAction.stop]() to deactivate the action.
 		</p>
 		</p>
 
 
 
 

+ 1 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r130
+// r130.1
 
 
 const cacheName = 'threejs-editor';
 const cacheName = 'threejs-editor';
 
 

+ 1 - 1
examples/js/controls/DragControls.js

@@ -144,7 +144,7 @@
 
 
 			}
 			}
 
 
-			function onPointerDown() {
+			function onPointerDown( event ) {
 
 
 				if ( scope.enabled === false ) return;
 				if ( scope.enabled === false ) return;
 				updatePointer( event );
 				updatePointer( event );

+ 31 - 8
examples/js/exporters/USDZExporter.js

@@ -45,7 +45,10 @@
 
 
 				const texture = textures[ id ];
 				const texture = textures[ id ];
 				const color = id.split( '_' )[ 1 ];
 				const color = id.split( '_' )[ 1 ];
-				files[ 'textures/Texture_' + id + '.jpg' ] = await imgToU8( texture.image, color );
+				const isRGBA = texture.format === 1023;
+				const canvas = imageToCanvas( texture.image, color );
+				const blob = await new Promise( resolve => canvas.toBlob( resolve, isRGBA ? 'image/png' : 'image/jpeg', 1 ) );
+				files[ `textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}` ] = new Uint8Array( await blob.arrayBuffer() );
 
 
 			} // 64 byte alignment
 			} // 64 byte alignment
 			// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
 			// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
@@ -84,7 +87,7 @@
 
 
 	}
 	}
 
 
-	async function imgToU8( image, color ) {
+	function imageToCanvas( image, color ) {
 
 
 		if ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) {
 		if ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement || typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement || typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas || typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) {
 
 
@@ -97,14 +100,26 @@
 
 
 			if ( color !== undefined ) {
 			if ( color !== undefined ) {
 
 
-				context.globalCompositeOperation = 'multiply';
-				context.fillStyle = `#${color}`;
-				context.fillRect( 0, 0, canvas.width, canvas.height );
+				const hex = parseInt( color, 16 );
+				const r = ( hex >> 16 & 255 ) / 255;
+				const g = ( hex >> 8 & 255 ) / 255;
+				const b = ( hex & 255 ) / 255;
+				const imagedata = context.getImageData( 0, 0, canvas.width, canvas.height );
+				const data = imagedata.data;
+
+				for ( let i = 0; i < data.length; i += 4 ) {
+
+					data[ i + 0 ] = data[ i + 0 ] * r;
+					data[ i + 1 ] = data[ i + 1 ] * g;
+					data[ i + 2 ] = data[ i + 2 ] * b;
+
+				}
+
+				context.putImageData( imagedata, 0, 0 );
 
 
 			}
 			}
 
 
-			const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg', 1 ) );
-			return new Uint8Array( await blob.arrayBuffer() );
+			return canvas;
 
 
 		}
 		}
 
 
@@ -141,6 +156,13 @@
 
 
 		const name = 'Object_' + object.id;
 		const name = 'Object_' + object.id;
 		const transform = buildMatrix( object.matrixWorld );
 		const transform = buildMatrix( object.matrixWorld );
+
+		if ( object.matrixWorld.determinant() < 0 ) {
+
+			console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', object );
+
+		}
+
 		return `def Xform "${name}" (
 		return `def Xform "${name}" (
     prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
     prepend references = @./geometries/Geometry_${geometry.id}.usd@</Geometry>
 )
 )
@@ -307,6 +329,7 @@ ${array.join( '' )}
 		function buildTexture( texture, mapType, color ) {
 		function buildTexture( texture, mapType, color ) {
 
 
 			const id = texture.id + ( color ? '_' + color.getHexString() : '' );
 			const id = texture.id + ( color ? '_' + color.getHexString() : '' );
+			const isRGBA = texture.format === 1023;
 			textures[ id ] = texture;
 			textures[ id ] = texture;
 			return `
 			return `
         def Shader "Transform2d_${mapType}" (
         def Shader "Transform2d_${mapType}" (
@@ -325,7 +348,7 @@ ${array.join( '' )}
         def Shader "Texture_${texture.id}_${mapType}"
         def Shader "Texture_${texture.id}_${mapType}"
         {
         {
             uniform token info:id = "UsdUVTexture"
             uniform token info:id = "UsdUVTexture"
-            asset inputs:file = @textures/Texture_${id}.jpg@
+            asset inputs:file = @textures/Texture_${id}.${isRGBA ? 'png' : 'jpg'}@
             float2 inputs:st.connect = </Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>
             float2 inputs:st.connect = </Materials/Material_${material.id}/Transform2d_${mapType}.outputs:result>
             token inputs:wrapS = "repeat"
             token inputs:wrapS = "repeat"
             token inputs:wrapT = "repeat"
             token inputs:wrapT = "repeat"

+ 10 - 14
examples/js/loaders/FBXLoader.js

@@ -3745,16 +3745,15 @@
 
 
 		}
 		}
 
 
-		const lLRM = new THREE.Matrix4().copy( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ); // Global Rotation
+		const lLRM = lPreRotationM.clone().multiply( lRotationM ).multiply( lPostRotationM ); // Global Rotation
 
 
 		const lParentGRM = new THREE.Matrix4();
 		const lParentGRM = new THREE.Matrix4();
 		lParentGRM.extractRotation( lParentGX ); // Global Shear*Scaling
 		lParentGRM.extractRotation( lParentGX ); // Global Shear*Scaling
 
 
 		const lParentTM = new THREE.Matrix4();
 		const lParentTM = new THREE.Matrix4();
 		lParentTM.copyPosition( lParentGX );
 		lParentTM.copyPosition( lParentGX );
-		const lParentGSM = new THREE.Matrix4();
-		const lParentGRSM = new THREE.Matrix4().copy( lParentTM ).invert().multiply( lParentGX );
-		lParentGSM.copy( lParentGRM ).invert().multiply( lParentGRSM );
+		const lParentGRSM = lParentTM.clone().invert().multiply( lParentGX );
+		const lParentGSM = lParentGRM.clone().invert().multiply( lParentGRSM );
 		const lLSM = lScalingM;
 		const lLSM = lScalingM;
 		const lGlobalRS = new THREE.Matrix4();
 		const lGlobalRS = new THREE.Matrix4();
 
 
@@ -3769,23 +3768,20 @@
 		} else {
 		} else {
 
 
 			const lParentLSM = new THREE.Matrix4().scale( new THREE.Vector3().setFromMatrixScale( lParentLX ) );
 			const lParentLSM = new THREE.Matrix4().scale( new THREE.Vector3().setFromMatrixScale( lParentLX ) );
-			const lParentLSM_inv = new THREE.Matrix4().copy( lParentLSM ).invert();
-			const lParentGSM_noLocal = new THREE.Matrix4().copy( lParentGSM ).multiply( lParentLSM_inv );
+			const lParentLSM_inv = lParentLSM.clone().invert();
+			const lParentGSM_noLocal = lParentGSM.clone().multiply( lParentLSM_inv );
 			lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
 			lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
 
 
 		}
 		}
 
 
-		const lRotationPivotM_inv = new THREE.Matrix4();
-		lRotationPivotM_inv.copy( lRotationPivotM ).invert();
-		const lScalingPivotM_inv = new THREE.Matrix4();
-		lScalingPivotM_inv.copy( lScalingPivotM ).invert(); // Calculate the local transform matrix
+		const lRotationPivotM_inv = lRotationPivotM.clone().invert();
+		const lScalingPivotM_inv = lScalingPivotM.clone().invert(); // Calculate the local transform matrix
 
 
-		let lTransform = new THREE.Matrix4();
-		lTransform.copy( lTranslationM ).multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM_inv ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM_inv );
+		let lTransform = lTranslationM.clone().multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM_inv ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM_inv );
 		const lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform );
 		const lLocalTWithAllPivotAndOffsetInfo = new THREE.Matrix4().copyPosition( lTransform );
-		const lGlobalTranslation = new THREE.Matrix4().copy( lParentGX ).multiply( lLocalTWithAllPivotAndOffsetInfo );
+		const lGlobalTranslation = lParentGX.clone().multiply( lLocalTWithAllPivotAndOffsetInfo );
 		lGlobalT.copyPosition( lGlobalTranslation );
 		lGlobalT.copyPosition( lGlobalTranslation );
-		lTransform = new THREE.Matrix4().copy( lGlobalT ).multiply( lGlobalRS ); // from global to local
+		lTransform = lGlobalT.clone().multiply( lGlobalRS ); // from global to local
 
 
 		lTransform.premultiply( lParentGX.invert() );
 		lTransform.premultiply( lParentGX.invert() );
 		return lTransform;
 		return lTransform;

+ 1 - 1
examples/jsm/controls/DragControls.js

@@ -136,7 +136,7 @@ class DragControls extends EventDispatcher {
 
 
 		}
 		}
 
 
-		function onPointerDown() {
+		function onPointerDown( event ) {
 
 
 			if ( scope.enabled === false ) return;
 			if ( scope.enabled === false ) return;
 
 

+ 33 - 8
examples/jsm/exporters/USDZExporter.js

@@ -52,8 +52,12 @@ class USDZExporter {
 
 
 			const texture = textures[ id ];
 			const texture = textures[ id ];
 			const color = id.split( '_' )[ 1 ];
 			const color = id.split( '_' )[ 1 ];
+			const isRGBA = texture.format === 1023;
 
 
-			files[ 'textures/Texture_' + id + '.jpg' ] = await imgToU8( texture.image, color );
+			const canvas = imageToCanvas( texture.image, color );
+			const blob = await new Promise( resolve => canvas.toBlob( resolve, isRGBA ? 'image/png' : 'image/jpeg', 1 ) );
+
+			files[ `textures/Texture_${ id }.${ isRGBA ? 'png' : 'jpg' }` ] = new Uint8Array( await blob.arrayBuffer() );
 
 
 		}
 		}
 
 
@@ -90,7 +94,7 @@ class USDZExporter {
 
 
 }
 }
 
 
-async function imgToU8( image, color ) {
+function imageToCanvas( image, color ) {
 
 
 	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
 	if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
 		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
 		( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
@@ -108,14 +112,28 @@ async function imgToU8( image, color ) {
 
 
 		if ( color !== undefined ) {
 		if ( color !== undefined ) {
 
 
-			context.globalCompositeOperation = 'multiply';
-			context.fillStyle = `#${ color }`;
-			context.fillRect( 0, 0, canvas.width, canvas.height );
+			const hex = parseInt( color, 16 );
+
+			const r = ( hex >> 16 & 255 ) / 255;
+			const g = ( hex >> 8 & 255 ) / 255;
+			const b = ( hex & 255 ) / 255;
+
+			const imagedata = context.getImageData( 0, 0, canvas.width, canvas.height );
+			const data = imagedata.data;
+
+			for ( let i = 0; i < data.length; i += 4 ) {
+
+				data[ i + 0 ] = data[ i + 0 ] * r;
+				data[ i + 1 ] = data[ i + 1 ] * g;
+				data[ i + 2 ] = data[ i + 2 ] * b;
+
+			}
+
+			context.putImageData( imagedata, 0, 0 );
 
 
 		}
 		}
 
 
-		const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/jpeg', 1 ) );
-		return new Uint8Array( await blob.arrayBuffer() );
+		return canvas;
 
 
 	}
 	}
 
 
@@ -155,6 +173,12 @@ function buildXform( object, geometry, material ) {
 	const name = 'Object_' + object.id;
 	const name = 'Object_' + object.id;
 	const transform = buildMatrix( object.matrixWorld );
 	const transform = buildMatrix( object.matrixWorld );
 
 
+	if ( object.matrixWorld.determinant() < 0 ) {
+
+		console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', object );
+
+	}
+
 	return `def Xform "${ name }" (
 	return `def Xform "${ name }" (
     prepend references = @./geometries/Geometry_${ geometry.id }.usd@</Geometry>
     prepend references = @./geometries/Geometry_${ geometry.id }.usd@</Geometry>
 )
 )
@@ -328,6 +352,7 @@ function buildMaterial( material, textures ) {
 	function buildTexture( texture, mapType, color ) {
 	function buildTexture( texture, mapType, color ) {
 
 
 		const id = texture.id + ( color ? '_' + color.getHexString() : '' );
 		const id = texture.id + ( color ? '_' + color.getHexString() : '' );
+		const isRGBA = texture.format === 1023;
 
 
 		textures[ id ] = texture;
 		textures[ id ] = texture;
 
 
@@ -348,7 +373,7 @@ function buildMaterial( material, textures ) {
         def Shader "Texture_${ texture.id }_${ mapType }"
         def Shader "Texture_${ texture.id }_${ mapType }"
         {
         {
             uniform token info:id = "UsdUVTexture"
             uniform token info:id = "UsdUVTexture"
-            asset inputs:file = @textures/Texture_${ id }.jpg@
+            asset inputs:file = @textures/Texture_${ id }.${ isRGBA ? 'png' : 'jpg' }@
             float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
             float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
             token inputs:wrapS = "repeat"
             token inputs:wrapS = "repeat"
             token inputs:wrapT = "repeat"
             token inputs:wrapT = "repeat"

+ 10 - 14
examples/jsm/loaders/FBXLoader.js

@@ -4049,7 +4049,7 @@ function generateTransform( transformData ) {
 
 
 	}
 	}
 
 
-	const lLRM = new Matrix4().copy( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM );
+	const lLRM = lPreRotationM.clone().multiply( lRotationM ).multiply( lPostRotationM );
 	// Global Rotation
 	// Global Rotation
 	const lParentGRM = new Matrix4();
 	const lParentGRM = new Matrix4();
 	lParentGRM.extractRotation( lParentGX );
 	lParentGRM.extractRotation( lParentGX );
@@ -4058,9 +4058,8 @@ function generateTransform( transformData ) {
 	const lParentTM = new Matrix4();
 	const lParentTM = new Matrix4();
 	lParentTM.copyPosition( lParentGX );
 	lParentTM.copyPosition( lParentGX );
 
 
-	const lParentGSM = new Matrix4();
-	const lParentGRSM = new Matrix4().copy( lParentTM ).invert().multiply( lParentGX );
-	lParentGSM.copy( lParentGRM ).invert().multiply( lParentGRSM );
+	const lParentGRSM = lParentTM.clone().invert().multiply( lParentGX );
+	const lParentGSM = lParentGRM.clone().invert().multiply( lParentGRSM );
 	const lLSM = lScalingM;
 	const lLSM = lScalingM;
 
 
 	const lGlobalRS = new Matrix4();
 	const lGlobalRS = new Matrix4();
@@ -4076,27 +4075,24 @@ function generateTransform( transformData ) {
 	} else {
 	} else {
 
 
 		const lParentLSM = new Matrix4().scale( new Vector3().setFromMatrixScale( lParentLX ) );
 		const lParentLSM = new Matrix4().scale( new Vector3().setFromMatrixScale( lParentLX ) );
-		const lParentLSM_inv = new Matrix4().copy( lParentLSM ).invert();
-		const lParentGSM_noLocal = new Matrix4().copy( lParentGSM ).multiply( lParentLSM_inv );
+		const lParentLSM_inv = lParentLSM.clone().invert();
+		const lParentGSM_noLocal = lParentGSM.clone().multiply( lParentLSM_inv );
 
 
 		lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
 		lGlobalRS.copy( lParentGRM ).multiply( lLRM ).multiply( lParentGSM_noLocal ).multiply( lLSM );
 
 
 	}
 	}
 
 
-	const lRotationPivotM_inv = new Matrix4();
-	lRotationPivotM_inv.copy( lRotationPivotM ).invert();
-	const lScalingPivotM_inv = new Matrix4();
-	lScalingPivotM_inv.copy( lScalingPivotM ).invert();
+	const lRotationPivotM_inv = lRotationPivotM.clone().invert();
+	const lScalingPivotM_inv = lScalingPivotM.clone().invert();
 	// Calculate the local transform matrix
 	// Calculate the local transform matrix
-	let lTransform = new Matrix4();
-	lTransform.copy( lTranslationM ).multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM_inv ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM_inv );
+	let lTransform = lTranslationM.clone().multiply( lRotationOffsetM ).multiply( lRotationPivotM ).multiply( lPreRotationM ).multiply( lRotationM ).multiply( lPostRotationM ).multiply( lRotationPivotM_inv ).multiply( lScalingOffsetM ).multiply( lScalingPivotM ).multiply( lScalingM ).multiply( lScalingPivotM_inv );
 
 
 	const lLocalTWithAllPivotAndOffsetInfo = new Matrix4().copyPosition( lTransform );
 	const lLocalTWithAllPivotAndOffsetInfo = new Matrix4().copyPosition( lTransform );
 
 
-	const lGlobalTranslation = new Matrix4().copy( lParentGX ).multiply( lLocalTWithAllPivotAndOffsetInfo );
+	const lGlobalTranslation = lParentGX.clone().multiply( lLocalTWithAllPivotAndOffsetInfo );
 	lGlobalT.copyPosition( lGlobalTranslation );
 	lGlobalT.copyPosition( lGlobalTranslation );
 
 
-	lTransform = new Matrix4().copy( lGlobalT ).multiply( lGlobalRS );
+	lTransform = lGlobalT.clone().multiply( lGlobalRS );
 
 
 	// from global to local
 	// from global to local
 	lTransform.premultiply( lParentGX.invert() );
 	lTransform.premultiply( lParentGX.invert() );

BIN
examples/textures/snellen.png


+ 1 - 1
examples/webgl_raymarching_reflect.html

@@ -289,7 +289,7 @@
 					uniforms: {
 					uniforms: {
 						resolution: { value: new THREE.Vector2( canvas.width, canvas.height ) },
 						resolution: { value: new THREE.Vector2( canvas.width, canvas.height ) },
 						cameraWorldMatrix: { value: camera.matrixWorld },
 						cameraWorldMatrix: { value: camera.matrixWorld },
-						cameraProjectionMatrixInverse: { value: new THREE.Matrix4().copy( camera.projectionMatrixInverse ) }
+						cameraProjectionMatrixInverse: { value: camera.projectionMatrixInverse.clone() }
 					},
 					},
 					vertexShader: document.getElementById( 'vertex_shader' ).textContent,
 					vertexShader: document.getElementById( 'vertex_shader' ).textContent,
 					fragmentShader: document.getElementById( 'fragment_shader' ).textContent
 					fragmentShader: document.getElementById( 'fragment_shader' ).textContent

+ 78 - 16
examples/webxr_vr_layers.html

@@ -29,7 +29,34 @@
 
 
 			let controls;
 			let controls;
 			let video;
 			let video;
-			let video2;
+			let snellenTexture;
+			let quadLayer;
+
+			// Data shared between the THREE.Mesh on the left side and WebXR Layer on the right side for
+			// the eye chart. See https://en.wikipedia.org/wiki/Snellen_chart for details about the math.
+			//
+			// The image was designed so that each 2x2px block on the 20/20 line subtends 1 minute of
+			// arc. That is
+			//    tan(1/60 deg) * 6.1m * 160px/142mm = 2px
+			// per block on line 8.
+			//
+			// This fidelity is beyond any modern consumer headset since it would require ~60px/deg of
+			// resolution. The Quest has ~16ppd and the Quest 2 has ~20ppd so only lines 3 or 4 will be
+			// legible when using layers. Without layers, you lose ~sqrt(2) in resolution due to the
+			// extra resampling.
+			const snellenConfig = {
+				// The height & width of the WebXR layer needs to match the given image size.
+				widthPx: 320,
+				heightPx: 450,
+
+				x: 0, // +/- widthMeters/2
+				y: 1.5,
+
+				z: - 6.1, // 20/20 vision @ 20ft = 6.1m
+
+				widthMeters: .268, // 320px image * (142mm/160px scale factor)
+				heightMeters: .382 // 450px image * (142mm/160px scale factor)
+			};
 
 
 			init();
 			init();
 			animate();
 			animate();
@@ -116,17 +143,23 @@
 				controller1.add( line.clone() );
 				controller1.add( line.clone() );
 				controller2.add( line.clone() );
 				controller2.add( line.clone() );
 
 
+				// Eye chart
+				snellenTexture = new THREE.TextureLoader().load( "textures/snellen.png" );
+				const snellenMesh = new THREE.Mesh(
+					new THREE.PlaneGeometry( snellenConfig.widthMeters, snellenConfig.heightMeters ),
+					new THREE.MeshBasicMaterial( { map: snellenTexture } ) );
+				snellenMesh.position.x = snellenConfig.x - snellenConfig.widthMeters / 2;
+				snellenMesh.position.y = snellenConfig.y;
+				snellenMesh.position.z = snellenConfig.z;
+				scene.add( snellenMesh );
+
 				//
 				//
 
 
 				window.addEventListener( 'resize', onWindowResize, false );
 				window.addEventListener( 'resize', onWindowResize, false );
 
 
 				video = document.createElement('video');
 				video = document.createElement('video');
 				video.loop = true;
 				video.loop = true;
-				video.src = 'textures/pano.webm';
-
-				video2 = document.createElement('video');
-				video2.loop = true;
-				video2.src = 'textures/MaryOculus.webm';
+				video.src = 'textures/MaryOculus.webm';
 			}
 			}
 
 
 			function onWindowResize() {
 			function onWindowResize() {
@@ -145,21 +178,50 @@
 
 
 			}
 			}
 
 
-			function render() {
+			function render( t, frame ) {
+
 				const xr = renderer.xr;
 				const xr = renderer.xr;
 				const session = xr.getSession();
 				const session = xr.getSession();
+				const gl = renderer.getContext();
+
+				// Init layers once in immersive mode and video is ready.
+				if ( session && session.renderState.layers !== undefined && session.hasMediaLayer === undefined && video.readyState >= 2 ) {
 
 
-				if ( session && session.renderState.layers !== undefined && session.hasMediaLayer === undefined && video.readyState >= 2 && video2.readyState >= 2) {
 					session.hasMediaLayer = true;
 					session.hasMediaLayer = true;
-					session.requestReferenceSpace('local').then((refSpace) => {
-						const mediaBinding = new XRMediaBinding(session);
-						const equirectLayer = mediaBinding.createEquirectLayer(video, {space: refSpace, layout: "mono"});
-						const quadLayer = mediaBinding.createQuadLayer(video2, {space: refSpace, layout: "stereo-left-right"});
-						quadLayer.transform = new XRRigidTransform({x: 1.5, y: 1.0, z: -2.0});
-						session.updateRenderState( { layers: [ equirectLayer, quadLayer, session.renderState.layers[0] ] } );
+					session.requestReferenceSpace( 'local' ).then( ( refSpace ) => {
+
+						// Create Quad layer for Snellen chart.
+						const glBinding = new XRWebGLBinding( session, gl );
+						quadLayer = glBinding.createQuadLayer( {
+							width: snellenConfig.widthMeters / 2,
+							height: snellenConfig.heightMeters / 2,
+							viewPixelWidth: snellenConfig.widthPx,
+							viewPixelHeight: snellenConfig.heightPx,
+							space: refSpace, layout: "mono",
+							transform: new XRRigidTransform( { x: snellenConfig.x + snellenConfig.widthMeters / 2, y: snellenConfig.y, z: snellenConfig.z } )
+						} );
+
+						// Create background EQR video layer.
+						const mediaBinding = new XRMediaBinding( session );
+						const equirectLayer = mediaBinding.createEquirectLayer( video, { space: refSpace, layout: "stereo-left-right" } );
+
+						session.updateRenderState( { layers: [ equirectLayer, quadLayer, session.renderState.layers[ 0 ] ] } );
 						video.play();
 						video.play();
-						video2.play();
-					});
+
+					} );
+
+				}
+
+				// Copy image to canvas as required.
+				// needsRedraw is set on creation or if the underlying GL resources of a layer are lost.
+				if ( quadLayer && quadLayer.needsRedraw ) {
+
+					const glBinding = new XRWebGLBinding( session, gl );
+					const glayer = glBinding.getSubImage( quadLayer, frame );
+					renderer.state.bindTexture( gl.TEXTURE_2D, glayer.colorTexture );
+					gl.pixelStorei( gl.UNPACK_FLIP_Y_WEBGL, true );
+					gl.texSubImage2D( gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, snellenTexture.image );
+
 				}
 				}
 
 
 				renderer.render( scene, camera );
 				renderer.render( scene, camera );

Some files were not shown because too many files changed in this diff