January 4, 2017

Show up Mesh Triangles from Data of HitTest

By Xiaodong Liang

We have some extensions that can get the triangles of viewer model, such as Autodesk.ADN.Viewing.Extension.MeshData.js. It is useful for geometry analysis, simulation etc. I tried to write more codes to calculate the hit point on one object, check which triangle the point locates in and build the THREE.js face above the corresponding viewer triangle.

Select-triangles

It can work, but finally I got to know I missed one existing method viewer.impl.hitTest. By passing a screen point, it can return the corresponding interaction point on the face, and the face information! This is quite useful. It tells the index of the vertices, the fragment Id and face normal. By this method, the codes will be more clean and simple.

GIF (Click it to open a new page, in which you can see the animation)

Select-face

I am sharing the codes as below. After the extension is loaded, the triangle will be displayed accordingly when mouse moving like the gif shows.

 

// **********************
// Extension for displaying temporary triangle face for the primitives of viewer face.
// **********************

function SelectFaceExtension(viewer, options) {
  Autodesk.Viewing.Extension.call(this, viewer, options);

  // viewer object
  var _viewer = viewer;

  //  THREE.js triangle for the facet
  var _currentTriangle = null;


  // when mouse move
  this.onMouseMove = function (event) {

    // get current screen point
    var screenPoint = {
      x: event.clientX,
      y: event.clientY
    };
    // hit test
    var hitTest = _viewer.impl.hitTest(screenPoint.x,screenPoint.y,true);
    // draw the temporary triangle face
    if(hitTest)
      drawMeshData(hitTest);
  }

  function drawMeshData(hitTest) {

    if( _currentTriangle != null)
    {
      // remove the last triangle face
      _viewer.impl.scene.remove(_currentTriangle);
      _currentTriangle = null;
    }

    var currentFragId = hitTest.fragId;

    // fragment proxy
    var fragProxy = _viewer.impl.getFragmentProxy(
      _viewer.model,
      currentFragId);

    // render proxy
    var renderProxy = _viewer.impl.getRenderProxy(
            _viewer.model,
            currentFragId);

    fragProxy.getAnimTransform();

    // transformation from fragment space to WCS
    var matrix = renderProxy.matrixWorld;

    // geometry data of the fragment
    var geometry = renderProxy.geometry;

    // information of the fragment
    var attributes = geometry.attributes;

    if (attributes.index !== undefined) {

      // index array of the vertices
      var indices = attributes.index.array || geometry.ib;
      // position array of the fragment
      var positions = geometry.vb ? geometry.vb : attributes.position.array;
      // unit range of the  vertices in the position array
      var stride = geometry.vb ? geometry.vbstride : 3;
      // geometry offset if any
      var offsets = geometry.offsets;

      // vertices
      var vA = new THREE.Vector3();
      var vB = new THREE.Vector3();
      var vC = new THREE.Vector3();

      // index of the vertices of the specific viewer face
      var a = hitTest.face.a;
      var b = hitTest.face.b;
      var c = hitTest.face.c;

      // positions of  the vertices
      vA.fromArray(positions, a* stride);
      vB.fromArray(positions, b* stride);
      vC.fromArray(positions, c* stride);

      // transform to WCS in model space
      vA.applyMatrix4(matrix);
      vB.applyMatrix4(matrix);
      vC.applyMatrix4(matrix);

      // normal of this facet
      normal =  hitTest.face.normal;

      // offset the temporary triangle above the native face
      var sal = 0.2;
      vA.x += normal.x * sal;vA.y += normal.y * sal;vA.z += normal.z*sal;
      vB.x += normal.x * sal;vB.y += normal.y * sal;vB.z += normal.z*sal;
      vC.x += normal.x * sal;vC.y += normal.y * sal;vC.z += normal.z*sal;

      // build THREE.js face
      var geomface = new THREE.Geometry();

      geomface.vertices.push(
        new THREE.Vector3( vA.x,  vA.y, vA.z ),
        new THREE.Vector3( vB.x,  vB.y, vB.z ),
        new THREE.Vector3(  vC.x,  vC.y, vC.z )
      );

      // face sequence
      var face = new THREE.Face3(0, 1, 2);

      // will use vertex color
      face.vertexColors[0] = new THREE.Color(0xff0000); // red
      face.vertexColors[1] = new THREE.Color(0x00ff00); // green
      face.vertexColors[2] = new THREE.Color(0x0000ff); // blue

      geomface.faces.push(face);
      geomface.computeBoundingSphere();
      geometry.computeFaceNormals();

      // vertex material
      var localmaterial = new THREE.MeshBasicMaterial({vertexColors: THREE.VertexColors});

      // create this triangle
      _currentTriangle = new THREE.Mesh(geomface, localmaterial);

      // add the triangle to the scene
      _viewer.impl.scene.add(_currentTriangle);
      _viewer.impl.invalidate(true, false, true);
    } 
  }

}
SelectFaceExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
SelectFaceExtension.prototype.constructor = SelectFaceExtension;

SelectFaceExtension.prototype.load = function() {

  console.log('SelectFaceExtension.prototype.load');

  $(_viewer.container).
  bind("mousemove", this.onMouseMove);

  return true;
};



SelectFaceExtension.prototype.unload = function() {

  console.log('SelectFaceExtension unload');

  // undeligate the event
  $(_viewer.container).
  unbind("mousemove", this.onMouseMove);

  console.log('mousemove unbinded');

  return true;
};

Autodesk.Viewing.theExtensionManager.registerExtension('SelectFaceExtension', SelectFaceExtension);

Some extra information: in recent Accelerators in China, a requirement is to identify the topology face of the model in the format of Forge Viewer. Unfortunately, from our engineer team, such information is not stored with current viewer API. The information would probably available with a separate file (prefix is 'topology') in svf package, but it is not always available when the source model is complex. In addition, there is not yet API to get it out. Only the primitives (triangles) of the mesh is provided by Forge Viewer.

Posted by

Xiaodong Liang has been with Autodesk since 2007, focusing on providing programming support, consulting, training and evangelism to external developers. He started his career China and now lives in Beijing, China. Xiaodong is currently a developer consultant in the team Developer Technical Services (DevTech), the worldwide team of API gurus providing technical services through the Autodesk Developer Network. He supports...

Related Posts