Skip to main content
Version: 4.1.4

3DVisualizr - Standalone JS library for 3D Digital Twin

3DVisualizr is designed to view and visualize factory data in a 3D format by connecting data sources to virtual assets. Virtual assets can display their state, by changing their color based on some predefined rules, and KPIs in a label that can be attached to each individual asset.

note

In the documentation, we mostly refer 3DVisualizr by its acronym 3DV. However, sometimes we can refer the library by its legacy name E3D.

Current Version

VersionDocumentation
4.1.6Documentation

Install

The 3d viewer can be installed as a dependency to a project using either yarn or npm. To do this you need to connect to a private registry which can be done as follows

  1. Connect to the private NPM registry for installing 3d viewer by creating .npmrc at the root of your project (next to package.json). The registry has npmjs.com as an upstream feed, meaning that you can still use regular packages from public sources. Add the following contents to .npmrc

    registry=https://pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/registry/
    always-auth=true
  2. The registry needs to be authenticated against

    • For Windows use vsts-npm-auth
    • For Others (unix) put the following in your user .npmrc (usually located located at ~/.npmrc) and replace both [BASE64_ENCODED_PERSONAL_ACCESS_TOKEN] with a token with read access to the registry
    ; begin auth token
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/registry/:username=elisasmartfactory
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/registry/:email=npm requires email to be set but doesn't use the value
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/:username=elisasmartfactory
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
    //pkgs.dev.azure.com/elisasmartfactory/_packaging/3dv/npm/:email=npm requires email to be set but doesn't use the value
    ; end auth token
  3. Finally install the 3d viewer as dependency

    yarn add e3d@latest
    npm install e3d@latest

    or @preview for stable development version

    or @next for latest development version

The package contains typings and can be easily imported into existing projects.

Getting started

Including the viewer to your project

Including e3d

In order to get started you need to include the 3d viewer to your project. Depending on the project and setup you can do one of the following

  • Import the module to your project by using default import { ... } from "e3d"; (Recommended if the project supports this).
    • Note: with this method you also need to include the e3d.app.css with the method of your choosing. For example by using the css-loader for Webpack
  • Copy the distribution files to your project and include the necessary files in the page manually
<link rel="stylesheet" type="text/css" href="path/to/the/files/e3d.app.css">
<script src="path/to/the/files/e3d.bundle.js"></script>
<!-- e3d.bundle.es5.js is also shipped with the package if es6 is not supported -->

First steps

Everything in the viewer starts with the Widget. The widget is used to initialise and start everything beneath it and will work as sort of a context for the specific runtime. Meaning that it will hold all the data and settings for the context during runtime. After initialisation the Viewer, which can be accessed through widget.viewer, will handle most of the orchestration of what's visible in the screen.

import { Widget } from "e3d";
const widget = new Widget();

Next the widget's render() method will return HTML string that contains all the required elements to initialise and start the viewer. Thus this content needs to be rendered somewhere into the page. After the returned HTML is rendered on to the page, everything else can be initialised by calling afterRender()

container.innerHTML = widget.render();
widget.afterRender();

Now the viewer is ready to use and you can load in your first scene either by calling widget.viewer.loadScene("URL_TO_THE_SCENE_FILE"); or by setting the SCENE_MODEL_URL setting.

Putting it together

import { Widget } from "e3d";

const container = document.querySelector(".e3d-container");
const widget = new Widget();

container.innerHTML = widget.render();
widget.afterRender();

widget.viewer.loadScene("scene_file.babylon");

This is all you need to get the viewer up and running with a scene.

Using settings

The viewer uses settings to control how certain features function and to store the current state of the viewer.

Some settings need to be set before the viewer is started, these settings are marked as not-updateable and setting them after viewer has started is disabled and has no effect. You can set these after creating the widget and before calling afterRender(). Also loading in possibly saved settings (e.g. default camera position) is best done before the initialisation.

The settings can be set directly from the widget using its setSetting(...) method or by calling the SetSetting(...) method from e3d. The latter requires supplying the context as a parameter as it can be called from anywhere.

Getting the current value of a setting works the same way; either by calling getSetting(...) from the widget or GetSetting(...) globally from e3d.

