diff --git a/htdocs/doc/ifc.adoc b/htdocs/doc/ifc.adoc index f41481948..569eb3c18 100644 --- a/htdocs/doc/ifc.adoc +++ b/htdocs/doc/ifc.adoc @@ -38,6 +38,22 @@ Specifications: As we like open 3D standards in _Castle Game Engine_ (_Michalis_ is preaching about his love for link:gltf[glTF] and link:x3d[X3D] at various link:conferences[]) so IFC naturally joins our capabilities. +## Testing + +1. First of all, you can just open any supported IFC file with any of our tools. Like this: ++ +Get or make a sample IFC file: ++ +-- +- For example take one of the testcases from https://github.com/buildingsmart-community/Community-Sample-Test-Files/tree/main/IFC%204.0.2.1%20(IFC%204)/ISO%20Spec%20archive[Community-Sample-Test-Files for IFC 4.x]. Rename the file from `.json` -> `.ifcjson`. + +- Or create IFC file using link:blender[Blender] and https://bonsaibim.org/[BonsaiBIM] add-on, a great add-on that turns Blender into a full-featured IFC editor. Save the IFC to `.ifcjson` format. +-- ++ +Then open it using link:castle-model-viewer[Castle Model Viewer]. + +2. Check out the dedicated example using IFC classes to add and modify walls, that can load, save and display IFC models. It's in the https://github.com/castle-engine/castle-engine/tree/master/examples/ifc[examples/ifc/] directory of the engine sources. + ## Supported IFC features We can load and save the IFC entities listed in API docs of cgeref:CastleIfc[] unit. This includes over 100 classes reflecting the IFC specification. @@ -65,19 +81,15 @@ We convert a lot of "presentational" IFC entities to X3D nodes and display them. Our conversion from IFC to X3D allows to display IFC models using _Castle Game Engine_ (using cgeref:TCastleScene[]) and allows to literally convert between model formats, from IFC to any format we can save (X3D, STL, and soon glTF). +X3D nodes can be mapped back to IFC classes, which is useful e.g. for picking 3D objects using mouse. _Castle Game Engine_ has APIs to determine what X3D node has been picked, and then you can map it back to an IFC _"product"_ (like a wall, a window, a door) using cgeref:TCastleIfcMapping.NodeToProduct[]. + NOTE: Let us know if you need more features related to IFC. Bug reports and PRs are welcome, as well as link:donate[donations toward the IFC support, we're happy to provide commercial support for companies too]. We're committed to IFC support. link:conferences[Michalis talks at conferences how he loves open 3D standards] --- well, IFC is one of them! ## Loading and saving IFC in TCastleScene Basically, you don't need to learn anything new to use IFC with _Castle Game Engine_. -Just get a sample IFC file, - -- for example one of the testcases from https://github.com/buildingsmart-community/Community-Sample-Test-Files/tree/main/IFC%204.0.2.1%20(IFC%204)/ISO%20Spec%20archive[Community-Sample-Test-Files for IFC 4.x], - -- or export IFC from link:blender[Blender] using the https://bonsaibim.org/[BonsaiBIM] add-on (great add-on that turns Blender into a full-featured IFC editor). - -Then load it: +Load a sample IFC file: - using the cgeref:TCastleSceneCore.Load[] method, - or by setting cgeref:TCastleSceneCore.Url[] property to the IFC file. @@ -126,28 +138,13 @@ and then operate on the loaded `IfcFile`, exploring the properties of cgeref:TIf Inspect and modify the properties of IFC classes as you wish. At one point, you can: -1. Save to result back to file using cgeref:IfcJsonSave[]. It creates a JSON object, this is how you can save it to a file: +1. Save to result back to file using cgeref:IfcJsonSave[]. Like this: + ```delphi -var - JsonObj: TJsonObject; - JsonString: String; - Stream: TStream; -begin - Stream := UrlSaveStream('output.ifcjson'); - try - JsonObj := IfcJsonSave(IfcFile); - try - JsonString := JsonObj.FormatJSON; - WriteStr(Stream, JsonString); - finally FreeAndNil(JsonObj) end; - finally FreeAndNil(Stream) end; -end; +IfcJsonSave(IfcFile, 'output.ifcjson'); ``` -+ -WARNING: TODO: Add `IfcJsonSave(TIfcFile, String)` utility to make above example a 1-liner. -2. Convert the IFC to a set of X3D nodes (to display them using cgeref:TCastleScene[]) using cgeref:IfcToX3D[], like this: +2. Convert the IFC to a set of X3D nodes (to display them using cgeref:TCastleScene[]) using cgeref:IfcToX3D[]. If you only want to do it once, do it like this (but if you want to do it multiple times, read next point): + ```delphi var @@ -158,64 +155,39 @@ begin end; ``` -3. And, what is possibly the most important, _update_ the displayed scene when you change the IFC classes. +3. You can also _load and then efficiently update multiple times_ the displayed scene (update display each time when you change the IFC classes). + -WARNING: TODO: Not yet implemented. For now, just repeatedly call `IfcToX3D` and `MyScene.Load` to update the displayed scene. -+ -This is done by using cgeref:TCastleIfcMapping[] class instead of cgeref:IfcToX3D[] and cgeref:X3DToIfc[]. + +This is done by using cgeref:TCastleIfcMapping[] class. Like this: + ```delphi var - MyRoot: TX3DRootNode; IfcFile: TIfcFile; IfcMapping: TCastleIfcMapping; begin - IfcMapping := TCastleIfcMapping.Create; + IfcFile := IfcFromJson('castle-data:/my_file.ifcjson'); try - (* - IfcFile := IfcFromJson('castle-data:/my_file.ifcjson'); + IfcMapping := TCastleIfcMapping.Create; try - IfcMapping.Load(IfcFile); - finally FreeAndNil(IfcFile) end; - *) - - // shorter version: - IfcMapping.Load('castle-data:/my_file.ifcjson'); + // convert IfcFile to X3D, in a way that allows to efficiently update it later + IfcMapping.Load(IfcFile, 'castle-data:/'); - // this does something similar to IfcToX3D, but also updates internal structures to be able to repeat it later - MyScene.Load(IfcMapping.RootNode); + // display the result using MyScene:TCastleScene; it will own the IfcMapping.RootNode + MyScene.Load(IfcMapping.RootNode, true); - // modify IfcFile, e.g. add a new wall - IfcFile.Walls.Add(TIfcWall.Create); // TODO make this example correct, try to keep it as simple + // modify IfcFile, e.g. add a new wall + // TODO make this example correct, but still try to keep it as simple as possible + IfcFile.Walls.Add(TIfcWall.Create); - // this will modify the contents of RootNode; - // the MyScene displaying it will automatically reflect the changes - IfcMapping.UpdateRootNode; // update the displayed scene - end; + // modify the contents of RootNode, to reflect above new wall. + // the MyScene displaying it will automatically reflect the changes + IfcMapping.Update(IfcFile); + finally FreeAndNil(IfcMapping) end; + finally FreeAndNil(IfcFile) end; ``` -+ -TODO: To be decided, maybe instead implement x3d-native diff, then the above example would change to -+ -```delphi -var - MyRoot: TX3DRootNode; - IfcFile: TIfcFile; -begin - IfcFile := IfcFromJson('castle-data:/my_file.ifcjson'); - MyRoot := IfcToX3D(IfcFile); - MyScene.Load(MyRoot, true); - - // modify IfcFile, e.g. add a new wall - IfcFile.Walls.Add(TIfcWall.Create); // TODO make this example correct, try to keep it as simple - - // this will modify the contents of RootNode; - // the MyScene displaying it will automatically reflect the changes. - // Note that UpdateNode owns the given 1st argument, it will free the given nodes (immediately, or by making them new children of MyScene.RootNode) - UpdateNode(IfcToX3D(IfcFile), MyScene.RootNode); -end; -``` +Consult the https://github.com/castle-engine/castle-engine/tree/master/examples/ifc[examples/ifc/] example application (in the engine sources) for a full code following this approach. ## Credits