20 Dec 2017
High-Performance 3D markups with PointCloud in the Forge Viewer
If you are using the Forge Viewer for a while and read that blog regularly, you've probably seen one of my popular sample dealing with Markup 3D. This was written a while ago and although working as expected it has a limitation in term of performances when loading a large number of markups inside a scene. The logic is based on 2D overlay SVG elements which are being updated every time the Autodesk.Viewing.CAMERA_CHANGE_EVENT is fired by the viewer. For for each interaction between the user and the model, the JavaScript logic needs to cycle through every markup, convert position from 3d to 2D, update the SVG markers and the arrows.
After experimenting recently with Using PointCloud in the Forge Viewer, I decided to write a new markup 3D feature based on PointCloud: rendering performances of a PointCloud are much higher than 2D SVG overlays - it can basically render thousand of points without a blink - and the points are directly rendered in the 3D scene, so no need for 2D/3D coordinates conversion. It is also pretty flexible and powerful as each point can be rendered through a custom shader, so you can use a texture, loaded from an image, or a more advanced shader implementation, like a dynamic texture generated programmatically like in the Forge Fader demo.
For this first version of the PointCloudMarkup, I wanted to have some of the features from the previous Markup3D, like the occlusion and the ability to save/restore markups from viewer states:
The occlusion feature runs some extra logic that will hide the markup if the selected point on the 3D model is no longer in the field of view. This requires hooking to camera events, 3D to 2D coordinates conversion and raytracing to determine if the intersected component/point is the one we attached the markup to. It obviously consumes some CPU to run that code, so if you are looking for optimal performances with a large number of markups in your scene, you have the option to disable the occlusion check on a per markup-basis.
Saving/restoring to the state is fairly straightforward assuming you already have a mechanism in place that allows you to save your states in a database and exposed an API to restore them later. For this task I am able to leverage my Config Manager demo and can simply hook to states being saved/restored by implementing the following methods in my extension:
/////////////////////////////////////////////////////////////////
//
// From viewer.getState:
// Allow extensions to inject their state data
//
// for (var extensionName in viewer.loadedExtensions) {
// viewer.loadedExtensions[extensionName].getState(
// viewerState);
// }
/////////////////////////////////////////////////////////////////
getState (state) {
state.pointCloudMarkup =
this.pointCloudMarkup.getState()
}
/////////////////////////////////////////////////////////////////
//
// From viewer.restoreState:
// Allow extensions to restore their data
//
// for (var extensionName in viewer.loadedExtensions) {
// viewer.loadedExtensions[extensionName].restoreState(
// viewerState, immediate);
// }
/////////////////////////////////////////////////////////////////
restoreState (state, immediate) {
if (immediate) {
this.pointCloudMarkup.restoreState(
state.pointCloudMarkup)
} else {
// restore markups once the camera transition has completed
const onCameraTransitionCompleted = () => {
this.pointCloudMarkup.restoreState(
state.pointCloudMarkup)
this.viewer.removeEventListener(
Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED,
onCameraTransitionCompleted)
}
this.viewer.addEventListener(
Autodesk.Viewing.CAMERA_TRANSITION_COMPLETED,
onCameraTransitionCompleted)
}
}
Those are the main highlights of the demo so far but I am planning to push it a bit further by adding a more advanced shader with a dynamic texture and a UI to attach metadata or comments to each markup ...
You can find the complete code of the PointCloudMarkup at Viewing.Extension.PointCloudMarkup, the live demo is running there and below is a recording of the extension in action where you can see me playing with the settings of each markup and loading them from a saved state. I also add as just as a reference the 1.0 version of the code which is likely going evolve, so prefer the github link for an up-to-date version.