// These two methods are equally valid
SetSetting(widget, Setting.DEBUG_MODE, true);
widget.setSetting(Setting.PERSONAL_ACCESS_TOKEN, "MY_PAT", true);

// NOTE: The last parameter of the widget's setSetting is "notify" which defaults to true. If set to false,
// no notification of the setting being updated is propagated to the viewer thus most likely not having
// the desired effect. The viewer itself uses false in the notify param some times to avoid triggering
// updates that it has already performed due to it triggering the setSetting itself.
// If unsure, just use the global SetSetting which will always trigger the notification

// These two methods are equally valid
GetSetting(widget, Setting.DEBUG_MODE);
widget.getSetting(Setting.PERSONAL_ACCESS_TOKEN);

The viewer itself doesn't touch most of the settings, but may at times modify the following settings

  • When a group selection changes, the following two settings are updated
    • SELECTED_GROUP_ID
    • SELECTED_GROUP_NAME
  • When the custom mode changes, the custom mode setting is updated
    • CUSTOM_MODE
  • When the user clicks on "Save Default Transform" button in the camera module, the following settings are updated
    • CAMERA_DEFAULT_POSITION
    • CAMERA_DEFAULT_TARGET_POSITION

When any one of the above is changed/updated by the viewer, an accompanying event is triggered that can be used to catch the new values. We'll have a look at the events later.

Listening to setting updates

When a setting is updated (and the notify is set to true), each updated triggers a notification that you can listen to. This is done using the SettingsUpdateService in which you can register and unregister listeners for setting updates. The viewer itself uses this service to listen to setting updates and acts accordingly, so the use outside of the viewer itself might be limited as the integration/implementation will most likely be the one actually updating the settings. It does allow for some form of indirection as you can set the settings somewhere in your code and catch the update elsewhere without having to pass anything else around.

To register a listener you can call the static method registerSettingUpdateListener(...) from the service, the method returns a handle to the listener which can be used to unregister the listener.

// The last param (null here) is the context of the callback if needed.
// Useful if you're passing in a method of an object that requires access to "this"
const handle = SettingsUpdateService.registerSettingUpdateListener(widget, (key, value) => {
console.log(`Setting ${key} was updated to value ${value}`);
}, null);

// Later (during destroy for example) unregister the listener
SettingsUpdateService.unregisterSettingUpdateListener(widgete, handle);

Persistent settings

The viewer also uses something referred to as "persistent settings", which are intended to be user/browser specific settings that are stored throughout sessions. Currently the GUI modules and wanted camera type are stored and loaded using the persistent settings but each implementation is responsible to do the actual storing and loading.

To implement the storing and loading, you need to call ImplSetPlatformPersistentSetting and ImplGetPlatformPersistentSetting which take in the context (widget) and a function that does the storing/loading. Easiest way to store these for the user is to use localStorage, but if localStorage is not supported, other ways can be implemented as well.

ImplGetPlatformPersistentSetting(widget, (key) =>
{
// We're using the widget's elementId as an additional identifier here
// Thus allowing separate configuration for multiple view(s/ers) in the same origin
return localStorage.getItem(`${widget.elementId}_${key}`);
});
ImplSetPlatformPersistentSetting(widget, (key, value) =>
{
localStorage.setItem(`${widget.elementId}_${key}`, value);
});

If the getter and/or setter for persistent settings are not implemented, the viewer will still work but they will not be utilised.

List of available settings

Below is a table of all available settings. The Updateable column defines whether the setting is updateable after startup.

The keys are available under the Setting enum.

 KeyDescriptionTypeDefault valueUpdateable
