Bläddra i källkod

Updated examples builds.

Mr.doob 3 år sedan
förälder
incheckning
8570c3f005

+ 67 - 51
examples/js/exporters/GLTFExporter.js

@@ -305,7 +305,7 @@
 
 		}
 
-		if ( typeof OffscreenCanvas !== 'undefined' ) {
+		if ( typeof document === 'undefined' && typeof OffscreenCanvas !== 'undefined' ) {
 
 			cachedCanvas = new OffscreenCanvas( 1, 1 );
 
@@ -317,6 +317,34 @@
 
 		return cachedCanvas;
 
+	}
+
+	function getToBlobPromise( canvas, mimeType ) {
+
+		if ( canvas.toBlob !== undefined ) {
+
+			return new Promise( resolve => canvas.toBlob( resolve, mimeType ) );
+
+		}
+
+		let quality; // Blink's implementation of convertToBlob seems to default to a quality level of 100%
+		// Use the Blink default quality levels of toBlob instead so that file sizes are comparable.
+
+		if ( mimeType === 'image/jpeg' ) {
+
+			quality = 0.92;
+
+		} else if ( mimeType === 'image/webp' ) {
+
+			quality = 0.8;
+
+		}
+
+		return canvas.convertToBlob( {
+			type: mimeType,
+			quality: quality
+		} );
+
 	}
 	/**
  * Writer
@@ -376,7 +404,6 @@
 				trs: false,
 				onlyVisible: true,
 				truncateDrawRange: true,
-				embedImages: true,
 				maxTextureSize: Infinity,
 				animations: [],
 				includeCustomExtensions: false
@@ -946,86 +973,75 @@
 			const imageDef = {
 				mimeType: mimeType
 			};
+			const canvas = getCanvas();
+			canvas.width = Math.min( image.width, options.maxTextureSize );
+			canvas.height = Math.min( image.height, options.maxTextureSize );
+			const ctx = canvas.getContext( '2d' );
 
-			if ( options.embedImages ) {
-
-				const canvas = getCanvas();
-				canvas.width = Math.min( image.width, options.maxTextureSize );
-				canvas.height = Math.min( image.height, options.maxTextureSize );
-				const ctx = canvas.getContext( '2d' );
-
-				if ( flipY === true ) {
-
-					ctx.translate( 0, canvas.height );
-					ctx.scale( 1, - 1 );
-
-				}
-
-				if ( image.data !== undefined ) {
-
-					// THREE.DataTexture
-					if ( format !== THREE.RGBAFormat ) {
+			if ( flipY === true ) {
 
-						console.error( 'GLTFExporter: Only THREE.RGBAFormat is supported.' );
+				ctx.translate( 0, canvas.height );
+				ctx.scale( 1, - 1 );
 
-					}
+			}
 
-					if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
+			if ( image.data !== undefined ) {
 
-						console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
+				// THREE.DataTexture
+				if ( format !== THREE.RGBAFormat ) {
 
-					}
+					console.error( 'GLTFExporter: Only THREE.RGBAFormat is supported.' );
 
-					const data = new Uint8ClampedArray( image.height * image.width * 4 );
+				}
 
-					for ( let i = 0; i < data.length; i += 4 ) {
+				if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
 
-						data[ i + 0 ] = image.data[ i + 0 ];
-						data[ i + 1 ] = image.data[ i + 1 ];
-						data[ i + 2 ] = image.data[ i + 2 ];
-						data[ i + 3 ] = image.data[ i + 3 ];
+					console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
 
-					}
+				}
 
-					ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
+				const data = new Uint8ClampedArray( image.height * image.width * 4 );
 
-				} else {
+				for ( let i = 0; i < data.length; i += 4 ) {
 
-					ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
+					data[ i + 0 ] = image.data[ i + 0 ];
+					data[ i + 1 ] = image.data[ i + 1 ];
+					data[ i + 2 ] = image.data[ i + 2 ];
+					data[ i + 3 ] = image.data[ i + 3 ];
 
 				}
 
-				if ( options.binary === true ) {
+				ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
 
-					let toBlobPromise;
+			} else {
 
-					if ( canvas.toBlob !== undefined ) {
+				ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
 
-						toBlobPromise = new Promise( resolve => canvas.toBlob( resolve, mimeType ) );
+			}
 
-					} else {
+			if ( options.binary === true ) {
 
-						toBlobPromise = canvas.convertToBlob( {
-							type: mimeType
-						} );
+				pending.push( getToBlobPromise( canvas, mimeType ).then( blob => writer.processBufferViewImage( blob ) ).then( bufferViewIndex => {
 
-					}
+					imageDef.bufferView = bufferViewIndex;
 
-					pending.push( toBlobPromise.then( blob => writer.processBufferViewImage( blob ).then( bufferViewIndex => {
+				} ) );
+
+			} else {
 
-						imageDef.bufferView = bufferViewIndex;
+				if ( canvas.toDataURL !== undefined ) {
 
-					} ) ) );
+					imageDef.uri = canvas.toDataURL( mimeType );
 
 				} else {
 
-					imageDef.uri = canvas.toDataURL( mimeType );
+					pending.push( getToBlobPromise( canvas, mimeType ).then( blob => new FileReader().readAsDataURL( blob ) ).then( dataURL => {
 
-				}
+						imageDef.uri = dataURL;
 
-			} else {
+					} ) );
 
-				imageDef.uri = image.src;
+				}
 
 			}
 

+ 163 - 18
examples/js/interactive/HTMLMesh.js

@@ -8,7 +8,8 @@
 			const geometry = new THREE.PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
 			const material = new THREE.MeshBasicMaterial( {
 				map: texture,
-				toneMapped: false
+				toneMapped: false,
+				transparent: true
 			} );
 			super( geometry, material );
 
@@ -110,6 +111,7 @@
 	function html2canvas( element ) {
 
 		const range = document.createRange();
+		const color = new THREE.Color();
 
 		function Clipper( context ) {
 
@@ -176,15 +178,29 @@
 
 				}
 
-				context.font = style.fontSize + ' ' + style.fontFamily;
+				context.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily;
 				context.textBaseline = 'top';
 				context.fillStyle = style.color;
-				context.fillText( string, x, y );
+				context.fillText( string, x, y + parseFloat( style.fontSize ) * 0.1 );
 
 			}
 
 		}
 
+		function buildRectPath( x, y, w, h, r ) {
+
+			if ( w < 2 * r ) r = w / 2;
+			if ( h < 2 * r ) r = h / 2;
+			context.beginPath();
+			context.moveTo( x + r, y );
+			context.arcTo( x + w, y, x + w, y + h, r );
+			context.arcTo( x + w, y + h, x, y + h, r );
+			context.arcTo( x, y + h, x, y, r );
+			context.arcTo( x, y, x + w, y, r );
+			context.closePath();
+
+		}
+
 		function drawBorder( style, which, x, y, width, height ) {
 
 			const borderWidth = style[ which + 'Width' ];
@@ -194,6 +210,7 @@
 			if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
 
 				context.strokeStyle = borderColor;
+				context.lineWidth = parseFloat( borderWidth );
 				context.beginPath();
 				context.moveTo( x, y );
 				context.lineTo( x + width, y + height );
@@ -243,31 +260,146 @@
 				y = rect.top - offset.top - 0.5;
 				width = rect.width;
 				height = rect.height;
-				style = window.getComputedStyle( element );
+				style = window.getComputedStyle( element ); // Get the border of the element used for fill and border
+
+				buildRectPath( x, y, width, height, parseFloat( style.borderRadius ) );
 				const backgroundColor = style.backgroundColor;
 
 				if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
 
 					context.fillStyle = backgroundColor;
-					context.fillRect( x, y, width, height );
+					context.fill();
+
+				} // If all the borders match then stroke the round rectangle
+
+
+				const borders = [ 'borderTop', 'borderLeft', 'borderBottom', 'borderRight' ];
+				let match = true;
+				let prevBorder = null;
+
+				for ( const border of borders ) {
+
+					if ( prevBorder !== null ) {
+
+						match = style[ border + 'Width' ] === style[ prevBorder + 'Width' ] && style[ border + 'Color' ] === style[ prevBorder + 'Color' ] && style[ border + 'Style' ] === style[ prevBorder + 'Style' ];
+
+					}
+
+					if ( match === false ) break;
+					prevBorder = border;
 
 				}
 
-				drawBorder( style, 'borderTop', x, y, width, 0 );
-				drawBorder( style, 'borderLeft', x, y, 0, height );
-				drawBorder( style, 'borderBottom', x, y + height, width, 0 );
-				drawBorder( style, 'borderRight', x + width, y, 0, height );
+				if ( match === true ) {
+
+					// They all match so stroke the rectangle from before allows for border-radius
+					const width = parseFloat( style.borderTopWidth );
+
+					if ( style.borderTopWidth !== '0px' && style.borderTopStyle !== 'none' && style.borderTopColor !== 'transparent' && style.borderTopColor !== 'rgba(0, 0, 0, 0)' ) {
 
-				if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
+						context.strokeStyle = style.borderTopColor;
+						context.lineWidth = width;
+						context.stroke();
 
-					clipper.add( {
-						x: x,
-						y: y,
-						width: width,
-						height: height
-					} );
-					drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
-					clipper.remove();
+					}
+
+				} else {
+
+					// Otherwise draw individual borders
+					drawBorder( style, 'borderTop', x, y, width, 0 );
+					drawBorder( style, 'borderLeft', x, y, 0, height );
+					drawBorder( style, 'borderBottom', x, y + height, width, 0 );
+					drawBorder( style, 'borderRight', x + width, y, 0, height );
+
+				}
+
+				if ( element instanceof HTMLInputElement ) {
+
+					let accentColor = style.accentColor;
+					if ( accentColor === undefined || accentColor === 'auto' ) accentColor = style.color;
+					color.set( accentColor );
+					const luminance = Math.sqrt( 0.299 * color.r ** 2 + 0.587 * color.g ** 2 + 0.114 * color.b ** 2 );
+					const accentTextColor = luminance < 0.5 ? 'white' : '#111111';
+
+					if ( element.type === 'radio' ) {
+
+						buildRectPath( x, y, width, height, height );
+						context.fillStyle = 'white';
+						context.strokeStyle = accentColor;
+						context.lineWidth = 1;
+						context.fill();
+						context.stroke();
+
+						if ( element.checked ) {
+
+							buildRectPath( x + 2, y + 2, width - 4, height - 4, height );
+							context.fillStyle = accentColor;
+							context.strokeStyle = accentTextColor;
+							context.lineWidth = 2;
+							context.fill();
+							context.stroke();
+
+						}
+
+					}
+
+					if ( element.type === 'checkbox' ) {
+
+						buildRectPath( x, y, width, height, 2 );
+						context.fillStyle = element.checked ? accentColor : 'white';
+						context.strokeStyle = element.checked ? accentTextColor : accentColor;
+						context.lineWidth = 1;
+						context.stroke();
+						context.fill();
+
+						if ( element.checked ) {
+
+							const currentTextAlign = context.textAlign;
+							context.textAlign = 'center';
+							const properties = {
+								color: accentTextColor,
+								fontFamily: style.fontFamily,
+								fontSize: height + 'px',
+								fontWeight: 'bold'
+							};
+							drawText( properties, x + width / 2, y, '✔' );
+							context.textAlign = currentTextAlign;
+
+						}
+
+					}
+
+					if ( element.type === 'range' ) {
+
+						const [ min, max, value ] = [ 'min', 'max', 'value' ].map( property => parseFloat( element[ property ] ) );
+						const position = ( value - min ) / ( max - min ) * ( width - height );
+						buildRectPath( x, y + height / 4, width, height / 2, height / 4 );
+						context.fillStyle = accentTextColor;
+						context.strokeStyle = accentColor;
+						context.lineWidth = 1;
+						context.fill();
+						context.stroke();
+						buildRectPath( x, y + height / 4, position + height / 2, height / 2, height / 4 );
+						context.fillStyle = accentColor;
+						context.fill();
+						buildRectPath( x + position, y, height, height, height / 2 );
+						context.fillStyle = accentColor;
+						context.fill();
+
+					}
+
+					if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
+
+						clipper.add( {
+							x: x,
+							y: y,
+							width: width,
+							height: height
+						} );
+						drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
+						clipper.remove();
+
+					}
 
 				}
 
@@ -345,6 +477,19 @@
 
 					element.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
 
+					if ( element instanceof HTMLInputElement && element.type === 'range' && ( event === 'mousedown' || event === 'click' ) ) {
+
+						const [ min, max ] = [ 'min', 'max' ].map( property => parseFloat( element[ property ] ) );
+						const width = rect.width;
+						const offsetX = x - rect.x;
+						const proportion = offsetX / width;
+						element.value = min + ( max - min ) * proportion;
+						element.dispatchEvent( new InputEvent( 'input', {
+							bubbles: true
+						} ) );
+
+					}
+
 				}
 
 				for ( let i = 0; i < element.childNodes.length; i ++ ) {

+ 1 - 1
examples/js/interactive/SelectionHelper.js

@@ -2,7 +2,7 @@
 
 	class SelectionHelper {
 
-		constructor( selectionBox, renderer, cssClassName ) {
+		constructor( renderer, cssClassName ) {
 
 			this.element = document.createElement( 'div' );
 			this.element.classList.add( cssClassName );

+ 2 - 1
examples/js/loaders/EXRLoader.js

@@ -1881,8 +1881,9 @@
 
 				}
 
-				if ( spec != 0 ) {
+				if ( ( spec & ~ 0x04 ) != 0 ) {
 
+					// unsupported tiled, deep-image, multi-part
 					console.error( 'EXRHeader:', EXRHeader );
 					throw new Error( 'THREE.EXRLoader: provided file is currently unsupported.' );
 

+ 1 - 9
examples/js/loaders/RGBELoader.js

@@ -371,7 +371,7 @@
 
 				if ( RGBE_RETURN_FAILURE !== image_rgba_data ) {
 
-					let data, format, type;
+					let data, type;
 					let numElements;
 
 					switch ( this.type ) {
@@ -417,7 +417,6 @@
 						header: rgbe_header_info.string,
 						gamma: rgbe_header_info.gamma,
 						exposure: rgbe_header_info.exposure,
-						format: format,
 						type: type
 					};
 
@@ -443,13 +442,6 @@
 				switch ( texture.type ) {
 
 					case THREE.FloatType:
-						texture.encoding = THREE.LinearEncoding;
-						texture.minFilter = THREE.LinearFilter;
-						texture.magFilter = THREE.LinearFilter;
-						texture.generateMipmaps = false;
-						texture.flipY = true;
-						break;
-
 					case THREE.HalfFloatType:
 						texture.encoding = THREE.LinearEncoding;
 						texture.minFilter = THREE.LinearFilter;

+ 1 - 1
examples/js/utils/GPUStatsPanel.js

@@ -88,7 +88,7 @@
 
 						this.activeQueries --;
 
-					} else {
+					} else if ( gl.isContextLost() === false ) {
 
 						// otherwise try again the next frame
 						requestAnimationFrame( checkQuery );