'How to control the order of models in the modelstructure panel

I am trying to improve the loading speed when loading multiple models. I can do so by using Promise.all which loads the models in parallel instead of in series. The problem however is that as you have no control over which model is loaded first that you also don't have control over the order of the models in the modulestructure panel.

Is there any way to control the order of models in the modelstructurepanel once the models are loaded? Could you perhaps move them up/down afterwards? Or even better, can you hook into some event just before adding it to the modelstructure where you can specify on what position it will be inserted?

The code below is not my real implementation, but is a modification of Michael Beale's example from https://forge.autodesk.com/blog/loading-multiple-models-forge-viewer-v7 and shows the principle of my goal here.


var viewer;

loadModels([
    { urn: "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dnJwYXJ0eTIvcnN0X2Jhc2ljX3NhbXBsZV9wcm9qZWN0LnJ2dA", xform: { x: -60, y: 0, z: 0 } },
    { urn: "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dnJwYXJ0eTIvNDMyJTIwTmFwYS5ydnQ", xform: { x: 60, y: 0, z: 0 } },
    { urn: "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dnJwYXJ0eTEvcmFjLnJ2dA", xform: { x: 50, y: 0, z: -50 } },
    { urn: "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dnJwYXJ0eTEvcmFjLnJ2dA", xform: { x: -50, y: 0, z: -50 } }
]);

function loadModels(urns) {
    const viewerOptions = {
        env: "AutodeskProduction",
        accessToken: _adsk.token.access_token,
        extensions: []
    };

    Autodesk.Viewing.Initializer(viewerOptions, async() => {
        const div = document.getElementById("forgeViewer");
        viewer = new Autodesk.Viewing.Private.GuiViewer3D(div);
        viewer.start();
        await Promise.all(urns.map(loadDocument));

        // Order of models in viewer.modelstructure is now depending on download speed of each model and not following the order in urns array
    });

    async function loadDocument(m) {
        return new Promise((resolve, reject) => {
            Autodesk.Viewing.Document.load(`urn:${m.urn}`, (doc)=>{
                loadDocumentNode(doc).then(m =>{
                    resolve();
                })
            });
        });
    }

    async function loadDocumentNode(doc) {
        var viewables = doc.getRoot().getDefaultGeometry();
        return await viewer.loadDocumentNode(doc, viewables, {
            preserveView: true,
            keepCurrentModels: true,
            keepCurrentModels: true,
            globalOffset: { x: 0, y: 0, z: 0 }
        });
    }
}



Solution 1:[1]

The trick is to load the first model, then wait the GEOMETRY_LOADED event, then load the rest of the models in parallel.

view = new SheetsView(viewer);
view.load(urn, sheets, viewState);


class SheetsView {

constructor(viewer) {
    this.viewer = viewer;
    this.sheets = [];  //Model objects for each 2D sheet
    this.model3d = null; // model object for root 3D node
    this.bub3D = null; // viewable for 3D model
    this.options = {keepCurrentModels: true};
}

// loads 3D base model and select 2D sheets
// URN:         urn of primary 3D model
// sheets:     array of sheets { id: index of 2d sheet, x,y,z, scale,  vert }
// viewState:  initial camera view state.  viewState = viewer.getState({viewport: true, objectSet: true});

async load(urn, sheets, viewState) {

    this.sheets = sheets;
    Autodesk.Viewing.Document.load( urn, async doc => {
        this.bub3D = doc.getRoot().search({type:'geometry', role: "3d" })[0];
        let bub2Ds = doc.getRoot().search({type:'geometry', role: "2d" });
        doc.downloadAecModelData();
        // first, load 3D model, then wait for GEOMETRY_LOADED_EVENT
        this.model3d = await this._loadmodel(doc, this.bub3D, this.options);

        // second, load 2D (or3D) models in parallel
        for (let sheet of sheets) {
            sheet.model = (await this._loadmodel(doc, bub2Ds[sheet.id], this.options )).model;
        };                
        this._finishLoad(viewState);
    })        
}


async _loadmodel(doc, viewable, options) {

    let viewer = this.viewer;
    return await new Promise(function (resolve) {
        viewer.loadDocumentNode(doc, viewable, options).then( model => {
            viewable.model = model;
            viewer.addEventListener( Autodesk.Viewing.GEOMETRY_LOADED_EVENT, e => { 
                resolve(e);
            }, {once : true}); 
        });
    });
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 michael beale