Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ the `IOViewer` package). The `IOViewer.Cli` tool only gains inputs.
- The HTML viewer shows `SceneElement.SourceId`, the element's id in its source format (such as a Revit UniqueId or an
IFC GlobalId), as its own row in the properties panel ("Source ID" / "ID источника"), and the model tree search finds
it. It is written to the page's element list only when set.
- The `ioviewer` tool reads IFC (`.ifc`, `.ifczip`) through `IfcIO.Scene` 0.11.0 and Revit (`.rvt`) through
`RvtIO.Scene` 0.4.0. The model key is the file name; the title is the IFC project name, or the Revit file name. What
the engine could not convert (such as an element without geometry) is printed to standard error as one warning or
error per kind with its count. The `IOViewer` library still references no engine; only the `IOViewer.Cli` tool
package ships IfcIO, RvtIO and their dependencies (see `THIRD-PARTY-NOTICES.md`).

### Changed
- `JsonViewerExporter` writes viewer JSON format 2: `{ "Kind": "ioviewer", "Format": 2, "Scene", "SectionPlanes",
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ The workflow builds and tests the solution, publishes the NuGet packages through
packages, symbols, source and checksums to a GitHub release, marked as a prerelease when the version has a hyphen
(`v0.3.0-preview.1`). The release notes are the section of `CHANGELOG.md` for the version, so add that section before
the tag; the workflow stops before publishing when it is missing.
Before the tag, run the Revit test with a real Revit 2022 file, which CI does not have: set `IOVIEWER_RVT_FILE` to its path and run `dotnet test IOViewer.slnx -c Release --filter Cli_ConvertRvtToHtml`.
The NuGet policy must name this repository, `release.yml`, the configured NuGet profile and the `IOViewer*` package pattern.

## Licensing contributions
Expand Down
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<ItemGroup>
<!-- Patch = additive, minor = breaking while IOScene is 0.x; the -0 keeps out previews of the next breaking line. -->
<PackageVersion Include="IOScene" Version="[0.1.0-preview.2, 0.2.0-0)" />
<PackageVersion Include="IfcIO.Scene" Version="0.11.0" />
<PackageVersion Include="RvtIO.Scene" Version="0.4.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.11.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.1" />
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![Platform](https://img.shields.io/badge/.NET-8.0%20%7C%2010.0-512BD4?style=flat-square&logo=dotnet)](https://dotnet.microsoft.com/)

IOViewer is a fast, 100% managed .NET library and command line tool that generates standalone interactive 3D WebGL / HTML BIM & CAD model viewers from code, glTF/GLB, Wavefront OBJ, or JSON. Geometry read by sibling libraries ([RvtIO](https://github.com/IOExtend/RvtIO), [IfcIO](https://github.com/IOExtend/IfcIO), and [NwcIO](https://github.com/IOExtend/NwcIO)) can be passed in by building a `SceneDocument` in code. The scene model comes from the [IOScene](https://github.com/IOExtend/IOScene) package; IOViewer has no direct dependency on the format libraries.
IOViewer is a fast, 100% managed .NET library and command line tool that generates standalone interactive 3D WebGL / HTML BIM & CAD model viewers from code, glTF/GLB, Wavefront OBJ, or JSON. Geometry read by sibling libraries ([RvtIO](https://github.com/IOExtend/RvtIO), [IfcIO](https://github.com/IOExtend/IfcIO), and [NwcIO](https://github.com/IOExtend/NwcIO)) can be passed in by building a `SceneDocument` in code, and the `ioviewer` command line tool reads IFC and Revit files directly through IfcIO and RvtIO. The scene model comes from the [IOScene](https://github.com/IOExtend/IOScene) package; the IOViewer library has no dependency on the format libraries.

The generated HTML is a single file with no external dependencies that works offline: it opens from disk (`file://`) in a current browser with WebGL 2 and makes no network requests. The viewer is built on [three.js](https://threejs.org/), which is embedded in the file (see [THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md)).

Expand Down Expand Up @@ -80,6 +80,10 @@ ioviewer model.glb -o viewer.html
# Convert Wavefront OBJ model to interactive HTML viewer
ioviewer model.obj -o viewer.html

# Convert an IFC model (.ifc or .ifczip, read with IfcIO) or a Revit model (.rvt, read with RvtIO)
ioviewer model.ifc
ioviewer model.rvt -o viewer.html

