Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions examples/jsm/lighting/LightProbeGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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).
//
Expand Down Expand Up @@ -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` );

Expand Down
Binary file modified examples/screenshots/webgl_loader_ifc.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
209 changes: 167 additions & 42 deletions examples/webgl_loader_ifc.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a>
-
IFC loader using
<a href="https://github.com/ThatOpen/engine_web-ifc" target="_blank" rel="noopener">web-ifc</a>.
See <a href="https://github.com/ThatOpen" target="_blank" rel="noopener">main project repository</a> for more information and BIM tools.
</div>

Expand All @@ -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"
}
}
</script>
Expand All @@ -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 );
Expand All @@ -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();

}
Expand All @@ -121,8 +248,6 @@

}

init();

</script>
</body>
</html>
Loading
Loading