🏗️ IFC Viewer

Note: IFC files need to be converted to GLB/GLTF format first using tools like Blender or FreeCAD.

Initializing viewer...
// Global variables let components; let world; let fragments; // Initialize the viewer async function initViewer() { try { updateStatus('Initializing modern viewer...', 'loading'); const container = document.getElementById('viewer-container'); // Initialize the components components = new OBC.Components(); // Create a world const worlds = components.get(OBC.Worlds); world = worlds.create(); // Create a simple scene, camera and renderer world.scene = new OBC.SimpleScene(components); world.renderer = new OBCF.PostproductionRenderer(components, container); world.camera = new OBC.SimpleCamera(components); // Initialize the world components.init(); // Set up the camera position world.camera.controls.setLookAt(10, 10, 10, 0, 0, 0); // Set up fragments manager for IFC loading fragments = components.get(OBC.FragmentsManager); // Get the IFC loader const ifcLoader = components.get(OBC.IfcLoader); await ifcLoader.setup(); // Set up file input const fileInput = document.getElementById('file-input'); fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; if (file) { await loadIfcFile(file); } }); // Make functions globally available window.loadSampleModel = loadSampleModel; window.fitToView = fitToView; window.resetCamera = resetCamera; window.clearModel = clearModel; updateStatus('Modern viewer ready! Choose an IFC file to load.', 'success'); } catch (error) { console.error('Failed to initialize viewer:', error); updateStatus(`Initialization failed: ${error.message}`, 'error'); } } // Load IFC file async function loadIfcFile(file) { try { updateStatus(`Loading ${file.name}...`, 'loading'); // Clear existing models fragments.dispose(); // Load the IFC file const buffer = await file.arrayBuffer(); const model = await components.get(OBC.IfcLoader).load(new Uint8Array(buffer)); // Add to scene world.scene.three.add(model); // Fit to view await world.camera.controls.fitToSphere(model, true); updateStatus(`${file.name} loaded successfully!`, 'success'); } catch (error) { console.error('Failed to load IFC file:', error); updateStatus(`Failed to load ${file.name}: ${error.message}`, 'error'); } } // Load sample model async function loadSampleModel(filename) { try { const button = document.querySelector(`[onclick="loadSampleModel('${filename}')"]`); button.classList.add('loading'); updateStatus(`Loading ${filename}...`, 'loading'); // Clear existing models fragments.dispose(); // Try different paths const paths = [ `../models/${filename}`, `models/${filename}`, `http://localhost:8000/models/${filename}` ]; let loaded = false; for (const path of paths) { try { const response = await fetch(path); if (!response.ok) continue; const buffer = await response.arrayBuffer(); const model = await components.get(OBC.IfcLoader).load(new Uint8Array(buffer)); // Add to scene world.scene.three.add(model); // Fit to view await world.camera.controls.fitToSphere(model, true); button.classList.remove('loading'); button.classList.add('loaded'); updateStatus(`${filename} loaded from ${path}`, 'success'); loaded = true; break; } catch (pathError) { console.warn(`Failed to load from ${path}:`, pathError); } } if (!loaded) { button.classList.remove('loading'); button.classList.add('error'); updateStatus(`Could not find ${filename}`, 'error'); } } catch (error) { console.error('Failed to load sample model:', error); const button = document.querySelector(`[onclick="loadSampleModel('${filename}')"]`); button.classList.remove('loading'); button.classList.add('error'); updateStatus(`Failed to load ${filename}: ${error.message}`, 'error'); } } // Utility functions function fitToView() { try { const meshes = []; world.scene.three.traverse((child) => { if (child.isMesh) { meshes.push(child); } }); if (meshes.length > 0) { const box = new THREE.Box3(); meshes.forEach(mesh => box.expandByObject(mesh)); const sphere = new THREE.Sphere(); box.getBoundingSphere(sphere); world.camera.controls.fitToSphere(sphere, true); updateStatus('Fitted to view', 'ready'); } else { updateStatus('No models to fit', 'ready'); } } catch (error) { updateStatus('Fit to view failed', 'error'); } } function resetCamera() { try { world.camera.controls.setLookAt(10, 10, 10, 0, 0, 0); updateStatus('Camera reset', 'ready'); } catch (error) { updateStatus('Camera reset failed', 'error'); } } function clearModel() { try { fragments.dispose(); updateStatus('Models cleared', 'ready'); // Reset button states const buttons = document.querySelectorAll('.model-btn'); buttons.forEach(btn => { btn.classList.remove('loaded', 'loading', 'error'); }); } catch (error) { updateStatus('Clear models failed', 'error'); } } function updateStatus(message, type = 'ready') { const statusEl = document.getElementById('status'); if (statusEl) { statusEl.textContent = message; statusEl.className = `status ${type}`; } } // Initialize when DOM is loaded initViewer();