TRANSITION_TO_DEFAULT_ON_SCENE_LOADIf set to true, camera will transition to default position after scene has finished loadingBooleantruetrue
CAMERA_FOLLOW_OBJECTSIf set to true, camera will follow moving objects (dynamic objects)Booleanfalsefalse
CAMERA_FOLLOW_ROTATIONIf set to true, camera will act as if it was parented to the moving object. This value depends on CameraFollowObjectsBooleanfalsefalse
CAMERA_DEFAULT_POSITIONDefault camera position, gets updated when UpdateCameraDefaultTransform is calledVector15, 15, 15true
CAMERA_DEFAULT_TARGET_POSITIONDefault camera target position, gets updated when UpdateCameraDefaultTransform is calledVector0, 0, 0true
CAMERA_PATH_SPEEDCamera path animation speed in units per secondFloat2true
CAMERA_PANNING_SENSITIVITYCamera panning sensitivity, smaller value will make the camera pan fasterFloat0.5true
CAMERA_ANGULAR_SENSITIVITYCamera angular sensitivity, smaller value will make the camera turn fasterInteger500true
CAMERA_MIN_RADIUSMinimum radius/distance from the focus point. Used for arc ball rotate camera (default camera). Set to <0 for no limitFloatIntegertrue
CAMERA_MAX_RADIUSMaximum radius/distance from the focus point. Used for arc ball rotate camera (default camera). Set to <0 for no limitFloat-1true
USE_MESH_DEFAULT_CAMERA_FOCUSWhether to use default focus points for meshes if mesh specific focus data is not specified.Booleanfalsetrue
MESH_DEFAULT_FOCUS_OFFSETDefault camera focus offset if no focus data is specifiedVector-2, 6, -7true
MESH_DEFAULT_FOCUS_TARGET_OFFSETDefault camera focus target offset if no focus data is specifiedVector0, 0, 0true
CUSTOM_MODETracks the current custom mode of the viewer.StringNonefalse
DYNAMIC_OBJECT_UNIT_SCALEUnit scale used in dynamic object positioning, all position values are multiplied by this valueVector1, 1, 1true
DYNAMIC_OBJECT_UNIT_OFFSETUnit offset used in dynamic object positioning, this value is added to all position values after scalingVector0, 0, 0true
DYNAMIC_OBJECT_IS_GEO_LOCATEDWhether position for a dynamic object is given as geo coordinatesBooleanfalsetrue
CANVAS_RENDERING_WIDTHMaximum rendering canvas width. Needs "LimitCanvasSize" to be set to true. NOTE: The canvas will still be stretched to fit the space, reducing this will only affect the internal render size which can improve performance for the cost of quality.Integer1920false
CANVAS_RENDERING_HEIGHTMaximum rendering canvas height. Needs "LimitCanvasSize" to be set to true. NOTE: The canvas will still be stretched to fit the space, reducing this will only affect the internal render size which can improve performance for the cost of quality."Integer1080false
LIMIT_CANVAS_SIZEWhen true, the rendering canvas width and height will be limited according to configuration.Booleanfalsefalse
MAX_FPSMaximum rendered frames per secondInteger144false
SHOW_PERFORMANCE_TIMINGSShow rendering related timingsBooleanfalsefalse
CONSTANTLY_UPDATE_VIEWERForce update viewer even if nothing's happeningBooleanfalsefalse
DEBUG_MODEEnable debug features and extra loggingBooleanfalsefalse
ENABLE_OFFLINE_SUPPORTEnable offline support, e.g. 3d model caching using client browser cache (.manifest files)Booleantruefalse
ENABLE_BACKGROUND_GRADIENTEnable background gradientBooleantruetrue
SHOW_ONLY_SELECTED_GROUPShows only the selected groupBooleantruefalse
OBJECT_DEFAULT_PICKABLEAre objects pickable by default.Booleanfalsefalse
ATTACHED_LABELS_DEFINE_PICKABLESIf set to true, only objects with attached labels will be pickable.Booleanfalsefalse
SCENE_NAMEUsually the name of the factory, used in various non-critical parts of the widget.Stringtrue
SCENE_MODEL_URLURL to the modelStringtrue
SCENE_MODEL_FILE_SIZEDownload size of the modelIntegerBooleantrue
RESET_SCENE_ON_MODEL_CHANGEReset the scene whenever the model changesBooleantruefalse
SCENE_BACKGROUND_COLOURThe background of the widget. Opacity is supportedColour#b1b1b1fftrue
REFERENCE_SCALEReference scale for the scene. Will be used to scale things like geo objects, asset library place holders, camera min and max z etc.FloatIntegertrue
GEO_FENCE_BASE_FONT_SIZEBase font size for the text in geo fences.Integer16true
GEO_SCENE_UNITSHow scene units correspond to real-world unitsInteger3false
SORT_COMPONENTS_IN_LABELSort components in a label by their typeBooleanfalsefalse
COMPONENT_FONT_COLOUR_FIELDWhich style field to use for colouring the component text/valueStringtextColourfalse
LABEL_STYLE_ICONSIZEGlobal label icon size (px) used in label stylingInteger25false
LABEL_ATLAS_SIZESize (width and height) of a label atlasInteger1024false
DYNAMIC_LIGHTINGUse dynamic lighting instead of unlit sceneBooleanfalsefalse
GLOBAL_LIGHT_INTENSITYUsed only if dynamic lighting is enabledFloat0.75true
ENVIRONMENT_TEXTUREEnvironment texture, useful for scenes using PBR materialsStringtrue
ENABLE_LOGGING_LIMITWhether to enable logging limiting or notBooleantruetrue
MIN_DURATION_BETWEEN_SAME_MESSAGEMinimum duration between two equal messages in ms. Only applies if EnableLoggingLimit is trueInteger1000true
OBJECT_COLOR_INTENSITYObject overlay color intensity, "intensity" can be also set with color alpha valueFloat0.5false
SELECTED_GROUP_IDThe id of the currently selected group in the sceneStringtrue
SELECTED_GROUP_NAMEThe name of the currently selected group in the sceneStringtrue
SELECTED_OBJECT_IDThe currently selected object in the sceneStringtrue
SELECTED_LABEL_IDThe currently selected label id, triggers changed eventStringtrue
SELECTED_MESH_COLOURColour of the outline of a selected meshColour#7ed8fffalse
HOVERED_MESH_COLOURColour of the outline of a selected meshColour#f9ef6bfalse
SELECTED_GROUP_COLOURColour of the outline of a selected mesh from group selectionColour#00a950false
PERSONAL_ACCESS_TOKENPersonal access token to add to scene requestsStringtrue
ENABLE_GAEnable Google AnalyticsBooleantruefalse

