4 Jan 2017
Show up Mesh Triangles from Data of HitTest
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.
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)
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.