+
Skip to content

Conway Viewer, current design and future directions. #269

@ConorStokes

Description

@ConorStokes

Introduction

Previously viewing for Conway has either been done via previously existing webifc related viewer components (via a shim implementing the webifc API with conway) or using exports to gltf/obj files to view geometry created by conway in external viewers.

We have now added the basis of a viewer to Conway, implemented on top of threejs. This viewer is currently capable of being used to embed the output of a file loaded by Conway (an IFC or AP214 STEP file) in a three.js scene, or using some helper utilities, create a simple scene and stand-up a rendering environment.

Viewer Architecture

The viewer uses an event driven architecture, with a SAX like sink for scene events (such as geometry being added) allowing maintenance of a state machine representing the geometry in three.js. These event listeners can be attached to any Conway Scene (in this case, a Conway Scene is the representation for geometry and transforms loaded from a model, such as an IFC file or AP 214 Step file), the interface for this purpose is SceneListener.

SceneListeners can optionally get a replay of events for a currently existing scenes when they are attached, allowing them to capture models after loading as well as models in the process of being loaded. This interface is implemented in the viewer as the SceneEventSink class, which is aggregated and wrapped by the SceneObject class, which acts as the threejs scene object representing the corresponding Conway model object.

This allows the created three.js scene object to be populated as the geometry is loaded, including in future when asynchronous per-product loading and multi-threading is implemented, progressive loading. In addition, SceneEventSink is implemented as a state machine that rectifies updates and has provision for handling deletes. So in future it could be used to render edits and updates to model files in place.

Underneath, SceneEventSink manages a BatchedMesh per material. BatchedMeshes may only have a single material, but they can have multiple different kinds of geometry, and multiple instances of each geometry. Where possible, extensions are used to reduce the possible draw call count this might create, and unlike the current viewer which flattens meshes, we keep geometry as its constituent parts. On WebGL 1 platforms without the requisite extensions and capabilities, this may create a fallback path that slows down rendering in some extreme cases, however on WebGL 2 platforms performance should be good and where the extension is present, rendering should be a single draw call per material.

We also implement a wider range of material properties, such as physically based materials (with index of refraction, metalness and roughness). This allows increased quality of appearance, with emulation for different material types when the values are not available in the underlying file. The three scene utility also implements shadowing, so that models can optionally be rendered with shadows, with various quality levels for a speed-quality trade-off. In future it is recommended that we automatically detect mobile devices with smaller screens and lower the quality to reduce power use and increase performance on lower end devices.

At the moment, due to limitations in how instances are managed and the fact that we would have to manage transform groups ourselves, we do not handle re-using BatchedMesh objects between instances of the same Conway model (i.e. where there are multiple versions of the same model in the scene). This may be possible in future, but it would require handling generating and managing all the transforms for an instance, and updating all the sub-instances within a Conway model instance to perform transforms etc. This is a limitation of the current three.js architecture.

Note, three.js is good for small embedding use cases, but it is not a replacement for a full 3D rendering engine and for larger scale use-cases, it would need to be replaced by something more serious with low-level optimisations for rendering, LOD support and culling.

Note, in addition to batching, SceneEventSink and the matching SceneObject support a bulk insert function, via the "cork" paradigm. The SceneObject can be (and will by default) start in a "cork" mode, which will aggregate data, but not instance it or allocate the underlying BatchedMesh data. The "uncork" member can then be called for this to be added in bulk, taking advantage of some optimisations that can be performed because the data loaded so far is of a known size.

Finally, note that Conway viewer uses a relative framing for its coordinate system, where there is an absolute reference point that the model is relative to. This is the "referencePoint" property on SceneObject and SceneEventSink. This presents precision problems related to single precision floating point and the large coordinate offsets used in IFC files, for example to geo-locate models. Keeping the reference point in double precision and building matching matrices relative to the eye to position objects in a scene means that these scenes can be constructed without large floating point errors due to loss of precision.

Usage

In addition to the viewer, there is now a loading utility class called the ConwayModelLoader. This class has a static async method "loadModelWithScene". Given a Uint8Array (that is a group of bytes) representing the model data (such as an IFC file), this can be used to load a model, as well as its corresponding Conway Scene.

The following would load a model and its corresponding Conway scene:

const buffer = await file.arrayBuffer()

const [model, scene] = ConwayModelLoader.loadModelWithScene( new Uint8Array( buffer ) )

After this, we could create an three.js object from the Conway scene which could be added to a three.js scene.:

const conwayThreeObject = new SceneObject( scene )

const threeScene = new THREE.Scene()

threeScene.add( conwayThreeObject )

There is also a simple option to simply get a full scene viewer for the Conway model, which can be attached to a HTML document or element within that document. This uses the SimpleViewerScene class, which can be initialised and then have a model loaded to it.

const sceneViewer = SimpleViewerScene.createSceneAttachedToElement( document.body )

await sceneViewer.load( await file.arrayBuffer() )

The src/rendering/threejs/html_viewer.ts file shows an example of this, including a drag and drop handler for loading a model.

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载