# Convert and immediately preview in default web browser via built-in HTTP server
ioviewer model.glb --serve
```
Expand All @@ -91,7 +95,8 @@ Export structured JSON snapshots:
ioviewer model.glb -o model.json
```

Diagnostics and progress go to standard error. Run `ioviewer --help` for the full list of options.
Diagnostics and progress go to standard error, including what IfcIO or RvtIO could not convert (one warning or error
per kind, with its count). Run `ioviewer --help` for the full list of options.

## Library

Expand Down
44 changes: 44 additions & 0 deletions THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,47 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```

## IOViewer.Cli tool package

The `IOViewer.Cli` tool package reads IFC and Revit files through the engine adapters, so besides `IOViewer` and
`IOScene` (MIT, copyright (c) 2026 mahach666) it ships the assemblies listed below. The `IOViewer` library package
references none of them.

| Component | Use | License |
|---|---|---|
| [IfcIO](https://github.com/IOExtend/IfcIO) 0.11.0 and IfcIO.Scene 0.11.0 | read IFC files into a scene | MIT, copyright (c) 2026 IfcIO contributors |
| [RvtIO](https://github.com/IOExtend/RvtIO) 0.4.0 and RvtIO.Scene 0.4.0 | read Revit files into a scene | MIT, copyright (c) 2026 mahach666 |
| .NET runtime libraries (DeflateManaged sources, compiled into RvtIO) | managed inflate implementation adapted for tolerant decoding | MIT, copyright (c) .NET Foundation and Contributors |
| [OpenMcdf](https://github.com/openmcdf/openmcdf) 3.4.0, used by RvtIO | reads the compound file container of Revit files | MPL-2.0, copyright (c) 2010-2026 Federico Blaseotto, Jeremy Powell |
| [SharpZipLib](https://github.com/icsharpcode/SharpZipLib) 1.4.2, used by RvtIO | stream decompression | MIT, copyright (c) 2000-2022 SharpZipLib Contributors |

OpenMcdf is distributed unmodified under the Mozilla Public License 2.0 (<https://mozilla.org/MPL/2.0/>); its source
code is available at <https://github.com/openmcdf/openmcdf>.

The MIT-licensed components above are distributed under the following terms, each with the copyright line given in the
table:

```text
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

buildingSMART and IFC are registered trademarks or standards of buildingSMART International Ltd.; IfcIO is an
independent implementation of the IFC schemas. Autodesk and Revit are trademarks of Autodesk, Inc.; RvtIO is not
affiliated with, endorsed by or sponsored by Autodesk.
7 changes: 6 additions & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ dotnet add package IOViewer

## Command line usage

Convert any glTF 2.0 Binary (`.glb`) or Wavefront OBJ (`.obj`) into a self-contained 3D HTML viewer:
Convert any glTF 2.0 Binary (`.glb`), Wavefront OBJ (`.obj`), IFC (`.ifc`, `.ifczip`, read with IfcIO) or Revit
(`.rvt`, read with RvtIO) model into a self-contained 3D HTML viewer:

```sh
# Convert 3D model into an interactive standalone HTML viewer
ioviewer model.glb -o viewer.html

# IFC and Revit models; what the engine could not convert is printed as warnings or errors on standard error
ioviewer model.ifc
ioviewer model.rvt

# Launch local preview server and open in default browser
ioviewer model.glb --serve
```
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The generated HTML is a single file with no external dependencies that works off

