Headless Viewer
Developers are able to instantiate the Viewer without any of the toolbar and panel UIs. We call this the “headless viewer”, which maintains all rendering features, but avoids adding any additional UI elements over the 3D canvas.
It can be useful to developers who want to provide their own UX and UI.
In this tutorial, we’ll add a couple buttons to navigate to the next and previous 3D/2D model.
Before You Begin
Make sure that you have registered an app and successfully acquired an OAuth token with scope viewables:read
.
You must also have uploaded and translated a design file using Model Derivative API.
This tutorial works best when there is more than one viewable referenced by the URN’s
manifest.
For more details, see the “Before You Begin” section from the Basic Viewer tutorial.
Step 1: Prepare your HTML
Begin by creating a file named headless.html
and copy the following content to it:
<html>
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
</head>
<body>
<div id="MyViewerDiv"></div>
<!-- The Viewer JS -->
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/three.min.js"></script>
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/viewer3D.min.js"></script>
<!-- Developer JS -->
<script>
var viewerApp;
var options = {
env: 'AutodeskProduction',
accessToken: '<YOUR_APPLICATION_TOKEN>'
};
var documentId = 'urn:<YOUR_URN_ID>';
Autodesk.Viewing.Initializer(options, onInitialized);
function onInitialized() {
viewerApp = new Autodesk.Viewing.ViewingApplication('MyViewerDiv');
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.Viewer3D);
viewerApp.loadDocument(documentId, onDocumentLoaded);
}
function onDocumentLoaded(lmvDoc) {
var modelNodes = viewerApp.bubble.search(av.BubbleNode.MODEL_NODE); // 3D designs
var sheetNodes = viewerApp.bubble.search(av.BubbleNode.SHEET_NODE); // 2D designs
var allNodes = modelNodes.concat(sheetNodes);
if (allNodes.length) {
viewerApp.selectItem(allNodes[0].data);
if (allNodes.length === 1){
alert('This tutorial works best with documents with more than one viewable!');
}
} else {
alert('There are no viewables for the provided URN!');
}
}
</script>
</body>
</html>
Notice that there is no need to include any CSS file that was previously included in Basic Viewer tutorial.
Remember to update values for '<YOUR_APPLICATION_TOKEN>'
and 'urn:<YOUR_URN_ID>'
before testing the HTML.
Step 2: Load another model or sheet from Document
In the above code, we are calling viewerApp.selectItem()
to load one of the
viewables within the array var allNodes
. Developers then want to provide a mechanism through some UI elements for users to load a different viewable from var allNodes
.
In this case we’ll add 2 buttons to navigate to the next and previous viewables. We’ll begin by adding the buttons to the HTML, just after our 3D canvas div.
<div id="MyViewerDiv"></div>
<button id="MyPrevButton" class="MyNavButton">Previous</button>
<button id="MyNextButton" class="MyNavButton">Next</button>
Add the following css on the header for a nicer look and feel.
<style>
.MyNavButton {
position: absolute;
top: 0;
z-index: 1;
padding: 1em;
font-size: 1em;
cursor: pointer;
}
#MyPrevButton {
left: 0;
}
#MyNextButton {
right: 0;
}
</style>
Next we need to enhance method onDocumentLoaded(lmvDoc)
.
var currentIndex = 0; // New variable to keep track of loaded viewable.
function onDocumentLoaded(lmvDoc) {
var modelNodes = viewerApp.bubble.search(av.BubbleNode.MODEL_NODE); // 3D designs
var sheetNodes = viewerApp.bubble.search(av.BubbleNode.SHEET_NODE); // 2D designs
var allNodes = modelNodes.concat(sheetNodes);
if (allNodes.length) {
viewerApp.selectItem(allNodes[0].data);
}
createCustomUI(allNodes); // Create UI for Prev and Next buttons
}
function createCustomUI(allNodes) {
// Previous button
var prevBtn = document.getElementById('MyPrevButton');
prevBtn.addEventListener('click', function(){
currentIndex = currentIndex === 0 ? (allNodes.length -1) : (currentIndex - 1);
loadModelCurrentIndex(allNodes);
});
// Next button
var nextBtn = document.getElementById('MyNextButton');
nextBtn.addEventListener('click', function(){
currentIndex = (currentIndex + 1) % allNodes.length;
loadModelCurrentIndex(allNodes);
});
}
function loadModelCurrentIndex(allNodes) {
viewerApp.selectItem(allNodes[currentIndex].data);
}
Refresh your app and try them out!