3D models

Add a 3D model of your robot or machine to a Grid dashboard, and make its parts move with your application’s parameters.

The Grid 3D widget shows a 3D model, a digital twin, of your robot or machine, and moves its parts with live parameters from your application: a joint turns when the real joint turns. You build the model in Blender or a CAD tool, export it as a glTF file, and write a small JSON file that links each moving part to a parameter.

The video shows the whole workflow, from creating a model to linking it to an application:

Prepare the model

The widget moves an object by setting its position, rotation or scale relative to its parent. So how you build the model decides how it moves:

Base z Link1 Link2 Link1 origin at joint 1 rotates about its own z: axis "rz" Link2 origin at joint 2 the pivot, not the centre Parent chain in Blender Base Link1 Link2 A child moves with its parent: turn Link1 and Link2 turns along with it.
  1. Build with z up. The camera treats z as up. In Blender’s glTF exporter, switch +Y Up off under Transform.
  2. Put each moving part’s origin at its pivot. In Blender, place the 3D cursor on the joint and use Object > Set Origin > Origin to 3D Cursor.
  3. Parent the parts in a chain, base to tool, so each part moves with the one before it.
  4. Apply rotation and scale to every moving part (Object > Apply > Rotation & Scale), so its local axes line up with the joint.
  5. Give moving parts simple names, without spaces, dots or other special characters. See the name rules below.
  6. Export as .glb, the binary format. Tick Apply Modifiers under Geometry if you use modifiers. Animations you made in Blender are exported with the model and can be played in the dashboard.

Keep the file small, so the dashboard loads quickly on a tablet: the stock robot models are 4 to 8 MB. There is no hard limit. Draco and Meshopt compression make files smaller and are supported. A .gltf text file also works, as long as its buffers and textures are embedded in it.

How object names change on import

The importer changes some characters in object names. Use the changed name in the JSON file:

In Blender In the widget Example
A space Becomes _ Upper Arm becomes Upper_Arm
. : / [ ] Removed Cube.001 becomes Cube001
The same name twice in one file _1, _2 and so on are added The second Link becomes Link_1

To see the final names, type grid3d.tree() in the browser console. See Inspect the scene in the browser console.

Write the model JSON

The JSON file tells the widget which model files to load and which parameter moves which object. The paths of model files are relative to the JSON file. Name the file <name>.3d.json, for example robot.3d.json: the file editor in the portal then checks it against the schema and suggests the keys as you type.

This minimal file loads one model and turns the object Link1 with the first joint position:

{
  "files": [
    { "file": "robot.glb" }
  ],
  "objects": [
    {
      "name": "Link1",
      "links": [
        { "link": "root/ManipulatorControl/jointPositionsActual", "channel": 0, "axis": "rz" }
      ]
    }
  ]
}

Each entry in links takes one value from a parameter and applies it to the object. These are the fields you need most:

Field What it does Default
link The parameter path
channel The element of an array parameter. Ignored for a single value 0
axis What the value sets, see the next table
gain, offset The widget uses value × gain + offset 1, 0
value A fixed value, used while the parameter has no data, or always when there is no link
axis Sets Values read
x, y, z Position along one local axis, in m 1
rx, ry, rz Rotation about one local axis, in rad 1
position Position x, y, z 3, from channel on
rotationZYX Rotation z, y, x 3, from channel on
pose Position x, y, z, then rotation z, y, x: a pose such as a tool pose 6, from channel on
s, sx, sy, sz Scale: all axes, or one 1
visible Shows the object when the value is 0.5 or more 1
opacity Transparency, from 0 to 1 1

The widget does not convert units: positions are in metres and rotations in radians, relative to the object’s parent. For a parameter in degrees, use gain 0.0174533; for millimetres, 0.001. Colours, glow, any other property of an object, vector lengths and numbered objects are on Models and objects.

Example: the joints and tool frame of a 6-axis arm

Every joint is linked the same way, to its element of the joint positions. The tool frame follows the tool pose:

"objects": [
  {
    "name": "Link1",
    "castShadow": true,
    "links": [{ "axis": "rz", "link": "root/ManipulatorControl/jointPositionsActual", "channel": 0 }]
  },
  {
    "name": "Link2",
    "castShadow": true,
    "links": [{ "axis": "ry", "link": "root/ManipulatorControl/jointPositionsActual", "channel": 1 }]
  },
  {
    "name": "ToolPose",
    "links": [{ "axis": "pose", "link": "root/ManipulatorControl/manipulatorToolPoseActual" }]
  }
]

