24 Jan 2018

How to add newest Three.js features to the Forge Viewer

    The Forge Viewer is using a customized version of Three.js release r71,  the current version of that library at the time of this writing is r89, so there is bunch of features which have been added by the community in between. Unfortunately is not easy for the Viewer development team to integrate those enhancements quickly in our code base, so how to proceed if you desperately need one of the latest feature of Three.js in your next Forge application...? Fear not! I'm here to give you tips that will make it effortless ;) 

    This post was inspired by an answer I provided to the following Stackoverflow question: How can you add text to the Forge Viewer with three.js? The developer was willing to add 3D Text to the Forge Viewer scene and Three.js provides a convenient helper for that: TextGeometry but it has been added post r71. So you could obviously dig out all the code for that object in Three.js source, along with all the required dependencies which may or may not be part of r71, create your own feature out of it and use it in your app, or you could also import the latest Three.js script as a whole but you would end up with twice as much code than you need since using the Viewer Three.js version is already required, you would also need to be careful about namespace collision, so is there an easier approach...?

    Well, we are developers, so by definition lazy. Here is how to get that done with minimal effort and maximal benefit for your app size: by using modern development tools such as bundlers (Webpack is the most popular one) you are able to leverage ES6 modules and import to your app only the strictly needed dependencies. The threejs-full-es6 package lets you import Three.js features to your app independently, so in that case we can import TextGeometry and Font, all other required dependencies will be taken care of by the package implementation.

    So as a proof of concept, here is a simple ES6 extension I created that wraps the TextGeometry creation:  

import { Font, TextGeometry } from 'threejs-full-es6'
import FontJson from './helvetiker_bold.typeface.json'

export default class TextExtension
  extends Autodesk.Viewing.Extension {

  /////////////////////////////////////////////////////////
  // Adds a color material to the viewer
  //
  /////////////////////////////////////////////////////////
  constructor (viewer, options) {

    super()

    this.viewer = viewer
  }

  load () {

    return true
  }

  unload () {

    return true
  }

  /////////////////////////////////////////////////////////
  // Adds a color material to the viewer
  //
  /////////////////////////////////////////////////////////
  createColorMaterial (color) {

    const material = new THREE.MeshPhongMaterial({
      specular: new THREE.Color(color),
      side: THREE.DoubleSide,
      reflectivity: 0.0,
      color
    })

    const materials = this.viewer.impl.getMaterials()

    materials.addMaterial(
      color.toString(),
      material,
      true)

    return material
  }

  /////////////////////////////////////////////////////////
  // Wraps TextGeometry object and adds a new mesh to
  // the scene
  /////////////////////////////////////////////////////////
  createText (params) {

    const geometry = new TextGeometry(params.text,
      Object.assign({}, {
        font: new Font(FontJson),
        params
      }))

    const material = this.createColorMaterial(
      params.color)

    const text = new THREE.Mesh(
      geometry , material)

    text.position.set(
      params.position.x,
      params.position.y,
      params.position.z)

    this.viewer.impl.scene.add(text)

    this.viewer.impl.sceneUpdated(true)
  }
}

Autodesk.Viewing.theExtensionManager.registerExtension(
  'Viewing.Extension.Text', TextExtension)

     To use it, simply load the extension and invoke createText method:

import './Viewing.Extension.Test'

// ...

viewer.loadExtension('Viewing.Extension.Test').then((extension) => {

    extension.createText({
      position: {x: -150, y: 50, z: 0},
      bevelEnabled: true,
      curveSegments: 24,
      bevelThickness: 1,
      color: 0xFFA500,
      text: 'Forge!',
      bevelSize: 1,
      height: 1,
      size: 1
    })
})

     This produces the result in the screenshot above. Done and time for a break!

Related Article