ForgeFader is a Forge viewer extension app that calculates and displays signal attenuation caused by distance and obstacles in a building model with a floor plan containing walls.
It makes use of JavaScript and three.js to implement a functionality similar to the Revit C# .NET add-in RvtFader:
Given a source point, calculate the attenuation in a widening circle around it and display that as a heat map.
You can try it out for yourself in the forge-rcdb.autodesk.io Fader sample.
Two signal attenuation values in decibels are defined in the application settings:
- Attenuation per metre in air
- Attenuation by a wall
The extension expects an RVT model with a floor element.
Two sample models are provided in the test subdirectory.
You can translate them for Forge using your credentials and pass in the resulting URN
to the viewer as described below.
Here is one of the models in Revit and the result of processing it using Forgefader:
The four-minute forgefader Autodesk Forge sample app YouTube video explains some of the background and shows this sample app live in action.
ForgeFader is based on Philippe Leefsma's Forge React boilerplate sample. Please refer to that for more details on the underlying architecture and components used.
The ForgeFader implementation lives in Viewing.Extension.Fader.Core.js.
On loading, in onGeometryLoaded
, it determines the Revit BIM wall fragments for subsequent ray tracing.
On picking a point on a floor in the model, in onSelection
, it launches the attenuationCalculator
function to do the work.
That determines the picked floor top face and adds a new mesh to the model on which to draw the attenuation map.
Once the mesh has been added, it in turn calls rayTraceToFindWalls
to create a bitmap representing the signal attenuation to be displayed by a custom shader.
The following sections describing details of the implementation process have also been published by The Building Coder:
- Adding custom geometry to the Forge viewer
- Three.js raytracing in the Forge Viewer
- Implementing a custom shader in the Forge Viewer
When debugging any kind of geometrical programming task, it is of utmost importance to be able to comfortably visualise the situation.
In this app, I add three different kinds of geometry dynamically to the model displayed by the Forge viewer:
- Points and lines representing the top face of the floor and the picked source point.
- A mesh representing the top face of the floor to be equipped with a custom shader and offset slightly above and away from the floor element surface.
- Points and lines representing the raytracing rays.
Three example screen snapshots illustrate what I mean.
Display points and lines for debugging using drawVertex
and drawLine
:
Create a mesh to represent the floor top face and offset it up slightly above the floor surface:
A debug helper displaying lines in the model representing the ray tracing rays:
You cannot call the three.js Raycaster.intersectObjects
directly on the Forge viewer fragments representing the Revit BIM walls.
One alternative approach is to analyse the Forge viewer fragments and generate new three.js mesh objects from them.
That is achieved by the getMeshFromRenderProxy
function that successfully processes Revit BIM floors and walls.
It is called in onGeometryLoaded
to generate meshes representing all walls in the model:
this.wallMeshes = fragIds.map((fragId) => { return this.getMeshFromRenderProxy( this.viewer.impl.getRenderProxy( this.viewer.model, fragId ), null, null, null ); })
These meshes are used to determine the number of walls between a given pair of source and target points by getWallCountBetween
.
I raised a question with the Forge viewer development team before embarking on the research to implement the above. Unfortunately, due to time differences, they replied only after I had completed it:
[Q@14:19] How can I invoke Raycaster.intersectObjects
with viewer fragments?
[Q@21:35] I solved my Raycaster.intersectObjects
challenge by generating my own threejs mesh from the lmv original.
[A@21:39] Ok well, for the record, you can intersect the whole scene using viewer.impl.rayIntersect
, or you can do it per model via model.rayIntersect
, or per mesh via VBIntersector.rayCast
. The first two approaches take advantage of the spatial index acceleration structure.
[Q@21:42] Thank you for the info! I assume these approaches would offer multiple advantages: (i) more efficient (ii) easier to set up and use (iii) more fool- and future-proof. Do you agree?
[A@21:43] Probably better to use the high level hit testing APIs instead of messing with internal mesh representation directly... i.e. avoid doing fragile stuff like this.viewer.impl.getRenderProxy(this.viewer.model, fragId)
.
In summary, you might be better off ignoring the interesting solution I present above and using the built-in viewer functionality instead.
In the main commit of release 0.0.30, Cyrille Fauvel shows how a custom shader can be applied to a Forge viewer element, initially, for testing puurposes, as a radial gradient:
In release 0.0.32 (cf. the diff) Cyrille replaces the hard-wired radial gradiant by the real thing, a testure map displaying the signal attenuation:
Configuration is controlled by NODE_ENV environment variable, make sure to set it properly to development or production, based on the configuration type you want to run.
In development, the client is dynamically built by the webpack-dev-middleware, so just run:
-
npm install
(downloads project dependencies locally) -
npm start
(builds client on the fly and run server) -
open http://localhost:3000 in your favourite browser
In production, the client requires a build step, so run:
-
npm install
(not required if you already run at previous step) -
npm run build-prod && npm start
(builds client and run server) -
open http://localhost:3000 in your favourite browser
To load a model into the Forge viewer for this extension to process, you first need to generate a viewable URN as documented in the Prepare a File for the Viewer tutorial.
One easy way to achieve this manually is to upload it to models.autodesk.io.
Using the same Forge ClientId & ClientSecret used to upload the model, populate environment variables used by the config files (in /config):
-
development:
FORGE_DEV_CLIENT_ID
FORGE_DEV_CLIENT_SECRET
-
production:
FORGE_CLIENT_ID
FORGE_CLIENT_SECRET
Restart the server; you can then directly load your model by specifying the resulting design URN as query parameter in the url of the viewer page, e.g.:
Using your Forge ClientId and ClientSecret obtained while Creating a new Forge App, press this button:
Check it out at autodesk-forge.github.io.
Look at the quickstarts guide to find the Forge SDK for the programming language of your choice.
- Cyrille Fauvel,
- Philippe Leefsma @F3lipek, and
- Jeremy Tammik, The Building Coder, ADN Open, Autodesk Inc.
This sample is licensed under the terms of the MIT License. Please see the LICENSE file for full details.