Older files put link, channel and axis directly on the object, for a single link. The widget still reads that form, but new files use links.

To find an object inside another one, separate the names with *: "Robot2*Link1" finds Link1 under Robot2. Use this when you load two models with the same object names. Give each one its own name in files, because duplicates across files are not renamed.

All options at a glance

The same file can do much more than move parts. Each topic has its own page:

Key What it does Details
files The model files to load, how often and where: one copy, several named copies, or thousands of copies drawn with GPU instancing Models and objects
objects Settings and links per object: shadows, and parameters that move, show, colour or change it Models and objects
animations Plays the animations in a model: by itself, on a trigger, at a linked speed, or at a linked position Animations
traceLine Draws the path of a point, such as the tool centre, coloured or thickened by a value such as the speed Trace lines
camera, cameraPosition, cameraLookAt Where you look from and at Camera, lights and rendering
hemisphereLight, spotLight The lights, and the shadows of the spot light Camera, lights and rendering
renderSettings Frame rate, lighting model, environment, background, ambient occlusion, bloom Camera, lights and rendering

Two rules apply to the whole file:

  • A key that ends in _ is ignored. JSON has no comments, so use this to keep an alternative setting: "spotLight_": { ... } switches the spot light off and keeps its settings, and a files entry with "file_" instead of "file" is not loaded.
  • Old keys keep working. Settings the schema marks as deprecated are still read, so files made for earlier versions show the same scene. The editor marks them, with what to use instead.

The schema

The schema describes every key, its type and its default: v-3d schema. The portal’s file editor uses it for files named *.3d.json. Other editors, such as Visual Studio Code, can use it through their JSON schema settings.

Add it to the dashboard

  1. Upload the model files and the JSON file to your Grid project, in the same folder or with matching relative paths.
  2. Add a 3D widget to your canvas.
  3. In its model property, select the JSON file.

The widget has two more settings: frequency divider, how often the parameters update (default 100), and disable view navigation, which locks the camera.

When something is wrong

While the widget loads the file, it checks it: files that are missing or are not a glTF model, objects that are not found, keys with a value it does not know. Errors and warnings show in a pop-up on the dashboard, and all of them, with the details, in the browser console (press F12). Each message names the place in the file, such as objects[3] or animations[0], and most say how to fix it. For an object that is not found, the message lists the names that come close.

Inspect the scene in the browser console

Open the browser console (F12) on the dashboard and type one of these:

Command Shows
grid3d.tree() The object hierarchy, names only, including empties: the names to use in objects and parent. grid3d.tree('Robot') shows only the branch under Robot
grid3d.find('Link1') The object with that name, to look at its position, rotation and materials
grid3d.animations() The animations each model can play, their length, and which entry plays them now. grid3d.animations('Conveyor') shows one model
grid3d.scene The whole scene of the 3D widget on screen
grid3d.views All 3D widgets on screen, by JSON file, each with its scene, camera and renderer

The console also shows the Grid version the dashboard runs, in the line that starts with grid version:.

Things to know

  • Materials are shared. opacity changes the object’s material, and every other object in the same file that uses that material changes with it. Give an object its own material before you make it transparent. Copies of a model from a name list or an instances list each get their own materials.
  • Shadows cost computing power. Objects cast no shadows unless you set castShadow or receiveShadow on them. Keep these to a few objects on slower devices.
  • Only changed values are applied, so a parameter that does not change does not cost anything. The scene is only drawn again when something changed, or while an animation plays.

Models and objects

Load model files, place copies of them, and link the objects in them to parameters: every option of files, objects and links.

Animations

Play the animations of a glTF model in a dashboard: by themselves, on a trigger from your application, at a linked speed, or at a linked position.

Trace lines

Draw the path of a moving point, such as a robot’s tool centre, and colour or thicken the line with a value such as the speed.

Camera, lights and rendering

Set where the 3D view looks from, how the scene is lit, and how it is rendered: environment, background, ambient occlusion, bloom and frame rate.

Generated environment

A room, hall, sky or colour gradient made by the widget itself.

Image environment

An environment from one equirectangular image, such as an HDR from an HDRI site or a Blender world texture.

Cube map environment

An environment from six square images, one per axis direction.