13 Sep 2017
Aggregate multi models in sequence in Forge Viewer
My colleague has produced a couple of articles about how to aggregate models into one Forge Viewer such as :
- Aggregating multiple models in the Viewer
- Preparing your viewing application for multi-model workflows
- Preparing your viewing application for multi-model workflows - Part 2: Model Loader
These mainly demo aggregating one model at one time. With this logic, it is easy to make a loop to load any number of models to Forge Viewer. However, since the loading process is not synchronized (loading document, loading viewable, and loading geometry are all asynchronized), we cannot know which model will be loaded completely first, which would be the next. While in some cases, it might probably need to aggregate the models in a specific sequence.
So I spent some time to investigate Promise of JavaScript. Basically, Promise provides flexible ways to manage the unit process. To make a sequence promise, I found a helpful discussion, and the demo test code:
https://stackoverflow.com/questions/29880715/how-to-synchronize-a-sequence-of-promises
based on that, I built a demo code at https://jsfiddle.net/xiaodongliang/4qzvu0da/. It shows loading each level of a model one by one. The core code snippet is as below.
//replace with your own urns
this.urn_model1 = <model1 urn>;
this.urn_model2 = <model2 urn>;
this.urn_model3 = <model3 urn>;;
this.urn_model4 = <model4 urn>;
//model info array
this.modelArray = [ {modelName:'urn_model1',urn:urn_model1,modelObj:null},
{modelName:'urn_model2',urn:urn_model2,modelObj:null},
{modelName:'urn_model3',urn:urn_model3,modelObj:null},
{modelName:'urn_model4',urn:urn_model4,modelObj:null}];
//viewer object
this.viewer = null;
function start(){
//replace with your own token
var token = <your token>;
//option to initialize viewer.
var options = {
env: 'AutodeskProduction',
accessToken: token
};
//It looks the static function of Viewer does not support ES6
//still use ES5
Autodesk.Viewing.Initializer(options, function onInitialized(){
//get the viewer div
var viewerDiv = document.getElementById('myViewer');
//initialize the viewer object
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv,{
});
//load model one by one in sequence
globalPromise(modelArray);
});
}
//load model by promise
globalPromise = (modelArray)=> {
var _this = this;
//each promise function
//input the index of model array
function promiseEachModel(index){
return new Promise((resolve, reject)=>
{
var modelName = modelArray[index].modelName;
var _index = index;
//when the document is loaded
function _onDocumentLoadSuccess(doc) {
console.log(modelName +
': Document Load Succeeded!');
_this.globalDocumentLoad(doc,
_onLoadModelSuccess,
_onLoadModelError);
};
//when the document failed to be loaded
function _onDocumentLoadFailure(viewerErrorCode) {
console.error(modelName +
': Document Load Failure, errorCode:' +
viewerErrorCode);
}
//when the model is loaded
function _onLoadModelSuccess(model) {
console.log(modelName + ': Load Model Succeeded!');
//delegate geometry loaded event
_this.viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
_onGeometryLoaded);
//map this item with the corresponding model in case of use
modelArray[index].modelObj = model
};
function _onLoadModelError(viewerErrorCode) {
console.error(modelName +
': Load Model Error, errorCode:' +
viewerErrorCode);
//any error
reject(modelName + ' Loading Failed!' + viewerErrorCode);
}
function _onGeometryLoaded(evt){
//_this.globalGeometryLoaded(modelName,evt.model);
_this.viewer.removeEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
_onGeometryLoaded);
console.log(modelName + ' Geometry Loaded!');
resolve(modelName + ' Geometry Loaded!');
}
//load the model
Autodesk.Viewing.Document.load(
modelArray[index].urn,
_onDocumentLoadSuccess,
_onDocumentLoadFailure );
}); //end of new promise
}//end of each promise function
//build the index array
var indexArr = [0,1,2,3];
//proces each promise
//refer to http://jsfiddle.net/jfriend00/h3zaw8u8/
function processArray(array, fn) {
var results = [];
return array.reduce(function(p, item) {
return p.then(function() {
return fn(item).then(function(data) {
results.push(data);
return results;
});
});
}, Promise.resolve());
}
//start to process
processArray(indexArr, promiseEachModel).then(function(result) {
console.log(result);
}, function(reason) {
console.log(reason);
});
}//end of function globalPromise
//when document is being loaded
globalDocumentLoad = (doc,_onLoadModelSuccess,_onLoadModelError)=>
{
//get available viewables
var viewables = Autodesk.Viewing.Document.getSubItemsWithProperties(
doc.getRootItem(), {'type':'geometry'}, true);
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Choose the first avialble viewables
var initialViewable = viewables[0];
var svfUrl = doc.getViewablePath(initialViewable);
var mat = new THREE.Matrix4();
//input the transformation
var loadOptions = {
placementTransform: mat ,
globalOffset:{x:0,y:0,z:0}, // to align the models
sharedPropertyDbPath: doc.getPropertyDbPath()
};
//if this is the first model
if(doc.myPath == this.urn_model1){
//load the first model
this.viewer.start(svfUrl,
loadOptions,
_onLoadModelSuccess,
_onLoadModelError);
}
else{
//other models
this.viewer.loadModel(svfUrl,
loadOptions,
_onLoadModelSuccess,
_onLoadModelError);
}
}
start();