10 Mar 2015
Transform built-in object in Viewer by Autodesk View & Data API
In last blog, I talked about fragments. Some of the manipulations are related to fragments such as transformation, texture, geometries etc.
viewer.impl.getRenderProxy(model, fragId) returns the Mesh object of Three.js. fragId is the index of the same geometry fragment in the various fragments arrays of the model's internal data model. You can query the fragId from the collection of viewer.model.getData().fragments. In many scenarios, we would run our workflow in the event: SELECTION_CHANGED_EVENT. When the item is selected, it also tells fragId of the selection.
Mesh.matrixWorld stores the matrix of the transformation. It follows the standard sequence of the 4X4 transformation matrix of graphics. So, it is much easy to modify any matrix values with your requirement.
You can also apply a transformation to the mesh.matrixWorld directly, and then set mesh.matrixWorldNeedsUpdate = true. This will also update the transformation.
In Three.js, we can call some movement methods of Mesh object such as translateOnAxis,rotateOnAxis. However in order to conserve memory, we skip initialization of some terms of the THREE.Mesh which prevents some of the convenience movement APIs from working. In particular those are position, rotation, quaternion, scale and matrix. The translateOnAxis function uses position and quaternion, so it will fail.
The code demo below shows the 3 methods mentioned above. It assumes you have started a View and delegated the event of selection change.
//deletate selection change event
_viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT,
onSelectedCallback);
function onSelectedCallback(event) {
//get the first fragment of the selection
var fragId = event.fragIdsArray[0];
//if nothing is selected
if (typeof fragId == 'undefined') {
return;
}
//Note: a fragment might contain sub fragments.
var fragIdsArray = (Array.isArray(fragId) ?
fragId :
[fragId]);
//move every fragment of the mesh
fragIdsArray.forEach(function (subFragId) {
//get Mesh of this fragment
var mesh = _viewer.impl.getRenderProxy(
_viewer,
subFragId);
var method = 1; // 2 or 3 // method we want to test
//Method 1: translate the mesh by explicitly setting the matrix values
if (method == 1) {
// WCS transformation of the mesh
var mtx_for_method1 = mesh.matrixWorld.elements;
//e.g. move along (X=10,Y-10,Z=10)
mtx_for_method1[12] += 50;
mtx_for_method1[13] += 50;
mtx_for_method1[14] += 50;
}
//Method 2: apply a transformation to the .matrixWorld property
else if (method == 2) {
//prepare a transformation
//rotate around X-Y-Z
var transMat = new THREE.Matrix4();
var m1 = new THREE.Matrix4();
var m2 = new THREE.Matrix4();
var m3 = new THREE.Matrix4();
var alpha = 0;
var beta = Math.PI;
var gamma = Math.PI / 2;
m1.makeRotationX(alpha);
m2.makeRotationY(beta);
m3.makeRotationZ(gamma);
transMat.multiplyMatrices(m1, m2);
transMat.multiply(m3);
//apply a transformation
var mtx_for_method2 = mesh.matrixWorld;
mtx_for_method2.multiply(transMat);
mtx_for_method2.matrixWorldNeedsUpdate = True;
}
//Method 3: movement methods of Mesh object
if (method == 3) {
//vector to translate
var vector = new THREE.Vector3(10, 0, 0);
//it will fail!
mesh.translateOnAxis(vector, 10);
}
});
_viewer.impl.invalidate(true);
}
The viewer.model.getData().fragments also stores the transforms , but they are not actively used during rendering. Those store the original positions of the model elements, in order to allow resetting the model after explode. So it is NOT recommended to modify viewer.model.getData()fragments.transforms.
My colleague Philippe produced an extension https://github.com/Autodesk-Forge/forge-rcdb.nodejs/tree/master/src/client/viewer.components/Viewer.Extensions.Dynamic/Viewing.Extension.Transform. It demos the transformation along with mouse moving.