Using data

For the more "complex" configuration of the viewer, data is used. The type of data that can be supplied to the viewer varies and some are more meant for building/editing of the scene where as others are used for presenting data in the 3d view.

Setting data

Similar to the settings, data can be set directly from the widget using its setData(...) method or by calling the SetData(...) method from e3d.

Setting the data is a destructive operation in that it will overwrite any existing data. Meaning that if you set label data for example, the full payload needs to be provided each time for the labels to persist.

Getting the current value for a data works the same way; either by calling getData(...) from the widget or GetData(...) globally from e3d.

Each data has a specific format it needs to be supplied in for the data to display as expected.

We'll have a look at how to construct each data payload in a later chapter.

Updating data

Partial data updates are currently WIP and initial support is available for label data.

To perform an update on the dataset you can call the widget's updateData(...) method or by calling the UpdateData(...) method from e3d.

The update method tries to find a corresponding entry in the current dataset and update its values accordingly.

List of available data

Below is a table of all available data fields for the viewer.

The keys are available under the Data enum.

KeyDescriptionExpected input
ASSET_LIBRARYAsset library data is used to initialise the asset library with all the available assets a user can place to the scene. NOTE: Requires Capabilities.EDITING to be set.
AssetLibraryData[]
CAMERA_FOCUSCamera focus data defines focus points for specific meshes in the scene. Allowing the camera to automatically transition to a desired view when a mesh is selected.
CameraFocusData[]
CAMERA_PATHCamera path data defines a set of points and directions that the camera will fly through when flythrough is triggered.
CameraPathData[]
DYNAMIC_OBJECTDynamic object data is used to present "dynamic" objects in the scene. After initialisation, any concurrent updates will move and rotate the objects from their previous locations to new ones.
DynamicObjectData[]
GEOAn object that contains arrays for both the geo markers and geo fences. Geo markers are used as reference points in the scene to transform scene positions to real world coordinates. The fences are then defined using coordinates. If no markers are defined, the scene origin is considered to be lat: 0, lon: 0, el: 0. For the most accurate results use three markers in which case barycentric coordinates are used to interpolate the coordinates.
GeoData
GROUPGroup data defines grouping of nodes (either groups or meshes in the scene). While nested groups are supported, any given node can directly belong only to a single group.
GroupData[]
LABELLabel data is used to define some more specific data about one or more meshes in the scene. Can be attached to a single mesh or a group of meshes.
ILabelData[]
LAYERLayer data allows grouping of meshes to layers which can be highlighted and/or shown/hidden or demand.
LayerData[]
SCENEThe scene data contains all the assets that have been brought in to the scene that are not part of the base file. This is most useful when building a scene and exporting the result to a single baked file should be preferred when presenting.
SceneData[]

