The latest release of our viewer has now support for multiple languages. So without letting you wait any longer, here is the list of supported languages and how to initialise the viewer with a different language:
Chinese Simplified: zh-cn
Chinese Traditional: zh-tw
Czech: cs
English: en
French: fr
German: de
Italian: it
Japanese: ja
Korean: ko
Polish: pl
Portuguese Brazil: pt-br
Russian: ru
Spanish: es
Turkish: tr
All it takes to switch language is basically setting the "language" property in options prior calling Autodesk.Viewing.Initializer and also make sure you are referencing API version 1.2.19 (or higher if you read that post in 2030). I wish my english lessons at school had been so easy...Et voila!
This is essentially a slick browser based IDE which allows you to easily deploy your project directly on the cloud, so you don't need to worry where you are going to host it - at least for quick prototyping and testing purpose.
When creating new a project - named as workspace - you can use a predefined template or clone an existing repository from github or bitbucket and this is very straightforward.
In order to test that feature, I created a minimalistic boilerplate for the View & Data API project based on node.js :
view.and.data-boilerplate
Using that as template in Cloud9, I can literally deploy a viewer webapp on the cloud in couple of minutes, while at the same time being able to tweak it to my will. Hard to beat to be honest!
2. Once you've got your keys, upload a test model. You can use one of our tool to do that without setup: http://models.autodesk.io
3. Sign up for a Cloud9 account if you haven't done it already, then create a new workspace using my boilerplate as template:
4. Fire "npm install" from Cloud9 command line:
5. In www/js/viewer.js replace the var urn = '...' with the urn of your model from step 2
6. Create environment variables CONSUMERKEY and CONSUMERSECRET using your API keys from step 1
7. Start the Cloud9 project... you've got a running cloud viewer!
Who doesn't like to watch giant robots fighting...?! Couple days ago, I was asked if I could help Megabots Guys to create a demo using View & Data to embed in their website. They provided me the 3D model and I put together some code to match their need:
They wanted to have their robot spinning on itself, while at the same time letting the user orbit manually if needed. When user starts interacting with the model, the rotation stops so it doesn't interfere. If no interaction occurs within 5 seconds, a default camera state is restored and the model starts spinning again. Lastly, when zooming in or out, the target remains focused on the centre of the model.
Here is the final result and the complete code packed into an extension. Also the Megabots page where they embedded my sample.
It's been a while I wanted to play with TypeScript as the technology seems to get a descent traction from the web development community. So here it is! You can check it out on the following github repository: TypeView
It's live version can be tested here. Don't expect anything crazy yet, it's simply loading a predefined model.
Big thanks to Jan Liska, my colleague from Autodesk Consulting, who put together the initial version of that sample. The View & Data client side API is rather big already so Jan created the type definition file only for the few methods that were needed in that specific sample. Hopefully over the time we will make that file bigger adding some more declarations to it. You could also use it as a boiler plate if you decide to start a Node.js + View & Data project or simply experiment on TypeScript as I did.
I am using WebStorm for my web projects which has some level of TypeScript support. However I find the file watcher approach not really flexible, without saying that it won't help you if you are not using WebStorm obviously. So I added to the project a gulp file that will automatically handle the transpiling of .ts files. All you have to do to build the project is a "npm install" followed by "gulp" commands. The gulp task also generates the sourcemaps, so it looks better in my WebStorm IDE: it will automatically gather generated files .js and .js.map files under the .ts so it reduces the mess:
After running command npm install for the required node modules, running gulp or gulp ts-build should automatically build the project, ready to run through your IDE or using start server.js from the app directory. Run ts-clean to remove all typescript generated files (.js and .js.map):
Pretty much everything is said in the title, so I'm not going to expand in explanations... I grabbed the snapping tool which has been implemented by our development team to create the measure command (you can find this code under viewer3D.js which is shipped with View & Data API, see Autodesk.Viewing.Extensions.Measure.Snapper) and tweaked it a little adding some custom methods, properties and events in order to turn it into a tool that can be used to snap THREE.js geometry as the user is hovering the mouse over the meshes and fire events.
The extension shows how you can filter weather the snapped geometry is a vertex, and edge or a face and also implement commands that will prompt the user to graphically pick some of that geometry on the model. Quite useful for those who want to leverage View & Data API to implement more complex commands where selection is involved.
Below is the code for the Geometry Selector extension but most of the logic is implemented in the Autodesk.ADN.Viewing.Tool.Snapper.js (attached in the zip):
Here is the third part of my Java EE series and this week it's time to tackle how to create a REST API using a proper framework. There are many existing Java frameworks that would help you achieve that, some of the most popular are Spark, RESTEasy, RESTX or Jersey. As I have no experience with any of them and no specific constraint, I picked up Jersey which seems to have a good API, descent support from the community and it's an implementation of the JAX-RS reference. So let's get started!
If you missed the two previous chapters, here are the links as I'm reusing the same project rather than starting one from scratch:
The first thing I'm going to do is to convert my project to a Maven project (obviously a prerequisite is to have Maven installed): Right-click your Eclipse project > Configure > Convert to Maven Project
This will generate a pom.xml file at the project root. Just copy all the Jersey dependencies (<dependencies> section) similarly to my pom.xml:
Then Right-click your Eclipse project > Maven > Update Project ... This should download locally all the Jersey dependencies defined in the pom.xml, sweet! ...
Make also sure those dependencies are part of the deployment package:
Right-click project > Properties > Java Build Path > Order and Export > Check Maven Dependencies
Then still from Properties > Deployment Assembly > Java Build Path Entries > Maven Dependencies
My Deployment package looks as follow:
Next we need to define a Servlet mapping, this is done by editing the web.xml. If you created a dynamic web project with module version 3.0, there are chances you don't have a web.xml, so Right-click project > Java EE Tools > Generate Deployment Descriptor Stub
From there we define a url mapping that will be redirected to the Jersey server and also a provider package: in my case all the classes part of "com.autodesk.adn.viewanddata.api" package will be considered as resource for the Jersey server.
The last step is the implementation of the API itself and if you take a look at Part II, you will notice that it is significantly more straightforward when using Jersey as it handles parsing of the url and parameters nicely:
You can then run your project from Eclipse which should launch the Tomcat server and let you test the API using a Rest client as described in Part II.
The implementation still relies on a clunky hashmap, so next time I will take a look at how to use a mongoDB connector so it will fetch the records from a real database... stay tuned!
Accessing the bounding box is a pretty straightforward thing to do from a standard Three.js model:
var bb = mesh.geometry.boundingBox
However optimisations in our custom renderer makes it a little bit less intuitive. Below are two snippets that illustrate how to compute bounding box for the selected component. Typically when you select a component visually, the Autodesk.Viewing.SELECTION_CHANGED_EVENT returns an object that contains the list of fragmentIds, which you need to pass to those methods (see full sample further down):
1 //returns bounding box as it appears in the viewer
2 // (transformations could be applied)
3 function getModifiedWorldBoundingBox(fragIds, fragList) {
4
5 var fragbBox = new THREE.Box3();
6 var nodebBox = new THREE.Box3();
7
8 fragIds.forEach(function(fragId) {
9
10 fragList.getWorldBounds(fragId, fragbBox);
11 nodebBox.union(fragbBox);
12 });
13
14 return nodebBox;
15 }
16
17 // Returns bounding box as loaded in the file
18 // (no explosion nor transformation)
19 function getOriginalWorldBoundingBox(fragIds, fragList) {
20
21 var fragBoundingBox = new THREE.Box3();
22 var nodeBoundingBox = new THREE.Box3();
23
24 var fragmentBoxes = fragList.boxes;
25
26 fragIds.forEach(function(fragId) {
27
28 var boffset = fragId * 6;
29
30 fragBoundingBox.min.x = fragmentBoxes[boffset];
31 fragBoundingBox.min.y = fragmentBoxes[boffset+1];
32 fragBoundingBox.min.z = fragmentBoxes[boffset+2];
33 fragBoundingBox.max.x = fragmentBoxes[boffset+3];
34 fragBoundingBox.max.y = fragmentBoxes[boffset+4];
35 fragBoundingBox.max.z = fragmentBoxes[boffset+5];
36
37 nodeBoundingBox.union(fragBoundingBox);
38 });
39
40 return nodeBoundingBox;
41 }
Here is the result of the extension code provided below. You can test a live version here:
Here is a little code snippet that you may find helpful. In a recent thread, we were helping a developer who wanted to select a component in the viewer as soon as the model was loaded using viewer.select([nodeId])
It appears that you need to wait for two different events if you want to make sure the selection is going to work reliably:
Autodesk.Viewing.GEOMETRY_LOADED_EVENT
Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT
However there is no guarantee about which event will be fired first upon loading a model. So we could come up with various code constructs that allow to wait for both events but since this is a fairly standard situation in JavaScript asynchronous programming, it's nice to come up with a more generic solution.
I'm using here async, a very handy library that you can install using "bower install async" for example (available also for node npm install async) and which expose many useful methods to wait or delay execution of custom code, see the doc for more details, you'll probably need it at some point if you write enough JavaScript ;)
Here we go, the following sample illustrates how to wait for both viewer events and parse the model structure once they have been fired. As you can tell, it will be very straightforward to add some more events or change them if needed:
Here is the second part of my Java EE tutorial series. Last week I took a look at setting up a Tomcat server and creating my first project with Eclipse. If you missed it, take a look here and make sure you are familiar with the basics.
The next step would be to create a RESTful API so our future web application will be able to easily talk to our backend. I want to start from the scratch to understand the challenges, so instead of using one of the many existing REST frameworks, I decided to implement myself a REST API using a simple servlet like the one I created last week.
I'm going to create an API that will perform the following tasks and look as follow:
- Get the list of models from our database or get a specific model based on its id:
GET /models
GET /models/{id}
- Add a new model to the database:
POST /models
- Update an existing model in database:
PUT /model/{id}
- Remove an existing model from the database:
DELETE /models/{id}
A model will be a very basic structure containing info to load into the viewer:
So let's get started...
1/ First step is to add a new servlet to our project: right-click your project > New > Servlet. I name it "Models". By default the servlet will be mapped to the following url: /AppName/Models. We need to allow more routes to be redirected to that servlet, so edit the @WebServlet decorator as follow, so every request starting with /model will be redirected to that servlet:
2/ My REST API is going to use JSON as data format, there are some built-in classes to handle serialization and deserialization, however I'm familiar with a powerful library from Google: GSON which I used previously in some Android projects. So download the .jar from there and add it to your project: Right-click > Properties > Java Build Path > Add External Jars > browser to gson.jar.
Once you've done that, there is another trick: as your project now depends on a third party library, you need to include that in your deployment package, so it will be available when it runs on the server. No worries: Still from project's Properties > Deployment Assembly > Add > Java Build Path Entries > select gson.jar > done!
3/ Implement doGet, doPut, doPost and doDelete methods in your servlet. In the example below I'm using a simple hashmap to "simulate" a backend database, so it can keep track of my records, this is obviously not a real world scenario but it serves the purpose of that tutorial.
Here is a very naive implementation of my API:
4/ Finally run the servlet from Eclipse and you can use a tool like Postman to test your API:
To wrap it up: we have a working RESTful API run by a servlet, however I found it pretty laborious to parse the url to extract parameters like the model Id although it's very basic.
If we have to add some more complexity to it, let's say for example GET /models/{id}/components/{nodeId}/material to find material attached to a specific component of a specific model, then it will quickly become very messy.
Next week I will pick up a popular REST framework and take a look how much easier it can be to achieve the same, stay tuned ...
Here is sample code I meant to produce for a while: providing a slick and easy way to visually drag components around in 3d inside our View & Data API viewer... well here is it at last!
Based on the Three.js transform control, the development team implemented the section command using this nice 3d manipulator allowing to select plane, axis and rotation. This is provided out of the box with the GuiViewer3D:
I reused some of their implementation to provide the ability to stick the control on the selected mesh and drag it around. It's more challenging that it may seem as the viewer has a complex way to work with Three.js meshes. This is due to enhancements that we created on top of that library to be able to support models with a huge number of meshes.
Here is a picture of the control in action, you can also click here to try a live sample...