13 Sep 2017

Aggregate multi models in sequence in Forge Viewer

Default blog image

load model in sequence

My colleague has produced a couple of articles about how to aggregate models into one Forge Viewer such as :

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();

 

 

Related Article