Skip to content

Commit

Permalink
- refactor sop/FileIFC
Browse files Browse the repository at this point in the history
- add sop/InstancedMeshToMesh
  • Loading branch information
frading committed Nov 9, 2023
1 parent 2dd21fd commit 7281f20
Show file tree
Hide file tree
Showing 20 changed files with 819 additions and 426 deletions.
2 changes: 2 additions & 0 deletions examples/presets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
fileDRCSopPresetRegister,
fileFBXSopPresetRegister,
fileGLTFSopPresetRegister,
fileIFCSopPresetRegister,
fileJSONSopPresetRegister,
fileMPDSopPresetRegister,
fileOBJSopPresetRegister,
Expand Down Expand Up @@ -140,6 +141,7 @@ class PresetLibraryClass {
this._registerPreset(fileFBXSopPresetRegister);
this._registerPreset(fileGEOJSONSopPresetRegister);
this._registerPreset(fileGLTFSopPresetRegister);
this._registerPreset(fileIFCSopPresetRegister);
this._registerPreset(fileJSONSopPresetRegister);
this._registerPreset(fileMPDSopPresetRegister);
this._registerPreset(fileOBJSopPresetRegister);
Expand Down
17 changes: 17 additions & 0 deletions examples/presets/sop/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {DEMO_ASSETS_ROOT_URL} from '../../../src/core/Assets';
import {FileDRCSopNode} from '../../../src/engine/nodes/sop/FileDRC';
import {FileFBXSopNode} from '../../../src/engine/nodes/sop/FileFBX';
import {FileGLTFSopNode} from '../../../src/engine/nodes/sop/FileGLTF';
import {FileIFCSopNode} from '../../../src/engine/nodes/sop/FileIFC';
import {FileJSONSopNode} from '../../../src/engine/nodes/sop/FileJSON';
import {FileMPDSopNode} from '../../../src/engine/nodes/sop/FileMPD';
import {FileOBJSopNode} from '../../../src/engine/nodes/sop/FileOBJ';
Expand Down Expand Up @@ -267,6 +268,18 @@ const fileGLTFSopNodePresetsCollectionFactory: PresetsCollectionFactory<FileGLTF

return collection;
};
const fileIFCSopNodePresetsCollectionFactory: PresetsCollectionFactory<FileIFCSopNode> = (node: FileIFCSopNode) => {
function ifcPreset(fileName: string) {
return new BasePreset().addEntry(node.p.url, `${DEMO_ASSETS_ROOT_URL}/models/ifc/${fileName}.ifc`);
}
const collection = new NodePresetsCollection();
collection.setPresets({
small: ifcPreset('small'),
rac_advanced_sample_project: ifcPreset('rac_advanced_sample_project'),
});

return collection;
};

