6 Nov 2019

combining PointClouds and Revit models in ForgeViewer

Come to our AU-Talk

With the lead-up to Autodesk University 2019 and DevCon, I wanted to give you a sneak peak into a presentation I'm doing on Point-Clouds and Forge Viewer, with the help of Airsquire.

"Viewing PointCloud in a browser with ForgeViewer and AirSquire"




This blog post loads in a point-cloud scan of a building, using the impressive Draco point-cloud compression, and then loads in the CAD / Revit model, and aligns the two things inside ForgeViewer.  It also adds a point-cloud shader to enhance detail lighting.

It's a little different from my previous point-cloud blog post and also Petr's post - it loads in a scanned dataset, instead of using dummy points. Because of that, it also means we need to address alignment, unit-scale and rendering issues, not seen in past blogs.







There are a few trends in the point-cloud industry, that involve BIM models:

1. point-clouds are cleaned (remove tree's, enhance power-lines) & analyze into segmented 'bits' (terrain, chairs, planes)

2. we want click a 'segmented-bit' back to the properties of the original CAD model 'entity'

3. make point-cloud and CAD models easily accessible, via a web browser

4. finally, be about to compare or 'measure' the difference between the 'scanned world' and the 'modeled world'


That last item#4 is called a 'verification workflow' and is what "AirSquire" provides professional software around.

Before we go any further, let's talk about item#3 "making this accessible via a web browser".  Raw point-cloud data is verbose  and not very web-friendly. To help this along, we should clean it up (this differs per industry), make it streamable (for the web) and compress it (precision is important).  Airsquire handles all of these, but I am going to demo the last bit, with Draco compression.

What is Draco Compression?

'Draco' is an open-source point-cloud and mesh compression format, built by Google and used in the upcoming glTF spec.  Think of glTF as a replacement for OBJ files, but can include object hierarchy IDs (like the ForgeViewer model-browser and Forge Model-Derivative APIs), PBR materials and impressive lossless & lossy compression ratio's via Draco.

This demo shows how to load in a Draco point-cloud file (.DRC) and combine it with a RVT Revit file, inside the ForgeViewer.  Both the Revit model and scan data-set, are courtesy of AirSquire.

Let's get started !

First, in your index.html file, include the Draco javascript file (include the WASM decoder library) and our point-cloud extension, like this:

    <script src="js/draco/DRACOLoader.js"></script>
    <script src="js/extensions/pointcloud.js"></script>

and kick off the 'load' command, like this:

dracoLoader.load( 'data/airsquire.drc', geometry => {
            geometry.isPoints = true;
            this.points = new THREE.PointCloud(geometry, material);

The Draco WASM loader does "all the hard work" of loading and decoding point-cloud data, via background web-workers, to provide a smooth UI experience (no blocking up the main thread).  
The Draco decoder libraries are small, coming in at <100k (zipped).  I needed to slightly modify them to work with three.js R71

Draco Tooling

Both Google and Cesium provide some useful tooling around Draco.  I used the 'draco-encoder' command line tool to convert from OBJ/PLY files to DRC.  You can also use Cesium's 'point-cloud-tiler' to convert from LAZ or LAS format into DRC.

I took the 80MB .PLY point-cloud file, ran 'draco-encoder' to get a 4MB .DRC.  A 20:1 compression ratio using default settings.  You can drop precision to get higher compression ratio's if distribution size is more important to you.

Here's a few "Pointer's"

By cherry picking the previous two point-cloud blog posts, you would have noticed a few differences:

  • sceneOverlay
  • BufferGeometry
  • Custom point-cloud Shader
  • Streaming


Use the 'overlay' technique, instead of 'scene' or 'sceneAfter', when adding the point-cloud to the Forge scene.  This avoids the point-cloud vanishing when the camera state moves (only for large CAD scenes).

this.viewer.impl.addOverlay('pointclouds', this.points);

The 'overlay' scene, integrates point-cloud material into the middle of the render-loop instead of at the end of a 'reset' cycle.  To trigger a refresh in the overlay layer, I use viewer.impl.invalidate(false, false,true); 


Use BUFFERGEOMETRY for the points, instead of GEOMETRY.  This will use less memory and better performance in the end.  Also, the Draco loader pass back a bufferGeometry object, anyway.  Because we're using an old version of three.js, we need to tell Forge Viewer that we are using bufferGeometry for the points, by setting...

geometry.isPoints = true;

Custom Point-Cloud Shader

Finally, the default point-cloud from three.js R71 used in Petr's demo, had a few quirks.  Firstly, the size parameter wasn't honored and hence didn't have any effect when zooming into the points, thus causing them to alias ('sparkle').  Even if we fixed the size issue, the resulting point-cloud looks 'flat', like this image:


To spice it up a little, we would normally add EDL and SSAO shader effects, but I'm going to take a lazy approach and use a embossed circle sprite... like this.  

var tex = THREE.ImageUtils.loadTexture("css/disc.png");

On it's own, the circle is just a circle, but when combined with millions of other points jammed together and viewed from a distance... 

  • The points no longer look flat, and instead our building appears to have a lighting effect, due to the embossed shading on the circle.  
  • Also, the circle's black outline, contributes to a weird edge enhancement on the building.  

Here's the point-cloud shader:

// Vertex shader
            uniform float size;
            void main() {
                vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
                gl_PointSize = size * ( 100.0 / -mvPosition.z );
                gl_Position = projectionMatrix * mvPosition;

// Fragment shader
            uniform sampler2D texture;
            void main() {
                gl_FragColor = texture2D( texture, gl_PointCoord );
                if (gl_FragColor.w < 0.5) discard;


Streaming Point Clouds

Loading a single point-cloud DRC file is easy...But, it won't help when you need to access 1TB of the original point-cloud scan.

That's where 'streaming' comes in.  

Start by creating a spatial index, like an Octree, and connect it to your loading system and the camera render loop.  You also want to put a limit on how much data sits in your GPU memory.  These are typical streaming techniques used by Potree, Airsquire and others.  Here's a 'preview' of AirSquire's streaming tech, running in a browser, (taken from our AU talk):



To find out how to code streaming, picking and measurement, come to the 'Airsquire' talk ...

  • "FDC324035" at Autodesk University,
  • Monday Nov 18 at 5pm, room Marcello (4403, Level 4).
  • Click the "Sign up" at the top of this blog post (or from the App) 


@micbeale Follow me on twitter - @micbeale

Related Article