- [Getting started](getting-started.md): install the package or CLI tool and generate your first 3D viewer.
- [Public API](public-api.md): supported surface and key entry points of the library.
- [Third-party notices](https://github.com/IOExtend/IOViewer/blob/main/THIRD-PARTY-NOTICES.md): the three.js license.
- [Third-party notices](https://github.com/IOExtend/IOViewer/blob/main/THIRD-PARTY-NOTICES.md): the three.js license and the components the command line tool ships.
- [Source on GitHub](https://github.com/IOExtend/IOViewer) and [DeepWiki](https://deepwiki.com/IOExtend/IOViewer) for an automatically generated overview of the repository.

IOViewer is not affiliated with, endorsed by or sponsored by Autodesk. Autodesk, Revit, Navisworks, ViewCube and TimeLiner are trademarks of Autodesk, Inc.
4 changes: 4 additions & 0 deletions src/IOViewer.Cli/IOViewer.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
<!-- An advisory against a dependency of an engine must not stop the release of the viewer. -->
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsNotAsErrors>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\IOViewer\IOViewer.csproj" />
<PackageReference Include="IfcIO.Scene" />
<PackageReference Include="RvtIO.Scene" />
</ItemGroup>

<ItemGroup>
Expand Down
45 changes: 39 additions & 6 deletions src/IOViewer.Cli/Program.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
using System.Diagnostics;
using System.Net;
using System.Reflection;
using IfcIO.Scene;
using IOScene;
using IOViewer.Exporters;
using IOViewer.Importers;
using IOViewer.Model;
using RvtIO.Scene;

namespace IOViewer.Cli;

internal static class Program
{
private const string UsageText = """
Usage: ioviewer <input.glb|input.obj|input.json> [output.html] [options]
Usage: ioviewer <input.glb|input.obj|input.json|input.ifc|input.rvt> [output.html] [options]
ioviewer --help | --version

Generates standalone interactive 3D WebGL / HTML BIM & CAD model viewers
from 3D models (glTF/GLB, Wavefront OBJ, JSON). The HTML file has no
external dependencies and works offline. JSON input is viewer JSON, an
IOScene scene file or an IOViewer 0.1/0.2 snapshot.
from 3D models (glTF/GLB, Wavefront OBJ, JSON, IFC, Revit). The HTML file
has no external dependencies and works offline. JSON input is viewer JSON,
an IOScene scene file or an IOViewer 0.1/0.2 snapshot. IFC (.ifc, .ifczip)
is read with IfcIO and Revit (.rvt) with RvtIO; what they could not convert
is reported as warnings or errors.

Arguments:
<input> Path to input 3D model (.glb, .obj, or .json).
<input> Path to input 3D model (.glb, .obj, .json, .ifc, .ifczip or .rvt).
[output] Destination path for exported file (.html or .json).
Default: <input-without-ext>.html.

Expand Down Expand Up @@ -167,7 +172,9 @@ public static int Main(string[] args)
".glb" => GlbViewerImporter.Load(inputPath),
".obj" => ObjViewerImporter.Load(inputPath),
".json" => JsonViewerImporter.Load(inputPath),
_ => throw new NotSupportedException($"Unsupported input file extension: '{ext}'. Supported: .glb, .obj, .json.")
".ifc" or ".ifczip" => FromIfc(inputPath),
".rvt" => FromRvt(inputPath),
_ => throw new NotSupportedException($"Unsupported input file extension: '{ext}'. Supported: .glb, .obj, .json, .ifc, .ifczip, .rvt.")
};
}
catch (Exception ex)
Expand Down Expand Up @@ -232,6 +239,32 @@ public static int Main(string[] args)
return 0;
}

private static ViewerDocument FromIfc(string path)
{
var result = IfcSceneReader.Read(path);
return FromScene(path, result.Document, result.Diagnostics);
}

private static ViewerDocument FromRvt(string path)
{
var result = RvtSceneReader.Read(path);
return FromScene(path, result.Document, result.Diagnostics);
}

// The engine adapters key the model by its file name, as the other importers do; the title falls back to it too.
internal static ViewerDocument FromScene(string path, SceneDocument scene, IReadOnlyList<SceneDiagnostic> diagnostics)
{
if (string.IsNullOrEmpty(scene.Title)) scene.Title = Path.GetFileNameWithoutExtension(path);
// Information entries (such as an element that has no geometry by design) are not reported; one line per kind.
foreach (var kind in diagnostics.Where(d => d.Severity != SceneSeverity.Info).GroupBy(d => (d.Severity, d.Code)))
{
int more = kind.Count() - 1;
string label = kind.Key.Severity == SceneSeverity.Error ? "Error" : "Warning";
Console.Error.WriteLine($"{label}: {kind.Key.Code}: {kind.First().Message}{(more > 0 ? $" ({more} more)" : "")}");
}
return new ViewerDocument(scene);
}

private static void StartServe(string htmlPath, int port)
{
string url = $"http://localhost:{port}/";
Expand Down
2 changes: 1 addition & 1 deletion tests/IOViewer.Tests/CliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Cli_Help_ReturnsSuccessAndShowsUsage()
int code = Program.Main(HelpArgs);
Assert.Equal(0, code);
string output = sw.ToString();
Assert.Contains("ioviewer <input.glb|input.obj|input.json>", output);
Assert.Contains("ioviewer <input.glb|input.obj|input.json|input.ifc|input.rvt>", output);
Assert.Contains("--wireframe", output);
Assert.Contains("--white-mode", output);
Assert.Contains("--serve", output);
Expand Down
Loading
Loading