IFCLoader.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. //Docs: https://agviegas.github.io/ifcjs-docs/#/
  2. import * as WebIFC from './ifc/web-ifc-api.js';
  3. import {
  4. FileLoader,
  5. Loader,
  6. Mesh,
  7. Color,
  8. MeshBasicMaterial,
  9. MeshLambertMaterial,
  10. DoubleSide,
  11. Matrix4,
  12. BufferGeometry,
  13. BufferAttribute,
  14. } from '../../../build/three.module.js';
  15. import {BufferGeometryUtils} from "../utils/BufferGeometryUtils.js"
  16. const ifcAPI = new WebIFC.IfcAPI();
  17. class IFCLoader extends Loader {
  18. constructor( manager ) {
  19. super( manager );
  20. this.modelID = 0;
  21. this.mapFaceindexID = {};
  22. this.mapIDGeometry = {};
  23. this.selectedObjects = [];
  24. this.highlightMaterial = new MeshBasicMaterial({ color: 0xff0000, depthTest: false, side: DoubleSide });
  25. }
  26. load( url, onLoad, onProgress, onError ) {
  27. const scope = this;
  28. const loader = new FileLoader( scope.manager );
  29. loader.setPath( scope.path );
  30. loader.setResponseType( 'arraybuffer' );
  31. loader.setRequestHeader( scope.requestHeader );
  32. loader.setWithCredentials( scope.withCredentials );
  33. loader.load(
  34. url,
  35. async function ( buffer ) {
  36. try {
  37. onLoad( await scope.parse( buffer ) );
  38. } catch ( e ) {
  39. if ( onError ) {
  40. onError( e );
  41. } else {
  42. console.error( e );
  43. }
  44. scope.manager.itemError( url );
  45. }
  46. },
  47. onProgress,
  48. onError
  49. );
  50. }
  51. setWasmPath( path ) {
  52. ifcAPI.SetWasmPath( path );
  53. }
  54. getExpressId( faceIndex ) {
  55. for (let index in this.mapFaceindexID) {
  56. if (parseInt(index) >= faceIndex) return this.mapFaceindexID[index];
  57. }
  58. return -1;
  59. }
  60. highlightItems( expressIds, scene, material = this.highlightMaterial ) {
  61. this.removePreviousSelection(scene);
  62. expressIds.forEach((id) => {
  63. if (!this.mapIDGeometry[id]) return;
  64. var mesh = new Mesh(this.mapIDGeometry[id], material);
  65. mesh.renderOrder = 1;
  66. scene.add(mesh);
  67. this.selectedObjects.push(mesh);
  68. return;
  69. });
  70. }
  71. removePreviousSelection( scene ) {
  72. if (this.selectedObjects.length > 0){
  73. this.selectedObjects.forEach((object) => scene.remove(object));
  74. }
  75. }
  76. setItemsVisibility( expressIds, geometry, visible = false ) {
  77. this.setupVisibility(geometry);
  78. var previous = 0;
  79. for (var current in this.mapFaceindexID) {
  80. if (expressIds.includes(this.mapFaceindexID[current])) {
  81. for (var i = previous; i <= current; i++) this.setVertexVisibility(geometry, i, visible);
  82. }
  83. previous = current;
  84. }
  85. geometry.attributes.visibility.needsUpdate = true;
  86. }
  87. setVertexVisibility( geometry, index, visible ) {
  88. var isVisible = visible ? 0 : 1;
  89. var geoIndex = geometry.index.array;
  90. geometry.attributes.visibility.setX(geoIndex[3 * index], isVisible);
  91. geometry.attributes.visibility.setX(geoIndex[3 * index + 1], isVisible);
  92. geometry.attributes.visibility.setX(geoIndex[3 * index + 2], isVisible);
  93. }
  94. setupVisibility( geometry ) {
  95. if (!geometry.attributes.visibility) {
  96. var visible = new Float32Array(geometry.getAttribute('position').count);
  97. geometry.setAttribute('visibility', new BufferAttribute(visible, 1));
  98. }
  99. }
  100. getItemProperties( elementID, all = false ) {
  101. const properties = ifcAPI.GetLine(this.modelID, elementID);
  102. if (all) {
  103. const propSetIds = this.getAllRelatedItemsOfType(elementID, WebIFC.IFCRELDEFINESBYPROPERTIES, "RelatedObjects", "RelatingPropertyDefinition");
  104. properties.hasPropertySets = propSetIds.map((id) => ifcAPI.GetLine(this.modelID, id, true));
  105. const typeId = this.getAllRelatedItemsOfType(elementID, WebIFC.IFCRELDEFINESBYTYPE, "RelatedObjects", "RelatingType");
  106. properties.hasType = typeId.map((id) => ifcAPI.GetLine(this.modelID, id, true));
  107. }
  108. // properties.type = properties.constructor.name;
  109. return properties;
  110. }
  111. getSpatialStructure() {
  112. let lines = ifcAPI.GetLineIDsWithType(this.modelID, WebIFC.IFCPROJECT);
  113. let ifcProjectId = lines.get(0);
  114. let ifcProject = ifcAPI.GetLine(this.modelID, ifcProjectId);
  115. this.getAllSpatialChildren(ifcProject);
  116. return ifcProject;
  117. }
  118. getAllSpatialChildren( spatialElement ) {
  119. const id = spatialElement.expressID;
  120. const spatialChildrenID = this.getAllRelatedItemsOfType(id, WebIFC.IFCRELAGGREGATES, "RelatingObject", "RelatedObjects");
  121. spatialElement.hasSpatialChildren = spatialChildrenID.map((id) => ifcAPI.GetLine(this.modelID, id, false));
  122. spatialElement.hasChildren = this.getAllRelatedItemsOfType(id, WebIFC.IFCRELCONTAINEDINSPATIALSTRUCTURE, "RelatingStructure", "RelatedElements");
  123. spatialElement.hasSpatialChildren.forEach(child => this.getAllSpatialChildren(child));
  124. }
  125. getAllRelatedItemsOfType ( elementID, type, relation, relatedProperty ) {
  126. const lines = ifcAPI.GetLineIDsWithType(this.modelID, type);
  127. const IDs = [];
  128. for (let i = 0; i < lines.size(); i++) {
  129. const relID = lines.get(i);
  130. const rel = ifcAPI.GetLine(this.modelID, relID);
  131. const relatedItems = rel[relation];
  132. let foundElement = false;
  133. if (Array.isArray(relatedItems)){
  134. relatedItems.forEach((relID) => {
  135. if (relID.value === elementID) foundElement = true;
  136. });
  137. }
  138. else foundElement = (relatedItems.value === elementID);
  139. if (foundElement) {
  140. var element = rel[relatedProperty];
  141. if (!Array.isArray(element)) IDs.push(element.value);
  142. else element.forEach(ele => IDs.push(ele.value))
  143. }
  144. }
  145. return IDs;
  146. }
  147. async parse( buffer ) {
  148. const geometryByMaterials = {};
  149. const mapIDGeometry = this.mapIDGeometry;
  150. const mapFaceindexID = this.mapFaceindexID;
  151. if ( ifcAPI.wasmModule === undefined ) {
  152. await ifcAPI.Init();
  153. }
  154. const data = new Uint8Array( buffer );
  155. this.modelID = ifcAPI.OpenModel( 'example.ifc', data );
  156. return loadAllGeometry( this.modelID );
  157. function loadAllGeometry(modelID) {
  158. saveAllPlacedGeometriesByMaterial(modelID);
  159. return generateAllGeometriesByMaterial();
  160. }
  161. function generateAllGeometriesByMaterial() {
  162. const { materials, geometries } = getMaterialsAndGeometries();
  163. const allGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries, true);
  164. return new Mesh(allGeometry, materials);
  165. }
  166. function getMaterialsAndGeometries() {
  167. const materials = [];
  168. const geometries = [];
  169. let totalFaceCount = 0;
  170. for (let i in geometryByMaterials) {
  171. materials.push(geometryByMaterials[i].material);
  172. const currentGeometries = geometryByMaterials[i].geometry;
  173. geometries.push(BufferGeometryUtils.mergeBufferGeometries(currentGeometries));
  174. for (let j in geometryByMaterials[i].indices) {
  175. const globalIndex = parseInt(j, 10) + parseInt(totalFaceCount, 10);
  176. mapFaceindexID[globalIndex] = geometryByMaterials[i].indices[j];
  177. }
  178. totalFaceCount += geometryByMaterials[i].lastIndex;
  179. }
  180. return { materials, geometries };
  181. }
  182. function saveAllPlacedGeometriesByMaterial(modelID) {
  183. const flatMeshes = ifcAPI.LoadAllGeometry(modelID);
  184. for (let i = 0; i < flatMeshes.size(); i++) {
  185. const flatMesh = flatMeshes.get(i);
  186. const productId = flatMesh.expressID;
  187. const placedGeometries = flatMesh.geometries;
  188. for (let j = 0; j < placedGeometries.size(); j++) {
  189. savePlacedGeometryByMaterial(modelID, placedGeometries.get(j), productId);
  190. }
  191. }
  192. }
  193. function savePlacedGeometryByMaterial(modelID, placedGeometry, productId) {
  194. const geometry = getBufferGeometry(modelID, placedGeometry);
  195. geometry.computeVertexNormals();
  196. const matrix = getMeshMatrix(placedGeometry.flatTransformation);
  197. geometry.applyMatrix4(matrix);
  198. storeGeometryForHighlight(productId, geometry);
  199. saveGeometryByMaterial(geometry, placedGeometry, productId);
  200. }
  201. function getBufferGeometry(modelID, placedGeometry) {
  202. const geometry = ifcAPI.GetGeometry(modelID, placedGeometry.geometryExpressID);
  203. const verts = ifcAPI.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
  204. const indices = ifcAPI.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
  205. return ifcGeometryToBuffer(verts, indices);
  206. }
  207. function getMeshMatrix(matrix) {
  208. const mat = new Matrix4();
  209. mat.fromArray(matrix);
  210. return mat;
  211. }
  212. function storeGeometryForHighlight(productId, geometry) {
  213. if (!mapIDGeometry[productId]) {
  214. mapIDGeometry[productId] = geometry;
  215. return;
  216. }
  217. const geometries = [mapIDGeometry[productId], geometry];
  218. mapIDGeometry[productId] = BufferGeometryUtils.mergeBufferGeometries(geometries, true);
  219. }
  220. function ifcGeometryToBuffer(vertexData, indexData) {
  221. const geometry = new BufferGeometry();
  222. const { vertices, normals } = extractVertexData(vertexData);
  223. geometry.setAttribute('position', new BufferAttribute(new Float32Array(vertices), 3));
  224. geometry.setAttribute('normal', new BufferAttribute(new Float32Array(normals), 3));
  225. geometry.setIndex(new BufferAttribute(indexData, 1));
  226. return geometry;
  227. }
  228. function extractVertexData(vertexData) {
  229. const vertices = [];
  230. const normals = [];
  231. let isNormalData = false;
  232. for (let i = 0; i < vertexData.length; i++) {
  233. isNormalData ? normals.push(vertexData[i]) : vertices.push(vertexData[i]);
  234. if ((i + 1) % 3 == 0) isNormalData = !isNormalData;
  235. }
  236. return { vertices, normals };
  237. }
  238. function saveGeometryByMaterial(geometry, placedGeometry, productId) {
  239. const color = placedGeometry.color;
  240. const id = `${color.x}${color.y}${color.z}${color.w}`;
  241. createMaterial(id, color);
  242. const currentGeometry = geometryByMaterials[id];
  243. currentGeometry.geometry.push(geometry);
  244. currentGeometry.lastIndex += geometry.index.count / 3;
  245. currentGeometry.indices[currentGeometry.lastIndex] = productId;
  246. }
  247. function createMaterial(id, color) {
  248. if (!geometryByMaterials[id]){
  249. const col = new Color(color.x, color.y, color.z);
  250. const newMaterial = new MeshLambertMaterial({ color: col, side: DoubleSide });
  251. newMaterial.onBeforeCompile = materialHider;
  252. newMaterial.transparent = color.w !== 1;
  253. if (newMaterial.transparent) newMaterial.opacity = color.w;
  254. geometryByMaterials[id] = initializeGeometryByMaterial(newMaterial);
  255. }
  256. }
  257. function initializeGeometryByMaterial(newMaterial) {
  258. return {
  259. material: newMaterial,
  260. geometry: [],
  261. indices: {},
  262. lastIndex: 0
  263. };
  264. }
  265. function materialHider(shader) {
  266. shader.vertexShader = `
  267. attribute float sizes;
  268. attribute float visibility;
  269. varying float vVisible;
  270. ${shader.vertexShader}`.replace(
  271. `#include <fog_vertex>`,
  272. `#include <fog_vertex>
  273. vVisible = visibility;
  274. `
  275. );
  276. shader.fragmentShader = `
  277. varying float vVisible;
  278. ${shader.fragmentShader}`.replace(
  279. `#include <clipping_planes_fragment>`,
  280. `
  281. if (vVisible > 0.5) discard;
  282. #include <clipping_planes_fragment>`
  283. );
  284. }
  285. }
  286. }
  287. export { IFCLoader };