Skip to content

Commit

Permalink
IfcMapping ideas finalized, IfcJsonSave convenience added
Browse files Browse the repository at this point in the history
  • Loading branch information
michaliskambi committed Nov 14, 2024
1 parent e922aa7 commit 0d75bf6
Showing 1 changed file with 40 additions and 68 deletions.
108 changes: 40 additions & 68 deletions htdocs/doc/ifc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down

0 comments on commit 0d75bf6

Please sign in to comment.