Some checks are pending
CI / backend-lint-and-test (push) Waiting to run
- New ifc_converter service using IfcOpenShell geometry serializer
- Preserves IFC GlobalIds as mesh names for click-to-inspect
- glb files served via /api/projects/{id}/model.glb endpoint
- New endpoint: lookup element by GlobalId for viewer integration
- Excludes IfcSpace and IfcOpeningElement from 3D output
79 lines
2.2 KiB
Python
79 lines
2.2 KiB
Python
"""Service for converting IFC files to glTF/glb for 3D visualization."""
|
|
|
|
import multiprocessing
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import ifcopenshell
|
|
import ifcopenshell.geom
|
|
|
|
|
|
def convert_ifc_to_glb(ifc_path: str, glb_path: str) -> None:
|
|
"""
|
|
Convert an IFC file to glTF Binary (.glb) format.
|
|
|
|
Uses IfcOpenShell's geometry serializer with element GUIDs preserved
|
|
as mesh names, enabling click-to-inspect in the 3D viewer.
|
|
"""
|
|
ifc_file = ifcopenshell.open(ifc_path)
|
|
|
|
settings = ifcopenshell.geom.settings()
|
|
settings.set(
|
|
"dimensionality",
|
|
ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS,
|
|
)
|
|
settings.set("apply-default-materials", True)
|
|
|
|
serialiser_settings = ifcopenshell.geom.serializer_settings()
|
|
serialiser_settings.set("use-element-guids", True)
|
|
|
|
serialiser = ifcopenshell.geom.serializers.gltf(
|
|
glb_path, settings, serialiser_settings
|
|
)
|
|
serialiser.setFile(ifc_file)
|
|
serialiser.setUnitNameAndMagnitude("METER", 1.0)
|
|
serialiser.writeHeader()
|
|
|
|
num_threads = max(1, multiprocessing.cpu_count() - 1)
|
|
iterator = ifcopenshell.geom.iterator(
|
|
settings,
|
|
ifc_file,
|
|
num_threads,
|
|
exclude=(
|
|
"IfcSpace",
|
|
"IfcOpeningElement",
|
|
),
|
|
)
|
|
|
|
if iterator.initialize():
|
|
while True:
|
|
serialiser.write(iterator.get())
|
|
if not iterator.next():
|
|
break
|
|
|
|
serialiser.finalize()
|
|
del serialiser # Ensures temp files are cleaned up
|
|
|
|
|
|
async def convert_ifc_bytes_to_glb(content: bytes, glb_output_path: str) -> None:
|
|
"""
|
|
Convert IFC file content (bytes) to a glb file.
|
|
|
|
Writes IFC to a temporary file first, since IfcOpenShell
|
|
requires a file path.
|
|
"""
|
|
import asyncio
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=".ifc", delete=False) as tmp:
|
|
tmp.write(content)
|
|
tmp_ifc = tmp.name
|
|
|
|
try:
|
|
# Run CPU-intensive conversion in a thread pool
|
|
loop = asyncio.get_event_loop()
|
|
await loop.run_in_executor(
|
|
None, convert_ifc_to_glb, tmp_ifc, glb_output_path
|
|
)
|
|
finally:
|
|
Path(tmp_ifc).unlink(missing_ok=True)
|
|
|