diff --git a/examples/js/controls/OrbitControls.js b/examples/js/controls/OrbitControls.js index f42894882378b0202f028fa481cfe5d61e0512c4..0bab4268c002ed42ae158aad430ebc9c6e743dd1 100644 --- a/examples/js/controls/OrbitControls.js +++ b/examples/js/controls/OrbitControls.js @@ -111,6 +111,12 @@ }; + this.getDistance = function () { + + return this.object.position.distanceTo( this.target ); + + }; + this.listenToKeyEvents = function ( domElement ) { domElement.addEventListener( 'keydown', onKeyDown ); diff --git a/examples/js/exporters/GLTFExporter.js b/examples/js/exporters/GLTFExporter.js index 2bfe7ded66afebd92317f1a3561bf58419122a4a..7587a4a20f7ac7b1bfba7698e51c7fd7c13e4de0 100644 --- a/examples/js/exporters/GLTFExporter.js +++ b/examples/js/exporters/GLTFExporter.js @@ -1126,14 +1126,10 @@ index: this.processTexture( material.normalMap ) }; - if ( material.normalScale && material.normalScale.x !== - 1 ) { - - if ( material.normalScale.x !== material.normalScale.y ) { - - console.warn( 'THREE.GLTFExporter: Normal scale components are different, ignoring Y and exporting X.' ); - - } + if ( material.normalScale && material.normalScale.x !== 1 ) { + // glTF normal scale is univariate. Ignore `y`, which may be flipped. + // Context: https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 normalMapDef.scale = material.normalScale.x; } diff --git a/examples/js/loaders/FBXLoader.js b/examples/js/loaders/FBXLoader.js index c3b46add904c1926351d074c02914bca008f9986..3381079a6a9e2c37a74c24dca3fb6f969d529be9 100644 --- a/examples/js/loaders/FBXLoader.js +++ b/examples/js/loaders/FBXLoader.js @@ -549,7 +549,13 @@ case 'DiffuseColor': case 'Maya|TEX_color_map': parameters.map = scope.getTexture( textureMap, child.ID ); - parameters.map.encoding = THREE.sRGBEncoding; + + if ( parameters.map !== undefined ) { + + parameters.map.encoding = THREE.sRGBEncoding; + + } + break; case 'DisplacementColor': @@ -558,7 +564,13 @@ case 'EmissiveColor': parameters.emissiveMap = scope.getTexture( textureMap, child.ID ); - parameters.emissiveMap.encoding = THREE.sRGBEncoding; + + if ( parameters.emissiveMap !== undefined ) { + + parameters.emissiveMap.encoding = THREE.sRGBEncoding; + + } + break; case 'NormalMap': @@ -568,13 +580,25 @@ case 'ReflectionColor': parameters.envMap = scope.getTexture( textureMap, child.ID ); - parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; - parameters.envMap.encoding = THREE.sRGBEncoding; + + if ( parameters.envMap !== undefined ) { + + parameters.envMap.mapping = THREE.EquirectangularReflectionMapping; + parameters.envMap.encoding = THREE.sRGBEncoding; + + } + break; case 'SpecularColor': parameters.specularMap = scope.getTexture( textureMap, child.ID ); - parameters.specularMap.encoding = THREE.sRGBEncoding; + + if ( parameters.specularMap !== undefined ) { + + parameters.specularMap.encoding = THREE.sRGBEncoding; + + } + break; case 'TransparentColor': @@ -612,7 +636,17 @@ } - return textureMap.get( id ); + const texture = textureMap.get( id ); + + if ( texture.image !== undefined ) { + + return texture; + + } else { + + return undefined; + + } } // Parse nodes in FBXTree.Objects.Deformer // Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here diff --git a/examples/js/loaders/GLTFLoader.js b/examples/js/loaders/GLTFLoader.js index 8dc4eb284f849525aec0ac993e29b9ccf9f6c5a4..0334395876ea48c9f4869cc1cf64d4da87735f23 100644 --- a/examples/js/loaders/GLTFLoader.js +++ b/examples/js/loaders/GLTFLoader.js @@ -2610,8 +2610,7 @@ if ( useVertexTangents ) { - cachedMaterial.vertexTangents = true; // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 - + // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995 if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1; if ( cachedMaterial.clearcoatNormalScale ) cachedMaterial.clearcoatNormalScale.y *= - 1; diff --git a/examples/js/loaders/LDrawLoader.js b/examples/js/loaders/LDrawLoader.js index 3478de3b615217d9c58f5686e8b4a948323b2ec6..fe120759d51555140c6f17755a4a90517ae58b36 100644 --- a/examples/js/loaders/LDrawLoader.js +++ b/examples/js/loaders/LDrawLoader.js @@ -17,89 +17,130 @@ const FILE_LOCATION_TRY_RELATIVE = 4; const FILE_LOCATION_TRY_ABSOLUTE = 5; const FILE_LOCATION_NOT_FOUND = 6; - const conditionalLineVertShader = -/* glsl */ -` - attribute vec3 control0; - attribute vec3 control1; - attribute vec3 direction; - varying float discardFlag; - - #include - #include - #include - #include - #include - void main() { - #include - - vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); - gl_Position = projectionMatrix * mvPosition; - - // Transform the line segment ends and control points into camera clip space - vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 ); - vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 ); - vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); - vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + direction, 1.0 ); - - c0.xy /= c0.w; - c1.xy /= c1.w; - p0.xy /= p0.w; - p1.xy /= p1.w; - - // Get the direction of the segment and an orthogonal vector - vec2 dir = p1.xy - p0.xy; - vec2 norm = vec2( -dir.y, dir.x ); - - // Get control point directions from the line - vec2 c0dir = c0.xy - p1.xy; - vec2 c1dir = c1.xy - p1.xy; - - // If the vectors to the controls points are pointed in different directions away - // from the line segment then the line should not be drawn. - float d0 = dot( normalize( norm ), normalize( c0dir ) ); - float d1 = dot( normalize( norm ), normalize( c1dir ) ); - discardFlag = float( sign( d0 ) != sign( d1 ) ); - - #include - #include - #include - } - `; - const conditionalLineFragShader = -/* glsl */ -` - uniform vec3 diffuse; - uniform float opacity; - varying float discardFlag; - - #include - #include - #include - #include - #include - void main() { - - if ( discardFlag > 0.5 ) discard; - - #include - vec3 outgoingLight = vec3( 0.0 ); - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - #include - outgoingLight = diffuseColor.rgb; // simple shader - gl_FragColor = vec4( outgoingLight, diffuseColor.a ); - #include - #include - #include - #include - } - `; const _tempVec0 = new THREE.Vector3(); const _tempVec1 = new THREE.Vector3(); + class LDrawConditionalLineMaterial extends THREE.ShaderMaterial { + + constructor( parameters ) { + + super( { + uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.fog, { + diffuse: { + value: new THREE.Color() + }, + opacity: { + value: 1.0 + } + } ] ), + vertexShader: + /* glsl */ + ` + attribute vec3 control0; + attribute vec3 control1; + attribute vec3 direction; + varying float discardFlag; + + #include + #include + #include + #include + #include + void main() { + #include + + vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); + gl_Position = projectionMatrix * mvPosition; + + // Transform the line segment ends and control points into camera clip space + vec4 c0 = projectionMatrix * modelViewMatrix * vec4( control0, 1.0 ); + vec4 c1 = projectionMatrix * modelViewMatrix * vec4( control1, 1.0 ); + vec4 p0 = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); + vec4 p1 = projectionMatrix * modelViewMatrix * vec4( position + direction, 1.0 ); + + c0.xy /= c0.w; + c1.xy /= c1.w; + p0.xy /= p0.w; + p1.xy /= p1.w; + + // Get the direction of the segment and an orthogonal vector + vec2 dir = p1.xy - p0.xy; + vec2 norm = vec2( -dir.y, dir.x ); + + // Get control point directions from the line + vec2 c0dir = c0.xy - p1.xy; + vec2 c1dir = c1.xy - p1.xy; + + // If the vectors to the controls points are pointed in different directions away + // from the line segment then the line should not be drawn. + float d0 = dot( normalize( norm ), normalize( c0dir ) ); + float d1 = dot( normalize( norm ), normalize( c1dir ) ); + discardFlag = float( sign( d0 ) != sign( d1 ) ); + + #include + #include + #include + } + `, + fragmentShader: + /* glsl */ + ` + uniform vec3 diffuse; + uniform float opacity; + varying float discardFlag; + + #include + #include + #include + #include + #include + void main() { + + if ( discardFlag > 0.5 ) discard; + + #include + vec3 outgoingLight = vec3( 0.0 ); + vec4 diffuseColor = vec4( diffuse, opacity ); + #include + #include + outgoingLight = diffuseColor.rgb; // simple shader + gl_FragColor = vec4( outgoingLight, diffuseColor.a ); + #include + #include + #include + #include + } + ` + } ); + Object.defineProperties( this, { + opacity: { + get: function () { + + return this.uniforms.opacity.value; + + }, + set: function ( value ) { + + this.uniforms.opacity.value = value; + + } + }, + color: { + get: function () { + + return this.uniforms.diffuse.value; + + } + } + } ); + this.setValues( parameters ); + + } + + } + function smoothNormals( triangles, lineSegments ) { function hashVertex( v ) { @@ -154,13 +195,7 @@ } - } // NOTE: Some of the normals wind up being skewed in an unexpected way because - // quads provide more "influence" to some vertex normals than a triangle due to - // the fact that a quad is made up of two triangles and all triangles are weighted - // equally. To fix this quads could be tracked separately so their vertex normals - // are weighted appropriately or we could try only adding a normal direction - // once per normal. - // Iterate until we've tried to connect all triangles to share normals + } // Iterate until we've tried to connect all triangles to share normals while ( true ) { @@ -181,14 +216,14 @@ if ( tri.n0 === null ) { - tri.n0 = faceNormal.clone(); + tri.n0 = faceNormal.clone().multiplyScalar( tri.fromQuad ? 0.5 : 1.0 ); normals.push( tri.n0 ); } if ( tri.n1 === null ) { - tri.n1 = faceNormal.clone(); + tri.n1 = faceNormal.clone().multiplyScalar( tri.fromQuad ? 0.5 : 1.0 ); normals.push( tri.n1 ); } @@ -249,7 +284,8 @@ const norm = tri[ `n${next}` ]; otherTri[ `n${otherIndex}` ] = norm; - norm.add( otherTri.faceNormal ); + const isDoubledVert = otherTri.fromQuad && otherIndex !== 2; + norm.addScaledVector( otherTri.faceNormal, isDoubledVert ? 0.5 : 1.0 ); } @@ -257,7 +293,8 @@ const norm = tri[ `n${index}` ]; otherTri[ `n${otherNext}` ] = norm; - norm.add( otherTri.faceNormal ); + const isDoubledVert = otherTri.fromQuad && otherNext !== 2; + norm.addScaledVector( otherTri.faceNormal, isDoubledVert ? 0.5 : 1.0 ); } @@ -723,7 +760,6 @@ let luminance = 0; let finishType = FINISH_TYPE_DEFAULT; - let canHaveEnvMap = true; let edgeMaterial = null; const name = lineParser.getToken(); @@ -904,7 +940,6 @@ roughness: 0.9, metalness: 0 } ); - canHaveEnvMap = false; break; case FINISH_TYPE_MATTE_METALLIC: @@ -937,7 +972,6 @@ material.depthWrite = ! isTransparent; material.polygonOffset = true; material.polygonOffsetFactor = 1; - material.userData.canHaveEnvMap = canHaveEnvMap; if ( luminance !== 0 ) { @@ -955,25 +989,15 @@ depthWrite: ! isTransparent } ); edgeMaterial.userData.code = code; - edgeMaterial.name = name + ' - Edge'; - edgeMaterial.userData.canHaveEnvMap = false; // This is the material used for conditional edges - - edgeMaterial.userData.conditionalEdgeMaterial = new THREE.ShaderMaterial( { - vertexShader: conditionalLineVertShader, - fragmentShader: conditionalLineFragShader, - uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib.fog, { - diffuse: { - value: new THREE.Color( edgeColour ) - }, - opacity: { - value: alpha - } - } ] ), + edgeMaterial.name = name + ' - Edge'; // This is the material used for conditional edges + + edgeMaterial.userData.conditionalEdgeMaterial = new LDrawConditionalLineMaterial( { fog: true, transparent: isTransparent, - depthWrite: ! isTransparent + depthWrite: ! isTransparent, + color: edgeColour, + opacity: alpha } ); - edgeMaterial.userData.conditionalEdgeMaterial.userData.canHaveEnvMap = false; } @@ -1367,7 +1391,8 @@ faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: false } ); if ( doubleSided === true ) { @@ -1381,7 +1406,8 @@ faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: false } ); } @@ -1415,17 +1441,20 @@ _tempVec1.subVectors( v2, v1 ); - faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize(); + faceNormal = new THREE.Vector3().crossVectors( _tempVec0, _tempVec1 ).normalize(); // specifically place the triangle diagonal in the v0 and v1 slots so we can + // account for the doubling of vertices later when smoothing normals. + triangles.push( { material: material, colourCode: material.userData.code, - v0: v0, - v1: v1, - v2: v2, + v0: v2, + v1: v0, + v2: v1, faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: true } ); triangles.push( { material: material, @@ -1436,7 +1465,8 @@ faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: true } ); if ( doubleSided === true ) { @@ -1450,18 +1480,20 @@ faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: true } ); triangles.push( { material: material, colourCode: material.userData.code, - v0: v0, - v1: v3, - v2: v2, + v0: v2, + v1: v0, + v2: v3, faceNormal: faceNormal, n0: null, n1: null, - n2: null + n2: null, + fromQuad: true } ); } diff --git a/examples/js/postprocessing/CubeTexturePass.js b/examples/js/postprocessing/CubeTexturePass.js index b245b3dff7b1a764f9bc36f097eb5988ee337508..7aabbaf94c337e6cd8115b50fc691946e33f1ca8 100644 --- a/examples/js/postprocessing/CubeTexturePass.js +++ b/examples/js/postprocessing/CubeTexturePass.js @@ -40,7 +40,7 @@ this.cubeCamera.projectionMatrix.copy( this.camera.projectionMatrix ); this.cubeCamera.quaternion.setFromRotationMatrix( this.camera.matrixWorld ); this.cubeMesh.material.uniforms.envMap.value = this.envMap; - this.cubeMesh.material.uniforms.flipEnvMap.value = this.envMap.isCubeTexture && this.envMap._needsFlipEnvMap ? - 1 : 1; + this.cubeMesh.material.uniforms.flipEnvMap.value = this.envMap.isCubeTexture && this.envMap.isRenderTargetTexture === false ? - 1 : 1; this.cubeMesh.material.uniforms.opacity.value = this.opacity; this.cubeMesh.material.transparent = this.opacity < 1.0; renderer.setRenderTarget( this.renderToScreen ? null : readBuffer ); diff --git a/examples/js/utils/GPUStatsPanel.js b/examples/js/utils/GPUStatsPanel.js new file mode 100644 index 0000000000000000000000000000000000000000..778d2fdfe42f03d668d21207f85b06ff6824f9bd --- /dev/null +++ b/examples/js/utils/GPUStatsPanel.js @@ -0,0 +1,134 @@ +( function () { + + // https://www.khronos.org/registry/webgl/extensions/EXT_disjoint_timer_query_webgl2/ + + class GPUStatsPanel extends Stats.Panel { + + constructor( context, name = 'GPU MS' ) { + + super( name, '#f90', '#210' ); + let isWebGL2 = true; + let extension = context.getExtension( 'EXT_disjoint_timer_query_webgl2' ); + + if ( extension === null ) { + + isWebGL2 = false; + extension = context.getExtension( 'EXT_disjoint_timer_query' ); + + if ( extension === null ) { + + console.warn( 'GPUStatsPanel: disjoint_time_query extension not available.' ); + + } + + } + + this.context = context; + this.extension = extension; + this.maxTime = 30; + this.activeQueries = 0; + + this.startQuery = function () { + + const gl = this.context; + const ext = this.extension; + + if ( ext === null ) { + + return; + + } // create the query object + + + let query; + + if ( isWebGL2 ) { + + query = gl.createQuery(); + gl.beginQuery( ext.TIME_ELAPSED_EXT, query ); + + } else { + + query = ext.createQueryEXT(); + ext.beginQueryEXT( ext.TIME_ELAPSED_EXT, query ); + + } + + this.activeQueries ++; + + const checkQuery = () => { + + // check if the query is available and valid + let available, disjoint, ns; + + if ( isWebGL2 ) { + + available = gl.getQueryParameter( query, gl.QUERY_RESULT_AVAILABLE ); + disjoint = gl.getParameter( ext.GPU_DISJOINT_EXT ); + ns = gl.getQueryParameter( query, gl.QUERY_RESULT ); + + } else { + + available = ext.getQueryObjectEXT( query, ext.QUERY_RESULT_AVAILABLE_EXT ); + disjoint = gl.getParameter( ext.GPU_DISJOINT_EXT ); + ns = ext.getQueryObjectEXT( query, ext.QUERY_RESULT_EXT ); + + } + + const ms = ns * 1e-6; + + if ( available ) { + + // update the display if it is valid + if ( ! disjoint ) { + + this.update( ms, this.maxTime ); + + } + + this.activeQueries --; + + } else { + + // otherwise try again the next frame + requestAnimationFrame( checkQuery ); + + } + + }; + + requestAnimationFrame( checkQuery ); + + }; + + this.endQuery = function () { + + // finish the query measurement + const ext = this.extension; + const gl = this.context; + + if ( ext === null ) { + + return; + + } + + if ( isWebGL2 ) { + + gl.endQuery( ext.TIME_ELAPSED_EXT ); + + } else { + + ext.endQueryEXT( ext.TIME_ELAPSED_EXT ); + + } + + }; + + } + + } + + THREE.GPUStatsPanel = GPUStatsPanel; + +} )();