From 72d450b817d9348386e54031e61bfef3123ef446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Gonz=C3=A1lez=20Viegas?= Date: Tue, 28 Apr 2026 22:59:27 +0200 Subject: [PATCH 1/3] Examples: Update webgl_loader_ifc to use web-ifc directly. (#33491) Co-authored-by: Claude Opus 4.7 (1M context) --- examples/screenshots/webgl_loader_ifc.jpg | Bin 27861 -> 25753 bytes examples/webgl_loader_ifc.html | 209 +++++++++++++++++----- 2 files changed, 167 insertions(+), 42 deletions(-) diff --git a/examples/screenshots/webgl_loader_ifc.jpg b/examples/screenshots/webgl_loader_ifc.jpg index d5df276544631cb5a6332151353b0db2c2bf194f..1cd09dcc6195ac584f64bea51fc9b03904340edd 100644 GIT binary patch delta 23927 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 qe_fh#0M)=b<54Gr14FUbE5z6@RX1V4IQ@! three.js - + IFC loader using + web-ifc. See main project repository for more information and BIM tools. @@ -22,10 +24,7 @@ "imports": { "three": "../build/three.module.js", "three/addons/": "./jsm/", - "three/examples/jsm/utils/BufferGeometryUtils": "./jsm/utils/BufferGeometryUtils.js", - "three-mesh-bvh": "https://cdn.jsdelivr.net/npm/three-mesh-bvh@0.5.23/build/index.module.js", - "web-ifc": "https://cdn.jsdelivr.net/npm/web-ifc@0.0.36/web-ifc-api.js", - "web-ifc-three": "https://cdn.jsdelivr.net/npm/web-ifc-three@0.0.126/IFCLoader.js" + "web-ifc": "https://cdn.jsdelivr.net/npm/web-ifc@0.0.77/web-ifc-api.js" } } @@ -34,31 +33,25 @@ import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; + import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js'; - import { IFCLoader } from 'web-ifc-three'; - import { IFCSPACE } from 'web-ifc'; + import { IfcAPI } from 'web-ifc'; + + const WEB_IFC_VERSION = '0.0.77'; + const WEB_IFC_WASM_PATH = `https://cdn.jsdelivr.net/npm/web-ifc@${ WEB_IFC_VERSION }/`; let scene, camera, renderer; + init(); + async function init() { - //Scene scene = new THREE.Scene(); scene.background = new THREE.Color( 0x8cc7de ); - //Camera camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 ); - camera.position.z = - 70; - camera.position.y = 25; - camera.position.x = 90; + camera.position.set( 82.48, 22.09, - 45.24 ); - //Initial cube - const geometry = new THREE.BoxGeometry(); - const material = new THREE.MeshPhongMaterial( { color: 0xffffff } ); - const cube = new THREE.Mesh( geometry, material ); - scene.add( cube ); - - //Lights const directionalLight1 = new THREE.DirectionalLight( 0xffeeff, 2.5 ); directionalLight1.position.set( 1, 1, 1 ); scene.add( directionalLight1 ); @@ -70,47 +63,181 @@ const ambientLight = new THREE.AmbientLight( 0xffffee, 0.75 ); scene.add( ambientLight ); - //Setup IFC Loader - const ifcLoader = new IFCLoader(); - await ifcLoader.ifcManager.setWasmPath( 'https://cdn.jsdelivr.net/npm/web-ifc@0.0.36/', true ); - - await ifcLoader.ifcManager.parser.setupOptionalCategories( { - [ IFCSPACE ]: false, - } ); - - await ifcLoader.ifcManager.applyWebIfcConfig( { - USE_FAST_BOOLS: true - } ); - - ifcLoader.load( 'models/ifc/rac_advanced_sample_project.ifc', function ( model ) { - - scene.add( model.mesh ); - render(); - - } ); - - //Renderer renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setPixelRatio( window.devicePixelRatio ); document.body.appendChild( renderer.domElement ); - //Controls const controls = new OrbitControls( camera, renderer.domElement ); + controls.target.set( 30.86, 7.73, 0.15 ); + controls.update(); controls.addEventListener( 'change', render ); window.addEventListener( 'resize', onWindowResize ); + const ifcAPI = new IfcAPI(); + ifcAPI.SetWasmPath( WEB_IFC_WASM_PATH ); + await ifcAPI.Init(); + + const response = await fetch( 'models/ifc/rac_advanced_sample_project.ifc' ); + const data = new Uint8Array( await response.arrayBuffer() ); + + const modelID = ifcAPI.OpenModel( data, { COORDINATE_TO_ORIGIN: true } ); + loadAllGeometry( ifcAPI, modelID ); + ifcAPI.CloseModel( modelID ); + render(); } + function loadAllGeometry( ifcAPI, modelID ) { + + const opaqueGeometries = []; + const transparentGeometries = []; + const materialCache = {}; + + ifcAPI.StreamAllMeshes( modelID, ( flatMesh ) => { + + const placedGeometries = flatMesh.geometries; + + for ( let i = 0; i < placedGeometries.size(); i ++ ) { + + const placedGeometry = placedGeometries.get( i ); + const mesh = getPlacedGeometry( ifcAPI, modelID, placedGeometry, materialCache ); + const geometry = mesh.geometry.applyMatrix4( mesh.matrix ); + + if ( placedGeometry.color.w !== 1 ) { + + transparentGeometries.push( geometry ); + + } else { + + opaqueGeometries.push( geometry ); + + } + + } + + } ); + + if ( opaqueGeometries.length > 0 ) { + + const merged = BufferGeometryUtils.mergeGeometries( opaqueGeometries ); + const material = new THREE.MeshPhongMaterial( { side: THREE.DoubleSide, vertexColors: true } ); + scene.add( new THREE.Mesh( merged, material ) ); + + } + + if ( transparentGeometries.length > 0 ) { + + const merged = BufferGeometryUtils.mergeGeometries( transparentGeometries ); + const material = new THREE.MeshPhongMaterial( { + side: THREE.DoubleSide, + vertexColors: true, + transparent: true, + } ); + scene.add( new THREE.Mesh( merged, material ) ); + + } + + } + + function getPlacedGeometry( ifcAPI, modelID, placedGeometry, materialCache ) { + + const geometry = getBufferGeometry( ifcAPI, modelID, placedGeometry ); + const material = getMeshMaterial( placedGeometry.color, materialCache ); + const mesh = new THREE.Mesh( geometry, material ); + mesh.matrix = new THREE.Matrix4().fromArray( placedGeometry.flatTransformation ); + mesh.matrixAutoUpdate = false; + return mesh; + + } + + function getBufferGeometry( ifcAPI, modelID, placedGeometry ) { + + const geometry = ifcAPI.GetGeometry( modelID, placedGeometry.geometryExpressID ); + const vertexData = ifcAPI.GetVertexArray( geometry.GetVertexData(), geometry.GetVertexDataSize() ); + const indexData = ifcAPI.GetIndexArray( geometry.GetIndexData(), geometry.GetIndexDataSize() ); + + const bufferGeometry = ifcGeometryToBuffer( placedGeometry.color, vertexData, indexData ); + + // Geometry is owned by the WASM heap and must be released. + geometry.delete(); + return bufferGeometry; + + } + + function getMeshMaterial( color, materialCache ) { + + const id = `${ color.x }-${ color.y }-${ color.z }-${ color.w }`; + const cached = materialCache[ id ]; + if ( cached ) return cached; + + const material = new THREE.MeshPhongMaterial( { + color: new THREE.Color( color.x, color.y, color.z ), + side: THREE.DoubleSide, + } ); + + if ( color.w !== 1 ) { + + material.transparent = true; + material.opacity = color.w; + + } + + materialCache[ id ] = material; + return material; + + } + + const _tmpColor = new THREE.Color(); + + function ifcGeometryToBuffer( color, vertexData, indexData ) { + + // web-ifc returns interleaved [px, py, pz, nx, ny, nz] per vertex. + const vertexCount = vertexData.length / 6; + const positions = new Float32Array( vertexCount * 3 ); + const normals = new Float32Array( vertexCount * 3 ); + const colors = new Float32Array( vertexCount * 4 ); + + // IFC stores colors in sRGB display space; convert to linear once per geometry. + _tmpColor.setRGB( color.x, color.y, color.z, THREE.SRGBColorSpace ); + + for ( let v = 0; v < vertexCount; v ++ ) { + + const src = v * 6; + const dst3 = v * 3; + const dst4 = v * 4; + + positions[ dst3 + 0 ] = vertexData[ src + 0 ]; + positions[ dst3 + 1 ] = vertexData[ src + 1 ]; + positions[ dst3 + 2 ] = vertexData[ src + 2 ]; + + normals[ dst3 + 0 ] = vertexData[ src + 3 ]; + normals[ dst3 + 1 ] = vertexData[ src + 4 ]; + normals[ dst3 + 2 ] = vertexData[ src + 5 ]; + + colors[ dst4 + 0 ] = _tmpColor.r; + colors[ dst4 + 1 ] = _tmpColor.g; + colors[ dst4 + 2 ] = _tmpColor.b; + colors[ dst4 + 3 ] = color.w; + + } + + const geometry = new THREE.BufferGeometry(); + geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + geometry.setAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + geometry.setAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) ); + geometry.setIndex( new THREE.BufferAttribute( indexData, 1 ) ); + return geometry; + + } + function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); - render(); } @@ -121,8 +248,6 @@ } - init(); - From 8c0d85f507d0437e1cb38ca7fb14b06ef792632b Mon Sep 17 00:00:00 2001 From: mrdoob Date: Wed, 29 Apr 2026 06:05:33 +0900 Subject: [PATCH 2/3] LightProbeGrid: Avoid redundant matrixWorld updates during bake. (#33489) Co-authored-by: Claude Opus 4.7 (1M context) --- examples/jsm/lighting/LightProbeGrid.js | 35 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/examples/jsm/lighting/LightProbeGrid.js b/examples/jsm/lighting/LightProbeGrid.js index b0644f065c3a4f..b6e414c827378f 100644 --- a/examples/jsm/lighting/LightProbeGrid.js +++ b/examples/jsm/lighting/LightProbeGrid.js @@ -45,8 +45,8 @@ let _batchTargetProbes = 0; // Reusable temp objects const _position = /*@__PURE__*/ new Vector3(); const _size = /*@__PURE__*/ new Vector3(); -const _savedViewport = /*@__PURE__*/ new Vector4(); -const _savedScissor = /*@__PURE__*/ new Vector4(); +const _currentViewport = /*@__PURE__*/ new Vector4(); +const _currentScissor = /*@__PURE__*/ new Vector4(); // Number of padding texels added at each boundary of every sub-volume in the atlas. const ATLAS_PADDING = 1; @@ -231,10 +231,19 @@ class LightProbeGrid extends Object3D { const batchTarget = _ensureBatchTarget( totalProbes ); // Save renderer state - const savedRenderTarget = renderer.getRenderTarget(); - renderer.getViewport( _savedViewport ); - renderer.getScissor( _savedScissor ); - const savedScissorTest = renderer.getScissorTest(); + const currentRenderTarget = renderer.getRenderTarget(); + renderer.getViewport( _currentViewport ); + renderer.getScissor( _currentScissor ); + const currentScissorTest = renderer.getScissorTest(); + + // Scene is static across the bake — update once and disable per-render auto updates. + const currentMatrixWorldAutoUpdate = scene.matrixWorldAutoUpdate; + if ( currentMatrixWorldAutoUpdate === true ) { + + scene.updateMatrixWorld( true ); + scene.matrixWorldAutoUpdate = false; + + } // Clear pooled batch target so skipped probes read as zero batchTarget.scissorTest = false; @@ -250,7 +259,7 @@ class LightProbeGrid extends Object3D { // Disable shadow map auto-update during bake — lights don't move between probes. // Force one shadow update on the first render so maps are initialized. - const savedShadowAutoUpdate = renderer.shadowMap.autoUpdate; + const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate; renderer.shadowMap.autoUpdate = false; renderer.shadowMap.needsUpdate = true; @@ -280,7 +289,7 @@ class LightProbeGrid extends Object3D { } - renderer.shadowMap.autoUpdate = savedShadowAutoUpdate; + renderer.shadowMap.autoUpdate = currentShadowAutoUpdate; // Phase 2: Repack SH data from batch target into the atlas 3D texture (GPU-to-GPU). // @@ -330,10 +339,12 @@ class LightProbeGrid extends Object3D { } // Restore renderer state - renderer.setRenderTarget( savedRenderTarget ); - renderer.setViewport( _savedViewport ); - renderer.setScissor( _savedScissor ); - renderer.setScissorTest( savedScissorTest ); + renderer.setRenderTarget( currentRenderTarget ); + renderer.setViewport( _currentViewport ); + renderer.setScissor( _currentScissor ); + renderer.setScissorTest( currentScissorTest ); + + scene.matrixWorldAutoUpdate = currentMatrixWorldAutoUpdate; // console.log( `LightProbeGrid: bake complete ${ ( performance.now() - t0 ).toFixed( 1 ) }ms` ); From 49a0e25ed0e334d71b2196de9b72928b6d7ec913 Mon Sep 17 00:00:00 2001 From: Rik Cabanier Date: Tue, 28 Apr 2026 14:15:17 -0700 Subject: [PATCH 3/3] XRManager: Use `foveateBoundTexture()` to enable foveation. (#33484) --- src/renderers/common/XRManager.js | 74 ++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/src/renderers/common/XRManager.js b/src/renderers/common/XRManager.js index 13c50cfb9fe919..20ac220668f3ec 100644 --- a/src/renderers/common/XRManager.js +++ b/src/renderers/common/XRManager.js @@ -14,7 +14,7 @@ import { CylinderGeometry } from '../../geometries/CylinderGeometry.js'; import { PlaneGeometry } from '../../geometries/PlaneGeometry.js'; import { MeshBasicMaterial } from '../../materials/MeshBasicMaterial.js'; import { Mesh } from '../../objects/Mesh.js'; -import { warn } from '../../utils.js'; +import { warn, warnOnce } from '../../utils.js'; import { renderOutput } from '../../nodes/display/RenderOutputNode.js'; const _cameraLPos = /*@__PURE__*/ new Vector3(); @@ -591,6 +591,20 @@ class XRManager extends EventDispatcher { } + /** + * Returns the current base layer. + * + * This is an `XRProjectionLayer` when the targeted XR device supports the + * WebXR Layers API, or an `XRWebGLLayer` otherwise. + * + * @return {?(XRWebGLLayer|XRProjectionLayer)} The XR base layer. + */ + getBaseLayer() { + + return this._glProjLayer !== null ? this._glProjLayer : this._glBaseLayer; + + } + /** * Returns the current XR binding. @@ -612,6 +626,61 @@ class XRManager extends EventDispatcher { } + /** + * Applies WebXR fixed foveation to the internal post-processing render target + * used by the first XR render pass before compositing into a projection layer. + * + * Browser-side `XRWebGLBinding.foveateBoundTexture()` failures are treated as + * non-fatal so they do not interrupt rendering. + * + * @param {RenderTarget} renderTarget - The internal render target. + */ + foveateBoundTexture( renderTarget ) { + + if ( renderTarget.isPostProcessingRenderTarget !== true ) return; + if ( this.isPresenting !== true ) return; + if ( this._glProjLayer === null ) return; + + const backend = this._renderer.backend; + + if ( backend === undefined || backend.isWebGLBackend !== true ) return; + if ( backend.state === null ) return; + + const outputRenderTarget = this._renderer.getOutputRenderTarget(); + + if ( outputRenderTarget === null || outputRenderTarget.isXRRenderTarget !== true ) return; + + const glBinding = this.getBinding(); + + if ( glBinding === null || typeof glBinding.foveateBoundTexture !== 'function' ) return; + + this._renderer._textures.updateRenderTarget( renderTarget ); + + const { textureGPU, glTextureType } = backend.get( renderTarget.texture ); + + if ( textureGPU === undefined || glTextureType === undefined ) return; + if ( renderTarget._xrFoveationTextureGPU === textureGPU ) return; + + renderTarget._xrFoveationTextureGPU = textureGPU; + + backend.state.bindTexture( glTextureType, textureGPU ); + + try { + + glBinding.foveateBoundTexture( glTextureType, this.getFoveation() ); + + } catch ( error ) { + + warnOnce( `XRManager: Unable to foveate bound XR post-processing texture. ${error.name}: ${error.message}` ); + + } finally { + + backend.state.unbindTexture(); + + } + + } + /** * Returns the current XR frame. * @@ -1652,6 +1721,9 @@ function onAnimationFrame( time, frame ) { renderer.setOutputRenderTarget( this._xrRenderTarget ); + const frameBufferTarget = renderer._getFrameBufferTarget(); + renderer.xr.foveateBoundTexture( frameBufferTarget ); + } //