Loading Models

Summary

In this chapter, we will provide code for you to load and add models to your application.

Concepts

  • Creating model thumbnail UI elements

  • Inserting models into the scene

  • Encapsulate event listeners


Set event callbacks

In the previous section, we waited for the modelStructureReady callback to fire before enabling the “Load Model” input button. We can now put some functionality behind this input button. This section of code has quite little to do with HOOPS Communicator, so we will not delve too deeply into it, but of course it is provided below. The idea is that we will create a file input element and listen for the onchange call when it is clicked. This will recognize a new file has been uploaded. We will then create the necessary thumbnail element, check if there is a model thumbnail image present, and set it in the thumbnail if found. We will then make the thumbnail clickable, and when clicked, it will call a modelLoad function we will write.

document.getElementById("open-model-button").onclick = () => {
        // Proxy to override the default behavior of file input type
        document.getElementById('file-input').click();
};
document.getElementById("file-input").onchange = (e) => {
        // Once a file has been selected by the user, use the file information to
        // gather the associated relevant data like thumbnails
        let fileChoice = e.target.value;
        let filename = fileChoice.replace(/^.*[\\\/]/, '');
        let modelThumbnail = document.createElement('a');
        let modelname = filename.split(".", 1)[0];
        modelThumbnail.id = modelname;
        modelThumbnail.href = "";
        modelThumbnail.className = "model-thumb";
        modelThumbnail.setAttribute("model", modelname);
        let imgPath = "/data/thumbnails/" + modelname + ".png";
        // Check to see if the selected model has a corresponding thumbnail made
        fetch(imgPath)
                .then((resp) => {
                if (resp.ok) {
                        let modelImg = document.createElement('img');
                        modelImg.src = imgPath;
                        modelThumbnail.appendChild(modelImg);
                }
                else {
                        modelThumbnail.innerHTML = modelname;
                        console.log("No Image for this Model was found.");
                }
        });
        document.getElementById("models-scroller").appendChild(modelThumbnail);
        // Now update the event callbacks for the thumbnails
        const thumbnailElements = document.getElementsByClassName("model-thumb");
        for (let thumbnail of thumbnailElements) {
                let thumbnailElement = thumbnail;
                thumbnailElement.onclick = (e) => {
                        e.preventDefault();
                        let elem = e.currentTarget;
                        let modelToLoad = elem.getAttribute("model");
                        // Load the model into the scene when clicked
                        this.loadModel(modelToLoad);
                };
        }
        ;
};

Loading the model into the assembly tree

Since we will potentially be loading an arbitrary number of models in our application, we can abstract the loading into its own loadModel function, as a part of the main class. The loadModel function will take one parameter – the model name to be loaded.

Within the function, we want to be sure we are adding our model to the model tree in a smart manner. Much like in the Getting Starting guide, we will create a new model node off the root node, name it, and load our model to that node. First, we must clear the current model state, so we can then load a new model. We can use the promise returned from the clear() command to then continue to load our model. Please note that in an SCS workflow, you cannot use the switchToModel() API on the Model class – this will only work in a Stream Cache Server workflow.

We also want to ensure that our camera is fit to the model boundaries, so we will use the then() method on the returned promise from loadSubtreeFromScsFile() to fit the camera.

Lastly, since we will have the model loaded, we can use HOOPS Communicator to query the file type and model name, and populate those HTML elements in our template.

loadModel(modelName) {
        this._viewer.model.clear()
                .then(() => {
                const nodeName = "Model-" + modelName;
                const modelNodeId = this._viewer.model.createNode(null, nodeName);
                this._viewer.model.loadSubtreeFromScsFile(modelNodeId, "/data/database/" + modelName + ".scs")
          .then(() => {
                this._viewer.view.fitWorld();
           // Get and set the rest of the model level info
           const modelRoot = this._viewer.model.getNodeChildren(modelNodeId)[0];
           const modelFileName = this._viewer.model.getModelFileNameFromNode(modelRoot);
           const modelFileFormat = this._viewer.model.getModelFileTypeFromNode(modelRoot);
           document.getElementById("model-file-name").innerHTML = modelFileName || "N/A";
           document.getElementById("model-file-type").innerHTML = Communicator.FileType[modelFileFormat] || "N/A";
          });
        });
}

Encapsulate the event listeners

We now have the two pieces we need to load models, we just need to set the callbacks in the application. Since we will have several UI elements we need to hook up when the application loads, we will create a function called setEventListeners with our event listener code, and call this function in the application constructor.

setEventListeners() {
document.getElementById("open-model-button").onclick = () => {
        // Proxy to override the default behavior of file input type
        document.getElementById('file-input').click();
};
document.getElementById("file-input").onchange = (e) => {
        // ...
}

In the constructor, you can make a call to the setEventListeners function.

this.setEventListeners();

Once you have placed your event listeners within the setEventListeners function, and called this function in your main constructor, run your application and try to load the “Moto” model. Remember that it will populate a thumbnail with your model image first, and clicking on that model thumbnail will actually load it into the viewer.

If you take a look over at our application, you will see the viewer with the model loaded and rendered!

../../_images/inventory-mgmt-moto.png

You should also now be able to navigate and make selections on the model within the WebViewer component. If you would like, feel free to load more models in the sidebar and click between the thumbnails to see how the application switches between models.