const fileJSONSopNodePresetsCollectionFactory: PresetsCollectionFactory<FileJSONSopNode> = (node: FileJSONSopNode) => {
const collection = new NodePresetsCollection();
Expand Down Expand Up @@ -328,6 +341,10 @@ export const fileGLTFSopPresetRegister: PresetRegister<typeof FileGLTFSopNode, F
nodeClass: FileGLTFSopNode,
setupFunc: fileGLTFSopNodePresetsCollectionFactory,
};
export const fileIFCSopPresetRegister: PresetRegister<typeof FileIFCSopNode, FileIFCSopNode> = {
nodeClass: FileIFCSopNode,
setupFunc: fileIFCSopNodePresetsCollectionFactory,
};
export const fileJSONSopPresetRegister: PresetRegister<typeof FileJSONSopNode, FileJSONSopNode> = {
nodeClass: FileJSONSopNode,
setupFunc: fileJSONSopNodePresetsCollectionFactory,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"manifold-3d": "2.1.0",
"mapbox-gl": "2.13.0",
"n8ao": "1.6.8",
"openbim-components": "1.1.6",
"opencascade.js": "2.0.0-beta.94e2944",
"opentype.js": "1.3.4",
"postprocessing": "6.33.2",
Expand All @@ -83,7 +84,6 @@
"three-mesh-bvh": "0.6.8",
"three-subdivide": "1.1.5",
"tone": "14.7.77",
"web-ifc-three": "0.0.125",
"xatlas-three": "0.2.0",
"xatlas-web": "0.1.0",
"yuka": "^0.7.8"
Expand Down
28 changes: 28 additions & 0 deletions src/core/geometry/ifc/IFCCommon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
import * as WEBIFC from 'web-ifc';
import {FragmentsGroup} from 'bim-fragment';
import {
IfcPropertiesManager,
FragmentIfcLoader,
IfcPropertiesFinder,
IfcCategories,
Components,
SimpleScene,
PostproductionRenderer,
SimpleCamera,
SimpleRaycaster,
} from 'openbim-components';

export {
IfcPropertiesManager,
FragmentIfcLoader,
IfcPropertiesFinder,
IfcCategories,
Components,
WEBIFC,
SimpleScene,
PostproductionRenderer,
SimpleCamera,
SimpleRaycaster,
FragmentsGroup,
};
182 changes: 121 additions & 61 deletions src/core/geometry/ifc/IFCUtils.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,139 @@
import * as WEB_IFC from 'web-ifc';
import type {IFCModel} from 'web-ifc-three/IFC/components/IFCModel';
import type {IFCManager} from 'web-ifc-three/IFC/components/IFCManager';
import {Object3D, InstancedMesh, Group} from 'three';
import {setToArray} from '../../SetUtils';
import {IFCLoaderHandler} from '../../loader/geometry/IFC';
import {Object3D} from 'three';
import {isArray} from '../../Type';
import * as WEB_IFC from 'web-ifc';
import {
Components,
SimpleScene,
PostproductionRenderer,
SimpleCamera,
SimpleRaycaster,
FragmentIfcLoader,
IfcCategories,
FragmentsGroup,
} from './IFCCommon';
import {isNumber} from '../../Type';
import {ThreejsCoreObject} from '../modules/three/ThreejsCoreObject';
import {copyObjectAllProperties} from '../modules/three/ThreejsObjectUtils';

export enum IFCAttribute {
MODEL_ID = 'modelId',
function _buildCategoryNameById() {
const dict: Record<number, string> = {};
const keys = Object.keys(WEB_IFC);
for (const key of keys) {
const value = (WEB_IFC as any)[key];
if (isNumber(value)) {
dict[value] = key;
}
}
return dict;
}
export const IFC_CATEGORY_NAME_BY_ID = _buildCategoryNameById();

interface StructureItem {
expressID: number;
type: string;
children?: StructureItem[];
export enum IFCAttribute {
ALL_CATEGORY_NAMES = 'IFCAllCategoryNames',
}
type StructureCallback = (structure: StructureItem) => void;
const _numberSet: Set<number> = new Set();
const _stringSet: Set<string> = new Set();

export async function getIFCModelCategories(object: Object3D) {
const modelId = (object as IFCModel).modelID;
if (modelId == null) {
return;
}
const ifcManager = IFCLoaderHandler.ifcManager();
return await ifcCategoriesNames(ifcManager, modelId);
const CATEGORY_SEPARATOR = ' ';
export function setObjectAttributeAllCategoryNames(object: Object3D, categoryNames: string[]) {
ThreejsCoreObject.addAttribute(object, IFCAttribute.ALL_CATEGORY_NAMES, categoryNames.join(CATEGORY_SEPARATOR));
}
export function getObjectAttributeAllCategoryNames(object: Object3D): string | undefined {
const categoriesJoined = ThreejsCoreObject.attribValue(object, IFCAttribute.ALL_CATEGORY_NAMES) as
| string
| undefined;
return categoriesJoined;
}
// interface CagegoryResult {
// categoryNames: string[];
// cagegoryIds: number[];
// }
export function ifcCategoryIds(categoryNames: string[]): number[] {
const cagegoryIds: number[] = [];
for (const categoryName of categoryNames) {
const id = (WEB_IFC as any as Record<string, number>)[categoryName];
if (id != null) {
cagegoryIds.push(id);
export function categoryNamesFromString(categoriesJoined: string): string[] {
return categoriesJoined.split(CATEGORY_SEPARATOR);
}

export function ifcFragmentsGroupToGroup(fragmentsGroup: FragmentsGroup): Object3D {
function buildThreeObject(ifcObject: Object3D): Object3D | Object3D[] | undefined {
if ((ifcObject as InstancedMesh).isInstancedMesh) {
const count = (ifcObject as InstancedMesh).count;
const geometry = (ifcObject as InstancedMesh).geometry;
const material = (ifcObject as InstancedMesh).material;
return copyObjectAllProperties(ifcObject, new InstancedMesh(geometry, material, count));
}
if ((ifcObject as Group).isGroup) {
return copyObjectAllProperties(ifcObject, new Group());
}
console.warn('no conversion found for IFC object', ifcObject);
}
return cagegoryIds;
}

export async function ifcElementIds(ifcManager: IFCManager, modelId: number, categoryIds: number[]): Promise<number[]> {
const promises = categoryIds.map(
(categoryId) => ifcManager.getAllItemsOfType(modelId, categoryId, false) as Promise<number[]>
);
const results = await Promise.all(promises);
const group = new Group();
const threeObjectByIFCObject: Map<Object3D, Object3D> = new Map();
threeObjectByIFCObject.set(fragmentsGroup, group);

_numberSet.clear();
for (const result of results) {
for (const id of result) {
_numberSet.add(id);
function _addObject(ifcObject: Object3D, threeObject: Object3D) {
threeObjectByIFCObject.set(ifcObject, threeObject);
const ifcParent = ifcObject.parent;
if (ifcParent) {
const threeParent = threeObjectByIFCObject.get(ifcParent);
if (threeParent) {
threeParent.add(threeObject);
}
}
}
return setToArray(_numberSet, []);
}

export async function ifcCategoriesNames(ifcManager: IFCManager, modelID: number): Promise<string[]> {
const root: StructureItem = await ifcManager.getSpatialStructure(modelID, false);
_stringSet.clear();
traverseStructure(root, (item) => {
_stringSet.add(item.type);
fragmentsGroup.traverse((ifcObject) => {
if (ifcObject == fragmentsGroup) {
return;
}

const threeObject = buildThreeObject(ifcObject);
if (!threeObject) {
return;
}
if (isArray(threeObject)) {
for (const obj of threeObject) {
_addObject(ifcObject, obj);
}
} else {
_addObject(ifcObject, threeObject);
}
});
const categoryNames = setToArray(_stringSet, []);

return categoryNames;
return group;
}
function traverseStructure(item: StructureItem, callback: StructureCallback, level = 0) {
// console.log(level, item);
callback(item);
if (!item.children) {
return;
}
const children = item.children;
for (const child of children) {
traverseStructure(child, callback, level + 1);

const _stringSet: Set<string> = new Set();

export function createFragmentIfcLoader() {
const components = new Components();
components.scene = new SimpleScene(components);
const container = document.createElement('div');
components.renderer = new PostproductionRenderer(components, container);
components.camera = new SimpleCamera(components);
components.raycaster = new SimpleRaycaster(components);
const fragmentIfcLoader = new FragmentIfcLoader(components);

fragmentIfcLoader.settings.wasm = {
path: 'https://unpkg.com/web-ifc@0.0.44/',
absolute: true,
};

return fragmentIfcLoader;
}

async function _getCategoryIds(fragmentIfcLoader: FragmentIfcLoader, byteArray: Uint8Array) {
const api = fragmentIfcLoader.get();
const {path, absolute} = fragmentIfcLoader.settings.wasm;
api.SetWasmPath(path, absolute);
await api.Init();
api.OpenModel(byteArray, fragmentIfcLoader.settings.webIfc);
const ifcCategories = new IfcCategories();
const categories = ifcCategories.getAll(api, 0);
return categories;
}
export async function getCategoryNames(fragmentIfcLoader: FragmentIfcLoader, byteArray: Uint8Array): Promise<string[]> {
const categoryIdByObjectId = await _getCategoryIds(fragmentIfcLoader, byteArray);
const categoryIds = Object.values(categoryIdByObjectId);
_stringSet.clear();
for (const categoryId of categoryIds) {
const categoryName = IFC_CATEGORY_NAME_BY_ID[categoryId];
_stringSet.add(categoryName);
}
const categoryNames: string[] = [];
setToArray(_stringSet, categoryNames);
return categoryNames;
}
24 changes: 23 additions & 1 deletion src/core/geometry/modules/three/ThreejsObjectUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Object3D} from 'three';
import {Object3D, Vector3, Euler, Quaternion, Matrix4} from 'three';
import {objectContentCopyProperties} from '../../ObjectContent';
export function copyObject3DProperties(srcObject: Object3D, destObject: Object3D) {
objectContentCopyProperties(srcObject, destObject);
Expand All @@ -7,3 +7,25 @@ export function copyObject3DProperties(srcObject: Object3D, destObject: Object3D
destObject.scale.copy(srcObject.scale);
destObject.matrix.copy(srcObject.matrix);
}

const UNCOPYABLE_PROPERTIES = new Set(['animations', 'children', 'layers', 'parent', 'userData']);
export function copyObjectAllProperties(srcObject: Object3D, destObject: Object3D) {
const keys = Object.keys(destObject);
for (const key of keys) {
if (UNCOPYABLE_PROPERTIES.has(key)) {
continue;
}
const destProperty = (destObject as any)[key];
if (
destProperty instanceof Vector3 ||
destProperty instanceof Euler ||
destProperty instanceof Quaternion ||
destProperty instanceof Matrix4
) {
destProperty.copy((srcObject as any)[key] as any);
} else {
(destObject as any)[key] = (srcObject as any)[key];
}
}
return destObject;
}

0 comments on commit 7281f20

Please sign in to comment.