In LuciadRIA , you can draw a 3D object as a mesh.

A mesh is a collection of three-dimensional points, lines, and surfaces that describe the shape of the object. You can paint all 3D icons using a FeaturePainter in combination with an Icon3DStyle.

To style a feature as a 3D icon mesh, you call drawIcon3D on a view/style/GeoCanvas instance. The FeaturePainter paint methods pass the GeoCanvas instance as the first parameter.

Program: Draw a 3D icon using a FeaturePainter
const painter = new FeaturePainter();
painter.paintBody = function(geoCanvas, feature, shape, map, layer, state) {
  const icon3DStyle = {
    meshUrl: "car.gltf"
  geoCanvas.drawIcon3D(shape, icon3DStyle);

Icon3DStyle gives you access to all kinds of options, but the one thing you must specify is the mesh to paint. You can do so by:

  • Specifying a URL to a GL Transmission Format (glTF) file with meshUrl. glTF files are JSON files that describe the structure and composition of a scene containing 3D models. In this case, they describe the meshes of the 3D objects that appear in the scene.

    meshUrl must point to a glTF file. You can specify all kinds of mesh properties in the glTF format.

  • Creating a simple configurable mesh using Simple3DMeshFactory.

  • Creating a mesh at the triangle level with MeshFactory:

    Program: Configuring the style with a mesh created by Simple3DMeshFactory (from samples/icons3d/StyleFactory.js)
    return {
      mesh: create3DDome(1000, 50),
      color: "rgba(255, 55, 55, 0.5)",
      rotation: {
        y: 180
      legacyAxis: false

    If you want more control over the creation of the meshes, you can use MeshFactory. You can use it to specify the individual triangles:

    Program: Using MeshFactory
    //Use the factory method from the MeshFactory module to create a 3D mesh
    const mesh = create3DMesh([
      0, 0, 0, // Vertex 0
      100000, 0, 100000, // Vertex 1
      0, 0, 100000  // Vertex 2
    ], [
      0, 1, 2  // Triangle 0-1-2
    const featurePainter = new FeaturePainter();
    featurePainter.paintBody = function(geocanvas, feature, shape, layer, map, paintState) {
      geocanvas.drawIcon3D(shape, {mesh: mesh});
    return featurePainter;

Placing a mesh in the world

In LuciadRIA 2023.1 the axis directions were changed, for compatibility reasons Icon3DStyle.legacyAxis was added. In LuciadRIA 2023.1 this by default is on, as a result the X-axis points to the north, the Y-axis to the west and the Z-axis points upwards. Starting from LuciadRIA 2024.0 the default value for Icon3DStyle.legacyAxis will be false, and the axis will be oriented as described in this article. It is already recommended to disable Icon3DStyle.legacyAxis in your application. When the legacy axes are disabled, glTF models will also be converted from their Y-up axis system to the axes LuciadRIA uses, making it simpler for you to rotate the model as needed.

The vertices of a mesh aren’t georeferenced. Instead, LuciadRIA assumes that the mesh is modeled in its own local coordinate system.

Figure 1. A 3D icon with its local coordinate system with transparency turned off. (X-Axis=red, Y-axis=green and Z-axis=blue)

As you can see in our example the plane has a hole where the cockpit should be. This is because this mesh uses transparent textures. To ensure that transparent textures are well handled you should set the transparency flag of the Icon3DStyle to true.

Figure 2. A 3D icon with its local coordinate system with transparency turned on
  • Transparency is disabled by default. Enabling transparency uses more resources and might result in lower performance.

  • LuciadRIA also assumes that the unit of measure is meters. If that’s not the case, you can use the scale factor of the style to scale to a different unit.

LuciadRIA places the mesh in the world at the location of the feature that you are styling, as in Figure 3, “Three icons, sharing the same mesh, placed in the world”. They share the same style so their orientation is the same. They all point in a certain direction. It depends on both the local and global coordinate system what direction that is.

Figure 3. Three icons, sharing the same mesh, placed in the world

Rotating the icon in its local reference

If you want to change the direction of an icon, you can specify a rotation property on Icon3DStyle. As a result, LuciadRIA applies the specified rotation to the mesh in its local coordinate system before it places it in the world. For example Figure 4, “A rotated 3D icon in its local coordinate system (X-Axis=red, Y-axis=green and Z-axis=blue).” shows a rotated version of the mesh in Figure 2, “A 3D icon with its local coordinate system with transparency turned on”.

Figure 4. A rotated 3D icon in its local coordinate system (X-Axis=red, Y-axis=green and Z-axis=blue).
Figure 5. Rotated icons placed in the world

Orienting the icon in the world

Technically, the options described so far are enough to place, scale, and orient your 3D icon in every possible way, but they are not always practical. For example, if you want to point all three icons in Figure 5, “Rotated icons placed in the world” to the north, you must calculate the correct rotation for each position. You need to do that because the appropriate amount of rotation for each icon depends on its location on the earth.

To make 3D icon positioning easier, LuciadRIA doesn’t just use the combination of the axes of the local and global coordinate system to place icons in the world. You can first orient each icon, using the orientation property of Icon3DStyle. You can set heading, pitch, and roll values. If you don’t specify any orientation, or if it has value 0, LuciadRIA applies a default orientation.

In the default orientation, the y-axis of the mesh points north, the x-axis points east, and the z-axis points up, perpendicular to the surface of the earth at that location.

If you want the icons to face east for example, you specify a heading of 90 degrees. LuciadRIA calculates the heading clockwise from the north.

Figure 6, “Three icons sharing the default orientation” shows the result of specifying the default orientation for the icons of Figure 5, “Rotated icons placed in the world”.

Figure 6. Three icons sharing the default orientation

Program: Configuring the style for a 3D icon shows, among other things, the usage of the orientation property.

Program: Configuring the style for a 3D icon (from samples/icons3d/StyleFactory.js)
return {
  meshUrl: "/sampledata/gltf/goldengate/golden_gate.gltf",
  rotation: {
    x: 0,
    y: 0,
    z: 180
  orientation: {
    heading: 0
  scale: {
    x: 1,
    y: 1,
    z: 1
  legacyAxis: false

Once you have defined the rotation of a mesh for one icon, other icons using the same mesh orient themselves with the orientation property. You don’t need to change the rotation for other instances.

See the API documentation of Icon3DStyle, Simple3DMeshFactory and MeshFactory for more detail.

Taking advantage of physically based materials

Some glTF files contain material parameters that you can use for physically based rendering (PBR) of 3D models. Such PBR shading looks more realistic than the default diffuse shading. See the glTF specification for more information about the material parameters for PBR.

To use this type of shading, configure the PBRSettings of your style. Note that you must configure the map with a light to make PBRSettings take effect.

You can control the PBR shading look with these properties:

  • directionalLighting: indicates whether to shade the model with the lighting of the map.

  • lightIntensity: increase or decrease the effect of the lighting on the model.

  • imageBasedLighting: indicates whether to shade the model with the reflection map.

  • material: configures scale factors for the PBR shading model. You can change the metalness and roughness of the material.

You can also style glTF files that don’t have PBR data with PBRSettings. You can use its PBRMaterial property to configure some uniform material properties.

See the API documentation of PBRSettings and EnvironmentMapEffect for more details.

Using videos as textures

If you create your mesh using the MeshFactory, you can decide to use a HTMLVideoElement as a texture. The first step is to load the video.

Program: Loading the video as a HTMLVideoElement
function loadVideo(videoUrl: string): HTMLVideoElement {
  const video = document.createElement("video");
  video.autoplay = true;
  video.muted = true;
  video.loop = true;
  video.src = videoUrl;;
  return video;

You can also add controls so that users can manage the playback of the video displayed on the mesh. For example, you can let users pause and unpause the video by pressing the letter p on the keyboard.

Program: Controlling the video playback
export function addPlaybackControl(video: HTMLVideoElement) {
  document.addEventListener("keypress", (a) => {
    if (a.key === "p") {
      if (video.paused) {;
      } else {

Then, when you create the mesh, use the video as the texture by passing it into the image field of the MeshCreateOptions.

Program: displaying a video on a square surface
const video = loadVideo(pathToVideo);
const positions = [
  0, 0, 1, // vertex 0
  0, 1, 0, // vertex 1
  0, 0, 0, // vertex 2
  0, 1, 1  // vertex 3
const indices = [
  0, 1, 2, // triangle 0-1-2
  0, 3, 1, // triangle 0-3-1
const texCoords = [
  0, 0, // mapped to vertex 0
  1, 1, // mapped to vertex 1
  0, 1, // mapped to vertex 2
  1, 0, // mapped to vertex 3
return create3DMesh(positions, indices, {
  image: video,
  texCoords: texCoords
  • If your video contains transparency, set the transparency property to true on the Icon3DStyle.

  • If you want to mimic a screen effect, as if the surface is emitting the video, we recommend that you set the lightIntensity option on the PBRSettings to 1.0. This setting brightens the part of the mesh not facing the sun in the scene.