Type definitions for each input type can be seen below

// Asset library
interface AssetLibraryData
{
name: string;
url: string;
thumbnail: string;
category: string;
tag: string;
}

// Camera focus
interface CameraFocusData
{
id: string;
relativePosition: Array<number>;
relativeTargetPosition: Array<number>;
}

// Camera path
interface CameraPathData
{
position: Array<number>;
direction: Array<number>;
}

// Coordinate, used by DynamicObjectData and GeoData
interface Coordinate
{
latitude: number;
longitude: number;
elevation: number;
}

// Dynamic object
interface DynamicObjectData
{
id: string;
url: string;
scale: Array<number>;
rotation: Array<number>;
position: Array<number> | Coordinate;
}

// Geo
interface GeoData
{
markers: GeoMarkerData[];
fences: GeoFenceData[];
}
interface GeoMarkerData
{
coordinates: Coordinate;
scenePosition: Array<number>;
}
interface GeoFenceData
{
name: string;
colour: string;
coordinates: Coordinate[];
}

// Group
interface GroupData
{
name: string;
id: string;
nodes: Array<string>;
}

// Label
interface ILabelData
{
id: string;
title?: string;
isGroupLabel?: boolean;
collapsedTitle?: string;
group?: string;
meshId?: string;
objectButtonText?: string;
layout?: LABEL_LAYOUT;
status?: Style;
components?: ComponentData[];
}

// Layer
interface LayerData
{
layerName: string;
meshIds: string[];
visibility: boolean;
dataDefinesOnlyVisibility?: boolean;
}

// Scene
type MeshNameMapping = { [origName: string]: string };
export interface SceneData
{
name: string;
meshNameMap: MeshNameMapping;
position: Array<number>;
scale: Array<number>;
rotation: Array<number>;
modelName: string;
url: string;
}

Using events

The viewer has events that are triggered when something in the viewer happens. Events can be listened to or propagated forward if wanted. The propagation and listening can be accessed using the EventDispatcher.

To register a listener for an event use registerEventListener(...) to register the listener, this method will return a handle that can later be used to unregister the listener using unregisterEventListener(...).

If you wish to propagate all events forward you can register an event dispatcher using the registerEventDispatcher(...) method. This callback will be given the event name and related arguments for every single event triggered in the context.

// To add a listener to an event, you can use the following method (shown its signature)
// Where eventName is the event to listen to
// callback is a function that will be called when the event happens
// owner is the owner of the callback function (if need be), can be set to window or null if the function's just generic
EventDispatcher.registerEventListener(ctx: ElemIdOrObject, eventName: EventName, callback: Function, owner: any): number

// If you want to propagate the events further (e.g. call something when any even occurs)
// You can use the EventDispatcher.registerEventDispatcher method to register an event dispatcher
EventDispatcher.registerEventDispatcher(ctx: ElemIdOrObject, dispatcher: Function, owner: any): number

// Both of the above methods return a handle to the listener/dispatcher which can be used to unregister
// from the events if wanted/need be with the following methods
EventDispatcher.unregisterEventDispatcher(ctx: ElemIdOrObject, handle: number)
EventDispatcher.unregisterEventListener(ctx: ElemIdOrObject, eventName: EventName, handle: number)

Simple example

In the example below, we register an event listener for the export ready event and download the result as a zip using BABYLON's Download tool.

import { EventDispatcher, EventName } from "e3d";
EventDispatcher.registerEventListener(widget, EventName.EXPORT_READY, (content: Blob, name: string) =>
{
BABYLON.Tools.Download(content, `${name}.zip`);
}, null);

