Basic Viewer
This tutorial takes you through the steps required to load a model translated using the Model Derivative API. It will then showcase how to change and load a different model into the same Viewer instance.
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 Prepared a File for the Viewer.
Your translated design file is identified with the URN of the seed and will look something like dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXktYnVja2V0L215LWF3ZXNvbWUtZm9yZ2UtZmlsZS5ydnQ=
.
The Viewer requires that the URN is encoded as an unpadded Base64 string. Meaning, without the trailing =
characters.
Base64 | dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXktYnVja2V0L215LWF3ZXNvbWUtZm9yZ2UtZmlsZS5ydnQ= |
Unpadded Base64 | dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXktYnVja2V0L215LWF3ZXNvbWUtZm9yZ2UtZmlsZS5ydnQ |
See the Prepare a File for the Viewer tutorial in the Model Derivative API documentation for more information on Base64 encoding.
Step 1: Prepare your HTML
Begin by creating a file named index.html
and copy the following content into it:
<head>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<meta charset="utf-8">
<!-- The Viewer CSS -->
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/2.*/style.min.css" type="text/css">
<!-- Developer CSS -->
<style>
body {
margin: 0;
}
#MyViewerDiv {
width: 100%;
height: 100%;
margin: 0;
background-color: #F0F8FF;
}
</style>
</head>
<body>
<!-- The Viewer will be instantiated here -->
<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 viewer;
var options = {
env: 'AutodeskProduction',
accessToken: '<YOUR_APPLICATION_TOKEN>'
};
var documentId = 'urn:<YOUR_URN_ID>';
Autodesk.Viewing.Initializer(options, function onInitialized(){
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
});
/**
* Autodesk.Viewing.Document.load() success callback.
* Proceeds with model initialization.
*/
function onDocumentLoadSuccess(doc) {
// A document contains references to 3D and 2D viewables.
var viewables = Autodesk.Viewing.Document.getSubItemsWithProperties(doc.getRootItem(), {'type':'geometry'}, true);
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Choose any of the avialble viewables
var initialViewable = viewables[0];
var svfUrl = doc.getViewablePath(initialViewable);
var modelOptions = {
sharedPropertyDbPath: doc.getPropertyDbPath()
};
var viewerDiv = document.getElementById('MyViewerDiv');
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv);
viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}
/**
* Autodesk.Viewing.Document.load() failuire callback.
*/
function onDocumentLoadFailure(viewerErrorCode) {
console.error('onDocumentLoadFailure() - errorCode:' + viewerErrorCode);
}
/**
* viewer.loadModel() success callback.
* Invoked after the model's SVF has been initially loaded.
* It may trigger before any geometry has been downloaded and displayed on-screen.
*/
function onLoadModelSuccess(model) {
console.log('onLoadModelSuccess()!');
console.log('Validate model loaded: ' + (viewer.model === model));
console.log(model);
}
/**
* viewer.loadModel() failure callback.
* Invoked when there's an error fetching the SVF file.
*/
function onLoadModelError(viewerErrorCode) {
console.error('onLoadModelError() - errorCode:' + viewerErrorCode);
}
</script>
</body>
Before the above code can be used, two strings need to be updated with a valid access token and the seed URN.
accessToken: '<YOUR_APPLICATION_TOKEN>'
var documentId = 'urn:<YOUR_URN_ID>';
Important: The value assigned to variable documentId
must be prefixed with string urn:
.
Error Codes
The above code contains a few error handling callback functions. These functions receive a viewerErrorCode
integer argument.
Find the meaning of the errorCodes below:
av.ErrorCodes = { /** An unknown failure has occurred. */ UNKNOWN_FAILURE: 1, /** Bad data (corrupted or malformed) was encountered. */ BAD_DATA: 2, /** A network failure was encountered. */ NETWORK_FAILURE: 3, /** Access was denied to a network resource (HTTP 403) */ NETWORK_ACCESS_DENIED: 4, /** A network resource could not be found (HTTP 404) */ NETWORK_FILE_NOT_FOUND: 5, /** A server error was returned when accessing a network resource (HTTP 5xx) */ NETWORK_SERVER_ERROR: 6, /** An unhandled response code was returned when accessing a network resource (HTTP 'everything else') */ NETWORK_UNHANDLED_RESPONSE_CODE: 7, /** Browser error: webGL is not supported by the current browser */ BROWSER_WEBGL_NOT_SUPPORTED: 8, /** There is nothing viewable in the fetched document */ BAD_DATA_NO_VIEWABLE_CONTENT: 9, /** Browser error: webGL is supported, but not enabled */ BROWSER_WEBGL_DISABLED: 10, /** There is no geomtry in loaded model */ BAD_DATA_MODEL_IS_EMPTY: 11, /** Collaboration server error */ RTC_ERROR: 12 };
Show More
Step 2: Token expiration
In step 1 we are providing an access token to grant read-access the model data. A limitation in this approach is that access tokens expire, making further data-read operations unauthorized.
To overcome this limitation, developers can (and should) provide a function that can fetch a new access token. The Viewer invokes this function and fetches a new access token before the current one expires.
Developers can provide the callback function as follows:
var options = {
env: 'AutodeskProduction',
getAccessToken: function(onGetAccessToken) {
//
// TODO: Replace static access token string below with call to fetch new token from your backend
// Both values are provided by Forge's Authentication (OAuth) API.
//
// Example Forge's Authentication (OAuth) API return value:
// {
// "access_token": "<YOUR_APPLICATION_TOKEN>",
// "token_type": "Bearer",
// "expires_in": 86400
// }
//
var accessToken = '<YOUR_APPLICATION_TOKEN>';
var expireTimeSeconds = 86400;
onGetAccessToken(accessToken, expireTimeSeconds);
}
}
Notice that the function is being added to the already existing options
object, replacing the accessToken
attribute.
Congratulations! You have the basic skeleton required to access and render 2D and 3D design data from supported browsers.
Step 3: Change model in Viewer
In the function onDocumentLoadSuccess()
, a decision was made to load one of the available viewables from the Document
object.
This step will showcase how to switch the model for another one from the same Document
object.
Start by adding a <button>
right after MyViewerDiv
.
<div id="MyViewerDiv"></div>
<button id="MyNextButton" onClick="loadNextModel()">Next!</button>
Add button style:
<style>
#MyNextButton {
position: absolute;
top: 5px;
left: 5px;
z-index: 1;
font-size: 40px;
cursor: pointer;
}
</style>
Add the button’s onClick
callback function:
/**
* Callback function for "Next" button.
* Attempts to load the next from the document. If there is only one model, then the
* current model gets unloaded and then is loaded again.
*/
function loadNextModel() {
console.log('TODO: Load Next Model');
}
Before loadNextModel()
is coded, we need to perform some changes to the initial HTML code.
Start by adding three global variables right after var viewer
:
var viewer;
var lmvDoc;
var viewables;
var indexViewable;
Replace the onDocumentLoadSuccess()
function with:
/**
* Autodesk.Viewing.Document.load() success callback.
* Proceeds with model initialization.
*/
function onDocumentLoadSuccess(doc) {
// A document contains references to 3D and 2D viewables.
viewables = Autodesk.Viewing.Document.getSubItemsWithProperties(doc.getRootItem(), {'type':'geometry'}, true);
if (viewables.length === 0) {
console.error('Document contains no viewables.');
return;
}
// Create Viewer instance and load model.
var viewerDiv = document.getElementById('MyViewerDiv');
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv);
var errorCode = viewer.start();
// Check for initialization errors.
if (errorCode) {
console.error('viewer.start() error - errorCode:' + errorCode);
return;
}
// Choose any of the available viewables.
indexViewable = 0;
lmvDoc = doc;
// Everything is set up, load the model.
loadModel();
}
Add a new function loadModel()
:
function loadModel() {
var initialViewable = viewables[indexViewable];
var svfUrl = lmvDoc.getViewablePath(initialViewable);
var modelOptions = {
sharedPropertyDbPath: lmvDoc.getPropertyDbPath()
};
viewer.loadModel(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}
And finally, replace the click callback function loadNextModel()
with:
function loadNextModel() {
viewer.tearDown();
viewer.setUp(viewer.config);
// Next viewable index. Loop back to 0 when overflown.
indexViewable = (indexViewable + 1) % viewables.length;
loadModel();
}
Try it out!
There are some changes here that need to be highlighted:
viewer.start() vs viewer.load()
The initial implementation was using viewer.start()
to load the model. Later, when loadNextModel()
got added, the implementation changed, and viewer.loadModel()
was used instead.
The reason is that viewer.start()
has dual purposes:
1. It initializes the Viewer instance and checks whether WebGL is enabled in the current context.
2. Optional: It may receive an argument to pass into viewer.loadModel()
.
viewer.start()
only needs to be invoked once. Therefore, when the Viewer is used in a scenario where different models
are to be loaded in sequence, it’s best to separate those two calls.
viewer.tearDown() and viewer.setUp()
These two methods are essential when changing the model currently loaded into the viewer.
Fun fact: When viewer.loadModel()
is called a second time without first invoking viewer.tearDown()
and viewer.setUp()
,
then the model will get added to the scene! This behavior is part of the Viewer’s multi-model feature support.
Multi-model scenarios are not yet fully supported by the default UI. We suggest using the Headless Viewer instead.
Viewer Version
The example code provided here is fetching the latest Viewer version available. We recommend that before your application is published to your customers, a specific version of the Viewer’s codebase gets requested.
More information on API Basics, section Getting the Code.
What’s Next?
Much of the code presented here is boilerplate, required to get things up and running.
Some of it can be simplified by using the ViewingApplication
object.
Take a look at the Basic Application Tutorial to see how it’s used.
Then check the Field Guide for more information on the benefits of using a ViewingApplication
.