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/6.*/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/6.*/viewer3D.min.js"></script>
<!-- Developer JS -->
<script>
var viewer;
var options = {
env: 'AutodeskProduction',
accessToken: '<YOUR_APPLICATION_TOKEN>',
api: 'derivativeV2' // for models uploaded to EMEA change this option to 'derivativeV2_EU'
};
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 geometries.
var geometries = doc.getRoot().search({'type':'geometry'});
if (geometries.length === 0) {
console.error('Document contains no geometries.');
return;
}
// Choose any of the avialable geometries
var initGeom = geometries[0];
// Create Viewer instance
var viewerDiv = document.getElementById('MyViewerDiv');
var config = {
extensions: initGeom.extensions() || []
};
viewer = new Autodesk.Viewing.Private.GuiViewer3D(viewerDiv, config);
// Load the chosen geometry
var svfUrl = doc.getViewablePath(initGeom);
var modelOptions = {
sharedPropertyDbPath: doc.getPropertyDbPath()
};
viewer.start(svfUrl, modelOptions, onLoadModelSuccess, onLoadModelError);
}
/**
* Autodesk.Viewing.Document.load() failure 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:
.
EMEA Data Center Support
Note that in some cases you may wish to determine if the bucketKey is from EMEA or US.
Start by base64 decoding the URN, then check if it contains urn:adsk.wipemea:xxx
(for EMEA) or urn:adsk.wipprod:xxx
(for US).
Read more in the blog about European data center support.
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 geometries 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 geometries;
var indexGeom;
Modify function onDocumentLoadSuccess()
to include these 2 lines at the end:
/**
* Autodesk.Viewing.Document.load() success callback.
* Proceeds with model initialization.
*/
function onDocumentLoadSuccess(doc) {
//
// Same as before
//
// Choose any of the available geometry.
indexGeom = 0;
lmvDoc = doc;
}
And then replace the click callback function loadNextModel()
with:
function loadNextModel() {
// Next geometry index. Loop back to 0 when overflown.
indexGeom = (indexGeom + 1) % geometries.length;
var nextGeom = geometries[indexGeom];
viewer.tearDown();
var config = {
extensions: nextGeom.extensions() || []
};
viewer.setUp(config);
var svfUrl = lmvDoc.getViewablePath(nextGeom);
var loaderOptions = {
sharedPropertyDbPath: lmvDoc.getPropertyDbPath()
};
viewer.loadModel(svfUrl, loaderOptions, onLoadModelSuccess, onLoadModelError);
}
Try it out!
There are some changes here that need to be highlighted:
viewer.start() vs viewer.load()
The initial model load uses viewer.start()
but any other model loaded after uses viewer.loadModel()
.
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
.