A list of available events

The constants can be accessed under EventName. The event arguments can be caught within the callback

ConstantDescriptionEvent arguments
Asset library events
SCENE_DATA_UPDATEDTriggered when scene data has been updateddata: SceneData[]
Camera events
CAMERA_TRANSITION_STOPPEDTriggered when a camera transition is stopped-
CAMERA_TRANSITION_DONETriggered when a camera transition finishes-
CAMERA_TRANSITION_TO_MESH_DONETriggered when a camera transition to a mesh is finished. If the mesh is null, no transition happened`mesh: BABYLON.Mesh
CAMERA_TRANSITION_TO_HOME_DONETriggered when a camera transition to home is finished-
CAMERA_PATH_UPDATEDTriggered when the camera flythrough path has been updateddata: CameraPathData[]
CAMERA_TRANSFORM_UPDATEDTriggered when a camera focus data has been updateddata: CameraFocusData[]
CAMERA_DEFAULT_TRANSFORM_UPDATEDTriggered when the default transform positions have been updatedposition: number[], target: number[]
Geo events
GEO_DATA_UPDATEDTriggered when geo data has been updateddata: GeoData
Group events
GROUP_HOVEREDTriggered when a group is hoveredgroup: GroupLabel
GROUP_CLICKEDTriggered when a group is clicked (either from a label or from home menu)group: GroupLabel or null
SCENE_GROUP_DATA_INITIALISEDTriggered once the viewer has initialised group data from the .babylon file-
Label events
LABEL_HOVEREDTriggered when a label is hoveredlabel: Label
LABEL_CLICKEDTriggered when a label is clickedlabel: Label
Layer events
LAYER_DATA_UPDATEDTriggered when layer data has been updateddata: LayerData[]
SCENE_LAYER_DATA_INITIALISEDTriggered once the viewer has initialised layer data from the .babylon file-
Object events
OBJECT_HOVEREDTriggered when an object has been hoveredmesh: BABYLON.Mesh
OBJECT_CLICKEDTriggered when an object has been clickedmesh: BABYLON.Mesh
Scene/mesh
MODEL_LOAD_DONETriggered when a model (scene) load succeeds and finishes-
MODEL_LOAD_ERRORTriggered when a model (scene) load fails-
MESH_LOAD_DONETriggered when a mesh load succeeds and finishes-
MESH_LOAD_ERRORTriggered when a mesh load fails-
Selections
SELECTED_LABEL_CHANGEDTriggered when a new label has been selectedlabel: UiElement
SELECTED_OBJECT_CHANGEDTriggered when a new object has been selectedobjectId: string
SELECTED_GROUP_CHANGEDTriggered when a new group has been selectedgroupId: string, groupName: string
Misc
HOME_CLICKEDTriggered when the home button is clicked-
CUSTOM_MODE_CHANGEDTriggered when a custom mode changescustomMode: CUSTOM_MODE
SCREENSHOT_CAPTUREDTriggered once the screenshot capture is finished after requestdata: string
HYPERLINK_COMPONENT_CLICKEDTriggered when a hyperlink component has been clickedid: string, parentId: string
BUTTON_COMPONENT_CLICKEDTriggered when a button component has been clickedid: string, value: string, parentId: string
EXPORT_READYTriggered after an export has finishedzipData: Blob, sceneName: string
EXPORT_FAILEDTriggered if an export failserrorMsg: string

Adding localisation support

All strings in the viewer are localisable (with some limitations on "dynamic" texts, texts that are a combination of multiple). Localisation is supported through the usage of tokens

The viewer supports simple localisation using tokens. To support localisation, implement a localisation function for the widget before viewer is initialised using ImplLocalisationFunction. You preferably return the default value if no localisation exists for a given token.

// Simple example of what the localisation implementation could be like
ImplLocalisationFunction(function (token: string, defaultValue: string): string
{
return PlatformSpecificLocalisationFunction(token) || defaultValue;
});

Some variable fields like label and component titles are also passed through the localisation function.

A list of currently used tokens and their default values

Token Default value
e3d.export.invalidExportTypeInvalid export type requested
e3d.export.noEmptyNameAllowedExport needs to have a name defined
e3d.generic.allAll
e3d.generic.alphaalpha
e3d.generic.blueblue
e3d.generic.colourColour
e3d.generic.disableDisable
e3d.generic.elevationElevation
e3d.generic.enableEnable
e3d.generic.greengreen
e3d.generic.hideHide
e3d.generic.latitudeLatitude
e3d.generic.longitudeLongitude
e3d.generic.nameName
e3d.generic.noneNone
e3d.generic.notAvailableN/A
e3d.generic.positionPosition
e3d.generic.redred
e3d.generic.searchSearch
e3d.generic.showShow
e3d.generic.unsortedUnsorted
e3d.groups.newGroupNew Group
e3d.input.combobox.noMatchingSuggestionsNo suggestions
e3d.layers.newLayerNew Layer
e3d.loading.changingSceneChanging scene
e3d.loading.clearingSceneClearing scene
e3d.loading.completeLoading complete
e3d.loading.downloadedDownloaded
e3d.loading.initialisingPropertiesInitializing viewer settings
e3d.loading.loadingSceneLoading scene
e3d.loading.scriptsLoading scripts
e3d.loading.settingUpSetting up scene
e3d.loading.waitingForSceneWaiting for scene
e3d.module.assetLibrary.activateEditModeInf Activate Edit Mode to Add or Move Objects
e3d.module.assetLibrary.noAssetsNo assets
e3d.module.assetLibrary.titleAsset Library
e3d.module.atlasDebugger.atlasesAtlases
e3d.module.atlasDebugger.titleLabel atlas debugger
e3d.module.camera.addWaypointAdd Waypoint
e3d.module.camera.flythroughPlay Flythrough
e3d.module.camera.focusObjectFocus on the Object
e3d.module.camera.focusPointsFocus Points
e3d.module.camera.modeMode
e3d.module.camera.orbitOrbit Camera
e3d.module.camera.removeWaypointRemove Waypoint
e3d.module.camera.rtsMaxHeightMaximum Height
e3d.module.camera.rtsMinHeightMinimum Height
e3d.module.camera.saveDefaultTransformSave Default Transform
e3d.module.camera.saveFocusPointSave Focus point
e3d.module.camera.saveWaypointsSave Waypoints
e3d.module.camera.titleCamera
e3d.module.camera.topDownTop Down Camera
e3d.module.camera.waypointsWaypoints
e3d.module.dev.attachTestLabelAttach test label
e3d.module.dev.decGlobalLightIntensityDecrease global light intensity
e3d.module.dev.downloadSnapshotDownload snapshot
e3d.module.dev.exportSceneExport scene
e3d.module.dev.fakeXREntryAndLeaveFake XR entry and leave
e3d.module.dev.geoMarkerTestGeomarker test
e3d.module.dev.incCinematicCamSpeedIncrease CinematicCamera speed
e3d.module.dev.incGlobalLightIntensityIncrease global light intensity
e3d.module.dev.labelTestLabel test
e3d.module.dev.logObjCoordsLog selected obj coords
e3d.module.dev.openInspectorOpen inspector
e3d.module.dev.reduceCinematicCamSpeedReduce CinematicCamera speed
e3d.module.dev.titleDevelopment tools
e3d.module.dev.transitionToDefaultTransition camera to default
e3d.module.dev.updateCameraDefaultTransformUpdate camera default transform
e3d.module.dev.updateCameraTransformUpdate camera transform
e3d.module.editLayer.addObjectsToLayerAdd Selected Objects to the Layer
e3d.module.editLayer.createLayerCreate New Layer
e3d.module.editLayer.deleteLayerDelete Layer
e3d.module.editLayer.selectedLayerSelected layer
e3d.module.editLayer.titleEdit Layers
e3d.module.export.babylonExport .babylon
e3d.module.export.customModeWarnCan't export while a custom mode is enabled
e3d.module.export.exportingExporting
e3d.module.export.exportNameExport name
e3d.module.export.filterFailedFilter for export name failed
e3d.module.export.finishedExport finished
e3d.module.export.missingFileNameExport missing filename
e3d.module.export.objExport .obj
e3d.module.export.objUpDirOBJ Up Direction
e3d.module.export.readyReady to export
e3d.module.export.titleExport
e3d.module.geo.addAreaAdd Area
e3d.module.geo.addMarkerAdd Geomarker
e3d.module.geo.editorGeo Editor
e3d.module.geo.removeAreaRemove Area
e3d.module.geo.removeMarkerRemove Geomarker
e3d.module.geo.selectedControlPointSelected Control Point
e3d.module.geo.selectedFenceSelected Geo Fence
e3d.module.geo.titleGeo
e3d.module.heatmap.enableCustomThresholdsEnable Custom Thresholds
e3d.module.heatmap.titleHeatmaps
e3d.module.home.homeButtonHome
e3d.module.home.nGroupsSelected%d groups selected
e3d.module.home.nObjectsSelected%d objects selected
e3d.module.home.noGroupSelectedNo group selected
e3d.module.home.noObjectSelectedNo object selected
e3d.module.home.titleScene
e3d.module.info.cameraCamera
e3d.module.info.directionDirection
e3d.module.info.fpsFPS
e3d.module.info.hoveredMeshHovered mesh
e3d.module.info.memoryMemory
e3d.module.info.noMeshSelectedNo mesh selected
e3d.module.info.positionPosition
e3d.module.info.resolutionResolution
e3d.module.info.selectedMeshidSelected mesh id
e3d.module.info.selectedMeshNameSelected mesh name
e3d.module.info.selectedMeshPosSelected mesh position
e3d.module.info.titleInfo
e3d.module.info.totalLabelsTotal labels
e3d.module.info.versionVersion
e3d.module.info.visibleLabelsVisible labels
e3d.module.label.noLabelSelectedNo label selected
e3d.module.label.noLabelSelectedNo label selected
e3d.module.label.titleLabel
e3d.module.log.titleLog
e3d.module.modifier.addModifierAdd modifier
e3d.module.modifier.titleModifiers
e3d.module.outliner.addObjectsToGroupAdd Selected Objects to Active Group
e3d.module.outliner.addObjectsToGroupDelete Group
e3d.module.outliner.newGroupCreate New Group
e3d.module.outliner.titleOutliner
e3d.module.refImage.clearClear Floorplan
e3d.module.refImage.dndHintDrag and drop a reference image here
e3d.module.refImage.imageOpacityHintImage Opacity (%)
e3d.module.refImage.imageWidthHintImage Width (m)
e3d.module.refImage.readyReady for drop
e3d.module.refImage.refImageFloorplan
e3d.module.refImage.rotationRotation
e3d.module.refImage.titleReference Image
e3d.module.sceneEdit.alignYAlign Y
e3d.module.sceneEdit.alignZAlign Z
e3d.module.sceneEdit.aligXAlign X
e3d.module.sceneEdit.deleteDelete
e3d.module.sceneEdit.duplicateDuplicate
e3d.module.sceneEdit.editModeEdit Mode
e3d.module.sceneEdit.positionPosition
e3d.module.sceneEdit.rotateRotate
e3d.module.sceneEdit.rotationRotation
e3d.module.sceneEdit.saveSave Changes
e3d.module.sceneEdit.scaleScale
e3d.module.sceneEdit.scalingScaling
e3d.module.sceneEdit.snapRotationSnap Rotation to 15 degrees
e3d.module.sceneEdit.titleScene Editing
e3d.module.sceneEdit.translateTranslate
e3d.module.theme.blueBlue
e3d.module.theme.defaultDefault
e3d.module.theme.lightLight
e3d.module.theme.titleTheme
e3d.module.viewLayer.noLayersNo layers
e3d.module.viewLayer.titleView Layers
e3d.module.webxr.enterVREnter VR
e3d.module.webxr.leaveVRLeave VR
e3d.module.webxr.notSupportedWebXR not supported
e3d.module.webxr.sslOnlyWebXR can only be served over HTTPS
e3d.module.webxr.titleWebXR
e3d.module.zoneProfiler.titleZone Profiler
e3d.module.zoneProfiler.zoneProfilerZone Profiler